Stardew Valley 6.x.x: The Content Update (#3478)
Focus of the Update: Compatibility with Stardew Valley 1.6 Released on March 19th 2024 This includes randomization for pretty much all of the new content, including but not limited to - Raccoon Bundles - Booksanity - Skill Masteries - New Recipes, Craftables, Fish, Maps, Farm Type, Festivals and Quests This also includes a significant reorganisation of the code into "Content Packs", to allow for easier modularity of various game mechanics between the settings and the supported mods. This improves maintainability quite a bit. In addition to that, a few **very** requested new features have been introduced, although they weren't the focus of this update - Walnutsanity - Player Buffs - More customizability in settings, such as shorter special orders, ER without farmhouse - New Remixed Bundles
This commit is contained in:
parent
f99ee77325
commit
9b22458f44
|
@ -1,12 +1,13 @@
|
|||
import logging
|
||||
from typing import Dict, Any, Iterable, Optional, Union, List, TextIO
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState
|
||||
from Options import PerGameCommonOptions
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from . import rules
|
||||
from .bundles.bundle_room import BundleRoom
|
||||
from .bundles.bundles import get_all_bundles
|
||||
from .content import content_packs, StardewContent, unpack_content, create_content
|
||||
from .early_items import setup_early_items
|
||||
from .items import item_table, create_items, ItemData, Group, items_by_group, get_all_filler_items, remove_limited_amount_packs
|
||||
from .locations import location_table, create_locations, LocationData, locations_by_tag
|
||||
|
@ -14,16 +15,17 @@ from .logic.bundle_logic import BundleLogic
|
|||
from .logic.logic import StardewLogic
|
||||
from .logic.time_logic import MAX_MONTHS
|
||||
from .option_groups import sv_option_groups
|
||||
from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, BundlePrice, NumberOfLuckBuffs, NumberOfMovementBuffs, \
|
||||
BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization
|
||||
from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, BundlePrice, EnabledFillerBuffs, NumberOfMovementBuffs, \
|
||||
BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization, FarmType, Walnutsanity
|
||||
from .presets import sv_options_presets
|
||||
from .regions import create_regions
|
||||
from .rules import set_rules
|
||||
from .stardew_rule import True_, StardewRule, HasProgressionPercent
|
||||
from .stardew_rule import True_, StardewRule, HasProgressionPercent, true_
|
||||
from .strings.ap_names.event_names import Event
|
||||
from .strings.entrance_names import Entrance as EntranceName
|
||||
from .strings.goal_names import Goal as GoalName
|
||||
from .strings.region_names import Region as RegionName
|
||||
from .strings.metal_names import Ore
|
||||
from .strings.region_names import Region as RegionName, LogicRegion
|
||||
|
||||
client_version = 0
|
||||
|
||||
|
@ -77,6 +79,7 @@ class StardewValleyWorld(World):
|
|||
|
||||
options_dataclass = StardewValleyOptions
|
||||
options: StardewValleyOptions
|
||||
content: StardewContent
|
||||
logic: StardewLogic
|
||||
|
||||
web = StardewWebWorld()
|
||||
|
@ -94,6 +97,7 @@ class StardewValleyWorld(World):
|
|||
|
||||
def generate_early(self):
|
||||
self.force_change_options_if_incompatible()
|
||||
self.content = create_content(self.options)
|
||||
|
||||
def force_change_options_if_incompatible(self):
|
||||
goal_is_walnut_hunter = self.options.goal == Goal.option_greatest_walnut_hunter
|
||||
|
@ -106,6 +110,11 @@ class StardewValleyWorld(World):
|
|||
player_name = self.multiworld.player_name[self.player]
|
||||
logging.warning(
|
||||
f"Goal '{goal_name}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})")
|
||||
if exclude_ginger_island and self.options.walnutsanity != Walnutsanity.preset_none:
|
||||
self.options.walnutsanity.value = Walnutsanity.preset_none
|
||||
player_name = self.multiworld.player_name[self.player]
|
||||
logging.warning(
|
||||
f"Walnutsanity requires Ginger Island. Ginger Island was excluded from {self.player} ({player_name})'s world, so walnutsanity was force disabled")
|
||||
|
||||
def create_regions(self):
|
||||
def create_region(name: str, exits: Iterable[str]) -> Region:
|
||||
|
@ -115,9 +124,10 @@ class StardewValleyWorld(World):
|
|||
|
||||
world_regions, world_entrances, self.randomized_entrances = create_regions(create_region, self.random, self.options)
|
||||
|
||||
self.logic = StardewLogic(self.player, self.options, world_regions.keys())
|
||||
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)
|
||||
|
||||
def add_location(name: str, code: Optional[int], region: str):
|
||||
|
@ -125,11 +135,12 @@ class StardewValleyWorld(World):
|
|||
location = StardewLocation(self.player, name, code, region)
|
||||
region.locations.append(location)
|
||||
|
||||
create_locations(add_location, self.modified_bundles, self.options, self.random)
|
||||
create_locations(add_location, self.modified_bundles, self.options, self.content, self.random)
|
||||
self.multiworld.regions.extend(world_regions.values())
|
||||
|
||||
def create_items(self):
|
||||
self.precollect_starting_season()
|
||||
self.precollect_farm_type_items()
|
||||
items_to_exclude = [excluded_items
|
||||
for excluded_items in self.multiworld.precollected_items[self.player]
|
||||
if not item_table[excluded_items.name].has_any_group(Group.RESOURCE_PACK,
|
||||
|
@ -143,7 +154,7 @@ class StardewValleyWorld(World):
|
|||
for location in self.multiworld.get_locations(self.player)
|
||||
if location.address is not None])
|
||||
|
||||
created_items = create_items(self.create_item, self.delete_item, locations_count, items_to_exclude, self.options,
|
||||
created_items = create_items(self.create_item, self.delete_item, locations_count, items_to_exclude, self.options, self.content,
|
||||
self.random)
|
||||
|
||||
self.multiworld.itempool += created_items
|
||||
|
@ -173,10 +184,15 @@ class StardewValleyWorld(World):
|
|||
starting_season = self.create_starting_item(self.random.choice(season_pool))
|
||||
self.multiworld.push_precollected(starting_season)
|
||||
|
||||
def precollect_farm_type_items(self):
|
||||
if self.options.farm_type == FarmType.option_meadowlands and self.options.building_progression & BuildingProgression.option_progressive:
|
||||
self.multiworld.push_precollected(self.create_starting_item("Progressive Coop"))
|
||||
|
||||
def setup_player_events(self):
|
||||
self.setup_construction_events()
|
||||
self.setup_quest_events()
|
||||
self.setup_action_events()
|
||||
self.setup_logic_events()
|
||||
|
||||
def setup_construction_events(self):
|
||||
can_construct_buildings = LocationData(None, RegionName.carpenter, Event.can_construct_buildings)
|
||||
|
@ -187,10 +203,26 @@ class StardewValleyWorld(World):
|
|||
self.create_event_location(start_dark_talisman_quest, self.logic.wallet.has_rusty_key(), Event.start_dark_talisman_quest)
|
||||
|
||||
def setup_action_events(self):
|
||||
can_ship_event = LocationData(None, RegionName.shipping, Event.can_ship_items)
|
||||
self.create_event_location(can_ship_event, True_(), Event.can_ship_items)
|
||||
can_ship_event = LocationData(None, LogicRegion.shipping, Event.can_ship_items)
|
||||
self.create_event_location(can_ship_event, true_, Event.can_ship_items)
|
||||
can_shop_pierre_event = LocationData(None, RegionName.pierre_store, Event.can_shop_at_pierre)
|
||||
self.create_event_location(can_shop_pierre_event, True_(), Event.can_shop_at_pierre)
|
||||
self.create_event_location(can_shop_pierre_event, true_, Event.can_shop_at_pierre)
|
||||
|
||||
spring_farming = LocationData(None, LogicRegion.spring_farming, Event.spring_farming)
|
||||
self.create_event_location(spring_farming, true_, Event.spring_farming)
|
||||
summer_farming = LocationData(None, LogicRegion.summer_farming, Event.summer_farming)
|
||||
self.create_event_location(summer_farming, true_, Event.summer_farming)
|
||||
fall_farming = LocationData(None, LogicRegion.fall_farming, Event.fall_farming)
|
||||
self.create_event_location(fall_farming, true_, Event.fall_farming)
|
||||
winter_farming = LocationData(None, LogicRegion.winter_farming, Event.winter_farming)
|
||||
self.create_event_location(winter_farming, true_, Event.winter_farming)
|
||||
|
||||
def setup_logic_events(self):
|
||||
def register_event(name: str, region: str, rule: StardewRule):
|
||||
event_location = LocationData(None, region, name)
|
||||
self.create_event_location(event_location, rule, name)
|
||||
|
||||
self.logic.setup_events(register_event)
|
||||
|
||||
def setup_victory(self):
|
||||
if self.options.goal == Goal.option_community_center:
|
||||
|
@ -211,7 +243,7 @@ class StardewValleyWorld(World):
|
|||
Event.victory)
|
||||
elif self.options.goal == Goal.option_master_angler:
|
||||
self.create_event_location(location_table[GoalName.master_angler],
|
||||
self.logic.fishing.can_catch_every_fish_in_slot(self.get_all_location_names()),
|
||||
self.logic.fishing.can_catch_every_fish_for_fishsanity(),
|
||||
Event.victory)
|
||||
elif self.options.goal == Goal.option_complete_collection:
|
||||
self.create_event_location(location_table[GoalName.complete_museum],
|
||||
|
@ -270,18 +302,13 @@ class StardewValleyWorld(World):
|
|||
if override_classification is None:
|
||||
override_classification = item.classification
|
||||
|
||||
if override_classification == ItemClassification.progression and item.name != Event.victory:
|
||||
if override_classification == ItemClassification.progression:
|
||||
self.total_progression_items += 1
|
||||
# if item.name not in self.all_progression_items:
|
||||
# self.all_progression_items[item.name] = 0
|
||||
# self.all_progression_items[item.name] += 1
|
||||
return StardewItem(item.name, override_classification, item.code, self.player)
|
||||
|
||||
def delete_item(self, item: Item):
|
||||
if item.classification & ItemClassification.progression:
|
||||
self.total_progression_items -= 1
|
||||
# if item.name in self.all_progression_items:
|
||||
# self.all_progression_items[item.name] -= 1
|
||||
|
||||
def create_starting_item(self, item: Union[str, ItemData]) -> StardewItem:
|
||||
if isinstance(item, str):
|
||||
|
@ -299,7 +326,11 @@ class StardewValleyWorld(World):
|
|||
location = StardewLocation(self.player, location_data.name, None, region)
|
||||
location.access_rule = rule
|
||||
region.locations.append(location)
|
||||
location.place_locked_item(self.create_item(item))
|
||||
location.place_locked_item(StardewItem(item, ItemClassification.progression, None, self.player))
|
||||
|
||||
# This is not ideal, but the rule count them so...
|
||||
if item != Event.victory:
|
||||
self.total_progression_items += 1
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self)
|
||||
|
@ -358,7 +389,7 @@ class StardewValleyWorld(World):
|
|||
quality = ""
|
||||
else:
|
||||
quality = f" ({item.quality.split(' ')[0]})"
|
||||
spoiler_handle.write(f"\t\t{item.amount}x {item.item_name}{quality}\n")
|
||||
spoiler_handle.write(f"\t\t{item.amount}x {item.get_item()}{quality}\n")
|
||||
|
||||
def add_entrances_to_spoiler_log(self):
|
||||
if self.options.entrance_randomization == EntranceRandomization.option_disabled:
|
||||
|
@ -373,9 +404,9 @@ 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.item_name}|{item.amount}|{item.quality}"
|
||||
bundles[room.name][bundle.name][i] = f"{item.get_item()}|{item.amount}|{item.quality}"
|
||||
|
||||
excluded_options = [BundleRandomization, NumberOfMovementBuffs, NumberOfLuckBuffs]
|
||||
excluded_options = [BundleRandomization, NumberOfMovementBuffs, EnabledFillerBuffs]
|
||||
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)
|
||||
|
@ -385,7 +416,29 @@ class StardewValleyWorld(World):
|
|||
"seed": self.random.randrange(1000000000), # Seed should be max 9 digits
|
||||
"randomized_entrances": self.randomized_entrances,
|
||||
"modified_bundles": bundles,
|
||||
"client_version": "5.0.0",
|
||||
"client_version": "6.0.0",
|
||||
})
|
||||
|
||||
return slot_data
|
||||
|
||||
def collect(self, state: CollectionState, item: StardewItem) -> bool:
|
||||
change = super().collect(state, item)
|
||||
if change:
|
||||
state.prog_items[self.player][Event.received_walnuts] += self.get_walnut_amount(item.name)
|
||||
return change
|
||||
|
||||
def remove(self, state: CollectionState, item: StardewItem) -> bool:
|
||||
change = super().remove(state, item)
|
||||
if change:
|
||||
state.prog_items[self.player][Event.received_walnuts] -= self.get_walnut_amount(item.name)
|
||||
return change
|
||||
|
||||
@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
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import math
|
||||
from dataclasses import dataclass
|
||||
from random import Random
|
||||
from typing import List
|
||||
from typing import List, Tuple
|
||||
|
||||
from .bundle_item import BundleItem
|
||||
from ..content import StardewContent
|
||||
from ..options import BundlePrice, StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
|
||||
from ..strings.currency_names import Currency
|
||||
|
||||
|
@ -26,7 +28,8 @@ class BundleTemplate:
|
|||
number_possible_items: int
|
||||
number_required_items: int
|
||||
|
||||
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int, number_required_items: int):
|
||||
def __init__(self, room: str, name: str, items: List[BundleItem], number_possible_items: int,
|
||||
number_required_items: int):
|
||||
self.room = room
|
||||
self.name = name
|
||||
self.items = items
|
||||
|
@ -35,17 +38,12 @@ class BundleTemplate:
|
|||
|
||||
@staticmethod
|
||||
def extend_from(template, items: List[BundleItem]):
|
||||
return BundleTemplate(template.room, template.name, items, template.number_possible_items, template.number_required_items)
|
||||
return BundleTemplate(template.room, template.name, items, template.number_possible_items,
|
||||
template.number_required_items)
|
||||
|
||||
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
|
||||
if bundle_price_option == BundlePrice.option_minimum:
|
||||
number_required = 1
|
||||
elif bundle_price_option == BundlePrice.option_maximum:
|
||||
number_required = 8
|
||||
else:
|
||||
number_required = self.number_required_items + bundle_price_option.value
|
||||
number_required = max(1, number_required)
|
||||
filtered_items = [item for item in self.items if item.can_appear(options)]
|
||||
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)
|
||||
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:
|
||||
|
@ -55,6 +53,7 @@ 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]
|
||||
return Bundle(self.room, self.name, chosen_items, number_required)
|
||||
|
||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
||||
|
@ -68,19 +67,13 @@ class CurrencyBundleTemplate(BundleTemplate):
|
|||
super().__init__(room, name, [item], 1, 1)
|
||||
self.item = item
|
||||
|
||||
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
|
||||
currency_amount = self.get_currency_amount(bundle_price_option)
|
||||
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
|
||||
currency_amount = self.get_currency_amount(options.bundle_price)
|
||||
return Bundle(self.room, self.name, [BundleItem(self.item.item_name, currency_amount)], 1)
|
||||
|
||||
def get_currency_amount(self, bundle_price_option: BundlePrice):
|
||||
if bundle_price_option == BundlePrice.option_minimum:
|
||||
price_multiplier = 0.1
|
||||
elif bundle_price_option == BundlePrice.option_maximum:
|
||||
price_multiplier = 4
|
||||
else:
|
||||
price_multiplier = round(1 + (bundle_price_option.value * 0.4), 2)
|
||||
|
||||
currency_amount = int(self.item.amount * price_multiplier)
|
||||
_, price_multiplier = get_bundle_final_prices(bundle_price_option, self.number_required_items, True)
|
||||
currency_amount = max(1, int(self.item.amount * price_multiplier))
|
||||
return currency_amount
|
||||
|
||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
||||
|
@ -95,11 +88,11 @@ class CurrencyBundleTemplate(BundleTemplate):
|
|||
|
||||
class MoneyBundleTemplate(CurrencyBundleTemplate):
|
||||
|
||||
def __init__(self, room: str, item: BundleItem):
|
||||
super().__init__(room, "", item)
|
||||
def __init__(self, room: str, default_name: str, item: BundleItem):
|
||||
super().__init__(room, default_name, item)
|
||||
|
||||
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
|
||||
currency_amount = self.get_currency_amount(bundle_price_option)
|
||||
def create_bundle(self, random: Random, content: StardewContent, options: StardewValleyOptions) -> Bundle:
|
||||
currency_amount = self.get_currency_amount(options.bundle_price)
|
||||
currency_name = "g"
|
||||
if currency_amount >= 1000:
|
||||
unit_amount = currency_amount % 1000
|
||||
|
@ -111,13 +104,8 @@ class MoneyBundleTemplate(CurrencyBundleTemplate):
|
|||
return Bundle(self.room, name, [BundleItem(self.item.item_name, currency_amount)], 1)
|
||||
|
||||
def get_currency_amount(self, bundle_price_option: BundlePrice):
|
||||
if bundle_price_option == BundlePrice.option_minimum:
|
||||
price_multiplier = 0.1
|
||||
elif bundle_price_option == BundlePrice.option_maximum:
|
||||
price_multiplier = 4
|
||||
else:
|
||||
price_multiplier = round(1 + (bundle_price_option.value * 0.4), 2)
|
||||
currency_amount = int(self.item.amount * price_multiplier)
|
||||
_, price_multiplier = get_bundle_final_prices(bundle_price_option, self.number_required_items, True)
|
||||
currency_amount = max(1, int(self.item.amount * price_multiplier))
|
||||
return currency_amount
|
||||
|
||||
|
||||
|
@ -134,30 +122,54 @@ class FestivalBundleTemplate(BundleTemplate):
|
|||
class DeepBundleTemplate(BundleTemplate):
|
||||
categories: List[List[BundleItem]]
|
||||
|
||||
def __init__(self, room: str, name: str, categories: List[List[BundleItem]], number_possible_items: int, number_required_items: int):
|
||||
def __init__(self, room: str, name: str, categories: List[List[BundleItem]], number_possible_items: int,
|
||||
number_required_items: int):
|
||||
super().__init__(room, name, [], number_possible_items, number_required_items)
|
||||
self.categories = categories
|
||||
|
||||
def create_bundle(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions) -> Bundle:
|
||||
if bundle_price_option == BundlePrice.option_minimum:
|
||||
number_required = 1
|
||||
elif bundle_price_option == BundlePrice.option_maximum:
|
||||
number_required = 8
|
||||
else:
|
||||
number_required = self.number_required_items + bundle_price_option.value
|
||||
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)
|
||||
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)
|
||||
chosen_categories = self.categories + random.choices(self.categories,
|
||||
k=number_chosen_categories - number_categories)
|
||||
else:
|
||||
chosen_categories = random.sample(self.categories, number_chosen_categories)
|
||||
|
||||
chosen_items = []
|
||||
for category in chosen_categories:
|
||||
filtered_items = [item for item in category if item.can_appear(options)]
|
||||
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 * 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)
|
||||
return number_required_items, price_multiplier
|
||||
|
||||
|
||||
def get_number_required_items(bundle_price_option: BundlePrice, default_required_items: int) -> int:
|
||||
if bundle_price_option == BundlePrice.option_minimum:
|
||||
return 1
|
||||
if bundle_price_option == BundlePrice.option_maximum:
|
||||
return 8
|
||||
number_required = default_required_items + bundle_price_option.value
|
||||
return min(8, max(1, number_required))
|
||||
|
||||
|
||||
def get_price_multiplier(bundle_price_option: BundlePrice, is_currency: bool) -> float:
|
||||
if bundle_price_option == BundlePrice.option_minimum:
|
||||
return 0.1 if is_currency else 0.2
|
||||
if bundle_price_option == BundlePrice.option_maximum:
|
||||
return 4 if is_currency else 1.4
|
||||
price_factor = 0.4 if is_currency else (0.2 if bundle_price_option.value <= 0 else 0.1)
|
||||
price_multiplier_difference = bundle_price_option.value * price_factor
|
||||
price_multiplier = 1 + price_multiplier_difference
|
||||
return round(price_multiplier, 2)
|
||||
|
|
|
@ -3,7 +3,8 @@ from __future__ import annotations
|
|||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
|
||||
from ..options import StardewValleyOptions, ExcludeGingerIsland, FestivalLocations
|
||||
from ..content import StardewContent
|
||||
from ..options import StardewValleyOptions, ExcludeGingerIsland, FestivalLocations, SkillProgression
|
||||
from ..strings.crop_names import Fruit
|
||||
from ..strings.currency_names import Currency
|
||||
from ..strings.quality_names import CropQuality, FishQuality, ForageQuality
|
||||
|
@ -30,27 +31,50 @@ class FestivalItemSource(BundleItemSource):
|
|||
return options.festival_locations != FestivalLocations.option_disabled
|
||||
|
||||
|
||||
class MasteryItemSource(BundleItemSource):
|
||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
||||
return options.skill_progression == SkillProgression.option_progressive_with_masteries
|
||||
|
||||
|
||||
class ContentItemSource(BundleItemSource):
|
||||
"""This is meant to be used for items that are managed by the content packs."""
|
||||
|
||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
||||
raise ValueError("This should not be called, check if the item is in the content instead.")
|
||||
|
||||
|
||||
@dataclass(frozen=True, order=True)
|
||||
class BundleItem:
|
||||
class Sources:
|
||||
vanilla = VanillaItemSource()
|
||||
island = IslandItemSource()
|
||||
festival = FestivalItemSource()
|
||||
masteries = MasteryItemSource()
|
||||
content = ContentItemSource()
|
||||
|
||||
item_name: str
|
||||
amount: int = 1
|
||||
quality: str = CropQuality.basic
|
||||
source: BundleItemSource = Sources.vanilla
|
||||
flavor: str = None
|
||||
can_have_quality: bool = True
|
||||
|
||||
@staticmethod
|
||||
def money_bundle(amount: int) -> BundleItem:
|
||||
return BundleItem(Currency.money, amount)
|
||||
|
||||
def get_item(self) -> str:
|
||||
if self.flavor is None:
|
||||
return self.item_name
|
||||
return f"{self.item_name} [{self.flavor}]"
|
||||
|
||||
def as_amount(self, amount: int) -> BundleItem:
|
||||
return BundleItem(self.item_name, amount, self.quality, self.source)
|
||||
return BundleItem(self.item_name, amount, self.quality, self.source, self.flavor)
|
||||
|
||||
def as_quality(self, quality: str) -> BundleItem:
|
||||
return BundleItem(self.item_name, self.amount, quality, self.source)
|
||||
if self.can_have_quality:
|
||||
return BundleItem(self.item_name, self.amount, quality, self.source, self.flavor)
|
||||
return BundleItem(self.item_name, self.amount, self.quality, self.source, self.flavor)
|
||||
|
||||
def as_quality_crop(self) -> BundleItem:
|
||||
amount = 5
|
||||
|
@ -67,7 +91,11 @@ class BundleItem:
|
|||
|
||||
def __repr__(self):
|
||||
quality = "" if self.quality == CropQuality.basic else self.quality
|
||||
return f"{self.amount} {quality} {self.item_name}"
|
||||
return f"{self.amount} {quality} {self.get_item()}"
|
||||
|
||||
def can_appear(self, content: StardewContent, options: StardewValleyOptions) -> bool:
|
||||
if isinstance(self.source, ContentItemSource):
|
||||
return self.get_item() in content.game_items
|
||||
|
||||
def can_appear(self, options: StardewValleyOptions) -> bool:
|
||||
return self.source.can_appear(options)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ from random import Random
|
|||
from typing import List
|
||||
|
||||
from .bundle import Bundle, BundleTemplate
|
||||
from ..content import StardewContent
|
||||
from ..options import BundlePrice, StardewValleyOptions
|
||||
|
||||
|
||||
|
@ -18,7 +19,25 @@ class BundleRoomTemplate:
|
|||
bundles: List[BundleTemplate]
|
||||
number_bundles: int
|
||||
|
||||
def create_bundle_room(self, bundle_price_option: BundlePrice, random: Random, options: StardewValleyOptions):
|
||||
def create_bundle_room(self, random: Random, content: StardewContent, options: StardewValleyOptions):
|
||||
filtered_bundles = [bundle for bundle in self.bundles if bundle.can_appear(options)]
|
||||
chosen_bundles = random.sample(filtered_bundles, self.number_bundles)
|
||||
return BundleRoom(self.name, [bundle.create_bundle(bundle_price_option, random, options) for bundle in chosen_bundles])
|
||||
|
||||
priority_bundles = []
|
||||
unpriority_bundles = []
|
||||
for bundle in filtered_bundles:
|
||||
if bundle.name in options.bundle_plando:
|
||||
priority_bundles.append(bundle)
|
||||
else:
|
||||
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:
|
||||
chosen_bundles.extend(random.sample(unpriority_bundles, num_remaining_bundles))
|
||||
|
||||
return BundleRoom(self.name, [bundle.create_bundle(random, content, options) for bundle in chosen_bundles])
|
||||
|
|
|
@ -1,65 +1,102 @@
|
|||
from random import Random
|
||||
from typing import List
|
||||
from typing import List, Tuple
|
||||
|
||||
from .bundle_room import BundleRoom
|
||||
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
|
||||
abandoned_joja_mart_thematic, abandoned_joja_mart_vanilla, abandoned_joja_mart_remixed, raccoon_vanilla, raccoon_thematic, raccoon_remixed, \
|
||||
community_center_remixed_anywhere
|
||||
from ..logic.logic import StardewLogic
|
||||
from ..options import BundleRandomization, StardewValleyOptions, ExcludeGingerIsland
|
||||
from ..options import BundleRandomization, StardewValleyOptions
|
||||
|
||||
|
||||
def get_all_bundles(random: Random, logic: StardewLogic, options: StardewValleyOptions) -> List[BundleRoom]:
|
||||
def get_all_bundles(random: Random, logic: StardewLogic, content: StardewContent, options: StardewValleyOptions) -> List[BundleRoom]:
|
||||
if options.bundle_randomization == BundleRandomization.option_vanilla:
|
||||
return get_vanilla_bundles(random, options)
|
||||
return get_vanilla_bundles(random, content, options)
|
||||
elif options.bundle_randomization == BundleRandomization.option_thematic:
|
||||
return get_thematic_bundles(random, options)
|
||||
return get_thematic_bundles(random, content, options)
|
||||
elif options.bundle_randomization == BundleRandomization.option_remixed:
|
||||
return get_remixed_bundles(random, options)
|
||||
return get_remixed_bundles(random, content, options)
|
||||
elif options.bundle_randomization == BundleRandomization.option_remixed_anywhere:
|
||||
return get_remixed_bundles_anywhere(random, content, options)
|
||||
elif options.bundle_randomization == BundleRandomization.option_shuffled:
|
||||
return get_shuffled_bundles(random, logic, options)
|
||||
return get_shuffled_bundles(random, logic, content, options)
|
||||
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def get_vanilla_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
|
||||
pantry = pantry_vanilla.create_bundle_room(options.bundle_price, random, options)
|
||||
crafts_room = crafts_room_vanilla.create_bundle_room(options.bundle_price, random, options)
|
||||
fish_tank = fish_tank_vanilla.create_bundle_room(options.bundle_price, random, options)
|
||||
boiler_room = boiler_room_vanilla.create_bundle_room(options.bundle_price, random, options)
|
||||
bulletin_board = bulletin_board_vanilla.create_bundle_room(options.bundle_price, random, options)
|
||||
vault = vault_vanilla.create_bundle_room(options.bundle_price, random, options)
|
||||
abandoned_joja_mart = abandoned_joja_mart_vanilla.create_bundle_room(options.bundle_price, random, options)
|
||||
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
|
||||
def get_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]
|
||||
|
||||
|
||||
def get_thematic_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
|
||||
pantry = pantry_thematic.create_bundle_room(options.bundle_price, random, options)
|
||||
crafts_room = crafts_room_thematic.create_bundle_room(options.bundle_price, random, options)
|
||||
fish_tank = fish_tank_thematic.create_bundle_room(options.bundle_price, random, options)
|
||||
boiler_room = boiler_room_thematic.create_bundle_room(options.bundle_price, random, options)
|
||||
bulletin_board = bulletin_board_thematic.create_bundle_room(options.bundle_price, random, options)
|
||||
vault = vault_thematic.create_bundle_room(options.bundle_price, random, options)
|
||||
abandoned_joja_mart = abandoned_joja_mart_thematic.create_bundle_room(options.bundle_price, random, options)
|
||||
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
|
||||
def get_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]
|
||||
|
||||
|
||||
def get_remixed_bundles(random: Random, options: StardewValleyOptions) -> List[BundleRoom]:
|
||||
pantry = pantry_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
crafts_room = crafts_room_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
fish_tank = fish_tank_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
boiler_room = boiler_room_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
bulletin_board = bulletin_board_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
vault = vault_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
abandoned_joja_mart = abandoned_joja_mart_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
return [pantry, crafts_room, fish_tank, boiler_room, bulletin_board, vault, abandoned_joja_mart]
|
||||
def get_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]
|
||||
|
||||
|
||||
def get_shuffled_bundles(random: Random, logic: StardewLogic, options: StardewValleyOptions) -> List[BundleRoom]:
|
||||
valid_bundle_items = [bundle_item for bundle_item in all_bundle_items_except_money if bundle_item.can_appear(options)]
|
||||
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)
|
||||
all_chosen_bundles = big_room.bundles
|
||||
random.shuffle(all_chosen_bundles)
|
||||
|
||||
rooms = [room for room in get_remixed_bundles(random, options) if room.name != "Vault"]
|
||||
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)
|
||||
|
||||
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]
|
||||
|
||||
|
||||
def create_room_from_bundles(template: BundleRoomTemplate, all_bundles: List[Bundle], end_index: int) -> Tuple[BundleRoom, int]:
|
||||
start_index = end_index
|
||||
end_index += template.number_bundles
|
||||
return BundleRoom(template.name, all_bundles[start_index:end_index]), end_index
|
||||
|
||||
|
||||
def get_shuffled_bundles(random: Random, logic: StardewLogic, content: StardewContent, options: StardewValleyOptions) -> List[BundleRoom]:
|
||||
valid_bundle_items = [bundle_item for bundle_item in all_bundle_items_except_money if bundle_item.can_appear(content, options)]
|
||||
|
||||
rooms = [room for room in get_remixed_bundles(random, content, options) if room.name != "Vault"]
|
||||
required_items = 0
|
||||
for room in rooms:
|
||||
for bundle in room.bundles:
|
||||
|
@ -67,14 +104,21 @@ def get_shuffled_bundles(random: Random, logic: StardewLogic, options: StardewVa
|
|||
random.shuffle(room.bundles)
|
||||
random.shuffle(rooms)
|
||||
|
||||
# Remove duplicates of the same item
|
||||
valid_bundle_items = [item1 for i, item1 in enumerate(valid_bundle_items)
|
||||
if not any(item1.item_name == item2.item_name and item1.quality == item2.quality for item2 in valid_bundle_items[:i])]
|
||||
chosen_bundle_items = random.sample(valid_bundle_items, required_items)
|
||||
sorted_bundle_items = sorted(chosen_bundle_items, key=lambda x: logic.has(x.item_name).get_difficulty())
|
||||
for room in rooms:
|
||||
for bundle in room.bundles:
|
||||
num_items = len(bundle.items)
|
||||
bundle.items = sorted_bundle_items[:num_items]
|
||||
sorted_bundle_items = sorted_bundle_items[num_items:]
|
||||
bundle.items = chosen_bundle_items[:num_items]
|
||||
chosen_bundle_items = chosen_bundle_items[num_items:]
|
||||
|
||||
vault = vault_remixed.create_bundle_room(options.bundle_price, random, options)
|
||||
vault = vault_remixed.create_bundle_room(random, content, options)
|
||||
return [*rooms, vault]
|
||||
|
||||
|
||||
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}"
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
from . import content_packs
|
||||
from .feature import cropsanity, friendsanity, fishsanity, booksanity
|
||||
from .game_content import ContentPack, StardewContent, StardewFeatures
|
||||
from .unpacking import unpack_content
|
||||
from .. import options
|
||||
|
||||
|
||||
def create_content(player_options: options.StardewValleyOptions) -> StardewContent:
|
||||
active_packs = choose_content_packs(player_options)
|
||||
features = choose_features(player_options)
|
||||
return unpack_content(features, active_packs)
|
||||
|
||||
|
||||
def choose_content_packs(player_options: options.StardewValleyOptions):
|
||||
active_packs = [content_packs.pelican_town, content_packs.the_desert, content_packs.the_farm, content_packs.the_mines]
|
||||
|
||||
if player_options.exclude_ginger_island == options.ExcludeGingerIsland.option_false:
|
||||
active_packs.append(content_packs.ginger_island_content_pack)
|
||||
|
||||
if player_options.special_order_locations & options.SpecialOrderLocations.value_qi:
|
||||
active_packs.append(content_packs.qi_board_content_pack)
|
||||
|
||||
for mod in player_options.mods.value:
|
||||
active_packs.append(content_packs.by_mod[mod])
|
||||
|
||||
return active_packs
|
||||
|
||||
|
||||
def choose_features(player_options: options.StardewValleyOptions) -> StardewFeatures:
|
||||
return StardewFeatures(
|
||||
choose_booksanity(player_options.booksanity),
|
||||
choose_cropsanity(player_options.cropsanity),
|
||||
choose_fishsanity(player_options.fishsanity),
|
||||
choose_friendsanity(player_options.friendsanity, player_options.friendsanity_heart_size)
|
||||
)
|
||||
|
||||
|
||||
booksanity_by_option = {
|
||||
options.Booksanity.option_none: booksanity.BooksanityDisabled(),
|
||||
options.Booksanity.option_power: booksanity.BooksanityPower(),
|
||||
options.Booksanity.option_power_skill: booksanity.BooksanityPowerSkill(),
|
||||
options.Booksanity.option_all: booksanity.BooksanityAll(),
|
||||
}
|
||||
|
||||
|
||||
def choose_booksanity(booksanity_option: options.Booksanity) -> booksanity.BooksanityFeature:
|
||||
booksanity_feature = booksanity_by_option.get(booksanity_option)
|
||||
|
||||
if booksanity_feature is None:
|
||||
raise ValueError(f"No booksanity feature mapped to {str(booksanity_option.value)}")
|
||||
|
||||
return booksanity_feature
|
||||
|
||||
|
||||
cropsanity_by_option = {
|
||||
options.Cropsanity.option_disabled: cropsanity.CropsanityDisabled(),
|
||||
options.Cropsanity.option_enabled: cropsanity.CropsanityEnabled(),
|
||||
}
|
||||
|
||||
|
||||
def choose_cropsanity(cropsanity_option: options.Cropsanity) -> cropsanity.CropsanityFeature:
|
||||
cropsanity_feature = cropsanity_by_option.get(cropsanity_option)
|
||||
|
||||
if cropsanity_feature is None:
|
||||
raise ValueError(f"No cropsanity feature mapped to {str(cropsanity_option.value)}")
|
||||
|
||||
return cropsanity_feature
|
||||
|
||||
|
||||
fishsanity_by_option = {
|
||||
options.Fishsanity.option_none: fishsanity.FishsanityNone(),
|
||||
options.Fishsanity.option_legendaries: fishsanity.FishsanityLegendaries(),
|
||||
options.Fishsanity.option_special: fishsanity.FishsanitySpecial(),
|
||||
options.Fishsanity.option_randomized: fishsanity.FishsanityAll(randomization_ratio=0.4),
|
||||
options.Fishsanity.option_all: fishsanity.FishsanityAll(),
|
||||
options.Fishsanity.option_exclude_legendaries: fishsanity.FishsanityExcludeLegendaries(),
|
||||
options.Fishsanity.option_exclude_hard_fish: fishsanity.FishsanityExcludeHardFish(),
|
||||
options.Fishsanity.option_only_easy_fish: fishsanity.FishsanityOnlyEasyFish(),
|
||||
}
|
||||
|
||||
|
||||
def choose_fishsanity(fishsanity_option: options.Fishsanity) -> fishsanity.FishsanityFeature:
|
||||
fishsanity_feature = fishsanity_by_option.get(fishsanity_option)
|
||||
|
||||
if fishsanity_feature is None:
|
||||
raise ValueError(f"No fishsanity feature mapped to {str(fishsanity_option.value)}")
|
||||
|
||||
return fishsanity_feature
|
||||
|
||||
|
||||
def choose_friendsanity(friendsanity_option: options.Friendsanity, heart_size: options.FriendsanityHeartSize) -> friendsanity.FriendsanityFeature:
|
||||
if friendsanity_option == options.Friendsanity.option_none:
|
||||
return friendsanity.FriendsanityNone()
|
||||
|
||||
if friendsanity_option == options.Friendsanity.option_bachelors:
|
||||
return friendsanity.FriendsanityBachelors(heart_size.value)
|
||||
|
||||
if friendsanity_option == options.Friendsanity.option_starting_npcs:
|
||||
return friendsanity.FriendsanityStartingNpc(heart_size.value)
|
||||
|
||||
if friendsanity_option == options.Friendsanity.option_all:
|
||||
return friendsanity.FriendsanityAll(heart_size.value)
|
||||
|
||||
if friendsanity_option == options.Friendsanity.option_all_with_marriage:
|
||||
return friendsanity.FriendsanityAllWithMarriage(heart_size.value)
|
||||
|
||||
raise ValueError(f"No friendsanity feature mapped to {str(friendsanity_option.value)}")
|
|
@ -0,0 +1,31 @@
|
|||
import importlib
|
||||
import pkgutil
|
||||
|
||||
from . import mods
|
||||
from .mod_registry import by_mod
|
||||
from .vanilla.base import base_game
|
||||
from .vanilla.ginger_island import ginger_island_content_pack
|
||||
from .vanilla.pelican_town import pelican_town
|
||||
from .vanilla.qi_board import qi_board_content_pack
|
||||
from .vanilla.the_desert import the_desert
|
||||
from .vanilla.the_farm import the_farm
|
||||
from .vanilla.the_mines import the_mines
|
||||
|
||||
assert base_game
|
||||
assert ginger_island_content_pack
|
||||
assert pelican_town
|
||||
assert qi_board_content_pack
|
||||
assert the_desert
|
||||
assert the_farm
|
||||
assert the_mines
|
||||
|
||||
# Dynamically register everything currently in the mods folder. This would ideally be done through a metaclass, but I have not looked into that yet.
|
||||
mod_modules = pkgutil.iter_modules(mods.__path__)
|
||||
|
||||
loaded_modules = {}
|
||||
for mod_module in mod_modules:
|
||||
module_name = mod_module.name
|
||||
module = importlib.import_module("." + module_name, mods.__name__)
|
||||
loaded_modules[module_name] = module
|
||||
|
||||
assert by_mod
|
|
@ -0,0 +1,4 @@
|
|||
from . import booksanity
|
||||
from . import cropsanity
|
||||
from . import fishsanity
|
||||
from . import friendsanity
|
|
@ -0,0 +1,72 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import ClassVar, Optional, Iterable
|
||||
|
||||
from ...data.game_item import GameItem, ItemTag
|
||||
from ...strings.book_names import ordered_lost_books
|
||||
|
||||
item_prefix = "Power: "
|
||||
location_prefix = "Read "
|
||||
|
||||
|
||||
def to_item_name(book: str) -> str:
|
||||
return item_prefix + book
|
||||
|
||||
|
||||
def to_location_name(book: str) -> str:
|
||||
return location_prefix + book
|
||||
|
||||
|
||||
def extract_book_from_location_name(location_name: str) -> Optional[str]:
|
||||
if not location_name.startswith(location_prefix):
|
||||
return None
|
||||
|
||||
return location_name[len(location_prefix):]
|
||||
|
||||
|
||||
class BooksanityFeature(ABC):
|
||||
is_enabled: ClassVar[bool]
|
||||
|
||||
to_item_name = staticmethod(to_item_name)
|
||||
progressive_lost_book = "Progressive Lost Book"
|
||||
to_location_name = staticmethod(to_location_name)
|
||||
extract_book_from_location_name = staticmethod(extract_book_from_location_name)
|
||||
|
||||
@abstractmethod
|
||||
def is_included(self, book: GameItem) -> bool:
|
||||
...
|
||||
|
||||
@staticmethod
|
||||
def get_randomized_lost_books() -> Iterable[str]:
|
||||
return []
|
||||
|
||||
|
||||
class BooksanityDisabled(BooksanityFeature):
|
||||
is_enabled = False
|
||||
|
||||
def is_included(self, book: GameItem) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class BooksanityPower(BooksanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, book: GameItem) -> bool:
|
||||
return ItemTag.BOOK_POWER in book.tags
|
||||
|
||||
|
||||
class BooksanityPowerSkill(BooksanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, book: GameItem) -> bool:
|
||||
return ItemTag.BOOK_POWER in book.tags or ItemTag.BOOK_SKILL in book.tags
|
||||
|
||||
|
||||
class BooksanityAll(BooksanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, book: GameItem) -> bool:
|
||||
return ItemTag.BOOK_POWER in book.tags or ItemTag.BOOK_SKILL in book.tags
|
||||
|
||||
@staticmethod
|
||||
def get_randomized_lost_books() -> Iterable[str]:
|
||||
return ordered_lost_books
|
|
@ -0,0 +1,42 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from typing import ClassVar, Optional
|
||||
|
||||
from ...data.game_item import GameItem, ItemTag
|
||||
|
||||
location_prefix = "Harvest "
|
||||
|
||||
|
||||
def to_location_name(crop: str) -> str:
|
||||
return location_prefix + crop
|
||||
|
||||
|
||||
def extract_crop_from_location_name(location_name: str) -> Optional[str]:
|
||||
if not location_name.startswith(location_prefix):
|
||||
return None
|
||||
|
||||
return location_name[len(location_prefix):]
|
||||
|
||||
|
||||
class CropsanityFeature(ABC):
|
||||
is_enabled: ClassVar[bool]
|
||||
|
||||
to_location_name = staticmethod(to_location_name)
|
||||
extract_crop_from_location_name = staticmethod(extract_crop_from_location_name)
|
||||
|
||||
@abstractmethod
|
||||
def is_included(self, crop: GameItem) -> bool:
|
||||
...
|
||||
|
||||
|
||||
class CropsanityDisabled(CropsanityFeature):
|
||||
is_enabled = False
|
||||
|
||||
def is_included(self, crop: GameItem) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class CropsanityEnabled(CropsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, crop: GameItem) -> bool:
|
||||
return ItemTag.CROPSANITY_SEED in crop.tags
|
|
@ -0,0 +1,101 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from typing import ClassVar, Optional
|
||||
|
||||
from ...data.fish_data import FishItem
|
||||
from ...strings.fish_names import Fish
|
||||
|
||||
location_prefix = "Fishsanity: "
|
||||
|
||||
|
||||
def to_location_name(fish: str) -> str:
|
||||
return location_prefix + fish
|
||||
|
||||
|
||||
def extract_fish_from_location_name(location_name: str) -> Optional[str]:
|
||||
if not location_name.startswith(location_prefix):
|
||||
return None
|
||||
|
||||
return location_name[len(location_prefix):]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FishsanityFeature(ABC):
|
||||
is_enabled: ClassVar[bool]
|
||||
|
||||
randomization_ratio: float = 1
|
||||
|
||||
to_location_name = staticmethod(to_location_name)
|
||||
extract_fish_from_location_name = staticmethod(extract_fish_from_location_name)
|
||||
|
||||
@property
|
||||
def is_randomized(self) -> bool:
|
||||
return self.randomization_ratio != 1
|
||||
|
||||
@abstractmethod
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
...
|
||||
|
||||
|
||||
class FishsanityNone(FishsanityFeature):
|
||||
is_enabled = False
|
||||
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class FishsanityLegendaries(FishsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
return fish.legendary
|
||||
|
||||
|
||||
class FishsanitySpecial(FishsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
included_fishes = {
|
||||
Fish.angler,
|
||||
Fish.crimsonfish,
|
||||
Fish.glacierfish,
|
||||
Fish.legend,
|
||||
Fish.mutant_carp,
|
||||
Fish.blobfish,
|
||||
Fish.lava_eel,
|
||||
Fish.octopus,
|
||||
Fish.scorpion_carp,
|
||||
Fish.ice_pip,
|
||||
Fish.super_cucumber,
|
||||
Fish.dorado
|
||||
}
|
||||
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
return fish.name in self.included_fishes
|
||||
|
||||
|
||||
class FishsanityAll(FishsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
class FishsanityExcludeLegendaries(FishsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
return not fish.legendary
|
||||
|
||||
|
||||
class FishsanityExcludeHardFish(FishsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
return fish.difficulty < 80
|
||||
|
||||
|
||||
class FishsanityOnlyEasyFish(FishsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def is_included(self, fish: FishItem) -> bool:
|
||||
return fish.difficulty < 50
|
|
@ -0,0 +1,139 @@
|
|||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass
|
||||
from functools import lru_cache
|
||||
from typing import Optional, Tuple, ClassVar
|
||||
|
||||
from ...data.villagers_data import Villager
|
||||
from ...strings.villager_names import NPC
|
||||
|
||||
suffix = " <3"
|
||||
location_prefix = "Friendsanity: "
|
||||
|
||||
|
||||
def to_item_name(npc_name: str) -> str:
|
||||
return npc_name + suffix
|
||||
|
||||
|
||||
def to_location_name(npc_name: str, heart: int) -> str:
|
||||
return location_prefix + npc_name + " " + str(heart) + suffix
|
||||
|
||||
|
||||
pet_heart_item_name = to_item_name(NPC.pet)
|
||||
|
||||
|
||||
def extract_npc_from_item_name(item_name: str) -> Optional[str]:
|
||||
if not item_name.endswith(suffix):
|
||||
return None
|
||||
|
||||
return item_name[:-len(suffix)]
|
||||
|
||||
|
||||
def extract_npc_from_location_name(location_name: str) -> Tuple[Optional[str], int]:
|
||||
if not location_name.endswith(suffix):
|
||||
return None, 0
|
||||
|
||||
trimmed = location_name[len(location_prefix):-len(suffix)]
|
||||
last_space = trimmed.rindex(" ")
|
||||
return trimmed[:last_space], int(trimmed[last_space + 1:])
|
||||
|
||||
|
||||
@lru_cache(maxsize=32) # Should not go pass 32 values if every friendsanity options are in the multi world
|
||||
def get_heart_steps(max_heart: int, heart_size: int) -> Tuple[int, ...]:
|
||||
return tuple(range(heart_size, max_heart + 1, heart_size)) + ((max_heart,) if max_heart % heart_size else ())
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FriendsanityFeature(ABC):
|
||||
is_enabled: ClassVar[bool]
|
||||
|
||||
heart_size: int
|
||||
|
||||
to_item_name = staticmethod(to_item_name)
|
||||
to_location_name = staticmethod(to_location_name)
|
||||
pet_heart_item_name = pet_heart_item_name
|
||||
extract_npc_from_item_name = staticmethod(extract_npc_from_item_name)
|
||||
extract_npc_from_location_name = staticmethod(extract_npc_from_location_name)
|
||||
|
||||
@abstractmethod
|
||||
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
|
||||
...
|
||||
|
||||
@property
|
||||
def is_pet_randomized(self):
|
||||
return bool(self.get_pet_randomized_hearts())
|
||||
|
||||
@abstractmethod
|
||||
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
|
||||
...
|
||||
|
||||
|
||||
class FriendsanityNone(FriendsanityFeature):
|
||||
is_enabled = False
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(1)
|
||||
|
||||
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
|
||||
return ()
|
||||
|
||||
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
|
||||
return ()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FriendsanityBachelors(FriendsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
|
||||
if not villager.bachelor:
|
||||
return ()
|
||||
|
||||
return get_heart_steps(8, self.heart_size)
|
||||
|
||||
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
|
||||
return ()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FriendsanityStartingNpc(FriendsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
|
||||
if not villager.available:
|
||||
return ()
|
||||
|
||||
if villager.bachelor:
|
||||
return get_heart_steps(8, self.heart_size)
|
||||
|
||||
return get_heart_steps(10, self.heart_size)
|
||||
|
||||
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
|
||||
return get_heart_steps(5, self.heart_size)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FriendsanityAll(FriendsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
|
||||
if villager.bachelor:
|
||||
return get_heart_steps(8, self.heart_size)
|
||||
|
||||
return get_heart_steps(10, self.heart_size)
|
||||
|
||||
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
|
||||
return get_heart_steps(5, self.heart_size)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FriendsanityAllWithMarriage(FriendsanityFeature):
|
||||
is_enabled = True
|
||||
|
||||
def get_randomized_hearts(self, villager: Villager) -> Tuple[int, ...]:
|
||||
if villager.bachelor:
|
||||
return get_heart_steps(14, self.heart_size)
|
||||
|
||||
return get_heart_steps(10, self.heart_size)
|
||||
|
||||
def get_pet_randomized_hearts(self) -> Tuple[int, ...]:
|
||||
return get_heart_steps(5, self.heart_size)
|
|
@ -0,0 +1,117 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Iterable, Set, Any, Mapping, Type, Tuple, Union
|
||||
|
||||
from .feature import booksanity, cropsanity, fishsanity, friendsanity
|
||||
from ..data.fish_data import FishItem
|
||||
from ..data.game_item import GameItem, ItemSource, ItemTag
|
||||
from ..data.skill import Skill
|
||||
from ..data.villagers_data import Villager
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StardewContent:
|
||||
features: StardewFeatures
|
||||
registered_packs: Set[str] = field(default_factory=set)
|
||||
|
||||
# regions -> To be used with can reach rule
|
||||
|
||||
game_items: Dict[str, GameItem] = field(default_factory=dict)
|
||||
fishes: Dict[str, FishItem] = field(default_factory=dict)
|
||||
villagers: Dict[str, Villager] = field(default_factory=dict)
|
||||
skills: Dict[str, Skill] = field(default_factory=dict)
|
||||
quests: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def find_sources_of_type(self, types: Union[Type[ItemSource], Tuple[Type[ItemSource]]]) -> Iterable[ItemSource]:
|
||||
for item in self.game_items.values():
|
||||
for source in item.sources:
|
||||
if isinstance(source, types):
|
||||
yield source
|
||||
|
||||
def source_item(self, item_name: str, *sources: ItemSource):
|
||||
item = self.game_items.setdefault(item_name, GameItem(item_name))
|
||||
item.add_sources(sources)
|
||||
|
||||
def tag_item(self, item_name: str, *tags: ItemTag):
|
||||
item = self.game_items.setdefault(item_name, GameItem(item_name))
|
||||
item.add_tags(tags)
|
||||
|
||||
def untag_item(self, item_name: str, tag: ItemTag):
|
||||
self.game_items[item_name].tags.remove(tag)
|
||||
|
||||
def find_tagged_items(self, tag: ItemTag) -> Iterable[GameItem]:
|
||||
# TODO might be worth caching this, but it need to only be cached once the content is finalized...
|
||||
for item in self.game_items.values():
|
||||
if tag in item.tags:
|
||||
yield item
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class StardewFeatures:
|
||||
booksanity: booksanity.BooksanityFeature
|
||||
cropsanity: cropsanity.CropsanityFeature
|
||||
fishsanity: fishsanity.FishsanityFeature
|
||||
friendsanity: friendsanity.FriendsanityFeature
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ContentPack:
|
||||
name: str
|
||||
|
||||
dependencies: Iterable[str] = ()
|
||||
""" Hard requirement, generation will fail if it's missing. """
|
||||
weak_dependencies: Iterable[str] = ()
|
||||
""" Not a strict dependency, only used only for ordering the packs to make sure hooks are applied correctly. """
|
||||
|
||||
# items
|
||||
# def item_hook
|
||||
# ...
|
||||
|
||||
harvest_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
|
||||
"""Harvest sources contains both crops and forageables, but also fruits from trees, the cave farm and stuff harvested from tapping like maple syrup."""
|
||||
|
||||
def harvest_source_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
shop_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
|
||||
|
||||
def shop_source_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
fishes: Iterable[FishItem] = ()
|
||||
|
||||
def fish_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
crafting_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
|
||||
|
||||
def crafting_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
artisan_good_sources: Mapping[str, Iterable[ItemSource]] = field(default_factory=dict)
|
||||
|
||||
def artisan_good_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
villagers: Iterable[Villager] = ()
|
||||
|
||||
def villager_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
skills: Iterable[Skill] = ()
|
||||
|
||||
def skill_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
quests: Iterable[Any] = ()
|
||||
|
||||
def quest_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
def finalize_hook(self, content: StardewContent):
|
||||
"""Last hook called on the pack, once all other content packs have been registered.
|
||||
|
||||
This is the place to do any final adjustments to the content, like adding rules based on tags applied by other packs.
|
||||
"""
|
||||
...
|
|
@ -0,0 +1,7 @@
|
|||
from .game_content import ContentPack
|
||||
|
||||
by_mod = {}
|
||||
|
||||
|
||||
def register_mod_content_pack(content_pack: ContentPack):
|
||||
by_mod[content_pack.name] = content_pack
|
|
@ -0,0 +1,20 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...data.game_item import ItemTag, Tag
|
||||
from ...data.shop import ShopSource
|
||||
from ...data.skill import Skill
|
||||
from ...mods.mod_data import ModNames
|
||||
from ...strings.book_names import ModBook
|
||||
from ...strings.region_names import LogicRegion
|
||||
from ...strings.skill_names import ModSkill
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.archaeology,
|
||||
shop_sources={
|
||||
ModBook.digging_like_worms: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=500, shop_region=LogicRegion.bookseller_1),),
|
||||
},
|
||||
skills=(Skill(name=ModSkill.archaeology, has_mastery=False),),
|
||||
|
||||
))
|
|
@ -0,0 +1,7 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...mods.mod_data import ModNames
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.big_backpack,
|
||||
))
|
|
@ -0,0 +1,13 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...data import villagers_data
|
||||
from ...mods.mod_data import ModNames
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.boarding_house,
|
||||
villagers=(
|
||||
villagers_data.gregory,
|
||||
villagers_data.sheila,
|
||||
villagers_data.joel,
|
||||
)
|
||||
))
|
|
@ -0,0 +1,28 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...data.harvest import ForagingSource
|
||||
from ...mods.mod_data import ModNames
|
||||
from ...strings.crop_names import Fruit
|
||||
from ...strings.flower_names import Flower
|
||||
from ...strings.region_names import DeepWoodsRegion
|
||||
from ...strings.season_names import Season
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.deepwoods,
|
||||
harvest_sources={
|
||||
# Deep enough to have seen such a tree at least once
|
||||
Fruit.apple: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Fruit.apricot: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Fruit.cherry: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Fruit.orange: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Fruit.peach: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Fruit.pomegranate: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Fruit.mango: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
|
||||
Flower.tulip: (ForagingSource(seasons=Season.not_winter, regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Flower.blue_jazz: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Flower.summer_spangle: (ForagingSource(seasons=Season.not_winter, regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Flower.poppy: (ForagingSource(seasons=Season.not_winter, regions=(DeepWoodsRegion.floor_10,)),),
|
||||
Flower.fairy_rose: (ForagingSource(regions=(DeepWoodsRegion.floor_10,)),),
|
||||
}
|
||||
))
|
|
@ -0,0 +1,17 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...data import villagers_data, fish_data
|
||||
from ...mods.mod_data import ModNames
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.distant_lands,
|
||||
fishes=(
|
||||
fish_data.void_minnow,
|
||||
fish_data.purple_algae,
|
||||
fish_data.swamp_leech,
|
||||
fish_data.giant_horsehoe_crab,
|
||||
),
|
||||
villagers=(
|
||||
villagers_data.zic,
|
||||
)
|
||||
))
|
|
@ -0,0 +1,14 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ..override import override
|
||||
from ...data import villagers_data
|
||||
from ...mods.mod_data import ModNames
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.jasper,
|
||||
villagers=(
|
||||
villagers_data.jasper,
|
||||
override(villagers_data.gunther, mod_name=ModNames.jasper),
|
||||
override(villagers_data.marlon, mod_name=ModNames.jasper),
|
||||
)
|
||||
))
|
|
@ -0,0 +1,10 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...data.skill import Skill
|
||||
from ...mods.mod_data import ModNames
|
||||
from ...strings.skill_names import ModSkill
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.magic,
|
||||
skills=(Skill(name=ModSkill.magic, has_mastery=False),)
|
||||
))
|
|
@ -0,0 +1,88 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...data import villagers_data
|
||||
from ...mods.mod_data import ModNames
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.alec,
|
||||
villagers=(
|
||||
villagers_data.alec,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.ayeisha,
|
||||
villagers=(
|
||||
villagers_data.ayeisha,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.delores,
|
||||
villagers=(
|
||||
villagers_data.delores,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.eugene,
|
||||
villagers=(
|
||||
villagers_data.eugene,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.juna,
|
||||
villagers=(
|
||||
villagers_data.juna,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.ginger,
|
||||
villagers=(
|
||||
villagers_data.kitty,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.shiko,
|
||||
villagers=(
|
||||
villagers_data.shiko,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.wellwick,
|
||||
villagers=(
|
||||
villagers_data.wellwick,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.yoba,
|
||||
villagers=(
|
||||
villagers_data.yoba,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.riley,
|
||||
villagers=(
|
||||
villagers_data.riley,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.alecto,
|
||||
villagers=(
|
||||
villagers_data.alecto,
|
||||
)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.lacey,
|
||||
villagers=(
|
||||
villagers_data.lacey,
|
||||
)
|
||||
))
|
|
@ -0,0 +1,25 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...data.skill import Skill
|
||||
from ...mods.mod_data import ModNames
|
||||
from ...strings.skill_names import ModSkill
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.luck_skill,
|
||||
skills=(Skill(name=ModSkill.luck, has_mastery=False),)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.socializing_skill,
|
||||
skills=(Skill(name=ModSkill.socializing, has_mastery=False),)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.cooking_skill,
|
||||
skills=(Skill(name=ModSkill.cooking, has_mastery=False),)
|
||||
))
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.binning_skill,
|
||||
skills=(Skill(name=ModSkill.binning, has_mastery=False),)
|
||||
))
|
|
@ -0,0 +1,7 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...mods.mod_data import ModNames
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.skull_cavern_elevator,
|
||||
))
|
|
@ -0,0 +1,126 @@
|
|||
from ..game_content import ContentPack, StardewContent
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ..override import override
|
||||
from ..vanilla.ginger_island import ginger_island_content_pack as ginger_island_content_pack
|
||||
from ...data import villagers_data, fish_data
|
||||
from ...data.harvest import ForagingSource
|
||||
from ...data.requirement import YearRequirement
|
||||
from ...mods.mod_data import ModNames
|
||||
from ...strings.crop_names import Fruit
|
||||
from ...strings.fish_names import WaterItem
|
||||
from ...strings.flower_names import Flower
|
||||
from ...strings.forageable_names import Mushroom, Forageable
|
||||
from ...strings.region_names import Region, SVERegion
|
||||
from ...strings.season_names import Season
|
||||
|
||||
|
||||
class SVEContentPack(ContentPack):
|
||||
|
||||
def fish_hook(self, content: StardewContent):
|
||||
if ginger_island_content_pack.name not in content.registered_packs:
|
||||
content.fishes.pop(fish_data.baby_lunaloo.name)
|
||||
content.fishes.pop(fish_data.clownfish.name)
|
||||
content.fishes.pop(fish_data.lunaloo.name)
|
||||
content.fishes.pop(fish_data.seahorse.name)
|
||||
content.fishes.pop(fish_data.shiny_lunaloo.name)
|
||||
content.fishes.pop(fish_data.starfish.name)
|
||||
content.fishes.pop(fish_data.sea_sponge.name)
|
||||
|
||||
# Remove Highlands fishes at it requires 2 Lance hearts for the quest to access it
|
||||
content.fishes.pop(fish_data.daggerfish.name)
|
||||
content.fishes.pop(fish_data.gemfish.name)
|
||||
|
||||
# Remove Fable Reef fishes at it requires 8 Lance hearts for the event to access it
|
||||
content.fishes.pop(fish_data.torpedo_trout.name)
|
||||
|
||||
def villager_hook(self, content: StardewContent):
|
||||
if ginger_island_content_pack.name not in content.registered_packs:
|
||||
# Remove Lance if Ginger Island is not in content since he is first encountered in Volcano Forge
|
||||
content.villagers.pop(villagers_data.lance.name)
|
||||
|
||||
|
||||
register_mod_content_pack(SVEContentPack(
|
||||
ModNames.sve,
|
||||
weak_dependencies=(
|
||||
ginger_island_content_pack.name,
|
||||
ModNames.jasper, # To override Marlon and Gunther
|
||||
),
|
||||
harvest_sources={
|
||||
Mushroom.red: (
|
||||
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.summer, Season.fall)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
|
||||
),
|
||||
Mushroom.purple: (
|
||||
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
|
||||
),
|
||||
Mushroom.morel: (
|
||||
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
|
||||
),
|
||||
Mushroom.chanterelle: (
|
||||
ForagingSource(regions=(SVERegion.forest_west,), seasons=(Season.fall,)), ForagingSource(regions=(SVERegion.sprite_spring_cave,), )
|
||||
),
|
||||
Flower.tulip: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring,)),),
|
||||
Flower.blue_jazz: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring,)),),
|
||||
Flower.summer_spangle: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.summer,)),),
|
||||
Flower.sunflower: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.summer,)),),
|
||||
Flower.fairy_rose: (ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.fall,)),),
|
||||
Fruit.ancient_fruit: (
|
||||
ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring, Season.summer, Season.fall), other_requirements=(YearRequirement(3),)),
|
||||
ForagingSource(regions=(SVERegion.sprite_spring_cave,)),
|
||||
),
|
||||
Fruit.sweet_gem_berry: (
|
||||
ForagingSource(regions=(SVERegion.sprite_spring,), seasons=(Season.spring, Season.summer, Season.fall), other_requirements=(YearRequirement(3),)),
|
||||
),
|
||||
|
||||
# Fable Reef
|
||||
WaterItem.coral: (ForagingSource(regions=(SVERegion.fable_reef,)),),
|
||||
Forageable.rainbow_shell: (ForagingSource(regions=(SVERegion.fable_reef,)),),
|
||||
WaterItem.sea_urchin: (ForagingSource(regions=(SVERegion.fable_reef,)),),
|
||||
},
|
||||
fishes=(
|
||||
fish_data.baby_lunaloo, # Removed when no ginger island
|
||||
fish_data.bonefish,
|
||||
fish_data.bull_trout,
|
||||
fish_data.butterfish,
|
||||
fish_data.clownfish, # Removed when no ginger island
|
||||
fish_data.daggerfish,
|
||||
fish_data.frog,
|
||||
fish_data.gemfish,
|
||||
fish_data.goldenfish,
|
||||
fish_data.grass_carp,
|
||||
fish_data.king_salmon,
|
||||
fish_data.kittyfish,
|
||||
fish_data.lunaloo, # Removed when no ginger island
|
||||
fish_data.meteor_carp,
|
||||
fish_data.minnow,
|
||||
fish_data.puppyfish,
|
||||
fish_data.radioactive_bass,
|
||||
fish_data.seahorse, # Removed when no ginger island
|
||||
fish_data.shiny_lunaloo, # Removed when no ginger island
|
||||
fish_data.snatcher_worm,
|
||||
fish_data.starfish, # Removed when no ginger island
|
||||
fish_data.torpedo_trout,
|
||||
fish_data.undeadfish,
|
||||
fish_data.void_eel,
|
||||
fish_data.water_grub,
|
||||
fish_data.sea_sponge, # Removed when no ginger island
|
||||
|
||||
),
|
||||
villagers=(
|
||||
villagers_data.claire,
|
||||
villagers_data.lance, # Removed when no ginger island
|
||||
villagers_data.mommy,
|
||||
villagers_data.sophia,
|
||||
villagers_data.victor,
|
||||
villagers_data.andy,
|
||||
villagers_data.apples,
|
||||
villagers_data.gunther,
|
||||
villagers_data.martin,
|
||||
villagers_data.marlon,
|
||||
villagers_data.morgan,
|
||||
villagers_data.scarlett,
|
||||
villagers_data.susan,
|
||||
villagers_data.morris,
|
||||
# The wizard leaves his tower on sunday, for like 1 hour... Good enough for entrance rando!
|
||||
override(villagers_data.wizard, locations=(Region.wizard_tower, Region.forest), bachelor=True, mod_name=ModNames.sve),
|
||||
)
|
||||
))
|
|
@ -0,0 +1,7 @@
|
|||
from ..game_content import ContentPack
|
||||
from ..mod_registry import register_mod_content_pack
|
||||
from ...mods.mod_data import ModNames
|
||||
|
||||
register_mod_content_pack(ContentPack(
|
||||
ModNames.tractor,
|
||||
))
|
|
@ -0,0 +1,7 @@
|
|||
from typing import Any
|
||||
|
||||
|
||||
def override(content: Any, **kwargs) -> Any:
|
||||
attributes = dict(content.__dict__)
|
||||
attributes.update(kwargs)
|
||||
return type(content)(**attributes)
|
|
@ -0,0 +1,97 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from typing import Iterable, Mapping, Callable
|
||||
|
||||
from .game_content import StardewContent, ContentPack, StardewFeatures
|
||||
from .vanilla.base import base_game as base_game_content_pack
|
||||
from ..data.game_item import GameItem, ItemSource
|
||||
|
||||
try:
|
||||
from graphlib import TopologicalSorter
|
||||
except ImportError:
|
||||
from graphlib_backport import TopologicalSorter # noqa
|
||||
|
||||
|
||||
def unpack_content(features: StardewFeatures, packs: Iterable[ContentPack]) -> StardewContent:
|
||||
# Base game is always registered first.
|
||||
content = StardewContent(features)
|
||||
packs_to_finalize = [base_game_content_pack]
|
||||
register_pack(content, base_game_content_pack)
|
||||
|
||||
# Content packs are added in order based on their dependencies
|
||||
sorter = TopologicalSorter()
|
||||
packs_by_name = {p.name: p for p in packs}
|
||||
|
||||
# Build the dependency graph
|
||||
for name, pack in packs_by_name.items():
|
||||
sorter.add(name,
|
||||
*pack.dependencies,
|
||||
*(wd for wd in pack.weak_dependencies if wd in packs_by_name))
|
||||
|
||||
# Graph is traversed in BFS
|
||||
sorter.prepare()
|
||||
while sorter.is_active():
|
||||
# Packs get shuffled in TopologicalSorter, most likely due to hash seeding.
|
||||
for pack_name in sorted(sorter.get_ready()):
|
||||
pack = packs_by_name[pack_name]
|
||||
register_pack(content, pack)
|
||||
sorter.done(pack_name)
|
||||
packs_to_finalize.append(pack)
|
||||
|
||||
prune_inaccessible_items(content)
|
||||
|
||||
for pack in packs_to_finalize:
|
||||
pack.finalize_hook(content)
|
||||
|
||||
# Maybe items without source should be removed at some point
|
||||
return content
|
||||
|
||||
|
||||
def register_pack(content: StardewContent, pack: ContentPack):
|
||||
# register regions
|
||||
|
||||
# register entrances
|
||||
|
||||
register_sources_and_call_hook(content, pack.harvest_sources, pack.harvest_source_hook)
|
||||
register_sources_and_call_hook(content, pack.shop_sources, pack.shop_source_hook)
|
||||
register_sources_and_call_hook(content, pack.crafting_sources, pack.crafting_hook)
|
||||
register_sources_and_call_hook(content, pack.artisan_good_sources, pack.artisan_good_hook)
|
||||
|
||||
for fish in pack.fishes:
|
||||
content.fishes[fish.name] = fish
|
||||
pack.fish_hook(content)
|
||||
|
||||
for villager in pack.villagers:
|
||||
content.villagers[villager.name] = villager
|
||||
pack.villager_hook(content)
|
||||
|
||||
for skill in pack.skills:
|
||||
content.skills[skill.name] = skill
|
||||
pack.skill_hook(content)
|
||||
|
||||
# register_quests
|
||||
|
||||
# ...
|
||||
|
||||
content.registered_packs.add(pack.name)
|
||||
|
||||
|
||||
def register_sources_and_call_hook(content: StardewContent,
|
||||
sources_by_item_name: Mapping[str, Iterable[ItemSource]],
|
||||
hook: Callable[[StardewContent], None]):
|
||||
for item_name, sources in sources_by_item_name.items():
|
||||
item = content.game_items.setdefault(item_name, GameItem(item_name))
|
||||
item.add_sources(sources)
|
||||
|
||||
for source in sources:
|
||||
for requirement_name, tags in source.requirement_tags.items():
|
||||
requirement_item = content.game_items.setdefault(requirement_name, GameItem(requirement_name))
|
||||
requirement_item.add_tags(tags)
|
||||
|
||||
hook(content)
|
||||
|
||||
|
||||
def prune_inaccessible_items(content: StardewContent):
|
||||
for item in list(content.game_items.values()):
|
||||
if not item.sources:
|
||||
content.game_items.pop(item.name)
|
|
@ -0,0 +1,172 @@
|
|||
from ..game_content import ContentPack, StardewContent
|
||||
from ...data.artisan import MachineSource
|
||||
from ...data.game_item import ItemTag, CustomRuleSource, GameItem
|
||||
from ...data.harvest import HarvestFruitTreeSource, HarvestCropSource
|
||||
from ...data.skill import Skill
|
||||
from ...strings.artisan_good_names import ArtisanGood
|
||||
from ...strings.craftable_names import WildSeeds
|
||||
from ...strings.crop_names import Fruit, Vegetable
|
||||
from ...strings.flower_names import Flower
|
||||
from ...strings.food_names import Beverage
|
||||
from ...strings.forageable_names import all_edible_mushrooms, Mushroom, Forageable
|
||||
from ...strings.fruit_tree_names import Sapling
|
||||
from ...strings.machine_names import Machine
|
||||
from ...strings.monster_names import Monster
|
||||
from ...strings.season_names import Season
|
||||
from ...strings.seed_names import Seed
|
||||
from ...strings.skill_names import Skill as SkillName
|
||||
|
||||
all_fruits = (
|
||||
Fruit.ancient_fruit, Fruit.apple, Fruit.apricot, Fruit.banana, Forageable.blackberry, Fruit.blueberry, Forageable.cactus_fruit, Fruit.cherry,
|
||||
Forageable.coconut, Fruit.cranberries, Forageable.crystal_fruit, Fruit.grape, Fruit.hot_pepper, Fruit.mango, Fruit.melon, Fruit.orange, Fruit.peach,
|
||||
Fruit.pineapple, Fruit.pomegranate, Fruit.powdermelon, Fruit.qi_fruit, Fruit.rhubarb, Forageable.salmonberry, Forageable.spice_berry, Fruit.starfruit,
|
||||
Fruit.strawberry
|
||||
)
|
||||
|
||||
all_vegetables = (
|
||||
Vegetable.amaranth, Vegetable.artichoke, Vegetable.beet, Vegetable.bok_choy, Vegetable.broccoli, Vegetable.carrot, Vegetable.cauliflower,
|
||||
Vegetable.corn, Vegetable.eggplant, Forageable.fiddlehead_fern, Vegetable.garlic, Vegetable.green_bean, Vegetable.hops, Vegetable.kale,
|
||||
Vegetable.parsnip, Vegetable.potato, Vegetable.pumpkin, Vegetable.radish, Vegetable.red_cabbage, Vegetable.summer_squash, Vegetable.taro_root,
|
||||
Vegetable.tea_leaves, Vegetable.tomato, Vegetable.unmilled_rice, Vegetable.wheat, Vegetable.yam
|
||||
)
|
||||
|
||||
non_juiceable_vegetables = (Vegetable.hops, Vegetable.tea_leaves, Vegetable.wheat, Vegetable.tea_leaves)
|
||||
|
||||
|
||||
# This will hold items, skills and stuff that is available everywhere across the game, but not directly needing pelican town (crops, ore, foraging, etc.)
|
||||
class BaseGameContentPack(ContentPack):
|
||||
|
||||
def harvest_source_hook(self, content: StardewContent):
|
||||
coffee_starter = content.game_items[Seed.coffee_starter]
|
||||
content.game_items[Seed.coffee_starter] = GameItem(Seed.coffee, sources=coffee_starter.sources, tags=coffee_starter.tags)
|
||||
|
||||
content.untag_item(WildSeeds.ancient, ItemTag.CROPSANITY_SEED)
|
||||
|
||||
for fruit in all_fruits:
|
||||
content.tag_item(fruit, ItemTag.FRUIT)
|
||||
|
||||
for vegetable in all_vegetables:
|
||||
content.tag_item(vegetable, ItemTag.VEGETABLE)
|
||||
|
||||
for edible_mushroom in all_edible_mushrooms:
|
||||
if edible_mushroom == Mushroom.magma_cap:
|
||||
continue
|
||||
|
||||
content.tag_item(edible_mushroom, ItemTag.EDIBLE_MUSHROOM)
|
||||
|
||||
def finalize_hook(self, content: StardewContent):
|
||||
# FIXME I hate this design. A listener design pattern would be more appropriate so artisan good are register at the exact moment a FRUIT tag is added.
|
||||
for fruit in tuple(content.find_tagged_items(ItemTag.FRUIT)):
|
||||
wine = ArtisanGood.specific_wine(fruit.name)
|
||||
content.source_item(wine, MachineSource(item=fruit.name, machine=Machine.keg))
|
||||
content.source_item(ArtisanGood.wine, MachineSource(item=fruit.name, machine=Machine.keg))
|
||||
|
||||
if fruit.name == Fruit.grape:
|
||||
content.source_item(ArtisanGood.raisins, MachineSource(item=fruit.name, machine=Machine.dehydrator))
|
||||
else:
|
||||
dried_fruit = ArtisanGood.specific_dried_fruit(fruit.name)
|
||||
content.source_item(dried_fruit, MachineSource(item=fruit.name, machine=Machine.dehydrator))
|
||||
content.source_item(ArtisanGood.dried_fruit, MachineSource(item=fruit.name, machine=Machine.dehydrator))
|
||||
|
||||
jelly = ArtisanGood.specific_jelly(fruit.name)
|
||||
content.source_item(jelly, MachineSource(item=fruit.name, machine=Machine.preserves_jar))
|
||||
content.source_item(ArtisanGood.jelly, MachineSource(item=fruit.name, machine=Machine.preserves_jar))
|
||||
|
||||
for vegetable in tuple(content.find_tagged_items(ItemTag.VEGETABLE)):
|
||||
if vegetable.name not in non_juiceable_vegetables:
|
||||
juice = ArtisanGood.specific_juice(vegetable.name)
|
||||
content.source_item(juice, MachineSource(item=vegetable.name, machine=Machine.keg))
|
||||
content.source_item(ArtisanGood.juice, MachineSource(item=vegetable.name, machine=Machine.keg))
|
||||
|
||||
pickles = ArtisanGood.specific_pickles(vegetable.name)
|
||||
content.source_item(pickles, MachineSource(item=vegetable.name, machine=Machine.preserves_jar))
|
||||
content.source_item(ArtisanGood.pickles, MachineSource(item=vegetable.name, machine=Machine.preserves_jar))
|
||||
|
||||
for mushroom in tuple(content.find_tagged_items(ItemTag.EDIBLE_MUSHROOM)):
|
||||
dried_mushroom = ArtisanGood.specific_dried_mushroom(mushroom.name)
|
||||
content.source_item(dried_mushroom, MachineSource(item=mushroom.name, machine=Machine.dehydrator))
|
||||
content.source_item(ArtisanGood.dried_mushroom, MachineSource(item=mushroom.name, machine=Machine.dehydrator))
|
||||
|
||||
# for fish in tuple(content.find_tagged_items(ItemTag.FISH)):
|
||||
# smoked_fish = ArtisanGood.specific_smoked_fish(fish.name)
|
||||
# content.source_item(smoked_fish, MachineSource(item=fish.name, machine=Machine.fish_smoker))
|
||||
# content.source_item(ArtisanGood.smoked_fish, MachineSource(item=fish.name, machine=Machine.fish_smoker))
|
||||
|
||||
|
||||
base_game = BaseGameContentPack(
|
||||
"Base game (Vanilla)",
|
||||
harvest_sources={
|
||||
# Fruit tree
|
||||
Fruit.apple: (HarvestFruitTreeSource(sapling=Sapling.apple, seasons=(Season.fall,)),),
|
||||
Fruit.apricot: (HarvestFruitTreeSource(sapling=Sapling.apricot, seasons=(Season.spring,)),),
|
||||
Fruit.cherry: (HarvestFruitTreeSource(sapling=Sapling.cherry, seasons=(Season.spring,)),),
|
||||
Fruit.orange: (HarvestFruitTreeSource(sapling=Sapling.orange, seasons=(Season.summer,)),),
|
||||
Fruit.peach: (HarvestFruitTreeSource(sapling=Sapling.peach, seasons=(Season.summer,)),),
|
||||
Fruit.pomegranate: (HarvestFruitTreeSource(sapling=Sapling.pomegranate, seasons=(Season.fall,)),),
|
||||
|
||||
# Crops
|
||||
Vegetable.parsnip: (HarvestCropSource(seed=Seed.parsnip, seasons=(Season.spring,)),),
|
||||
Vegetable.green_bean: (HarvestCropSource(seed=Seed.bean, seasons=(Season.spring,)),),
|
||||
Vegetable.cauliflower: (HarvestCropSource(seed=Seed.cauliflower, seasons=(Season.spring,)),),
|
||||
Vegetable.potato: (HarvestCropSource(seed=Seed.potato, seasons=(Season.spring,)),),
|
||||
Flower.tulip: (HarvestCropSource(seed=Seed.tulip, seasons=(Season.spring,)),),
|
||||
Vegetable.kale: (HarvestCropSource(seed=Seed.kale, seasons=(Season.spring,)),),
|
||||
Flower.blue_jazz: (HarvestCropSource(seed=Seed.jazz, seasons=(Season.spring,)),),
|
||||
Vegetable.garlic: (HarvestCropSource(seed=Seed.garlic, seasons=(Season.spring,)),),
|
||||
Vegetable.unmilled_rice: (HarvestCropSource(seed=Seed.rice, seasons=(Season.spring,)),),
|
||||
|
||||
Fruit.melon: (HarvestCropSource(seed=Seed.melon, seasons=(Season.summer,)),),
|
||||
Vegetable.tomato: (HarvestCropSource(seed=Seed.tomato, seasons=(Season.summer,)),),
|
||||
Fruit.blueberry: (HarvestCropSource(seed=Seed.blueberry, seasons=(Season.summer,)),),
|
||||
Fruit.hot_pepper: (HarvestCropSource(seed=Seed.pepper, seasons=(Season.summer,)),),
|
||||
Vegetable.wheat: (HarvestCropSource(seed=Seed.wheat, seasons=(Season.summer, Season.fall)),),
|
||||
Vegetable.radish: (HarvestCropSource(seed=Seed.radish, seasons=(Season.summer,)),),
|
||||
Flower.poppy: (HarvestCropSource(seed=Seed.poppy, seasons=(Season.summer,)),),
|
||||
Flower.summer_spangle: (HarvestCropSource(seed=Seed.spangle, seasons=(Season.summer,)),),
|
||||
Vegetable.hops: (HarvestCropSource(seed=Seed.hops, seasons=(Season.summer,)),),
|
||||
Vegetable.corn: (HarvestCropSource(seed=Seed.corn, seasons=(Season.summer, Season.fall)),),
|
||||
Flower.sunflower: (HarvestCropSource(seed=Seed.sunflower, seasons=(Season.summer, Season.fall)),),
|
||||
Vegetable.red_cabbage: (HarvestCropSource(seed=Seed.red_cabbage, seasons=(Season.summer,)),),
|
||||
|
||||
Vegetable.eggplant: (HarvestCropSource(seed=Seed.eggplant, seasons=(Season.fall,)),),
|
||||
Vegetable.pumpkin: (HarvestCropSource(seed=Seed.pumpkin, seasons=(Season.fall,)),),
|
||||
Vegetable.bok_choy: (HarvestCropSource(seed=Seed.bok_choy, seasons=(Season.fall,)),),
|
||||
Vegetable.yam: (HarvestCropSource(seed=Seed.yam, seasons=(Season.fall,)),),
|
||||
Fruit.cranberries: (HarvestCropSource(seed=Seed.cranberry, seasons=(Season.fall,)),),
|
||||
Flower.fairy_rose: (HarvestCropSource(seed=Seed.fairy, seasons=(Season.fall,)),),
|
||||
Vegetable.amaranth: (HarvestCropSource(seed=Seed.amaranth, seasons=(Season.fall,)),),
|
||||
Fruit.grape: (HarvestCropSource(seed=Seed.grape, seasons=(Season.fall,)),),
|
||||
Vegetable.artichoke: (HarvestCropSource(seed=Seed.artichoke, seasons=(Season.fall,)),),
|
||||
|
||||
Vegetable.broccoli: (HarvestCropSource(seed=Seed.broccoli, seasons=(Season.fall,)),),
|
||||
Vegetable.carrot: (HarvestCropSource(seed=Seed.carrot, seasons=(Season.spring,)),),
|
||||
Fruit.powdermelon: (HarvestCropSource(seed=Seed.powdermelon, seasons=(Season.summer,)),),
|
||||
Vegetable.summer_squash: (HarvestCropSource(seed=Seed.summer_squash, seasons=(Season.summer,)),),
|
||||
|
||||
Fruit.strawberry: (HarvestCropSource(seed=Seed.strawberry, seasons=(Season.spring,)),),
|
||||
Fruit.sweet_gem_berry: (HarvestCropSource(seed=Seed.rare_seed, seasons=(Season.fall,)),),
|
||||
Fruit.ancient_fruit: (HarvestCropSource(seed=WildSeeds.ancient, seasons=(Season.spring, Season.summer, Season.fall,)),),
|
||||
|
||||
Seed.coffee_starter: (CustomRuleSource(lambda logic: logic.traveling_merchant.has_days(3) & logic.monster.can_kill_many(Monster.dust_sprite)),),
|
||||
Seed.coffee: (HarvestCropSource(seed=Seed.coffee_starter, seasons=(Season.spring, Season.summer,)),),
|
||||
|
||||
Vegetable.tea_leaves: (CustomRuleSource(lambda logic: logic.has(Sapling.tea) & logic.time.has_lived_months(2) & logic.season.has_any_not_winter()),),
|
||||
},
|
||||
artisan_good_sources={
|
||||
Beverage.beer: (MachineSource(item=Vegetable.wheat, machine=Machine.keg),),
|
||||
# Ingredient.vinegar: (MachineSource(item=Ingredient.rice, machine=Machine.keg),),
|
||||
Beverage.coffee: (MachineSource(item=Seed.coffee, machine=Machine.keg),
|
||||
CustomRuleSource(lambda logic: logic.has(Machine.coffee_maker)),
|
||||
CustomRuleSource(lambda logic: logic.has("Hot Java Ring"))),
|
||||
ArtisanGood.green_tea: (MachineSource(item=Vegetable.tea_leaves, machine=Machine.keg),),
|
||||
ArtisanGood.mead: (MachineSource(item=ArtisanGood.honey, machine=Machine.keg),),
|
||||
ArtisanGood.pale_ale: (MachineSource(item=Vegetable.hops, machine=Machine.keg),),
|
||||
},
|
||||
skills=(
|
||||
Skill(SkillName.farming, has_mastery=True),
|
||||
Skill(SkillName.foraging, has_mastery=True),
|
||||
Skill(SkillName.fishing, has_mastery=True),
|
||||
Skill(SkillName.mining, has_mastery=True),
|
||||
Skill(SkillName.combat, has_mastery=True),
|
||||
)
|
||||
)
|
|
@ -0,0 +1,81 @@
|
|||
from .pelican_town import pelican_town as pelican_town_content_pack
|
||||
from ..game_content import ContentPack, StardewContent
|
||||
from ...data import villagers_data, fish_data
|
||||
from ...data.game_item import ItemTag, Tag
|
||||
from ...data.harvest import ForagingSource, HarvestFruitTreeSource, HarvestCropSource
|
||||
from ...data.shop import ShopSource
|
||||
from ...strings.book_names import Book
|
||||
from ...strings.crop_names import Fruit, Vegetable
|
||||
from ...strings.fish_names import Fish
|
||||
from ...strings.forageable_names import Forageable, Mushroom
|
||||
from ...strings.fruit_tree_names import Sapling
|
||||
from ...strings.metal_names import Fossil, Mineral
|
||||
from ...strings.region_names import Region
|
||||
from ...strings.season_names import Season
|
||||
from ...strings.seed_names import Seed
|
||||
|
||||
|
||||
class GingerIslandContentPack(ContentPack):
|
||||
|
||||
def harvest_source_hook(self, content: StardewContent):
|
||||
content.tag_item(Fruit.banana, ItemTag.FRUIT)
|
||||
content.tag_item(Fruit.pineapple, ItemTag.FRUIT)
|
||||
content.tag_item(Fruit.mango, ItemTag.FRUIT)
|
||||
content.tag_item(Vegetable.taro_root, ItemTag.VEGETABLE)
|
||||
content.tag_item(Mushroom.magma_cap, ItemTag.EDIBLE_MUSHROOM)
|
||||
|
||||
|
||||
ginger_island_content_pack = GingerIslandContentPack(
|
||||
"Ginger Island (Vanilla)",
|
||||
weak_dependencies=(
|
||||
pelican_town_content_pack.name,
|
||||
),
|
||||
harvest_sources={
|
||||
# Foraging
|
||||
Forageable.dragon_tooth: (
|
||||
ForagingSource(regions=(Region.volcano_floor_10,)),
|
||||
),
|
||||
Forageable.ginger: (
|
||||
ForagingSource(regions=(Region.island_west,)),
|
||||
),
|
||||
Mushroom.magma_cap: (
|
||||
ForagingSource(regions=(Region.volcano_floor_5,)),
|
||||
),
|
||||
|
||||
# Fruit tree
|
||||
Fruit.banana: (HarvestFruitTreeSource(sapling=Sapling.banana, seasons=(Season.summer,)),),
|
||||
Fruit.mango: (HarvestFruitTreeSource(sapling=Sapling.mango, seasons=(Season.summer,)),),
|
||||
|
||||
# Crop
|
||||
Vegetable.taro_root: (HarvestCropSource(seed=Seed.taro, seasons=(Season.summer,)),),
|
||||
Fruit.pineapple: (HarvestCropSource(seed=Seed.pineapple, seasons=(Season.summer,)),),
|
||||
|
||||
},
|
||||
shop_sources={
|
||||
Seed.taro: (ShopSource(items_price=((2, Fossil.bone_fragment),), shop_region=Region.island_trader),),
|
||||
Seed.pineapple: (ShopSource(items_price=((1, Mushroom.magma_cap),), shop_region=Region.island_trader),),
|
||||
Sapling.banana: (ShopSource(items_price=((5, Forageable.dragon_tooth),), shop_region=Region.island_trader),),
|
||||
Sapling.mango: (ShopSource(items_price=((75, Fish.mussel_node),), shop_region=Region.island_trader),),
|
||||
|
||||
# This one is 10 diamonds, should maybe add time?
|
||||
Book.the_diamond_hunter: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(items_price=((10, Mineral.diamond),), shop_region=Region.volcano_dwarf_shop),
|
||||
),
|
||||
|
||||
},
|
||||
fishes=(
|
||||
# TODO override region so no need to add inaccessible regions in logic
|
||||
fish_data.blue_discus,
|
||||
fish_data.lionfish,
|
||||
fish_data.midnight_carp,
|
||||
fish_data.pufferfish,
|
||||
fish_data.stingray,
|
||||
fish_data.super_cucumber,
|
||||
fish_data.tilapia,
|
||||
fish_data.tuna
|
||||
),
|
||||
villagers=(
|
||||
villagers_data.leo,
|
||||
)
|
||||
)
|
|
@ -0,0 +1,393 @@
|
|||
from ..game_content import ContentPack
|
||||
from ...data import villagers_data, fish_data
|
||||
from ...data.game_item import GenericSource, ItemTag, Tag, CustomRuleSource
|
||||
from ...data.harvest import ForagingSource, SeasonalForagingSource, ArtifactSpotSource
|
||||
from ...data.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement
|
||||
from ...data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource
|
||||
from ...strings.book_names import Book
|
||||
from ...strings.crop_names import Fruit
|
||||
from ...strings.fish_names import WaterItem
|
||||
from ...strings.food_names import Beverage, Meal
|
||||
from ...strings.forageable_names import Forageable, Mushroom
|
||||
from ...strings.fruit_tree_names import Sapling
|
||||
from ...strings.generic_names import Generic
|
||||
from ...strings.material_names import Material
|
||||
from ...strings.region_names import Region, LogicRegion
|
||||
from ...strings.season_names import Season
|
||||
from ...strings.seed_names import Seed, TreeSeed
|
||||
from ...strings.skill_names import Skill
|
||||
from ...strings.tool_names import Tool, ToolMaterial
|
||||
|
||||
pelican_town = ContentPack(
|
||||
"Pelican Town (Vanilla)",
|
||||
harvest_sources={
|
||||
# Spring
|
||||
Forageable.daffodil: (
|
||||
ForagingSource(seasons=(Season.spring,), regions=(Region.bus_stop, Region.town, Region.railroad)),
|
||||
),
|
||||
Forageable.dandelion: (
|
||||
ForagingSource(seasons=(Season.spring,), regions=(Region.bus_stop, Region.forest, Region.railroad)),
|
||||
),
|
||||
Forageable.leek: (
|
||||
ForagingSource(seasons=(Season.spring,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
|
||||
),
|
||||
Forageable.wild_horseradish: (
|
||||
ForagingSource(seasons=(Season.spring,), regions=(Region.backwoods, Region.mountain, Region.forest, Region.secret_woods)),
|
||||
),
|
||||
Forageable.salmonberry: (
|
||||
SeasonalForagingSource(season=Season.spring, days=(15, 16, 17, 18),
|
||||
regions=(Region.backwoods, Region.mountain, Region.town, Region.forest, Region.tunnel_entrance, Region.railroad)),
|
||||
),
|
||||
Forageable.spring_onion: (
|
||||
ForagingSource(seasons=(Season.spring,), regions=(Region.forest,)),
|
||||
),
|
||||
|
||||
# Summer
|
||||
Fruit.grape: (
|
||||
ForagingSource(seasons=(Season.summer,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
|
||||
),
|
||||
Forageable.spice_berry: (
|
||||
ForagingSource(seasons=(Season.summer,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.forest, Region.railroad)),
|
||||
),
|
||||
Forageable.sweet_pea: (
|
||||
ForagingSource(seasons=(Season.summer,), regions=(Region.bus_stop, Region.town, Region.forest, Region.railroad)),
|
||||
),
|
||||
Forageable.fiddlehead_fern: (
|
||||
ForagingSource(seasons=(Season.summer,), regions=(Region.secret_woods,)),
|
||||
),
|
||||
|
||||
# Fall
|
||||
Forageable.blackberry: (
|
||||
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.town, Region.forest, Region.railroad)),
|
||||
SeasonalForagingSource(season=Season.fall, days=(8, 9, 10, 11),
|
||||
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.tunnel_entrance,
|
||||
Region.railroad)),
|
||||
),
|
||||
Forageable.hazelnut: (
|
||||
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.railroad)),
|
||||
),
|
||||
Forageable.wild_plum: (
|
||||
ForagingSource(seasons=(Season.fall,), regions=(Region.mountain, Region.bus_stop, Region.railroad)),
|
||||
),
|
||||
|
||||
# Winter
|
||||
Forageable.crocus: (
|
||||
ForagingSource(seasons=(Season.winter,),
|
||||
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.secret_woods)),
|
||||
),
|
||||
Forageable.crystal_fruit: (
|
||||
ForagingSource(seasons=(Season.winter,),
|
||||
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad)),
|
||||
),
|
||||
Forageable.holly: (
|
||||
ForagingSource(seasons=(Season.winter,),
|
||||
regions=(Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad)),
|
||||
),
|
||||
Forageable.snow_yam: (
|
||||
ForagingSource(seasons=(Season.winter,),
|
||||
regions=(Region.farm, Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad,
|
||||
Region.secret_woods, Region.beach),
|
||||
other_requirements=(ToolRequirement(Tool.hoe),)),
|
||||
),
|
||||
Forageable.winter_root: (
|
||||
ForagingSource(seasons=(Season.winter,),
|
||||
regions=(Region.farm, Region.backwoods, Region.mountain, Region.bus_stop, Region.town, Region.forest, Region.railroad,
|
||||
Region.secret_woods, Region.beach),
|
||||
other_requirements=(ToolRequirement(Tool.hoe),)),
|
||||
),
|
||||
|
||||
# Mushrooms
|
||||
Mushroom.common: (
|
||||
ForagingSource(seasons=(Season.spring,), regions=(Region.secret_woods,)),
|
||||
ForagingSource(seasons=(Season.fall,), regions=(Region.backwoods, Region.mountain, Region.forest)),
|
||||
),
|
||||
Mushroom.chanterelle: (
|
||||
ForagingSource(seasons=(Season.fall,), regions=(Region.secret_woods,)),
|
||||
),
|
||||
Mushroom.morel: (
|
||||
ForagingSource(seasons=(Season.spring, Season.fall), regions=(Region.secret_woods,)),
|
||||
),
|
||||
Mushroom.red: (
|
||||
ForagingSource(seasons=(Season.summer, Season.fall), regions=(Region.secret_woods,)),
|
||||
),
|
||||
|
||||
# Beach
|
||||
WaterItem.coral: (
|
||||
ForagingSource(regions=(Region.tide_pools,)),
|
||||
SeasonalForagingSource(season=Season.summer, days=(12, 13, 14), regions=(Region.beach,)),
|
||||
),
|
||||
WaterItem.nautilus_shell: (
|
||||
ForagingSource(seasons=(Season.winter,), regions=(Region.beach,)),
|
||||
),
|
||||
Forageable.rainbow_shell: (
|
||||
ForagingSource(seasons=(Season.summer,), regions=(Region.beach,)),
|
||||
),
|
||||
WaterItem.sea_urchin: (
|
||||
ForagingSource(regions=(Region.tide_pools,)),
|
||||
),
|
||||
|
||||
Seed.mixed: (
|
||||
ForagingSource(seasons=(Season.spring, Season.summer, Season.fall,), regions=(Region.town, Region.farm, Region.forest)),
|
||||
),
|
||||
|
||||
Seed.mixed_flower: (
|
||||
ForagingSource(seasons=(Season.summer,), regions=(Region.town, Region.farm, Region.forest)),
|
||||
),
|
||||
|
||||
# Books
|
||||
Book.jack_be_nimble_jack_be_thick: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ArtifactSpotSource(amount=22),), # After 22 spots, there are 50.48% chances player received the book.
|
||||
Book.woodys_secret: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
GenericSource(regions=(Region.forest, Region.mountain),
|
||||
other_requirements=(ToolRequirement(Tool.axe, ToolMaterial.iron), SkillRequirement(Skill.foraging, 5))),),
|
||||
},
|
||||
shop_sources={
|
||||
# Saplings
|
||||
Sapling.apple: (ShopSource(money_price=4000, shop_region=Region.pierre_store),),
|
||||
Sapling.apricot: (ShopSource(money_price=2000, shop_region=Region.pierre_store),),
|
||||
Sapling.cherry: (ShopSource(money_price=3400, shop_region=Region.pierre_store),),
|
||||
Sapling.orange: (ShopSource(money_price=4000, shop_region=Region.pierre_store),),
|
||||
Sapling.peach: (ShopSource(money_price=6000, shop_region=Region.pierre_store),),
|
||||
Sapling.pomegranate: (ShopSource(money_price=6000, shop_region=Region.pierre_store),),
|
||||
|
||||
# Crop seeds, assuming they are bought in season, otherwise price is different with missing stock list.
|
||||
Seed.parsnip: (ShopSource(money_price=20, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.bean: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.cauliflower: (ShopSource(money_price=80, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.potato: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.tulip: (ShopSource(money_price=20, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.kale: (ShopSource(money_price=70, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.jazz: (ShopSource(money_price=30, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.garlic: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
Seed.rice: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.spring,)),),
|
||||
|
||||
Seed.melon: (ShopSource(money_price=80, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.tomato: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.blueberry: (ShopSource(money_price=80, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.pepper: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.wheat: (ShopSource(money_price=10, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
|
||||
Seed.radish: (ShopSource(money_price=40, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.poppy: (ShopSource(money_price=100, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.spangle: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.hops: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
Seed.corn: (ShopSource(money_price=150, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
|
||||
Seed.sunflower: (ShopSource(money_price=200, shop_region=Region.pierre_store, seasons=(Season.summer, Season.fall)),),
|
||||
Seed.red_cabbage: (ShopSource(money_price=100, shop_region=Region.pierre_store, seasons=(Season.summer,)),),
|
||||
|
||||
Seed.eggplant: (ShopSource(money_price=20, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.pumpkin: (ShopSource(money_price=100, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.bok_choy: (ShopSource(money_price=50, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.yam: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.cranberry: (ShopSource(money_price=240, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.fairy: (ShopSource(money_price=200, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.amaranth: (ShopSource(money_price=70, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.grape: (ShopSource(money_price=60, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
Seed.artichoke: (ShopSource(money_price=30, shop_region=Region.pierre_store, seasons=(Season.fall,)),),
|
||||
|
||||
Seed.broccoli: (ShopSource(items_price=((5, Material.moss),), shop_region=LogicRegion.raccoon_shop),),
|
||||
Seed.carrot: (ShopSource(items_price=((1, TreeSeed.maple),), shop_region=LogicRegion.raccoon_shop),),
|
||||
Seed.powdermelon: (ShopSource(items_price=((2, TreeSeed.acorn),), shop_region=LogicRegion.raccoon_shop),),
|
||||
Seed.summer_squash: (ShopSource(items_price=((15, Material.sap),), shop_region=LogicRegion.raccoon_shop),),
|
||||
|
||||
Seed.strawberry: (ShopSource(money_price=100, shop_region=LogicRegion.egg_festival, seasons=(Season.spring,)),),
|
||||
Seed.rare_seed: (ShopSource(money_price=1000, shop_region=LogicRegion.traveling_cart, seasons=(Season.spring, Season.summer)),),
|
||||
|
||||
# Saloon
|
||||
Beverage.beer: (ShopSource(money_price=400, shop_region=Region.saloon),),
|
||||
Meal.salad: (ShopSource(money_price=220, shop_region=Region.saloon),),
|
||||
Meal.bread: (ShopSource(money_price=100, shop_region=Region.saloon),),
|
||||
Meal.spaghetti: (ShopSource(money_price=240, shop_region=Region.saloon),),
|
||||
Meal.pizza: (ShopSource(money_price=600, shop_region=Region.saloon),),
|
||||
Beverage.coffee: (ShopSource(money_price=300, shop_region=Region.saloon),),
|
||||
|
||||
# Books
|
||||
Book.animal_catalogue: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=5000, shop_region=Region.ranch),),
|
||||
Book.book_of_mysteries: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
MysteryBoxSource(amount=38),), # After 38 boxes, there are 49.99% chances player received the book.
|
||||
Book.dwarvish_safety_manual: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=4000, shop_region=LogicRegion.mines_dwarf_shop),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.friendship_101: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
PrizeMachineSource(amount=9),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.horse_the_book: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=25000, shop_region=LogicRegion.bookseller_2),),
|
||||
Book.jack_be_nimble_jack_be_thick: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.jewels_of_the_sea: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
FishingTreasureChestSource(amount=21), # After 21 chests, there are 49.44% chances player received the book.
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.mapping_cave_systems: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
GenericSource(regions=Region.adventurer_guild_bedroom),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.monster_compendium: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
CustomRuleSource(create_rule=lambda logic: logic.monster.can_kill_many(Generic.any)),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.ol_slitherlegs: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=25000, shop_region=LogicRegion.bookseller_2),),
|
||||
Book.price_catalogue: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=3000, shop_region=LogicRegion.bookseller_2),),
|
||||
Book.the_alleyway_buffet: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
GenericSource(regions=Region.town,
|
||||
other_requirements=(ToolRequirement(Tool.axe, ToolMaterial.iron), ToolRequirement(Tool.pickaxe, ToolMaterial.iron))),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.the_art_o_crabbing: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
GenericSource(regions=Region.beach,
|
||||
other_requirements=(ToolRequirement(Tool.fishing_rod, ToolMaterial.iridium),
|
||||
SkillRequirement(Skill.fishing, 6),
|
||||
SeasonRequirement(Season.winter))),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.treasure_appraisal_guide: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ArtifactTroveSource(amount=18), # After 18 troves, there is 49,88% chances player received the book.
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
Book.raccoon_journal: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),
|
||||
ShopSource(items_price=((999, Material.fiber),), shop_region=LogicRegion.raccoon_shop),),
|
||||
Book.way_of_the_wind_pt_1: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=15000, shop_region=LogicRegion.bookseller_2),),
|
||||
Book.way_of_the_wind_pt_2: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=35000, shop_region=LogicRegion.bookseller_2, other_requirements=(BookRequirement(Book.way_of_the_wind_pt_1),)),),
|
||||
Book.woodys_secret: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
|
||||
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
|
||||
|
||||
# Experience Books
|
||||
Book.book_of_stars: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
|
||||
Book.bait_and_bobber: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
|
||||
Book.combat_quarterly: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
|
||||
Book.mining_monthly: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
|
||||
Book.stardew_valley_almanac: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
|
||||
Book.woodcutters_weekly: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=5000, shop_region=LogicRegion.bookseller_1),),
|
||||
Book.queen_of_sauce_cookbook: (
|
||||
Tag(ItemTag.BOOK, ItemTag.BOOK_SKILL),
|
||||
ShopSource(money_price=50000, shop_region=LogicRegion.bookseller_2),), # Worst book ever
|
||||
},
|
||||
fishes=(
|
||||
fish_data.albacore,
|
||||
fish_data.anchovy,
|
||||
fish_data.bream,
|
||||
fish_data.bullhead,
|
||||
fish_data.carp,
|
||||
fish_data.catfish,
|
||||
fish_data.chub,
|
||||
fish_data.dorado,
|
||||
fish_data.eel,
|
||||
fish_data.flounder,
|
||||
fish_data.goby,
|
||||
fish_data.halibut,
|
||||
fish_data.herring,
|
||||
fish_data.largemouth_bass,
|
||||
fish_data.lingcod,
|
||||
fish_data.midnight_carp, # Ginger island override
|
||||
fish_data.octopus,
|
||||
fish_data.perch,
|
||||
fish_data.pike,
|
||||
fish_data.pufferfish, # Ginger island override
|
||||
fish_data.rainbow_trout,
|
||||
fish_data.red_mullet,
|
||||
fish_data.red_snapper,
|
||||
fish_data.salmon,
|
||||
fish_data.sardine,
|
||||
fish_data.sea_cucumber,
|
||||
fish_data.shad,
|
||||
fish_data.slimejack,
|
||||
fish_data.smallmouth_bass,
|
||||
fish_data.squid,
|
||||
fish_data.sturgeon,
|
||||
fish_data.sunfish,
|
||||
fish_data.super_cucumber, # Ginger island override
|
||||
fish_data.tiger_trout,
|
||||
fish_data.tilapia, # Ginger island override
|
||||
fish_data.tuna, # Ginger island override
|
||||
fish_data.void_salmon,
|
||||
fish_data.walleye,
|
||||
fish_data.woodskip,
|
||||
fish_data.blobfish,
|
||||
fish_data.midnight_squid,
|
||||
fish_data.spook_fish,
|
||||
|
||||
# Legendaries
|
||||
fish_data.angler,
|
||||
fish_data.crimsonfish,
|
||||
fish_data.glacierfish,
|
||||
fish_data.legend,
|
||||
fish_data.mutant_carp,
|
||||
|
||||
# Crab pot
|
||||
fish_data.clam,
|
||||
fish_data.cockle,
|
||||
fish_data.crab,
|
||||
fish_data.crayfish,
|
||||
fish_data.lobster,
|
||||
fish_data.mussel,
|
||||
fish_data.oyster,
|
||||
fish_data.periwinkle,
|
||||
fish_data.shrimp,
|
||||
fish_data.snail,
|
||||
),
|
||||
villagers=(
|
||||
villagers_data.josh,
|
||||
villagers_data.elliott,
|
||||
villagers_data.harvey,
|
||||
villagers_data.sam,
|
||||
villagers_data.sebastian,
|
||||
villagers_data.shane,
|
||||
villagers_data.abigail,
|
||||
villagers_data.emily,
|
||||
villagers_data.haley,
|
||||
villagers_data.leah,
|
||||
villagers_data.maru,
|
||||
villagers_data.penny,
|
||||
villagers_data.caroline,
|
||||
villagers_data.clint,
|
||||
villagers_data.demetrius,
|
||||
villagers_data.evelyn,
|
||||
villagers_data.george,
|
||||
villagers_data.gus,
|
||||
villagers_data.jas,
|
||||
villagers_data.jodi,
|
||||
villagers_data.kent,
|
||||
villagers_data.krobus,
|
||||
villagers_data.lewis,
|
||||
villagers_data.linus,
|
||||
villagers_data.marnie,
|
||||
villagers_data.pam,
|
||||
villagers_data.pierre,
|
||||
villagers_data.robin,
|
||||
villagers_data.vincent,
|
||||
villagers_data.willy,
|
||||
villagers_data.wizard,
|
||||
)
|
||||
)
|
|
@ -0,0 +1,36 @@
|
|||
from .ginger_island import ginger_island_content_pack as ginger_island_content_pack
|
||||
from .pelican_town import pelican_town as pelican_town_content_pack
|
||||
from ..game_content import ContentPack, StardewContent
|
||||
from ...data import fish_data
|
||||
from ...data.game_item import GenericSource, ItemTag
|
||||
from ...data.harvest import HarvestCropSource
|
||||
from ...strings.crop_names import Fruit
|
||||
from ...strings.region_names import Region
|
||||
from ...strings.season_names import Season
|
||||
from ...strings.seed_names import Seed
|
||||
|
||||
|
||||
class QiBoardContentPack(ContentPack):
|
||||
def harvest_source_hook(self, content: StardewContent):
|
||||
content.untag_item(Seed.qi_bean, ItemTag.CROPSANITY_SEED)
|
||||
|
||||
|
||||
qi_board_content_pack = QiBoardContentPack(
|
||||
"Qi Board (Vanilla)",
|
||||
dependencies=(
|
||||
pelican_town_content_pack.name,
|
||||
ginger_island_content_pack.name,
|
||||
),
|
||||
harvest_sources={
|
||||
# This one is a bit special, because it's only available during the special order, but it can be found from like, everywhere.
|
||||
Seed.qi_bean: (GenericSource(regions=(Region.qi_walnut_room,)),),
|
||||
Fruit.qi_fruit: (HarvestCropSource(seed=Seed.qi_bean),),
|
||||
},
|
||||
fishes=(
|
||||
fish_data.ms_angler,
|
||||
fish_data.son_of_crimsonfish,
|
||||
fish_data.glacierfish_jr,
|
||||
fish_data.legend_ii,
|
||||
fish_data.radioactive_carp,
|
||||
)
|
||||
)
|
|
@ -0,0 +1,46 @@
|
|||
from .pelican_town import pelican_town as pelican_town_content_pack
|
||||
from ..game_content import ContentPack
|
||||
from ...data import fish_data, villagers_data
|
||||
from ...data.harvest import ForagingSource, HarvestCropSource
|
||||
from ...data.shop import ShopSource
|
||||
from ...strings.crop_names import Fruit, Vegetable
|
||||
from ...strings.forageable_names import Forageable, Mushroom
|
||||
from ...strings.region_names import Region
|
||||
from ...strings.season_names import Season
|
||||
from ...strings.seed_names import Seed
|
||||
|
||||
the_desert = ContentPack(
|
||||
"The Desert (Vanilla)",
|
||||
dependencies=(
|
||||
pelican_town_content_pack.name,
|
||||
),
|
||||
harvest_sources={
|
||||
Forageable.cactus_fruit: (
|
||||
ForagingSource(regions=(Region.desert,)),
|
||||
HarvestCropSource(seed=Seed.cactus, seasons=())
|
||||
),
|
||||
Forageable.coconut: (
|
||||
ForagingSource(regions=(Region.desert,)),
|
||||
),
|
||||
Mushroom.purple: (
|
||||
ForagingSource(regions=(Region.skull_cavern_25,)),
|
||||
),
|
||||
|
||||
Fruit.rhubarb: (HarvestCropSource(seed=Seed.rhubarb, seasons=(Season.spring,)),),
|
||||
Fruit.starfruit: (HarvestCropSource(seed=Seed.starfruit, seasons=(Season.summer,)),),
|
||||
Vegetable.beet: (HarvestCropSource(seed=Seed.beet, seasons=(Season.fall,)),),
|
||||
},
|
||||
shop_sources={
|
||||
Seed.cactus: (ShopSource(money_price=150, shop_region=Region.oasis),),
|
||||
Seed.rhubarb: (ShopSource(money_price=100, shop_region=Region.oasis, seasons=(Season.spring,)),),
|
||||
Seed.starfruit: (ShopSource(money_price=400, shop_region=Region.oasis, seasons=(Season.summer,)),),
|
||||
Seed.beet: (ShopSource(money_price=20, shop_region=Region.oasis, seasons=(Season.fall,)),),
|
||||
},
|
||||
fishes=(
|
||||
fish_data.sandfish,
|
||||
fish_data.scorpion_carp,
|
||||
),
|
||||
villagers=(
|
||||
villagers_data.sandy,
|
||||
),
|
||||
)
|
|
@ -0,0 +1,43 @@
|
|||
from .pelican_town import pelican_town as pelican_town_content_pack
|
||||
from ..game_content import ContentPack
|
||||
from ...data.harvest import FruitBatsSource, MushroomCaveSource
|
||||
from ...strings.forageable_names import Forageable, Mushroom
|
||||
|
||||
the_farm = ContentPack(
|
||||
"The Farm (Vanilla)",
|
||||
dependencies=(
|
||||
pelican_town_content_pack.name,
|
||||
),
|
||||
harvest_sources={
|
||||
# Fruit cave
|
||||
Forageable.blackberry: (
|
||||
FruitBatsSource(),
|
||||
),
|
||||
Forageable.salmonberry: (
|
||||
FruitBatsSource(),
|
||||
),
|
||||
Forageable.spice_berry: (
|
||||
FruitBatsSource(),
|
||||
),
|
||||
Forageable.wild_plum: (
|
||||
FruitBatsSource(),
|
||||
),
|
||||
|
||||
# Mushrooms
|
||||
Mushroom.common: (
|
||||
MushroomCaveSource(),
|
||||
),
|
||||
Mushroom.chanterelle: (
|
||||
MushroomCaveSource(),
|
||||
),
|
||||
Mushroom.morel: (
|
||||
MushroomCaveSource(),
|
||||
),
|
||||
Mushroom.purple: (
|
||||
MushroomCaveSource(),
|
||||
),
|
||||
Mushroom.red: (
|
||||
MushroomCaveSource(),
|
||||
),
|
||||
}
|
||||
)
|
|
@ -0,0 +1,35 @@
|
|||
from .pelican_town import pelican_town as pelican_town_content_pack
|
||||
from ..game_content import ContentPack
|
||||
from ...data import fish_data, villagers_data
|
||||
from ...data.harvest import ForagingSource
|
||||
from ...data.requirement import ToolRequirement
|
||||
from ...strings.forageable_names import Forageable, Mushroom
|
||||
from ...strings.region_names import Region
|
||||
from ...strings.tool_names import Tool
|
||||
|
||||
the_mines = ContentPack(
|
||||
"The Mines (Vanilla)",
|
||||
dependencies=(
|
||||
pelican_town_content_pack.name,
|
||||
),
|
||||
harvest_sources={
|
||||
Forageable.cave_carrot: (
|
||||
ForagingSource(regions=(Region.mines_floor_10,), other_requirements=(ToolRequirement(Tool.hoe),)),
|
||||
),
|
||||
Mushroom.red: (
|
||||
ForagingSource(regions=(Region.mines_floor_95,)),
|
||||
),
|
||||
Mushroom.purple: (
|
||||
ForagingSource(regions=(Region.mines_floor_95,)),
|
||||
)
|
||||
},
|
||||
fishes=(
|
||||
fish_data.ghostfish,
|
||||
fish_data.ice_pip,
|
||||
fish_data.lava_eel,
|
||||
fish_data.stonefish,
|
||||
),
|
||||
villagers=(
|
||||
villagers_data.dwarf,
|
||||
),
|
||||
)
|
|
@ -1,2 +0,0 @@
|
|||
from .crops_data import CropItem, SeedItem, all_crops, all_purchasable_seeds
|
||||
from .fish_data import FishItem, all_fish
|
|
@ -0,0 +1,10 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from .game_item import kw_only, ItemSource
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class MachineSource(ItemSource):
|
||||
item: str # this should be optional (worm bin)
|
||||
machine: str
|
||||
# seasons
|
|
@ -1,17 +1,19 @@
|
|||
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
|
||||
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.fish_names import Fish, WaterItem, Trash, all_fish
|
||||
from ..strings.flower_names import Flower
|
||||
from ..strings.food_names import Beverage, Meal
|
||||
from ..strings.forageable_names import Forageable
|
||||
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
|
||||
|
@ -19,27 +21,27 @@ 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
|
||||
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(Forageable.morel)
|
||||
common_mushroom = BundleItem(Forageable.common_mushroom)
|
||||
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(Forageable.red_mushroom)
|
||||
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(Forageable.chanterelle)
|
||||
chanterelle = BundleItem(Mushroom.chanterelle)
|
||||
|
||||
winter_root = BundleItem(Forageable.winter_root)
|
||||
crystal_fruit = BundleItem(Forageable.crystal_fruit)
|
||||
|
@ -50,7 +52,7 @@ holly = BundleItem(Forageable.holly)
|
|||
coconut = BundleItem(Forageable.coconut)
|
||||
cactus_fruit = BundleItem(Forageable.cactus_fruit)
|
||||
cave_carrot = BundleItem(Forageable.cave_carrot)
|
||||
purple_mushroom = BundleItem(Forageable.purple_mushroom)
|
||||
purple_mushroom = BundleItem(Mushroom.purple)
|
||||
maple_syrup = BundleItem(ArtisanGood.maple_syrup)
|
||||
oak_resin = BundleItem(ArtisanGood.oak_resin)
|
||||
pine_tar = BundleItem(ArtisanGood.pine_tar)
|
||||
|
@ -62,13 +64,25 @@ clam = BundleItem(Fish.clam)
|
|||
cockle = BundleItem(Fish.cockle)
|
||||
mussel = BundleItem(Fish.mussel)
|
||||
oyster = BundleItem(Fish.oyster)
|
||||
seaweed = BundleItem(WaterItem.seaweed)
|
||||
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)
|
||||
|
@ -106,8 +120,13 @@ 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.island)
|
||||
taro_root = BundleItem(Vegetable.taro_root, source=BundleItem.Sources.island, )
|
||||
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)
|
||||
|
@ -151,8 +170,8 @@ orange = BundleItem(Fruit.orange)
|
|||
peach = BundleItem(Fruit.peach)
|
||||
pomegranate = BundleItem(Fruit.pomegranate)
|
||||
cherry = BundleItem(Fruit.cherry)
|
||||
banana = BundleItem(Fruit.banana, source=BundleItem.Sources.island)
|
||||
mango = BundleItem(Fruit.mango, source=BundleItem.Sources.island)
|
||||
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)
|
||||
|
@ -300,6 +319,13 @@ 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)
|
||||
|
@ -370,6 +396,7 @@ 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)
|
||||
|
@ -377,18 +404,67 @@ 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, 10)
|
||||
magic_bait = BundleItem(Fishing.magic_bait, 5, source=BundleItem.Sources.island)
|
||||
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.island)
|
||||
magma_cap = BundleItem(Forageable.magma_cap, source=BundleItem.Sources.island)
|
||||
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]
|
||||
|
@ -436,42 +512,50 @@ island_foraging_bundle = IslandBundleTemplate(CCRoom.crafts_room, BundleName.isl
|
|||
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(1)
|
||||
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]})
|
||||
*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, wild_medicine_bundle, quality_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]
|
||||
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_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]
|
||||
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})
|
||||
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]
|
||||
|
@ -492,7 +576,8 @@ artisan_bundle_thematic = BundleTemplate.extend_from(artisan_bundle_vanilla, art
|
|||
rare_crops_items = [ancient_fruit, sweet_gem_berry]
|
||||
rare_crops_bundle = BundleTemplate(CCRoom.pantry, BundleName.rare_crops, rare_crops_items, 2, 2)
|
||||
|
||||
fish_farmer_items = [roe.as_amount(15), aged_roe.as_amount(15), squid_ink]
|
||||
# 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]
|
||||
|
@ -516,12 +601,20 @@ slime_farmer_items = [slime.as_amount(99), petrified_slime.as_amount(10), blue_s
|
|||
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]
|
||||
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)
|
||||
|
@ -579,8 +672,11 @@ winter_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.winter_fish, wi
|
|||
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) 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, 4)
|
||||
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)
|
||||
|
@ -591,21 +687,31 @@ legendary_fish_bundle = BundleTemplate(CCRoom.fish_tank, BundleName.legendary_fi
|
|||
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, cork_bobber, lead_bobber, treasure_hunter, barbed_hook, curiosity_lure, quality_bobber]
|
||||
tackle_bundle = IslandBundleTemplate(CCRoom.fish_tank, BundleName.tackle, tackle_items, 3, 2)
|
||||
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]
|
||||
bait_bundle = IslandBundleTemplate(CCRoom.fish_tank, BundleName.bait, bait_items, 2, 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]
|
||||
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)
|
||||
|
@ -670,12 +776,12 @@ chef_bundle_vanilla = BundleTemplate(CCRoom.bulletin_board, BundleName.chef, che
|
|||
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]
|
||||
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]
|
||||
dye_green_items = [fiddlehead_fern, kale, artichoke, bok_choy, green_bean]
|
||||
dye_blue_items = [blueberry, blue_jazz, blackberry, crystal_fruit]
|
||||
dye_purple_items = [beet, crocus, eggplant, red_cabbage, sweet_pea]
|
||||
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)
|
||||
|
@ -710,12 +816,31 @@ home_cook_items = [egg.as_amount(10), milk.as_amount(10), wheat_flour.as_amount(
|
|||
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, bartender_bundle]
|
||||
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)
|
||||
|
@ -738,16 +863,15 @@ abandoned_joja_mart_vanilla = BundleRoomTemplate(CCRoom.abandoned_joja_mart, aba
|
|||
abandoned_joja_mart_thematic = BundleRoomTemplate(CCRoom.abandoned_joja_mart, abandoned_joja_mart_bundles_thematic, 1)
|
||||
abandoned_joja_mart_remixed = abandoned_joja_mart_thematic
|
||||
|
||||
# Make thematic with other currencies
|
||||
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, vault_2500_gold)
|
||||
vault_5000_bundle = MoneyBundleTemplate(CCRoom.vault, vault_5000_gold)
|
||||
vault_10000_bundle = MoneyBundleTemplate(CCRoom.vault, vault_10000_gold)
|
||||
vault_25000_bundle = MoneyBundleTemplate(CCRoom.vault, vault_25000_gold)
|
||||
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)
|
||||
|
@ -768,9 +892,14 @@ 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]
|
||||
*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)
|
||||
|
||||
|
|
|
@ -1,25 +1,28 @@
|
|||
from typing import Dict, List, Optional
|
||||
|
||||
from ..mods.mod_data import ModNames
|
||||
from .recipe_source import RecipeSource, StarterSource, QueenOfSauceSource, ShopSource, SkillSource, FriendshipSource, ShopTradeSource, CutsceneSource, \
|
||||
ArchipelagoSource, LogicSource, SpecialOrderSource, FestivalShopSource, QuestSource
|
||||
ArchipelagoSource, LogicSource, SpecialOrderSource, FestivalShopSource, QuestSource, MasterySource
|
||||
from ..mods.mod_data import ModNames
|
||||
from ..strings.animal_product_names import AnimalProduct
|
||||
from ..strings.artisan_good_names import ArtisanGood
|
||||
from ..strings.craftable_names import Bomb, Fence, Sprinkler, WildSeeds, Floor, Fishing, Ring, Consumable, Edible, Lighting, Storage, Furniture, Sign, Craftable, \
|
||||
ModEdible, ModCraftable, ModMachine, ModFloor, ModConsumable
|
||||
from ..strings.craftable_names import Bomb, Fence, Sprinkler, WildSeeds, Floor, Fishing, Ring, Consumable, Edible, Lighting, Storage, Furniture, Sign, \
|
||||
Craftable, \
|
||||
ModEdible, ModCraftable, ModMachine, ModFloor, ModConsumable, Statue
|
||||
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
|
||||
from ..strings.fish_names import Fish, WaterItem, ModTrash
|
||||
from ..strings.flower_names import Flower
|
||||
from ..strings.food_names import Meal
|
||||
from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable
|
||||
from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable, Mushroom
|
||||
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, Fossil, Artifact, Mineral, ModFossil
|
||||
from ..strings.monster_drop_names import Loot
|
||||
from ..strings.monster_drop_names import Loot, ModLoot
|
||||
from ..strings.quest_names import Quest
|
||||
from ..strings.region_names import Region, SVERegion
|
||||
from ..strings.region_names import Region, SVERegion, LogicRegion
|
||||
from ..strings.seed_names import Seed, TreeSeed
|
||||
from ..strings.skill_names import Skill, ModSkill
|
||||
from ..strings.special_order_names import SpecialOrder
|
||||
|
@ -61,6 +64,11 @@ def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int],
|
|||
return create_recipe(name, ingredients, source, mod_name)
|
||||
|
||||
|
||||
def mastery_recipe(name: str, skill: str, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
|
||||
source = MasterySource(skill)
|
||||
return create_recipe(name, ingredients, source, mod_name)
|
||||
|
||||
|
||||
def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CraftingRecipe:
|
||||
source = ShopSource(region, price)
|
||||
return create_recipe(name, ingredients, source, mod_name)
|
||||
|
@ -133,27 +141,37 @@ cask = cellar_recipe(Machine.cask, {Material.wood: 40, Material.hardwood: 1})
|
|||
cheese_press = skill_recipe(Machine.cheese_press, Skill.farming, 6, {Material.wood: 45, Material.stone: 45, Material.hardwood: 10, MetalBar.copper: 1})
|
||||
keg = skill_recipe(Machine.keg, Skill.farming, 8, {Material.wood: 30, MetalBar.copper: 1, MetalBar.iron: 1, ArtisanGood.oak_resin: 1})
|
||||
loom = skill_recipe(Machine.loom, Skill.farming, 7, {Material.wood: 60, Material.fiber: 30, ArtisanGood.pine_tar: 1})
|
||||
mayonnaise_machine = skill_recipe(Machine.mayonnaise_machine, Skill.farming, 2, {Material.wood: 15, Material.stone: 15, Mineral.earth_crystal: 10, MetalBar.copper: 1})
|
||||
mayonnaise_machine = skill_recipe(Machine.mayonnaise_machine, Skill.farming, 2,
|
||||
{Material.wood: 15, Material.stone: 15, Mineral.earth_crystal: 10, MetalBar.copper: 1})
|
||||
oil_maker = skill_recipe(Machine.oil_maker, Skill.farming, 8, {Loot.slime: 50, Material.hardwood: 20, MetalBar.gold: 1})
|
||||
preserves_jar = skill_recipe(Machine.preserves_jar, Skill.farming, 4, {Material.wood: 50, Material.stone: 40, Material.coal: 8})
|
||||
fish_smoker = shop_recipe(Machine.fish_smoker, Region.fish_shop, 10000,
|
||||
{Material.hardwood: 10, WaterItem.sea_jelly: 1, WaterItem.river_jelly: 1, WaterItem.cave_jelly: 1})
|
||||
dehydrator = shop_recipe(Machine.dehydrator, Region.pierre_store, 10000, {Material.wood: 30, Material.clay: 2, Mineral.fire_quartz: 1})
|
||||
|
||||
basic_fertilizer = skill_recipe(Fertilizer.basic, Skill.farming, 1, {Material.sap: 2})
|
||||
quality_fertilizer = skill_recipe(Fertilizer.quality, Skill.farming, 9, {Material.sap: 2, Fish.any: 1})
|
||||
|
||||
quality_fertilizer = skill_recipe(Fertilizer.quality, Skill.farming, 9, {Material.sap: 4, Fish.any: 1})
|
||||
deluxe_fertilizer = ap_recipe(Fertilizer.deluxe, {MetalBar.iridium: 1, Material.sap: 40})
|
||||
basic_speed_gro = skill_recipe(SpeedGro.basic, Skill.farming, 3, {ArtisanGood.pine_tar: 1, Fish.clam: 1})
|
||||
deluxe_speed_gro = skill_recipe(SpeedGro.deluxe, Skill.farming, 8, {ArtisanGood.oak_resin: 1, WaterItem.coral: 1})
|
||||
|
||||
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})
|
||||
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})
|
||||
deluxe_retaining_soil = shop_trade_recipe(RetainingSoil.deluxe, Region.island_trader, Currency.cinder_shard, 50,
|
||||
{Material.stone: 5, Material.fiber: 3, Material.clay: 1})
|
||||
tree_fertilizer = skill_recipe(Fertilizer.tree, Skill.foraging, 7, {Material.fiber: 5, Material.stone: 5})
|
||||
|
||||
spring_seeds = skill_recipe(WildSeeds.spring, Skill.foraging, 1, {Forageable.wild_horseradish: 1, Forageable.daffodil: 1, Forageable.leek: 1, Forageable.dandelion: 1})
|
||||
spring_seeds = skill_recipe(WildSeeds.spring, Skill.foraging, 1,
|
||||
{Forageable.wild_horseradish: 1, Forageable.daffodil: 1, Forageable.leek: 1, Forageable.dandelion: 1})
|
||||
summer_seeds = skill_recipe(WildSeeds.summer, Skill.foraging, 4, {Forageable.spice_berry: 1, Fruit.grape: 1, Forageable.sweet_pea: 1})
|
||||
fall_seeds = skill_recipe(WildSeeds.fall, Skill.foraging, 6, {Forageable.common_mushroom: 1, Forageable.wild_plum: 1, Forageable.hazelnut: 1, Forageable.blackberry: 1})
|
||||
winter_seeds = skill_recipe(WildSeeds.winter, Skill.foraging, 7, {Forageable.winter_root: 1, Forageable.crystal_fruit: 1, Forageable.snow_yam: 1, Forageable.crocus: 1})
|
||||
fall_seeds = skill_recipe(WildSeeds.fall, Skill.foraging, 6, {Mushroom.common: 1, Forageable.wild_plum: 1, Forageable.hazelnut: 1, Forageable.blackberry: 1})
|
||||
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})
|
||||
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})
|
||||
|
@ -161,7 +179,7 @@ fiber_seeds = special_order_recipe(WildSeeds.fiber, SpecialOrder.community_clean
|
|||
wood_floor = shop_recipe(Floor.wood, Region.carpenter, 100, {Material.wood: 1})
|
||||
rustic_floor = shop_recipe(Floor.rustic, Region.carpenter, 200, {Material.wood: 1})
|
||||
straw_floor = shop_recipe(Floor.straw, Region.carpenter, 200, {Material.wood: 1, Material.fiber: 1})
|
||||
weathered_floor = shop_recipe(Floor.weathered, Region.mines_dwarf_shop, 500, {Material.wood: 1})
|
||||
weathered_floor = shop_recipe(Floor.weathered, LogicRegion.mines_dwarf_shop, 500, {Material.wood: 1})
|
||||
crystal_floor = shop_recipe(Floor.crystal, Region.sewer, 500, {MetalBar.quartz: 1})
|
||||
stone_floor = shop_recipe(Floor.stone, Region.carpenter, 100, {Material.stone: 1})
|
||||
stone_walkway_floor = shop_recipe(Floor.stone_walkway, Region.carpenter, 200, {Material.stone: 1})
|
||||
|
@ -174,6 +192,7 @@ crystal_path = shop_recipe(Floor.crystal_path, Region.carpenter, 200, {MetalBar.
|
|||
|
||||
spinner = skill_recipe(Fishing.spinner, Skill.fishing, 6, {MetalBar.iron: 2})
|
||||
trap_bobber = skill_recipe(Fishing.trap_bobber, Skill.fishing, 6, {MetalBar.copper: 1, Material.sap: 10})
|
||||
sonar_bobber = skill_recipe(Fishing.sonar_bobber, Skill.fishing, 6, {MetalBar.iron: 1, MetalBar.quartz: 2})
|
||||
cork_bobber = skill_recipe(Fishing.cork_bobber, Skill.fishing, 7, {Material.wood: 10, Material.hardwood: 5, Loot.slime: 10})
|
||||
quality_bobber = special_order_recipe(Fishing.quality_bobber, SpecialOrder.juicy_bugs_wanted, {MetalBar.copper: 1, Material.sap: 20, Loot.solar_essence: 5})
|
||||
treasure_hunter = skill_recipe(Fishing.treasure_hunter, Skill.fishing, 7, {MetalBar.gold: 2})
|
||||
|
@ -181,6 +200,7 @@ dressed_spinner = skill_recipe(Fishing.dressed_spinner, Skill.fishing, 8, {Metal
|
|||
barbed_hook = skill_recipe(Fishing.barbed_hook, Skill.fishing, 8, {MetalBar.copper: 1, MetalBar.iron: 1, MetalBar.gold: 1})
|
||||
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})
|
||||
crab_pot = skill_recipe(Machine.crab_pot, Skill.fishing, 3, {Material.wood: 40, MetalBar.iron: 3})
|
||||
|
@ -191,11 +211,11 @@ ring_of_yoba = skill_recipe(Ring.ring_of_yoba, Skill.combat, 7, {MetalBar.gold:
|
|||
thorns_ring = skill_recipe(Ring.thorns_ring, Skill.combat, 7, {Fossil.bone_fragment: 50, Material.stone: 50, MetalBar.gold: 1})
|
||||
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, Region.traveling_cart, 500, {MetalBar.iridium: 5, Mineral.prismatic_shard: 1})
|
||||
wedding_ring = shop_recipe(Ring.wedding_ring, LogicRegion.traveling_cart, 500, {MetalBar.iridium: 5, Mineral.prismatic_shard: 1})
|
||||
|
||||
field_snack = skill_recipe(Edible.field_snack, Skill.foraging, 1, {TreeSeed.acorn: 1, TreeSeed.maple: 1, TreeSeed.pine: 1})
|
||||
bug_steak = skill_recipe(Edible.bug_steak, Skill.combat, 1, {Loot.bug_meat: 10})
|
||||
life_elixir = skill_recipe(Edible.life_elixir, Skill.combat, 2, {Forageable.red_mushroom: 1, Forageable.purple_mushroom: 1, Forageable.morel: 1, Forageable.chanterelle: 1})
|
||||
life_elixir = skill_recipe(Edible.life_elixir, Skill.combat, 2, {Mushroom.red: 1, Mushroom.purple: 1, Mushroom.morel: 1, Mushroom.chanterelle: 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})
|
||||
|
@ -203,8 +223,10 @@ fairy_dust = quest_recipe(Consumable.fairy_dust, Quest.the_pirates_wife, {Minera
|
|||
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})
|
||||
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})
|
||||
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})
|
||||
|
@ -219,13 +241,17 @@ skull_brazier = shop_recipe(Lighting.skull_brazier, Region.carpenter, 3000, {Fos
|
|||
marble_brazier = shop_recipe(Lighting.marble_brazier, Region.carpenter, 5000, {Mineral.marble: 1, Mineral.aquamarine: 1, Material.stone: 100})
|
||||
wood_lamp_post = shop_recipe(Lighting.wood_lamp_post, Region.carpenter, 500, {Material.wood: 50, ArtisanGood.battery_pack: 1})
|
||||
iron_lamp_post = shop_recipe(Lighting.iron_lamp_post, Region.carpenter, 1000, {MetalBar.iron: 1, ArtisanGood.battery_pack: 1})
|
||||
jack_o_lantern = festival_shop_recipe(Lighting.jack_o_lantern, Region.spirit_eve, 2000, {Vegetable.pumpkin: 1, Lighting.torch: 1})
|
||||
jack_o_lantern = festival_shop_recipe(Lighting.jack_o_lantern, LogicRegion.spirit_eve, 2000, {Vegetable.pumpkin: 1, Lighting.torch: 1})
|
||||
|
||||
bone_mill = special_order_recipe(Machine.bone_mill, SpecialOrder.fragments_of_the_past, {Fossil.bone_fragment: 10, Material.clay: 3, Material.stone: 20})
|
||||
charcoal_kiln = skill_recipe(Machine.charcoal_kiln, Skill.foraging, 4, {Material.wood: 20, MetalBar.copper: 2})
|
||||
bait_maker = skill_recipe(Machine.bait_maker, Skill.fishing, 6, {MetalBar.iron: 3, WaterItem.coral: 3, WaterItem.sea_urchin: 1})
|
||||
|
||||
charcoal_kiln = skill_recipe(Machine.charcoal_kiln, Skill.foraging, 2, {Material.wood: 20, MetalBar.copper: 2})
|
||||
|
||||
crystalarium = skill_recipe(Machine.crystalarium, Skill.mining, 9, {Material.stone: 99, MetalBar.gold: 5, MetalBar.iridium: 2, ArtisanGood.battery_pack: 1})
|
||||
furnace = skill_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})
|
||||
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})
|
||||
|
@ -234,20 +260,27 @@ seed_maker = skill_recipe(Machine.seed_maker, Skill.farming, 9, {Material.wood:
|
|||
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})
|
||||
tapper = skill_recipe(Machine.tapper, Skill.foraging, 3, {Material.wood: 40, MetalBar.copper: 2})
|
||||
worm_bin = skill_recipe(Machine.worm_bin, Skill.fishing, 8, {Material.hardwood: 25, MetalBar.gold: 1, MetalBar.iron: 1, Material.fiber: 50})
|
||||
|
||||
tub_o_flowers = festival_shop_recipe(Furniture.tub_o_flowers, Region.flower_dance, 2000, {Material.wood: 15, Seed.tulip: 1, Seed.jazz: 1, Seed.poppy: 1, Seed.spangle: 1})
|
||||
tapper = skill_recipe(Machine.tapper, Skill.foraging, 4, {Material.wood: 40, MetalBar.copper: 2})
|
||||
|
||||
worm_bin = skill_recipe(Machine.worm_bin, Skill.fishing, 4, {Material.hardwood: 25, MetalBar.gold: 1, MetalBar.iron: 1, Material.fiber: 50})
|
||||
deluxe_worm_bin = skill_recipe(Machine.deluxe_worm_bin, Skill.fishing, 8, {Machine.worm_bin: 1, Material.moss: 30})
|
||||
|
||||
tub_o_flowers = festival_shop_recipe(Furniture.tub_o_flowers, LogicRegion.flower_dance, 2000,
|
||||
{Material.wood: 15, Seed.tulip: 1, Seed.jazz: 1, Seed.poppy: 1, Seed.spangle: 1})
|
||||
wicked_statue = shop_recipe(Furniture.wicked_statue, Region.sewer, 1000, {Material.stone: 25, Material.coal: 5})
|
||||
flute_block = cutscene_recipe(Furniture.flute_block, Region.carpenter, NPC.robin, 6, {Material.wood: 10, Ore.copper: 2, Material.fiber: 20})
|
||||
drum_block = cutscene_recipe(Furniture.drum_block, Region.carpenter, NPC.robin, 6, {Material.stone: 10, Ore.copper: 2, Material.fiber: 20})
|
||||
|
||||
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})
|
||||
|
||||
wood_sign = starter_recipe(Sign.wood, {Material.wood: 25})
|
||||
stone_sign = starter_recipe(Sign.stone, {Material.stone: 25})
|
||||
dark_sign = friendship_recipe(Sign.dark, NPC.krobus, 3, {Loot.bat_wing: 5, Fossil.bone_fragment: 5})
|
||||
text_sign = starter_recipe(Sign.text, {Material.wood: 25})
|
||||
|
||||
garden_pot = ap_recipe(Craftable.garden_pot, {Material.clay: 1, Material.stone: 10, MetalBar.quartz: 1}, "Greenhouse")
|
||||
scarecrow = skill_recipe(Craftable.scarecrow, Skill.farming, 1, {Material.wood: 50, Material.coal: 1, Material.fiber: 20})
|
||||
|
@ -258,56 +291,84 @@ transmute_fe = skill_recipe(Craftable.transmute_fe, Skill.mining, 4, {MetalBar.c
|
|||
transmute_au = skill_recipe(Craftable.transmute_au, Skill.mining, 7, {MetalBar.iron: 2})
|
||||
mini_jukebox = cutscene_recipe(Craftable.mini_jukebox, Region.saloon, NPC.gus, 5, {MetalBar.iron: 2, ArtisanGood.battery_pack: 1})
|
||||
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})
|
||||
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})
|
||||
cookout_kit = skill_recipe(Craftable.cookout_kit, Skill.foraging, 9, {Material.wood: 15, Material.fiber: 10, Material.coal: 3})
|
||||
|
||||
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})
|
||||
|
||||
statue_of_blessings = mastery_recipe(Statue.blessings, Skill.farming, {Material.sap: 999, Material.fiber: 999, Material.stone: 999})
|
||||
statue_of_dwarf_king = mastery_recipe(Statue.dwarf_king, Skill.mining, {MetalBar.iridium: 20})
|
||||
heavy_furnace = mastery_recipe(Machine.heavy_furnace, Skill.mining, {Machine.furnace: 2, MetalBar.iron: 3, Material.stone: 50})
|
||||
mystic_tree_seed = mastery_recipe(TreeSeed.mystic, Skill.foraging, {TreeSeed.acorn: 5, TreeSeed.maple: 5, TreeSeed.pine: 5, TreeSeed.mahogany: 5})
|
||||
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})
|
||||
|
||||
travel_charm = shop_recipe(ModCraftable.travel_core, Region.adventurer_guild, 250, {Loot.solar_essence: 1, Loot.void_essence: 1}, ModNames.magic)
|
||||
preservation_chamber = skill_recipe(ModMachine.preservation_chamber, ModSkill.archaeology, 2, {MetalBar.copper: 1, Material.wood: 15, ArtisanGood.oak_resin: 30},
|
||||
preservation_chamber = skill_recipe(ModMachine.preservation_chamber, ModSkill.archaeology, 1,
|
||||
{MetalBar.copper: 1, Material.wood: 15, ArtisanGood.oak_resin: 30},
|
||||
ModNames.archaeology)
|
||||
preservation_chamber_h = skill_recipe(ModMachine.hardwood_preservation_chamber, ModSkill.archaeology, 7, {MetalBar.copper: 1, Material.hardwood: 15,
|
||||
restoration_table = skill_recipe(ModMachine.restoration_table, ModSkill.archaeology, 1, {Material.wood: 15, MetalBar.copper: 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, 8, {Artifact.rusty_cog: 10, MetalBar.iron: 5, ArtisanGood.battery_pack: 1}, ModNames.archaeology)
|
||||
ancient_battery = skill_recipe(ModMachine.ancient_battery, ModSkill.archaeology, 6, {Material.stone: 40, MetalBar.copper: 10, MetalBar.iron: 5},
|
||||
grinder = skill_recipe(ModMachine.grinder, ModSkill.archaeology, 2, {Artifact.rusty_cog: 10, MetalBar.iron: 5, ArtisanGood.battery_pack: 1},
|
||||
ModNames.archaeology)
|
||||
glass_bazier = skill_recipe(ModCraftable.glass_bazier, ModSkill.archaeology, 1, {Artifact.glass_shards: 10}, ModNames.archaeology)
|
||||
glass_path = skill_recipe(ModFloor.glass_path, ModSkill.archaeology, 1, {Artifact.glass_shards: 1}, ModNames.archaeology)
|
||||
glass_fence = skill_recipe(ModCraftable.glass_fence, ModSkill.archaeology, 1, {Artifact.glass_shards: 5}, ModNames.archaeology)
|
||||
bone_path = skill_recipe(ModFloor.bone_path, ModSkill.archaeology, 3, {Fossil.bone_fragment: 1}, ModNames.archaeology)
|
||||
ancient_battery = skill_recipe(ModMachine.ancient_battery, ModSkill.archaeology, 7, {Material.stone: 40, MetalBar.copper: 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)
|
||||
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, 2, {Material.wood: 25}, 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)
|
||||
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, SVEForage.void_soul: 5, Ingredient.sugar: 1,
|
||||
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)
|
||||
hero_elixir = shop_recipe(ModEdible.hero_elixir, SVERegion.isaac_shop, 65000, {SVEForage.void_pebble: 3, SVEForage.void_soul: 5, Ingredient.oil: 1,
|
||||
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)
|
||||
armor_elixir = shop_recipe(ModEdible.armor_elixir, SVERegion.alesia_shop, 50000, {Loot.solar_essence: 30, SVEForage.void_soul: 5, Ingredient.vinegar: 5,
|
||||
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)
|
||||
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)
|
||||
Material.cinder_shard: 1, DistantLandsForageable.swamp_herb: 1},
|
||||
ModNames.distant_lands)
|
||||
|
||||
neanderthal_skeleton = shop_recipe(ModCraftable.neanderthal_skeleton, Region.mines_dwarf_shop, 5000,
|
||||
{ModFossil.neanderthal_skull: 1, ModFossil.neanderthal_ribs: 1, ModFossil.neanderthal_pelvis: 1, ModFossil.neanderthal_limb_bones: 1,
|
||||
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)
|
||||
pterodactyl_skeleton_l = shop_recipe(ModCraftable.pterodactyl_skeleton_l, Region.mines_dwarf_shop, 5000,
|
||||
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)
|
||||
pterodactyl_skeleton_m = shop_recipe(ModCraftable.pterodactyl_skeleton_m, Region.mines_dwarf_shop, 5000,
|
||||
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)
|
||||
pterodactyl_skeleton_r = shop_recipe(ModCraftable.pterodactyl_skeleton_r, Region.mines_dwarf_shop, 5000,
|
||||
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)
|
||||
trex_skeleton_l = shop_recipe(ModCraftable.trex_skeleton_l, Region.mines_dwarf_shop, 5000,
|
||||
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)
|
||||
trex_skeleton_m = shop_recipe(ModCraftable.trex_skeleton_m, Region.mines_dwarf_shop, 5000,
|
||||
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)
|
||||
trex_skeleton_r = shop_recipe(ModCraftable.trex_skeleton_r, Region.mines_dwarf_shop, 5000,
|
||||
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)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
all_crafting_recipes_by_name = {recipe.item: recipe for recipe in all_crafting_recipes}
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
crop,farm_growth_seasons,seed,seed_seasons,seed_regions,requires_island
|
||||
Amaranth,Fall,Amaranth Seeds,Fall,"Pierre's General Store",False
|
||||
Artichoke,Fall,Artichoke Seeds,Fall,"Pierre's General Store",False
|
||||
Beet,Fall,Beet Seeds,Fall,Oasis,False
|
||||
Blue Jazz,Spring,Jazz Seeds,Spring,"Pierre's General Store",False
|
||||
Blueberry,Summer,Blueberry Seeds,Summer,"Pierre's General Store",False
|
||||
Bok Choy,Fall,Bok Choy Seeds,Fall,"Pierre's General Store",False
|
||||
Cactus Fruit,,Cactus Seeds,,Oasis,False
|
||||
Cauliflower,Spring,Cauliflower Seeds,Spring,"Pierre's General Store",False
|
||||
Coffee Bean,"Spring,Summer",Coffee Bean,"Summer,Fall","Traveling Cart",False
|
||||
Corn,"Summer,Fall",Corn Seeds,"Summer,Fall","Pierre's General Store",False
|
||||
Cranberries,Fall,Cranberry Seeds,Fall,"Pierre's General Store",False
|
||||
Eggplant,Fall,Eggplant Seeds,Fall,"Pierre's General Store",False
|
||||
Fairy Rose,Fall,Fairy Seeds,Fall,"Pierre's General Store",False
|
||||
Garlic,Spring,Garlic Seeds,Spring,"Pierre's General Store",False
|
||||
Grape,Fall,Grape Starter,Fall,"Pierre's General Store",False
|
||||
Green Bean,Spring,Bean Starter,Spring,"Pierre's General Store",False
|
||||
Hops,Summer,Hops Starter,Summer,"Pierre's General Store",False
|
||||
Hot Pepper,Summer,Pepper Seeds,Summer,"Pierre's General Store",False
|
||||
Kale,Spring,Kale Seeds,Spring,"Pierre's General Store",False
|
||||
Melon,Summer,Melon Seeds,Summer,"Pierre's General Store",False
|
||||
Parsnip,Spring,Parsnip Seeds,Spring,"Pierre's General Store",False
|
||||
Pineapple,Summer,Pineapple Seeds,Summer,"Island Trader",True
|
||||
Poppy,Summer,Poppy Seeds,Summer,"Pierre's General Store",False
|
||||
Potato,Spring,Potato Seeds,Spring,"Pierre's General Store",False
|
||||
Qi Fruit,"Spring,Summer,Fall,Winter",Qi Bean,"Spring,Summer,Fall,Winter","Qi's Walnut Room",True
|
||||
Pumpkin,Fall,Pumpkin Seeds,Fall,"Pierre's General Store",False
|
||||
Radish,Summer,Radish Seeds,Summer,"Pierre's General Store",False
|
||||
Red Cabbage,Summer,Red Cabbage Seeds,Summer,"Pierre's General Store",False
|
||||
Rhubarb,Spring,Rhubarb Seeds,Spring,Oasis,False
|
||||
Starfruit,Summer,Starfruit Seeds,Summer,Oasis,False
|
||||
Strawberry,Spring,Strawberry Seeds,Spring,"Pierre's General Store",False
|
||||
Summer Spangle,Summer,Spangle Seeds,Summer,"Pierre's General Store",False
|
||||
Sunflower,"Summer,Fall",Sunflower Seeds,"Summer,Fall","Pierre's General Store",False
|
||||
Sweet Gem Berry,Fall,Rare Seed,"Spring,Summer",Traveling Cart,False
|
||||
Taro Root,Summer,Taro Tuber,Summer,"Island Trader",True
|
||||
Tomato,Summer,Tomato Seeds,Summer,"Pierre's General Store",False
|
||||
Tulip,Spring,Tulip Bulb,Spring,"Pierre's General Store",False
|
||||
Unmilled Rice,Spring,Rice Shoot,Spring,"Pierre's General Store",False
|
||||
Wheat,"Summer,Fall",Wheat Seeds,"Summer,Fall","Pierre's General Store",False
|
||||
Yam,Fall,Yam Seeds,Fall,"Pierre's General Store",False
|
|
|
@ -1,50 +0,0 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Tuple
|
||||
|
||||
from .. import data
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SeedItem:
|
||||
name: str
|
||||
seasons: Tuple[str]
|
||||
regions: Tuple[str]
|
||||
requires_island: bool
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CropItem:
|
||||
name: str
|
||||
farm_growth_seasons: Tuple[str]
|
||||
seed: SeedItem
|
||||
|
||||
|
||||
def load_crop_csv():
|
||||
import csv
|
||||
try:
|
||||
from importlib.resources import files
|
||||
except ImportError:
|
||||
from importlib_resources import files # noqa
|
||||
|
||||
with files(data).joinpath("crops.csv").open() as file:
|
||||
reader = csv.DictReader(file)
|
||||
crops = []
|
||||
seeds = []
|
||||
|
||||
for item in reader:
|
||||
seeds.append(SeedItem(item["seed"],
|
||||
tuple(season for season in item["seed_seasons"].split(","))
|
||||
if item["seed_seasons"] else tuple(),
|
||||
tuple(region for region in item["seed_regions"].split(","))
|
||||
if item["seed_regions"] else tuple(),
|
||||
item["requires_island"] == "True"))
|
||||
crops.append(CropItem(item["crop"],
|
||||
tuple(season for season in item["farm_growth_seasons"].split(","))
|
||||
if item["farm_growth_seasons"] else tuple(),
|
||||
seeds[-1]))
|
||||
return crops, seeds
|
||||
|
||||
|
||||
# TODO Those two should probably be split to we can include rest of seeds
|
||||
all_crops, all_purchasable_seeds = load_crop_csv()
|
||||
crops_by_name = {crop.name: crop for crop in all_crops}
|
|
@ -1,10 +1,10 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import List, Tuple, Union, Optional, Set
|
||||
from typing import Tuple, Union, Optional
|
||||
|
||||
from . import season_data as season
|
||||
from ..strings.fish_names import Fish, SVEFish, DistantLandsFish
|
||||
from ..strings.region_names import Region, SVERegion
|
||||
from ..mods.mod_data import ModNames
|
||||
from ..strings.fish_names import Fish, SVEFish, DistantLandsFish
|
||||
from ..strings.region_names import Region, SVERegion, LogicRegion
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
@ -30,6 +30,7 @@ town_river = (Region.town,)
|
|||
mountain_lake = (Region.mountain,)
|
||||
forest_pond = (Region.forest,)
|
||||
forest_river = (Region.forest,)
|
||||
forest_waterfall = (LogicRegion.forest_waterfall,)
|
||||
secret_woods = (Region.secret_woods,)
|
||||
mines_floor_20 = (Region.mines_floor_20,)
|
||||
mines_floor_60 = (Region.mines_floor_60,)
|
||||
|
@ -50,8 +51,6 @@ sprite_spring = (SVERegion.sprite_spring,)
|
|||
fable_reef = (SVERegion.fable_reef,)
|
||||
vineyard = (SVERegion.blue_moon_vineyard,)
|
||||
|
||||
all_fish: List[FishItem] = []
|
||||
|
||||
|
||||
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:
|
||||
|
@ -59,63 +58,63 @@ def create_fish(name: str, locations: Tuple[str, ...], seasons: Union[str, Tuple
|
|||
seasons = (seasons,)
|
||||
|
||||
fish_item = FishItem(name, locations, seasons, difficulty, legendary, extended_family, mod_name)
|
||||
all_fish.append(fish_item)
|
||||
return fish_item
|
||||
|
||||
|
||||
albacore = create_fish("Albacore", ocean, (season.fall, season.winter), 60)
|
||||
anchovy = create_fish("Anchovy", ocean, (season.spring, season.fall), 30)
|
||||
blue_discus = create_fish("Blue Discus", ginger_island_river, season.all_seasons, 60)
|
||||
bream = create_fish("Bream", town_river + forest_river, season.all_seasons, 35)
|
||||
bullhead = create_fish("Bullhead", mountain_lake, season.all_seasons, 46)
|
||||
albacore = create_fish(Fish.albacore, ocean, (season.fall, season.winter), 60)
|
||||
anchovy = create_fish(Fish.anchovy, ocean, (season.spring, season.fall), 30)
|
||||
blue_discus = create_fish(Fish.blue_discus, ginger_island_river, season.all_seasons, 60)
|
||||
bream = create_fish(Fish.bream, town_river + forest_river, season.all_seasons, 35)
|
||||
bullhead = create_fish(Fish.bullhead, mountain_lake, season.all_seasons, 46)
|
||||
carp = create_fish(Fish.carp, mountain_lake + secret_woods + sewers + mutant_bug_lair, season.not_winter, 15)
|
||||
catfish = create_fish("Catfish", town_river + forest_river + secret_woods, (season.spring, season.fall), 75)
|
||||
chub = create_fish("Chub", forest_river + mountain_lake, season.all_seasons, 35)
|
||||
dorado = create_fish("Dorado", forest_river, season.summer, 78)
|
||||
eel = create_fish("Eel", ocean, (season.spring, season.fall), 70)
|
||||
flounder = create_fish("Flounder", ocean, (season.spring, season.summer), 50)
|
||||
ghostfish = create_fish("Ghostfish", mines_floor_20 + mines_floor_60, season.all_seasons, 50)
|
||||
halibut = create_fish("Halibut", ocean, season.not_fall, 50)
|
||||
herring = create_fish("Herring", ocean, (season.spring, season.winter), 25)
|
||||
ice_pip = create_fish("Ice Pip", mines_floor_60, season.all_seasons, 85)
|
||||
largemouth_bass = create_fish("Largemouth Bass", mountain_lake, season.all_seasons, 50)
|
||||
lava_eel = create_fish("Lava Eel", mines_floor_100, season.all_seasons, 90)
|
||||
lingcod = create_fish("Lingcod", town_river + forest_river + mountain_lake, season.winter, 85)
|
||||
lionfish = create_fish("Lionfish", ginger_island_ocean, season.all_seasons, 50)
|
||||
midnight_carp = create_fish("Midnight Carp", mountain_lake + forest_pond + ginger_island_river,
|
||||
catfish = create_fish(Fish.catfish, town_river + forest_river + secret_woods, (season.spring, season.fall), 75)
|
||||
chub = create_fish(Fish.chub, forest_river + mountain_lake, season.all_seasons, 35)
|
||||
dorado = create_fish(Fish.dorado, forest_river, season.summer, 78)
|
||||
eel = create_fish(Fish.eel, ocean, (season.spring, season.fall), 70)
|
||||
flounder = create_fish(Fish.flounder, ocean, (season.spring, season.summer), 50)
|
||||
ghostfish = create_fish(Fish.ghostfish, mines_floor_20 + mines_floor_60, season.all_seasons, 50)
|
||||
goby = create_fish(Fish.goby, forest_waterfall, season.all_seasons, 55)
|
||||
halibut = create_fish(Fish.halibut, ocean, season.not_fall, 50)
|
||||
herring = create_fish(Fish.herring, ocean, (season.spring, season.winter), 25)
|
||||
ice_pip = create_fish(Fish.ice_pip, mines_floor_60, season.all_seasons, 85)
|
||||
largemouth_bass = create_fish(Fish.largemouth_bass, mountain_lake, season.all_seasons, 50)
|
||||
lava_eel = create_fish(Fish.lava_eel, mines_floor_100, season.all_seasons, 90)
|
||||
lingcod = create_fish(Fish.lingcod, town_river + forest_river + mountain_lake, season.winter, 85)
|
||||
lionfish = create_fish(Fish.lionfish, ginger_island_ocean, season.all_seasons, 50)
|
||||
midnight_carp = create_fish(Fish.midnight_carp, mountain_lake + forest_pond + ginger_island_river,
|
||||
(season.fall, season.winter), 55)
|
||||
octopus = create_fish("Octopus", ocean, season.summer, 95)
|
||||
perch = create_fish("Perch", town_river + forest_river + forest_pond + mountain_lake, season.winter, 35)
|
||||
pike = create_fish("Pike", town_river + forest_river + forest_pond, (season.summer, season.winter), 60)
|
||||
pufferfish = create_fish("Pufferfish", ocean + ginger_island_ocean, season.summer, 80)
|
||||
rainbow_trout = create_fish("Rainbow Trout", town_river + forest_river + mountain_lake, season.summer, 45)
|
||||
red_mullet = create_fish("Red Mullet", ocean, (season.summer, season.winter), 55)
|
||||
red_snapper = create_fish("Red Snapper", ocean, (season.summer, season.fall), 40)
|
||||
salmon = create_fish("Salmon", town_river + forest_river, season.fall, 50)
|
||||
sandfish = create_fish("Sandfish", desert, season.all_seasons, 65)
|
||||
sardine = create_fish("Sardine", ocean, (season.spring, season.fall, season.winter), 30)
|
||||
scorpion_carp = create_fish("Scorpion Carp", desert, season.all_seasons, 90)
|
||||
sea_cucumber = create_fish("Sea Cucumber", ocean, (season.fall, season.winter), 40)
|
||||
shad = create_fish("Shad", town_river + forest_river, season.not_winter, 45)
|
||||
slimejack = create_fish("Slimejack", mutant_bug_lair, season.all_seasons, 55)
|
||||
smallmouth_bass = create_fish("Smallmouth Bass", town_river + forest_river, (season.spring, season.fall), 28)
|
||||
squid = create_fish("Squid", ocean, season.winter, 75)
|
||||
stingray = create_fish("Stingray", pirate_cove, season.all_seasons, 80)
|
||||
stonefish = create_fish("Stonefish", mines_floor_20, season.all_seasons, 65)
|
||||
sturgeon = create_fish("Sturgeon", mountain_lake, (season.summer, season.winter), 78)
|
||||
sunfish = create_fish("Sunfish", town_river + forest_river, (season.spring, season.summer), 30)
|
||||
super_cucumber = create_fish("Super Cucumber", ocean + ginger_island_ocean, (season.summer, season.fall), 80)
|
||||
tiger_trout = create_fish("Tiger Trout", town_river + forest_river, (season.fall, season.winter), 60)
|
||||
tilapia = create_fish("Tilapia", ocean + ginger_island_ocean, (season.summer, season.fall), 50)
|
||||
octopus = create_fish(Fish.octopus, ocean, season.summer, 95)
|
||||
perch = create_fish(Fish.perch, town_river + forest_river + forest_pond + mountain_lake, season.winter, 35)
|
||||
pike = create_fish(Fish.pike, town_river + forest_river + forest_pond, (season.summer, season.winter), 60)
|
||||
pufferfish = create_fish(Fish.pufferfish, ocean + ginger_island_ocean, season.summer, 80)
|
||||
rainbow_trout = create_fish(Fish.rainbow_trout, town_river + forest_river + mountain_lake, season.summer, 45)
|
||||
red_mullet = create_fish(Fish.red_mullet, ocean, (season.summer, season.winter), 55)
|
||||
red_snapper = create_fish(Fish.red_snapper, ocean, (season.summer, season.fall), 40)
|
||||
salmon = create_fish(Fish.salmon, town_river + forest_river, season.fall, 50)
|
||||
sandfish = create_fish(Fish.sandfish, desert, season.all_seasons, 65)
|
||||
sardine = create_fish(Fish.sardine, ocean, (season.spring, season.fall, season.winter), 30)
|
||||
scorpion_carp = create_fish(Fish.scorpion_carp, desert, season.all_seasons, 90)
|
||||
sea_cucumber = create_fish(Fish.sea_cucumber, ocean, (season.fall, season.winter), 40)
|
||||
shad = create_fish(Fish.shad, town_river + forest_river, season.not_winter, 45)
|
||||
slimejack = create_fish(Fish.slimejack, mutant_bug_lair, season.all_seasons, 55)
|
||||
smallmouth_bass = create_fish(Fish.smallmouth_bass, town_river + forest_river, (season.spring, season.fall), 28)
|
||||
squid = create_fish(Fish.squid, ocean, season.winter, 75)
|
||||
stingray = create_fish(Fish.stingray, pirate_cove, season.all_seasons, 80)
|
||||
stonefish = create_fish(Fish.stonefish, mines_floor_20, season.all_seasons, 65)
|
||||
sturgeon = create_fish(Fish.sturgeon, mountain_lake, (season.summer, season.winter), 78)
|
||||
sunfish = create_fish(Fish.sunfish, town_river + forest_river, (season.spring, season.summer), 30)
|
||||
super_cucumber = create_fish(Fish.super_cucumber, ocean + ginger_island_ocean, (season.summer, season.fall), 80)
|
||||
tiger_trout = create_fish(Fish.tiger_trout, town_river + forest_river, (season.fall, season.winter), 60)
|
||||
tilapia = create_fish(Fish.tilapia, ocean + ginger_island_ocean, (season.summer, season.fall), 50)
|
||||
# Tuna has different seasons on ginger island. Should be changed when the whole fish thing is refactored
|
||||
tuna = create_fish("Tuna", ocean + ginger_island_ocean, (season.summer, season.winter), 70)
|
||||
void_salmon = create_fish("Void Salmon", witch_swamp, season.all_seasons, 80)
|
||||
walleye = create_fish("Walleye", town_river + forest_river + forest_pond + mountain_lake, season.fall, 45)
|
||||
woodskip = create_fish("Woodskip", secret_woods, season.all_seasons, 50)
|
||||
tuna = create_fish(Fish.tuna, ocean + ginger_island_ocean, (season.summer, season.winter), 70)
|
||||
void_salmon = create_fish(Fish.void_salmon, witch_swamp, season.all_seasons, 80)
|
||||
walleye = create_fish(Fish.walleye, town_river + forest_river + forest_pond + mountain_lake, season.fall, 45)
|
||||
woodskip = create_fish(Fish.woodskip, secret_woods, season.all_seasons, 50)
|
||||
|
||||
blob_fish = create_fish("Blobfish", night_market, season.winter, 75)
|
||||
midnight_squid = create_fish("Midnight Squid", night_market, season.winter, 55)
|
||||
spook_fish = create_fish("Spook Fish", night_market, season.winter, 60)
|
||||
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, ocean, season.summer, 95, True, False)
|
||||
|
@ -155,37 +154,21 @@ undeadfish = create_fish(SVEFish.undeadfish, crimson_badlands, season.all_season
|
|||
void_eel = create_fish(SVEFish.void_eel, witch_swamp, season.all_seasons, 100, mod_name=ModNames.sve)
|
||||
water_grub = create_fish(SVEFish.water_grub, mutant_bug_lair, season.all_seasons, 60, mod_name=ModNames.sve)
|
||||
sea_sponge = create_fish(SVEFish.sea_sponge, ginger_island_ocean, season.all_seasons, 40, mod_name=ModNames.sve)
|
||||
dulse_seaweed = create_fish(SVEFish.dulse_seaweed, vineyard, season.all_seasons, 50, mod_name=ModNames.sve)
|
||||
|
||||
void_minnow = create_fish(DistantLandsFish.void_minnow, witch_swamp, season.all_seasons, 15, mod_name=ModNames.distant_lands)
|
||||
purple_algae = create_fish(DistantLandsFish.purple_algae, witch_swamp, season.all_seasons, 15, mod_name=ModNames.distant_lands)
|
||||
swamp_leech = create_fish(DistantLandsFish.swamp_leech, witch_swamp, season.all_seasons, 15, mod_name=ModNames.distant_lands)
|
||||
giant_horsehoe_crab = create_fish(DistantLandsFish.giant_horsehoe_crab, witch_swamp, season.all_seasons, 90, mod_name=ModNames.distant_lands)
|
||||
|
||||
clam = create_fish(Fish.clam, ocean, season.all_seasons, -1)
|
||||
cockle = create_fish(Fish.cockle, ocean, season.all_seasons, -1)
|
||||
crab = create_fish(Fish.crab, ocean, season.all_seasons, -1)
|
||||
crayfish = create_fish(Fish.crayfish, fresh_water, season.all_seasons, -1)
|
||||
lobster = create_fish(Fish.lobster, ocean, season.all_seasons, -1)
|
||||
mussel = create_fish(Fish.mussel, ocean, season.all_seasons, -1)
|
||||
oyster = create_fish(Fish.oyster, ocean, season.all_seasons, -1)
|
||||
periwinkle = create_fish(Fish.periwinkle, fresh_water, season.all_seasons, -1)
|
||||
shrimp = create_fish(Fish.shrimp, ocean, season.all_seasons, -1)
|
||||
snail = create_fish(Fish.snail, fresh_water, season.all_seasons, -1)
|
||||
|
||||
clam = create_fish("Clam", ocean, season.all_seasons, -1)
|
||||
cockle = create_fish("Cockle", ocean, season.all_seasons, -1)
|
||||
crab = create_fish("Crab", ocean, season.all_seasons, -1)
|
||||
crayfish = create_fish("Crayfish", fresh_water, season.all_seasons, -1)
|
||||
lobster = create_fish("Lobster", ocean, season.all_seasons, -1)
|
||||
mussel = create_fish("Mussel", ocean, season.all_seasons, -1)
|
||||
oyster = create_fish("Oyster", ocean, season.all_seasons, -1)
|
||||
periwinkle = create_fish("Periwinkle", fresh_water, season.all_seasons, -1)
|
||||
shrimp = create_fish("Shrimp", ocean, season.all_seasons, -1)
|
||||
snail = create_fish("Snail", fresh_water, season.all_seasons, -1)
|
||||
|
||||
legendary_fish = [angler, crimsonfish, glacierfish, legend, mutant_carp]
|
||||
extended_family = [ms_angler, son_of_crimsonfish, glacierfish_jr, legend_ii, radioactive_carp]
|
||||
special_fish = [*legendary_fish, blob_fish, lava_eel, octopus, scorpion_carp, ice_pip, super_cucumber, dorado]
|
||||
island_fish = [lionfish, blue_discus, stingray, *extended_family]
|
||||
|
||||
all_fish_by_name = {fish.name: fish for fish in all_fish}
|
||||
|
||||
|
||||
def get_fish_for_mods(mods: Set[str]) -> List[FishItem]:
|
||||
fish_for_mods = []
|
||||
for fish in all_fish:
|
||||
if fish.mod_name and fish.mod_name not in mods:
|
||||
continue
|
||||
fish_for_mods.append(fish)
|
||||
return fish_for_mods
|
||||
vanilla_legendary_fish = [angler, crimsonfish, glacierfish, legend, mutant_carp]
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
import enum
|
||||
import sys
|
||||
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 ..stardew_rule.protocol import StardewRule
|
||||
|
||||
if sys.version_info >= (3, 10):
|
||||
kw_only = {"kw_only": True}
|
||||
else:
|
||||
kw_only = {}
|
||||
|
||||
DEFAULT_REQUIREMENT_TAGS = MappingProxyType({})
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Requirement(ABC):
|
||||
...
|
||||
|
||||
|
||||
class ItemTag(enum.Enum):
|
||||
CROPSANITY_SEED = enum.auto()
|
||||
CROPSANITY = enum.auto()
|
||||
FISH = enum.auto()
|
||||
FRUIT = enum.auto()
|
||||
VEGETABLE = enum.auto()
|
||||
EDIBLE_MUSHROOM = enum.auto()
|
||||
BOOK = enum.auto()
|
||||
BOOK_POWER = enum.auto()
|
||||
BOOK_SKILL = enum.auto()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ItemSource(ABC):
|
||||
add_tags: ClassVar[Tuple[ItemTag]] = ()
|
||||
|
||||
@property
|
||||
def requirement_tags(self) -> Mapping[str, Tuple[ItemTag, ...]]:
|
||||
return DEFAULT_REQUIREMENT_TAGS
|
||||
|
||||
# FIXME this should just be an optional field, but kw_only requires python 3.10...
|
||||
@property
|
||||
def other_requirements(self) -> Iterable[Requirement]:
|
||||
return ()
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class GenericSource(ItemSource):
|
||||
regions: Tuple[str, ...] = ()
|
||||
"""No region means it's available everywhere."""
|
||||
other_requirements: Tuple[Requirement, ...] = ()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CustomRuleSource(ItemSource):
|
||||
"""Hopefully once everything is migrated to sources, we won't need these custom logic anymore."""
|
||||
create_rule: Callable[[Any], StardewRule]
|
||||
|
||||
|
||||
class Tag(ItemSource):
|
||||
"""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, ...]
|
||||
|
||||
def __init__(self, *tag: ItemTag):
|
||||
self.tag = tag # noqa
|
||||
|
||||
@property
|
||||
def add_tags(self):
|
||||
return self.tag
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GameItem:
|
||||
name: str
|
||||
sources: List[ItemSource] = field(default_factory=list)
|
||||
tags: Set[ItemTag] = field(default_factory=set)
|
||||
|
||||
def add_sources(self, sources: Iterable[ItemSource]):
|
||||
self.sources.extend(source for source in sources if type(source) is not Tag)
|
||||
for source in sources:
|
||||
self.add_tags(source.add_tags)
|
||||
|
||||
def add_tags(self, tags: Iterable[ItemTag]):
|
||||
self.tags.update(tags)
|
|
@ -0,0 +1,66 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Tuple, Sequence, Mapping
|
||||
|
||||
from .game_item import ItemSource, kw_only, ItemTag, Requirement
|
||||
from ..strings.season_names import Season
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class ForagingSource(ItemSource):
|
||||
regions: Tuple[str, ...]
|
||||
seasons: Tuple[str, ...] = Season.all
|
||||
other_requirements: Tuple[Requirement, ...] = ()
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class SeasonalForagingSource(ItemSource):
|
||||
season: str
|
||||
days: Sequence[int]
|
||||
regions: Tuple[str, ...]
|
||||
|
||||
def as_foraging_source(self) -> ForagingSource:
|
||||
return ForagingSource(seasons=(self.season,), regions=self.regions)
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class FruitBatsSource(ItemSource):
|
||||
...
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class MushroomCaveSource(ItemSource):
|
||||
...
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class HarvestFruitTreeSource(ItemSource):
|
||||
add_tags = (ItemTag.CROPSANITY,)
|
||||
|
||||
sapling: str
|
||||
seasons: Tuple[str, ...] = Season.all
|
||||
|
||||
@property
|
||||
def requirement_tags(self) -> Mapping[str, Tuple[ItemTag, ...]]:
|
||||
return {
|
||||
self.sapling: (ItemTag.CROPSANITY_SEED,)
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class HarvestCropSource(ItemSource):
|
||||
add_tags = (ItemTag.CROPSANITY,)
|
||||
|
||||
seed: str
|
||||
seasons: Tuple[str, ...] = Season.all
|
||||
"""Empty means it can't be grown on the farm."""
|
||||
|
||||
@property
|
||||
def requirement_tags(self) -> Mapping[str, Tuple[ItemTag, ...]]:
|
||||
return {
|
||||
self.seed: (ItemTag.CROPSANITY_SEED,)
|
||||
}
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class ArtifactSpotSource(ItemSource):
|
||||
amount: int
|
|
@ -54,7 +54,7 @@ id,name,classification,groups,mod_name
|
|||
68,Progressive Watering Can,progression,PROGRESSIVE_TOOLS,
|
||||
69,Progressive Trash Can,progression,PROGRESSIVE_TOOLS,
|
||||
70,Progressive Fishing Rod,progression,PROGRESSIVE_TOOLS,
|
||||
71,Golden Scythe,useful,,
|
||||
71,Golden Scythe,useful,DEPRECATED,
|
||||
72,Progressive Mine Elevator,progression,,
|
||||
73,Farming Level,progression,SKILL_LEVEL_UP,
|
||||
74,Fishing Level,progression,SKILL_LEVEL_UP,
|
||||
|
@ -92,8 +92,8 @@ id,name,classification,groups,mod_name
|
|||
106,Galaxy Sword,filler,"WEAPON,DEPRECATED",
|
||||
107,Galaxy Dagger,filler,"WEAPON,DEPRECATED",
|
||||
108,Galaxy Hammer,filler,"WEAPON,DEPRECATED",
|
||||
109,Movement Speed Bonus,progression,,
|
||||
110,Luck Bonus,progression,,
|
||||
109,Movement Speed Bonus,useful,,
|
||||
110,Luck Bonus,filler,PLAYER_BUFF,
|
||||
111,Lava Katana,filler,"WEAPON,DEPRECATED",
|
||||
112,Progressive House,progression,,
|
||||
113,Traveling Merchant: Sunday,progression,TRAVELING_MERCHANT_DAY,
|
||||
|
@ -104,7 +104,7 @@ id,name,classification,groups,mod_name
|
|||
118,Traveling Merchant: Friday,progression,TRAVELING_MERCHANT_DAY,
|
||||
119,Traveling Merchant: Saturday,progression,TRAVELING_MERCHANT_DAY,
|
||||
120,Traveling Merchant Stock Size,useful,,
|
||||
121,Traveling Merchant Discount,useful,,
|
||||
121,Traveling Merchant Discount,useful,DEPRECATED,
|
||||
122,Return Scepter,useful,,
|
||||
123,Progressive Season,progression,,
|
||||
124,Spring,progression,SEASON,
|
||||
|
@ -398,6 +398,7 @@ id,name,classification,groups,mod_name
|
|||
417,Tropical Curry Recipe,progression,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
|
||||
418,Trout Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_QOS",
|
||||
419,Vegetable Medley Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
|
||||
420,Moss Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",
|
||||
425,Gate Recipe,progression,CRAFTSANITY,
|
||||
426,Wood Fence Recipe,progression,CRAFTSANITY,
|
||||
427,Deluxe Retaining Soil Recipe,progression,"CRAFTSANITY,GINGER_ISLAND",
|
||||
|
@ -430,7 +431,7 @@ id,name,classification,groups,mod_name
|
|||
454,Marble Brazier Recipe,progression,CRAFTSANITY,
|
||||
455,Wood Lamp-post Recipe,progression,CRAFTSANITY,
|
||||
456,Iron Lamp-post Recipe,progression,CRAFTSANITY,
|
||||
457,Furnace Recipe,progression,CRAFTSANITY,
|
||||
457,Furnace Recipe,progression,"CRAFTSANITY",
|
||||
458,Wicked Statue Recipe,progression,CRAFTSANITY,
|
||||
459,Chest Recipe,progression,CRAFTSANITY,
|
||||
460,Wood Sign Recipe,progression,CRAFTSANITY,
|
||||
|
@ -439,6 +440,75 @@ id,name,classification,groups,mod_name
|
|||
470,Fruit Bats,progression,,
|
||||
471,Mushroom Boxes,progression,,
|
||||
475,The Gateway Gazette,progression,TV_CHANNEL,
|
||||
476,Carrot Seeds,progression,CROPSANITY,
|
||||
477,Summer Squash Seeds,progression,CROPSANITY,
|
||||
478,Broccoli Seeds,progression,CROPSANITY,
|
||||
479,Powdermelon Seeds,progression,CROPSANITY,
|
||||
480,Progressive Raccoon,progression,,
|
||||
481,Farming Mastery,progression,SKILL_MASTERY,
|
||||
482,Mining Mastery,progression,SKILL_MASTERY,
|
||||
483,Foraging Mastery,progression,SKILL_MASTERY,
|
||||
484,Fishing Mastery,progression,SKILL_MASTERY,
|
||||
485,Combat Mastery,progression,SKILL_MASTERY,
|
||||
486,Fish Smoker Recipe,progression,CRAFTSANITY,
|
||||
487,Dehydrator Recipe,progression,CRAFTSANITY,
|
||||
488,Big Chest Recipe,progression,CRAFTSANITY,
|
||||
489,Big Stone Chest Recipe,progression,CRAFTSANITY,
|
||||
490,Text Sign Recipe,progression,CRAFTSANITY,
|
||||
491,Blue Grass Starter Recipe,progression,"QI_CRAFTING_RECIPE,GINGER_ISLAND",
|
||||
492,Mastery Of The Five Ways,progression,SKILL_MASTERY,
|
||||
493,Progressive Scythe,useful,,
|
||||
494,Progressive Pan,progression,PROGRESSIVE_TOOLS,
|
||||
495,Calico Statue,filler,FESTIVAL,
|
||||
496,Mummy Mask,filler,FESTIVAL,
|
||||
497,Free Cactis,filler,FESTIVAL,
|
||||
498,Gil's Hat,filler,FESTIVAL,
|
||||
499,Bucket Hat,filler,FESTIVAL,
|
||||
500,Mounted Trout,filler,FESTIVAL,
|
||||
501,'Squid Kid',filler,FESTIVAL,
|
||||
502,Squid Hat,filler,FESTIVAL,
|
||||
503,Resource Pack: 200 Calico Egg,useful,"FESTIVAL",
|
||||
504,Resource Pack: 120 Calico Egg,useful,"FESTIVAL",
|
||||
505,Resource Pack: 100 Calico Egg,useful,"FESTIVAL",
|
||||
506,Resource Pack: 50 Calico Egg,useful,"FESTIVAL",
|
||||
507,Resource Pack: 40 Calico Egg,useful,"FESTIVAL",
|
||||
508,Resource Pack: 35 Calico Egg,useful,"FESTIVAL",
|
||||
509,Resource Pack: 30 Calico Egg,useful,"FESTIVAL",
|
||||
510,Book: The Art O' Crabbing,useful,"FESTIVAL",
|
||||
511,Mr Qi's Plane Ride,progression,,
|
||||
521,Power: Price Catalogue,useful,"BOOK_POWER",
|
||||
522,Power: Mapping Cave Systems,useful,"BOOK_POWER",
|
||||
523,Power: Way Of The Wind pt. 1,progression,"BOOK_POWER",
|
||||
524,Power: Way Of The Wind pt. 2,useful,"BOOK_POWER",
|
||||
525,Power: Monster Compendium,useful,"BOOK_POWER",
|
||||
526,Power: Friendship 101,useful,"BOOK_POWER",
|
||||
527,"Power: Jack Be Nimble, Jack Be Thick",useful,"BOOK_POWER",
|
||||
528,Power: Woody's Secret,useful,"BOOK_POWER",
|
||||
529,Power: Raccoon Journal,useful,"BOOK_POWER",
|
||||
530,Power: Jewels Of The Sea,useful,"BOOK_POWER",
|
||||
531,Power: Dwarvish Safety Manual,useful,"BOOK_POWER",
|
||||
532,Power: The Art O' Crabbing,useful,"BOOK_POWER",
|
||||
533,Power: The Alleyway Buffet,useful,"BOOK_POWER",
|
||||
534,Power: The Diamond Hunter,useful,"BOOK_POWER",
|
||||
535,Power: Book of Mysteries,progression,"BOOK_POWER",
|
||||
536,Power: Horse: The Book,useful,"BOOK_POWER",
|
||||
537,Power: Treasure Appraisal Guide,useful,"BOOK_POWER",
|
||||
538,Power: Ol' Slitherlegs,useful,"BOOK_POWER",
|
||||
539,Power: Animal Catalogue,useful,"BOOK_POWER",
|
||||
541,Progressive Lost Book,progression,"LOST_BOOK",
|
||||
551,Golden Walnut,progression,"RESOURCE_PACK,GINGER_ISLAND",
|
||||
552,3 Golden Walnuts,progression,"GINGER_ISLAND",
|
||||
553,5 Golden Walnuts,progression,"GINGER_ISLAND",
|
||||
554,Damage Bonus,filler,PLAYER_BUFF,
|
||||
555,Defense Bonus,filler,PLAYER_BUFF,
|
||||
556,Immunity Bonus,filler,PLAYER_BUFF,
|
||||
557,Health Bonus,filler,PLAYER_BUFF,
|
||||
558,Energy Bonus,filler,PLAYER_BUFF,
|
||||
559,Bite Rate Bonus,filler,PLAYER_BUFF,
|
||||
560,Fish Trap Bonus,filler,PLAYER_BUFF,
|
||||
561,Fishing Bar Size Bonus,filler,PLAYER_BUFF,
|
||||
562,Quality Bonus,filler,PLAYER_BUFF,
|
||||
563,Glow Bonus,filler,PLAYER_BUFF,
|
||||
4001,Burnt Trap,trap,TRAP,
|
||||
4002,Darkness Trap,trap,TRAP,
|
||||
4003,Frozen Trap,trap,TRAP,
|
||||
|
@ -464,6 +534,8 @@ id,name,classification,groups,mod_name
|
|||
4023,Benjamin Budton Trap,trap,TRAP,
|
||||
4024,Inflation Trap,trap,TRAP,
|
||||
4025,Bomb Trap,trap,TRAP,
|
||||
4026,Nudge Trap,trap,TRAP,
|
||||
4501,Deflation Bonus,filler,,
|
||||
5000,Resource Pack: 500 Money,useful,"BASE_RESOURCE,RESOURCE_PACK",
|
||||
5001,Resource Pack: 1000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK",
|
||||
5002,Resource Pack: 1500 Money,useful,"BASE_RESOURCE,RESOURCE_PACK",
|
||||
|
@ -701,9 +773,9 @@ id,name,classification,groups,mod_name
|
|||
5234,Resource Pack: 10 Qi Seasoning,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5235,Mr. Qi's Hat,filler,"MAXIMUM_ONE,RESOURCE_PACK",
|
||||
5236,Aquatic Sanctuary,filler,RESOURCE_PACK,
|
||||
5237,Leprechaun Hat,filler,"MAXIMUM_ONE,RESOURCE_PACK",
|
||||
5242,Exotic Double Bed,filler,RESOURCE_PACK,
|
||||
5243,Resource Pack: 2 Qi Gem,filler,"GINGER_ISLAND,RESOURCE_PACK,DEPRECATED",
|
||||
5245,Golden Walnut,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,GINGER_ISLAND",
|
||||
5247,Fairy Dust,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5248,Seed Maker,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5249,Keg,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
|
@ -726,6 +798,27 @@ id,name,classification,groups,mod_name
|
|||
5266,Resource Pack: 5 Staircase,filler,"RESOURCE_PACK",
|
||||
5267,Auto-Petter,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5268,Auto-Grabber,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5269,Resource Pack: 10 Calico Egg,filler,"RESOURCE_PACK",
|
||||
5270,Resource Pack: 20 Calico Egg,filler,"RESOURCE_PACK",
|
||||
5272,Tent Kit,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5273,Resource Pack: 4 Mystery Box,filler,"RESOURCE_PACK",
|
||||
5274,Prize Ticket,filler,"RESOURCE_PACK",
|
||||
5275,Resource Pack: 20 Deluxe Bait,filler,"RESOURCE_PACK",
|
||||
5276,Resource Pack: 2 Triple Shot Espresso,filler,"RESOURCE_PACK",
|
||||
5277,Dish O' The Sea,filler,"RESOURCE_PACK",
|
||||
5278,Seafoam Pudding,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5279,Trap Bobber,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
|
||||
5280,Treasure Chest,filler,"RESOURCE_PACK",
|
||||
5281,Resource Pack: 15 Mixed Seeds,filler,"RESOURCE_PACK",
|
||||
5282,Resource Pack: 15 Mixed Flower Seeds,filler,"RESOURCE_PACK",
|
||||
5283,Resource Pack: 5 Cherry Bomb,filler,"RESOURCE_PACK",
|
||||
5284,Resource Pack: 3 Bomb,filler,"RESOURCE_PACK",
|
||||
5285,Resource Pack: 2 Mega Bomb,filler,"RESOURCE_PACK",
|
||||
5286,Resource Pack: 2 Life Elixir,filler,"RESOURCE_PACK",
|
||||
5287,Resource Pack: 5 Coffee,filler,"RESOURCE_PACK",
|
||||
5289,Prismatic Shard,filler,"RESOURCE_PACK",
|
||||
5290,Stardrop Tea,filler,"RESOURCE_PACK",
|
||||
5291,Resource Pack: 2 Artifact Trove,filler,"RESOURCE_PACK",
|
||||
10001,Luck Level,progression,SKILL_LEVEL_UP,Luck Skill
|
||||
10002,Magic Level,progression,SKILL_LEVEL_UP,Magic
|
||||
10003,Socializing Level,progression,SKILL_LEVEL_UP,Socializing Skill
|
||||
|
@ -802,11 +895,16 @@ id,name,classification,groups,mod_name
|
|||
10409,Void Delight Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
|
||||
10410,Void Salmon Sushi Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
|
||||
10411,Mushroom Kebab Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
|
||||
10412,Crayfish Soup Recipe,progression,,Distant Lands - Witch Swamp Overhaul
|
||||
10412,Crayfish Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
|
||||
10413,Pemmican Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
|
||||
10414,Void Mint Tea Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
|
||||
10415,Ginger Tincture Recipe,progression,GINGER_ISLAND,Distant Lands - Witch Swamp Overhaul
|
||||
10416,Special Pumpkin Soup Recipe,progression,,Boarding House and Bus Stop Extension
|
||||
10415,Ginger Tincture Recipe,progression,"GINGER_ISLAND,CHEFSANITY,CHEFSANITY_FRIENDSHIP",Distant Lands - Witch Swamp Overhaul
|
||||
10416,Special Pumpkin Soup Recipe,progression,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Boarding House and Bus Stop Extension
|
||||
10417,Rocky Root Coffee Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",Archaeology
|
||||
10418,Digger's Delight Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",Archaeology
|
||||
10419,Ancient Jello Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",Archaeology
|
||||
10420,Grilled Cheese Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",Binning Skill
|
||||
10421,Fish Casserole Recipe,progression,"CHEFSANITY,CHEFSANITY_SKILL",Binning Skill
|
||||
10450,Void Mint Seeds,progression,DEPRECATED,Distant Lands - Witch Swamp Overhaul
|
||||
10451,Vile Ancient Fruit Seeds,progression,DEPRECATED,Distant Lands - Witch Swamp Overhaul
|
||||
10501,Marlon's Boat Paddle,progression,GINGER_ISLAND,Stardew Valley Expanded
|
||||
|
@ -850,10 +948,15 @@ id,name,classification,groups,mod_name
|
|||
10707,Resource Pack: 5 Wooden Display,filler,RESOURCE_PACK,Archaeology
|
||||
10708,Grinder,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Archaeology
|
||||
10709,Ancient Battery Production Station,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Archaeology
|
||||
10710,Hero Elixir,filler,RESOURCE_PACK,Starde Valley Expanded
|
||||
10710,Hero Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
|
||||
10711,Aegis Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
|
||||
10712,Haste Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
|
||||
10713,Lightning Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
|
||||
10714,Armor Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
|
||||
10715,Gravity Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
|
||||
10716,Barbarian Elixir,filler,RESOURCE_PACK,Stardew Valley Expanded
|
||||
10717,Restoration Table,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Archaeology
|
||||
10718,Trash Bin,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Binning Skill
|
||||
10719,Composter,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Binning Skill
|
||||
10720,Recycling Bin,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Binning Skill
|
||||
10721,Advanced Recycling Machine,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",Binning Skill
|
||||
|
|
|
|
@ -36,6 +36,7 @@ id,region,name,tags,mod_name
|
|||
35,Boiler Room,Complete Boiler Room,COMMUNITY_CENTER_ROOM,
|
||||
36,Bulletin Board,Complete Bulletin Board,COMMUNITY_CENTER_ROOM,
|
||||
37,Vault,Complete Vault,COMMUNITY_CENTER_ROOM,
|
||||
38,Crafts Room,Forest Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
|
||||
39,Fish Tank,Deep Fishing Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,FISH_TANK_BUNDLE",
|
||||
40,Crafts Room,Beach Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
|
||||
41,Crafts Room,Mines Foraging Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
|
||||
|
@ -78,7 +79,6 @@ id,region,name,tags,mod_name
|
|||
78,Vault,500g Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
|
||||
79,Vault,"1,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
|
||||
80,Vault,"2,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
|
||||
81,Vault,"5,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
|
||||
82,Vault,"1,500g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
|
||||
83,Vault,"3,000g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
|
||||
84,Vault,"3,500g Bundle","BUNDLE,COMMUNITY_CENTER_BUNDLE,VAULT_BUNDLE",
|
||||
|
@ -124,6 +124,20 @@ id,region,name,tags,mod_name
|
|||
124,Beach,Bamboo Pole Cutscene,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
|
||||
125,Willy's Fish Shop,Purchase Fiberglass Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
|
||||
126,Willy's Fish Shop,Purchase Iridium Rod,"FISHING_ROD_UPGRADE,TOOL_UPGRADE",
|
||||
127,Mountain,Copper Pan Cutscene,"TOOL_UPGRADE,PAN_UPGRADE",
|
||||
128,Blacksmith Iron Upgrades,Iron Pan Upgrade,"TOOL_UPGRADE,PAN_UPGRADE",
|
||||
129,Blacksmith Gold Upgrades,Gold Pan Upgrade,"TOOL_UPGRADE,PAN_UPGRADE",
|
||||
130,Blacksmith Iridium Upgrades,Iridium Pan Upgrade,"TOOL_UPGRADE,PAN_UPGRADE",
|
||||
151,Bulletin Board,Helper's Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
152,Bulletin Board,Spirit's Eve Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
153,Bulletin Board,Winter Star Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
154,Bulletin Board,Calico Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
155,Pantry,Sommelier Bundle,"PANTRY_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
156,Pantry,Dry Bundle,"PANTRY_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
157,Fish Tank,Fish Smoker Bundle,"FISH_TANK_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
158,Bulletin Board,Raccoon Bundle,"BULLETIN_BOARD_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
159,Crafts Room,Green Rain Bundle,"BUNDLE,COMMUNITY_CENTER_BUNDLE,CRAFTS_ROOM_BUNDLE",
|
||||
160,Fish Tank,Specific Fishing Bundle,"FISH_TANK_BUNDLE,BUNDLE,COMMUNITY_CENTER_BUNDLE",
|
||||
201,The Mines - Floor 10,The Mines Floor 10 Treasure,"MANDATORY,THE_MINES_TREASURE",
|
||||
202,The Mines - Floor 20,The Mines Floor 20 Treasure,"MANDATORY,THE_MINES_TREASURE",
|
||||
203,The Mines - Floor 40,The Mines Floor 40 Treasure,"MANDATORY,THE_MINES_TREASURE",
|
||||
|
@ -161,18 +175,18 @@ id,region,name,tags,mod_name
|
|||
235,The Mines - Floor 110,Floor 110 Elevator,ELEVATOR,
|
||||
236,The Mines - Floor 115,Floor 115 Elevator,ELEVATOR,
|
||||
237,The Mines - Floor 120,Floor 120 Elevator,ELEVATOR,
|
||||
250,Shipping,Demetrius's Breakthrough,MANDATORY
|
||||
250,Shipping,Demetrius's Breakthrough,MANDATORY,
|
||||
251,Volcano - Floor 10,Volcano Caldera Treasure,"GINGER_ISLAND,MANDATORY",
|
||||
301,Farming,Level 1 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
302,Farming,Level 2 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
303,Farming,Level 3 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
304,Farming,Level 4 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
305,Farming,Level 5 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
306,Farming,Level 6 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
307,Farming,Level 7 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
308,Farming,Level 8 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
309,Farming,Level 9 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
310,Farming,Level 10 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
301,Farm,Level 1 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
302,Farm,Level 2 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
303,Farm,Level 3 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
304,Farm,Level 4 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
305,Farm,Level 5 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
306,Farm,Level 6 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
307,Farm,Level 7 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
308,Farm,Level 8 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
309,Farm,Level 9 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
310,Farm,Level 10 Farming,"FARMING_LEVEL,SKILL_LEVEL",
|
||||
311,Fishing,Level 1 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
|
||||
312,Fishing,Level 2 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
|
||||
313,Fishing,Level 3 Fishing,"FISHING_LEVEL,SKILL_LEVEL",
|
||||
|
@ -213,6 +227,11 @@ id,region,name,tags,mod_name
|
|||
348,The Mines - Floor 90,Level 8 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
|
||||
349,The Mines - Floor 100,Level 9 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
|
||||
350,The Mines - Floor 110,Level 10 Combat,"COMBAT_LEVEL,SKILL_LEVEL",
|
||||
351,Mastery Cave,Farming Mastery,"MASTERY_LEVEL,SKILL_LEVEL",
|
||||
352,Mastery Cave,Fishing Mastery,"MASTERY_LEVEL,SKILL_LEVEL",
|
||||
353,Mastery Cave,Foraging Mastery,"MASTERY_LEVEL,SKILL_LEVEL",
|
||||
354,Mastery Cave,Mining Mastery,"MASTERY_LEVEL,SKILL_LEVEL",
|
||||
355,Mastery Cave,Combat Mastery,"MASTERY_LEVEL,SKILL_LEVEL",
|
||||
401,Carpenter Shop,Coop Blueprint,BUILDING_BLUEPRINT,
|
||||
402,Carpenter Shop,Big Coop Blueprint,BUILDING_BLUEPRINT,
|
||||
403,Carpenter Shop,Deluxe Coop Blueprint,BUILDING_BLUEPRINT,
|
||||
|
@ -279,6 +298,8 @@ id,region,name,tags,mod_name
|
|||
546,Mutant Bug Lair,Dark Talisman,"STORY_QUEST",
|
||||
547,Witch's Swamp,Goblin Problem,"STORY_QUEST",
|
||||
548,Witch's Hut,Magic Ink,"STORY_QUEST",
|
||||
549,Forest,The Giant Stump,"STORY_QUEST",
|
||||
550,Farm,Feeding Animals,"STORY_QUEST",
|
||||
601,JotPK World 1,JotPK: Boots 1,"ARCADE_MACHINE,JOTPK",
|
||||
602,JotPK World 1,JotPK: Boots 2,"ARCADE_MACHINE,JOTPK",
|
||||
603,JotPK World 1,JotPK: Gun 1,"ARCADE_MACHINE,JOTPK",
|
||||
|
@ -307,6 +328,7 @@ id,region,name,tags,mod_name
|
|||
705,Farmhouse,Have Another Baby,BABY,
|
||||
706,Farmhouse,Spouse Stardrop,,
|
||||
707,Sewer,Krobus Stardrop,MANDATORY,
|
||||
708,Forest,Pot Of Gold,MANDATORY,
|
||||
801,Forest,Help Wanted: Gathering 1,HELP_WANTED,
|
||||
802,Forest,Help Wanted: Gathering 2,HELP_WANTED,
|
||||
803,Forest,Help Wanted: Gathering 3,HELP_WANTED,
|
||||
|
@ -454,6 +476,7 @@ id,region,name,tags,mod_name
|
|||
1068,Beach,Fishsanity: Legend II,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
|
||||
1069,Beach,Fishsanity: Ms. Angler,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
|
||||
1070,Beach,Fishsanity: Radioactive Carp,"FISHSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
|
||||
1071,Fishing,Fishsanity: Goby,FISHSANITY,
|
||||
1100,Museum,Museumsanity: 5 Donations,MUSEUM_MILESTONES,
|
||||
1101,Museum,Museumsanity: 10 Donations,MUSEUM_MILESTONES,
|
||||
1102,Museum,Museumsanity: 15 Donations,MUSEUM_MILESTONES,
|
||||
|
@ -1021,6 +1044,57 @@ id,region,name,tags,mod_name
|
|||
2034,Dance of the Moonlight Jellies,Moonlight Jellies Banner,FESTIVAL,
|
||||
2035,Dance of the Moonlight Jellies,Starport Decal,FESTIVAL,
|
||||
2036,Casino,Rarecrow #3 (Alien),FESTIVAL,
|
||||
2041,Desert Festival,Calico Race,FESTIVAL,
|
||||
2042,Desert Festival,Mummy Mask,FESTIVAL_HARD,
|
||||
2043,Desert Festival,Calico Statue,FESTIVAL,
|
||||
2044,Desert Festival,Emily's Outfit Services,FESTIVAL,
|
||||
2045,Desert Festival,Earthy Mousse,DESERT_FESTIVAL_CHEF,
|
||||
2046,Desert Festival,Sweet Bean Cake,DESERT_FESTIVAL_CHEF,
|
||||
2047,Desert Festival,Skull Cave Casserole,DESERT_FESTIVAL_CHEF,
|
||||
2048,Desert Festival,Spicy Tacos,DESERT_FESTIVAL_CHEF,
|
||||
2049,Desert Festival,Mountain Chili,DESERT_FESTIVAL_CHEF,
|
||||
2050,Desert Festival,Crystal Cake,DESERT_FESTIVAL_CHEF,
|
||||
2051,Desert Festival,Cave Kebab,DESERT_FESTIVAL_CHEF,
|
||||
2052,Desert Festival,Hot Log,DESERT_FESTIVAL_CHEF,
|
||||
2053,Desert Festival,Sour Salad,DESERT_FESTIVAL_CHEF,
|
||||
2054,Desert Festival,Superfood Cake,DESERT_FESTIVAL_CHEF,
|
||||
2055,Desert Festival,Warrior Smoothie,DESERT_FESTIVAL_CHEF,
|
||||
2056,Desert Festival,Rumpled Fruit Skin,DESERT_FESTIVAL_CHEF,
|
||||
2057,Desert Festival,Calico Pizza,DESERT_FESTIVAL_CHEF,
|
||||
2058,Desert Festival,Stuffed Mushrooms,DESERT_FESTIVAL_CHEF,
|
||||
2059,Desert Festival,Elf Quesadilla,DESERT_FESTIVAL_CHEF,
|
||||
2060,Desert Festival,Nachos Of The Desert,DESERT_FESTIVAL_CHEF,
|
||||
2061,Desert Festival,Cioppino,DESERT_FESTIVAL_CHEF,
|
||||
2062,Desert Festival,Rainforest Shrimp,DESERT_FESTIVAL_CHEF,
|
||||
2063,Desert Festival,Shrimp Donut,DESERT_FESTIVAL_CHEF,
|
||||
2064,Desert Festival,Smell Of The Sea,DESERT_FESTIVAL_CHEF,
|
||||
2065,Desert Festival,Desert Gumbo,DESERT_FESTIVAL_CHEF,
|
||||
2066,Desert Festival,Free Cactis,FESTIVAL,
|
||||
2067,Desert Festival,Monster Hunt,FESTIVAL_HARD,
|
||||
2068,Desert Festival,Deep Dive,FESTIVAL_HARD,
|
||||
2069,Desert Festival,Treasure Hunt,FESTIVAL_HARD,
|
||||
2070,Desert Festival,Touch A Calico Statue,FESTIVAL,
|
||||
2071,Desert Festival,Real Calico Egg Hunter,FESTIVAL,
|
||||
2072,Desert Festival,Willy's Challenge,FESTIVAL_HARD,
|
||||
2073,Desert Festival,Desert Scholar,FESTIVAL,
|
||||
2074,Trout Derby,Trout Derby Reward 1,FESTIVAL,
|
||||
2075,Trout Derby,Trout Derby Reward 2,FESTIVAL,
|
||||
2076,Trout Derby,Trout Derby Reward 3,FESTIVAL,
|
||||
2077,Trout Derby,Trout Derby Reward 4,FESTIVAL_HARD,
|
||||
2078,Trout Derby,Trout Derby Reward 5,FESTIVAL_HARD,
|
||||
2079,Trout Derby,Trout Derby Reward 6,FESTIVAL_HARD,
|
||||
2080,Trout Derby,Trout Derby Reward 7,FESTIVAL_HARD,
|
||||
2081,Trout Derby,Trout Derby Reward 8,FESTIVAL_HARD,
|
||||
2082,Trout Derby,Trout Derby Reward 9,FESTIVAL_HARD,
|
||||
2083,Trout Derby,Trout Derby Reward 10,FESTIVAL_HARD,
|
||||
2084,SquidFest,SquidFest Day 1 Copper,FESTIVAL,
|
||||
2085,SquidFest,SquidFest Day 1 Iron,FESTIVAL,
|
||||
2086,SquidFest,SquidFest Day 1 Gold,FESTIVAL_HARD,
|
||||
2087,SquidFest,SquidFest Day 1 Iridium,FESTIVAL_HARD,
|
||||
2088,SquidFest,SquidFest Day 2 Copper,FESTIVAL,
|
||||
2089,SquidFest,SquidFest Day 2 Iron,FESTIVAL,
|
||||
2090,SquidFest,SquidFest Day 2 Gold,FESTIVAL_HARD,
|
||||
2091,SquidFest,SquidFest Day 2 Iridium,FESTIVAL_HARD,
|
||||
2101,Town,Island Ingredients,"GINGER_ISLAND,SPECIAL_ORDER_BOARD",
|
||||
2102,The Mines - Floor 75,Cave Patrol,SPECIAL_ORDER_BOARD,
|
||||
2103,Fishing,Aquatic Overpopulation,SPECIAL_ORDER_BOARD,
|
||||
|
@ -1065,53 +1139,59 @@ id,region,name,tags,mod_name
|
|||
2214,Island West,Parrot Express,"GINGER_ISLAND,WALNUT_PURCHASE",
|
||||
2215,Dig Site,Open Professor Snail Cave,GINGER_ISLAND,
|
||||
2216,Field Office,Complete Island Field Office,GINGER_ISLAND,
|
||||
2301,Farming,Harvest Amaranth,CROPSANITY,
|
||||
2302,Farming,Harvest Artichoke,CROPSANITY,
|
||||
2303,Farming,Harvest Beet,CROPSANITY,
|
||||
2304,Farming,Harvest Blue Jazz,CROPSANITY,
|
||||
2305,Farming,Harvest Blueberry,CROPSANITY,
|
||||
2306,Farming,Harvest Bok Choy,CROPSANITY,
|
||||
2307,Farming,Harvest Cauliflower,CROPSANITY,
|
||||
2308,Farming,Harvest Corn,CROPSANITY,
|
||||
2309,Farming,Harvest Cranberries,CROPSANITY,
|
||||
2310,Farming,Harvest Eggplant,CROPSANITY,
|
||||
2311,Farming,Harvest Fairy Rose,CROPSANITY,
|
||||
2312,Farming,Harvest Garlic,CROPSANITY,
|
||||
2313,Farming,Harvest Grape,CROPSANITY,
|
||||
2314,Farming,Harvest Green Bean,CROPSANITY,
|
||||
2315,Farming,Harvest Hops,CROPSANITY,
|
||||
2316,Farming,Harvest Hot Pepper,CROPSANITY,
|
||||
2317,Farming,Harvest Kale,CROPSANITY,
|
||||
2318,Farming,Harvest Melon,CROPSANITY,
|
||||
2319,Farming,Harvest Parsnip,CROPSANITY,
|
||||
2320,Farming,Harvest Poppy,CROPSANITY,
|
||||
2321,Farming,Harvest Potato,CROPSANITY,
|
||||
2322,Farming,Harvest Pumpkin,CROPSANITY,
|
||||
2323,Farming,Harvest Radish,CROPSANITY,
|
||||
2324,Farming,Harvest Red Cabbage,CROPSANITY,
|
||||
2325,Farming,Harvest Rhubarb,CROPSANITY,
|
||||
2326,Farming,Harvest Starfruit,CROPSANITY,
|
||||
2327,Farming,Harvest Strawberry,CROPSANITY,
|
||||
2328,Farming,Harvest Summer Spangle,CROPSANITY,
|
||||
2329,Farming,Harvest Sunflower,CROPSANITY,
|
||||
2330,Farming,Harvest Tomato,CROPSANITY,
|
||||
2331,Farming,Harvest Tulip,CROPSANITY,
|
||||
2332,Farming,Harvest Unmilled Rice,CROPSANITY,
|
||||
2333,Farming,Harvest Wheat,CROPSANITY,
|
||||
2334,Farming,Harvest Yam,CROPSANITY,
|
||||
2335,Farming,Harvest Cactus Fruit,CROPSANITY,
|
||||
2336,Farming,Harvest Pineapple,"CROPSANITY,GINGER_ISLAND",
|
||||
2337,Farming,Harvest Taro Root,"CROPSANITY,GINGER_ISLAND",
|
||||
2338,Farming,Harvest Sweet Gem Berry,CROPSANITY,
|
||||
2339,Farming,Harvest Apple,CROPSANITY,
|
||||
2340,Farming,Harvest Apricot,CROPSANITY,
|
||||
2341,Farming,Harvest Cherry,CROPSANITY,
|
||||
2342,Farming,Harvest Orange,CROPSANITY,
|
||||
2343,Farming,Harvest Pomegranate,CROPSANITY,
|
||||
2344,Farming,Harvest Peach,CROPSANITY,
|
||||
2345,Farming,Harvest Banana,"CROPSANITY,GINGER_ISLAND",
|
||||
2346,Farming,Harvest Mango,"CROPSANITY,GINGER_ISLAND",
|
||||
2347,Farming,Harvest Coffee Bean,CROPSANITY,
|
||||
2301,Fall Farming,Harvest Amaranth,CROPSANITY,
|
||||
2302,Fall Farming,Harvest Artichoke,CROPSANITY,
|
||||
2303,Fall Farming,Harvest Beet,CROPSANITY,
|
||||
2304,Spring Farming,Harvest Blue Jazz,CROPSANITY,
|
||||
2305,Summer Farming,Harvest Blueberry,CROPSANITY,
|
||||
2306,Fall Farming,Harvest Bok Choy,CROPSANITY,
|
||||
2307,Spring Farming,Harvest Cauliflower,CROPSANITY,
|
||||
2308,Summer or Fall Farming,Harvest Corn,CROPSANITY,
|
||||
2309,Fall Farming,Harvest Cranberries,CROPSANITY,
|
||||
2310,Fall Farming,Harvest Eggplant,CROPSANITY,
|
||||
2311,Fall Farming,Harvest Fairy Rose,CROPSANITY,
|
||||
2312,Spring Farming,Harvest Garlic,CROPSANITY,
|
||||
2313,Fall Farming,Harvest Grape,CROPSANITY,
|
||||
2314,Spring Farming,Harvest Green Bean,CROPSANITY,
|
||||
2315,Summer Farming,Harvest Hops,CROPSANITY,
|
||||
2316,Summer Farming,Harvest Hot Pepper,CROPSANITY,
|
||||
2317,Spring Farming,Harvest Kale,CROPSANITY,
|
||||
2318,Summer Farming,Harvest Melon,CROPSANITY,
|
||||
2319,Spring Farming,Harvest Parsnip,CROPSANITY,
|
||||
2320,Summer Farming,Harvest Poppy,CROPSANITY,
|
||||
2321,Spring Farming,Harvest Potato,CROPSANITY,
|
||||
2322,Fall Farming,Harvest Pumpkin,CROPSANITY,
|
||||
2323,Summer Farming,Harvest Radish,CROPSANITY,
|
||||
2324,Summer Farming,Harvest Red Cabbage,CROPSANITY,
|
||||
2325,Spring Farming,Harvest Rhubarb,CROPSANITY,
|
||||
2326,Summer Farming,Harvest Starfruit,CROPSANITY,
|
||||
2327,Spring Farming,Harvest Strawberry,CROPSANITY,
|
||||
2328,Summer Farming,Harvest Summer Spangle,CROPSANITY,
|
||||
2329,Summer or Fall Farming,Harvest Sunflower,CROPSANITY,
|
||||
2330,Summer Farming,Harvest Tomato,CROPSANITY,
|
||||
2331,Spring Farming,Harvest Tulip,CROPSANITY,
|
||||
2332,Spring Farming,Harvest Unmilled Rice,CROPSANITY,
|
||||
2333,Summer or Fall Farming,Harvest Wheat,CROPSANITY,
|
||||
2334,Fall Farming,Harvest Yam,CROPSANITY,
|
||||
2335,Indoor Farming,Harvest Cactus Fruit,CROPSANITY,
|
||||
2336,Summer Farming,Harvest Pineapple,"CROPSANITY,GINGER_ISLAND",
|
||||
2337,Summer Farming,Harvest Taro Root,"CROPSANITY,GINGER_ISLAND",
|
||||
2338,Fall Farming,Harvest Sweet Gem Berry,CROPSANITY,
|
||||
2339,Fall Farming,Harvest Apple,CROPSANITY,
|
||||
2340,Spring Farming,Harvest Apricot,CROPSANITY,
|
||||
2341,Spring Farming,Harvest Cherry,CROPSANITY,
|
||||
2342,Summer Farming,Harvest Orange,CROPSANITY,
|
||||
2343,Fall Farming,Harvest Pomegranate,CROPSANITY,
|
||||
2344,Summer Farming,Harvest Peach,CROPSANITY,
|
||||
2345,Summer Farming,Harvest Banana,"CROPSANITY,GINGER_ISLAND",
|
||||
2346,Summer Farming,Harvest Mango,"CROPSANITY,GINGER_ISLAND",
|
||||
2347,Indoor Farming,Harvest Coffee Bean,CROPSANITY,
|
||||
2348,Fall Farming,Harvest Broccoli,CROPSANITY,
|
||||
2349,Spring Farming,Harvest Carrot,CROPSANITY,
|
||||
2350,Summer Farming,Harvest Powdermelon,CROPSANITY,
|
||||
2351,Summer Farming,Harvest Summer Squash,CROPSANITY,
|
||||
2352,Indoor Farming,Harvest Ancient Fruit,CROPSANITY,
|
||||
2353,Indoor Farming,Harvest Qi Fruit,"CROPSANITY,GINGER_ISLAND",
|
||||
2401,Shipping,Shipsanity: Duck Egg,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
2402,Shipping,Shipsanity: Duck Feather,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
2403,Shipping,Shipsanity: Egg,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
|
@ -1431,7 +1511,7 @@ id,region,name,tags,mod_name
|
|||
2717,Shipping,Shipsanity: Cactus Fruit,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
2718,Shipping,Shipsanity: Cave Carrot,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
2719,Shipping,Shipsanity: Chanterelle,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
2720,Shipping,Shipsanity: Clam,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
2720,Shipping,Shipsanity: Clam,"SHIPSANITY,SHIPSANITY_FISH",
|
||||
2721,Shipping,Shipsanity: Coconut,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
2722,Shipping,Shipsanity: Common Mushroom,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
2723,Shipping,Shipsanity: Coral,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
|
@ -1683,7 +1763,7 @@ id,region,name,tags,mod_name
|
|||
2969,Shipping,Shipsanity: Mango Sapling,"GINGER_ISLAND,SHIPSANITY",
|
||||
2970,Shipping,Shipsanity: Mushroom Tree Seed,"GINGER_ISLAND,SHIPSANITY,REQUIRES_QI_ORDERS",
|
||||
2971,Shipping,Shipsanity: Pineapple Seeds,"GINGER_ISLAND,SHIPSANITY",
|
||||
2972,Shipping,Shipsanity: Qi Bean,"GINGER_ISLAND,SHIPSANITY",
|
||||
2972,Shipping,Shipsanity: Qi Bean,"GINGER_ISLAND,SHIPSANITY,REQUIRES_QI_ORDERS",
|
||||
2973,Shipping,Shipsanity: Taro Tuber,"GINGER_ISLAND,SHIPSANITY",
|
||||
3001,Adventurer's Guild,Monster Eradication: Slimes,"MONSTERSANITY,MONSTERSANITY_GOALS",
|
||||
3002,Adventurer's Guild,Monster Eradication: Void Spirits,"MONSTERSANITY,MONSTERSANITY_GOALS",
|
||||
|
@ -1852,6 +1932,7 @@ id,region,name,tags,mod_name
|
|||
3278,Kitchen,Cook Tropical Curry,"COOKSANITY,GINGER_ISLAND",
|
||||
3279,Kitchen,Cook Trout Soup,"COOKSANITY,COOKSANITY_QOS",
|
||||
3280,Kitchen,Cook Vegetable Medley,COOKSANITY,
|
||||
3281,Kitchen,Cook Moss Soup,COOKSANITY,
|
||||
3301,Farm,Algae Soup Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
|
||||
3302,The Queen of Sauce,Artichoke Dip Recipe,"CHEFSANITY,CHEFSANITY_QOS",
|
||||
3303,Farm,Autumn's Bounty Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
|
||||
|
@ -1932,6 +2013,7 @@ id,region,name,tags,mod_name
|
|||
3378,Island Resort,Tropical Curry Recipe,"CHEFSANITY,GINGER_ISLAND,CHEFSANITY_PURCHASE",
|
||||
3379,The Queen of Sauce,Trout Soup Recipe,"CHEFSANITY,CHEFSANITY_QOS",
|
||||
3380,Farm,Vegetable Medley Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",
|
||||
3381,Farm,Moss Soup Recipe,"CHEFSANITY,CHEFSANITY_SKILL",
|
||||
3401,Farm,Craft Cherry Bomb,CRAFTSANITY,
|
||||
3402,Farm,Craft Bomb,CRAFTSANITY,
|
||||
3403,Farm,Craft Mega Bomb,CRAFTSANITY,
|
||||
|
@ -2062,6 +2144,26 @@ id,region,name,tags,mod_name
|
|||
3528,Farm,Craft Farm Computer,CRAFTSANITY,
|
||||
3529,Farm,Craft Hopper,"CRAFTSANITY,GINGER_ISLAND",
|
||||
3530,Farm,Craft Cookout Kit,CRAFTSANITY,
|
||||
3531,Farm,Craft Fish Smoker,"CRAFTSANITY",
|
||||
3532,Farm,Craft Dehydrator,"CRAFTSANITY",
|
||||
3533,Farm,Craft Blue Grass Starter,"CRAFTSANITY,GINGER_ISLAND",
|
||||
3534,Farm,Craft Mystic Tree Seed,"CRAFTSANITY,REQUIRES_MASTERIES",
|
||||
3535,Farm,Craft Sonar Bobber,"CRAFTSANITY",
|
||||
3536,Farm,Craft Challenge Bait,"CRAFTSANITY,REQUIRES_MASTERIES",
|
||||
3537,Farm,Craft Treasure Totem,"CRAFTSANITY,REQUIRES_MASTERIES",
|
||||
3538,Farm,Craft Heavy Furnace,"CRAFTSANITY,REQUIRES_MASTERIES",
|
||||
3539,Farm,Craft Deluxe Worm Bin,"CRAFTSANITY",
|
||||
3540,Farm,Craft Mushroom Log,"CRAFTSANITY",
|
||||
3541,Farm,Craft Big Chest,"CRAFTSANITY",
|
||||
3542,Farm,Craft Big Stone Chest,"CRAFTSANITY",
|
||||
3543,Farm,Craft Text Sign,"CRAFTSANITY",
|
||||
3544,Farm,Craft Tent Kit,"CRAFTSANITY",
|
||||
3545,Farm,Craft Statue Of The Dwarf King,"CRAFTSANITY,REQUIRES_MASTERIES",
|
||||
3546,Farm,Craft Statue Of Blessings,"CRAFTSANITY,REQUIRES_MASTERIES",
|
||||
3547,Farm,Craft Anvil,"CRAFTSANITY,REQUIRES_MASTERIES",
|
||||
3548,Farm,Craft Mini-Forge,"CRAFTSANITY,GINGER_ISLAND,REQUIRES_MASTERIES",
|
||||
3549,Farm,Craft Deluxe Bait,"CRAFTSANITY",
|
||||
3550,Farm,Craft Bait Maker,"CRAFTSANITY",
|
||||
3551,Pierre's General Store,Grass Starter Recipe,CRAFTSANITY,
|
||||
3552,Carpenter Shop,Wood Floor Recipe,CRAFTSANITY,
|
||||
3553,Carpenter Shop,Rustic Plank Floor Recipe,CRAFTSANITY,
|
||||
|
@ -2088,6 +2190,226 @@ id,region,name,tags,mod_name
|
|||
3574,Sewer,Wicked Statue Recipe,CRAFTSANITY,
|
||||
3575,Desert,Warp Totem: Desert Recipe,"CRAFTSANITY",
|
||||
3576,Island Trader,Deluxe Retaining Soil Recipe,"CRAFTSANITY,GINGER_ISLAND",
|
||||
3577,Willy's Fish Shop,Fish Smoker Recipe,CRAFTSANITY,
|
||||
3578,Pierre's General Store,Dehydrator Recipe,CRAFTSANITY,
|
||||
3579,Carpenter Shop,Big Chest Recipe,CRAFTSANITY,
|
||||
3580,Mines Dwarf Shop,Big Stone Chest Recipe,CRAFTSANITY,
|
||||
3701,Raccoon Bundles,Raccoon Request 1,"BUNDLE,RACCOON_BUNDLES",
|
||||
3702,Raccoon Bundles,Raccoon Request 2,"BUNDLE,RACCOON_BUNDLES",
|
||||
3703,Raccoon Bundles,Raccoon Request 3,"BUNDLE,RACCOON_BUNDLES",
|
||||
3704,Raccoon Bundles,Raccoon Request 4,"BUNDLE,RACCOON_BUNDLES",
|
||||
3705,Raccoon Bundles,Raccoon Request 5,"BUNDLE,RACCOON_BUNDLES",
|
||||
3706,Raccoon Bundles,Raccoon Request 6,"BUNDLE,RACCOON_BUNDLES",
|
||||
3707,Raccoon Bundles,Raccoon Request 7,"BUNDLE,RACCOON_BUNDLES",
|
||||
3708,Raccoon Bundles,Raccoon Request 8,"BUNDLE,RACCOON_BUNDLES",
|
||||
3801,Shipping,Shipsanity: Goby,"SHIPSANITY,SHIPSANITY_FISH",
|
||||
3802,Shipping,Shipsanity: Fireworks (Red),"SHIPSANITY",
|
||||
3803,Shipping,Shipsanity: Fireworks (Purple),"SHIPSANITY",
|
||||
3804,Shipping,Shipsanity: Fireworks (Green),"SHIPSANITY",
|
||||
3805,Shipping,Shipsanity: Far Away Stone,"SHIPSANITY",
|
||||
3806,Shipping,Shipsanity: Calico Egg,"SHIPSANITY",
|
||||
3807,Shipping,Shipsanity: Mixed Flower Seeds,"SHIPSANITY",
|
||||
3808,Shipping,Shipsanity: Mystery Box,"SHIPSANITY",
|
||||
3809,Shipping,Shipsanity: Golden Tag,"SHIPSANITY",
|
||||
3810,Shipping,Shipsanity: Deluxe Bait,"SHIPSANITY",
|
||||
3811,Shipping,Shipsanity: Moss,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
3812,Shipping,Shipsanity: Mossy Seed,"SHIPSANITY",
|
||||
3813,Shipping,Shipsanity: Sonar Bobber,"SHIPSANITY",
|
||||
3814,Shipping,Shipsanity: Tent Kit,"SHIPSANITY",
|
||||
3815,Shipping,Shipsanity: Mystic Tree Seed,"SHIPSANITY,REQUIRES_MASTERIES",
|
||||
3816,Shipping,Shipsanity: Mystic Syrup,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
3817,Shipping,Shipsanity: Raisins,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
3818,Shipping,Shipsanity: Dried Fruit,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
3819,Shipping,Shipsanity: Dried Mushrooms,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
3820,Shipping,Shipsanity: Stardrop Tea,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
3821,Shipping,Shipsanity: Prize Ticket,"SHIPSANITY",
|
||||
3822,Shipping,Shipsanity: Treasure Totem,"SHIPSANITY,REQUIRES_MASTERIES",
|
||||
3823,Shipping,Shipsanity: Challenge Bait,"SHIPSANITY,REQUIRES_MASTERIES",
|
||||
3824,Shipping,Shipsanity: Carrot Seeds,"SHIPSANITY",
|
||||
3825,Shipping,Shipsanity: Carrot,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
3826,Shipping,Shipsanity: Summer Squash Seeds,"SHIPSANITY",
|
||||
3827,Shipping,Shipsanity: Summer Squash,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
3828,Shipping,Shipsanity: Broccoli Seeds,"SHIPSANITY",
|
||||
3829,Shipping,Shipsanity: Broccoli,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
3830,Shipping,Shipsanity: Powdermelon Seeds,"SHIPSANITY",
|
||||
3831,Shipping,Shipsanity: Powdermelon,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",
|
||||
3832,Shipping,Shipsanity: Smoked Fish,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",
|
||||
3833,Shipping,Shipsanity: Book Of Stars,"SHIPSANITY",
|
||||
3834,Shipping,Shipsanity: Stardew Valley Almanac,"SHIPSANITY",
|
||||
3835,Shipping,Shipsanity: Woodcutter's Weekly,"SHIPSANITY",
|
||||
3836,Shipping,Shipsanity: Bait And Bobber,"SHIPSANITY",
|
||||
3837,Shipping,Shipsanity: Mining Monthly,"SHIPSANITY",
|
||||
3838,Shipping,Shipsanity: Combat Quarterly,"SHIPSANITY",
|
||||
3839,Shipping,Shipsanity: The Alleyway Buffet,"SHIPSANITY",
|
||||
3840,Shipping,Shipsanity: The Art O' Crabbing,"SHIPSANITY",
|
||||
3841,Shipping,Shipsanity: Dwarvish Safety Manual,"SHIPSANITY",
|
||||
3842,Shipping,Shipsanity: Jewels Of The Sea,"SHIPSANITY",
|
||||
3843,Shipping,Shipsanity: Raccoon Journal,"SHIPSANITY",
|
||||
3844,Shipping,Shipsanity: Woody's Secret,"SHIPSANITY",
|
||||
3845,Shipping,"Shipsanity: Jack Be Nimble, Jack Be Thick","SHIPSANITY",
|
||||
3846,Shipping,Shipsanity: Friendship 101,"SHIPSANITY",
|
||||
3847,Shipping,Shipsanity: Monster Compendium,"SHIPSANITY",
|
||||
3848,Shipping,Shipsanity: Way Of The Wind pt. 1,"SHIPSANITY",
|
||||
3849,Shipping,Shipsanity: Mapping Cave Systems,"SHIPSANITY",
|
||||
3850,Shipping,Shipsanity: Price Catalogue,"SHIPSANITY",
|
||||
3851,Shipping,Shipsanity: Queen Of Sauce Cookbook,"SHIPSANITY",
|
||||
3852,Shipping,Shipsanity: The Diamond Hunter,"SHIPSANITY,GINGER_ISLAND",
|
||||
3853,Shipping,Shipsanity: Book of Mysteries,"SHIPSANITY",
|
||||
3854,Shipping,Shipsanity: Animal Catalogue,"SHIPSANITY",
|
||||
3855,Shipping,Shipsanity: Way Of The Wind pt. 2,"SHIPSANITY",
|
||||
3856,Shipping,Shipsanity: Golden Animal Cracker,"SHIPSANITY,REQUIRES_MASTERIES",
|
||||
3857,Shipping,Shipsanity: Golden Mystery Box,"SHIPSANITY,REQUIRES_MASTERIES",
|
||||
3858,Shipping,Shipsanity: Sea Jelly,"SHIPSANITY,SHIPSANITY_FISH",
|
||||
3859,Shipping,Shipsanity: Cave Jelly,"SHIPSANITY,SHIPSANITY_FISH",
|
||||
3860,Shipping,Shipsanity: River Jelly,"SHIPSANITY,SHIPSANITY_FISH",
|
||||
3861,Shipping,Shipsanity: Treasure Appraisal Guide,"SHIPSANITY",
|
||||
3862,Shipping,Shipsanity: Horse: The Book,"SHIPSANITY",
|
||||
3863,Shipping,Shipsanity: Butterfly Powder,"SHIPSANITY",
|
||||
3864,Shipping,Shipsanity: Blue Grass Starter,"SHIPSANITY,GINGER_ISLAND,REQUIRES_QI_ORDERS",
|
||||
3865,Shipping,Shipsanity: Moss Soup,"SHIPSANITY",
|
||||
3866,Shipping,Shipsanity: Ol' Slitherlegs,"SHIPSANITY",
|
||||
3867,Shipping,Shipsanity: Targeted Bait,"SHIPSANITY",
|
||||
4001,Farm,Read Price Catalogue,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4002,Farm,Read Mapping Cave Systems,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4003,Farm,Read Way Of The Wind pt. 1,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4004,Farm,Read Way Of The Wind pt. 2,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4005,Farm,Read Monster Compendium,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4006,Farm,Read Friendship 101,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4007,Farm,"Read Jack Be Nimble, Jack Be Thick","BOOKSANITY,BOOKSANITY_POWER",
|
||||
4008,Farm,Read Woody's Secret,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4009,Farm,Read Raccoon Journal,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4010,Farm,Read Jewels Of The Sea,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4011,Farm,Read Dwarvish Safety Manual,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4012,Farm,Read The Art O' Crabbing,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4013,Farm,Read The Alleyway Buffet,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4014,Farm,Read The Diamond Hunter,"BOOKSANITY,BOOKSANITY_POWER,GINGER_ISLAND",
|
||||
4015,Farm,Read Book of Mysteries,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4016,Farm,Read Horse: The Book,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4017,Farm,Read Treasure Appraisal Guide,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4018,Farm,Read Ol' Slitherlegs,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4019,Farm,Read Animal Catalogue,"BOOKSANITY,BOOKSANITY_POWER",
|
||||
4031,Farm,Read Bait And Bobber,"BOOKSANITY,BOOKSANITY_SKILL",
|
||||
4032,Farm,Read Book Of Stars,"BOOKSANITY,BOOKSANITY_SKILL",
|
||||
4033,Farm,Read Combat Quarterly,"BOOKSANITY,BOOKSANITY_SKILL",
|
||||
4034,Farm,Read Mining Monthly,"BOOKSANITY,BOOKSANITY_SKILL",
|
||||
4035,Farm,Read Queen Of Sauce Cookbook,"BOOKSANITY,BOOKSANITY_SKILL",
|
||||
4036,Farm,Read Stardew Valley Almanac,"BOOKSANITY,BOOKSANITY_SKILL",
|
||||
4037,Farm,Read Woodcutter's Weekly,"BOOKSANITY,BOOKSANITY_SKILL",
|
||||
4051,Museum,Read Tips on Farming,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4052,Museum,Read This is a book by Marnie,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4053,Museum,Read On Foraging,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4054,Museum,"Read The Fisherman, Act 1","BOOKSANITY,BOOKSANITY_LOST",
|
||||
4055,Museum,Read How Deep do the mines go?,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4056,Museum,Read An Old Farmer's Journal,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4057,Museum,Read Scarecrows,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4058,Museum,Read The Secret of the Stardrop,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4059,Museum,Read Journey of the Prairie King -- The Smash Hit Video Game!,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4060,Museum,Read A Study on Diamond Yields,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4061,Museum,Read Brewmaster's Guide,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4062,Museum,Read Mysteries of the Dwarves,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4063,Museum,Read Highlights From The Book of Yoba,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4064,Museum,Read Marriage Guide for Farmers,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4065,Museum,"Read The Fisherman, Act II","BOOKSANITY,BOOKSANITY_LOST",
|
||||
4066,Museum,Read Technology Report!,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4067,Museum,Read Secrets of the Legendary Fish,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4068,Museum,Read Gunther Tunnel Notice,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4069,Museum,Read Note From Gunther,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4070,Museum,Read Goblins by M. Jasper,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4071,Museum,Read Secret Statues Acrostics,"BOOKSANITY,BOOKSANITY_LOST",
|
||||
4101,Clint's Blacksmith,Open Golden Coconut,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4102,Island West,Fishing Walnut 1,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4103,Island West,Fishing Walnut 2,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4104,Island North,Fishing Walnut 3,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4105,Island North,Fishing Walnut 4,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4106,Island Southeast,Fishing Walnut 5,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4107,Island East,Jungle Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4108,Island East,Banana Altar,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4109,Leo's Hut,Leo's Tree,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4110,Island Shrine,Gem Birds Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4111,Island Shrine,Gem Birds Shrine,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4112,Island West,Harvesting Walnut 1,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4113,Island West,Harvesting Walnut 2,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4114,Island West,Harvesting Walnut 3,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4115,Island West,Harvesting Walnut 4,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4116,Island West,Harvesting Walnut 5,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4117,Gourmand Frog Cave,Gourmand Frog Melon,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4118,Gourmand Frog Cave,Gourmand Frog Wheat,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4119,Gourmand Frog Cave,Gourmand Frog Garlic,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4120,Island West,Journal Scrap #6,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4121,Island West,Mussel Node Walnut 1,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4122,Island West,Mussel Node Walnut 2,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4123,Island West,Mussel Node Walnut 3,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4124,Island West,Mussel Node Walnut 4,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4125,Island West,Mussel Node Walnut 5,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4126,Shipwreck,Shipwreck Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4127,Island West,Whack A Mole,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4128,Island West,Starfish Triangle,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4129,Island West,Starfish Diamond,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4130,Island West,X in the sand,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4131,Island West,Diamond Of Indents,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4132,Island West,Bush Behind Coconut Tree,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4133,Island West,Journal Scrap #4,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4134,Island West,Walnut Room Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4135,Island West,Coast Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4136,Island West,Tiger Slime Walnut,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4137,Island West,Bush Behind Mahogany Tree,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4138,Island West,Circle Of Grass,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4139,Island West,Below Colored Crystals Cave Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4140,Colored Crystals Cave,Colored Crystals,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4141,Island West,Cliff Edge Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4142,Island West,Diamond Of Pebbles,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4143,Island West,Farm Parrot Express Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4144,Island West,Farmhouse Cliff Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4145,Island North,Big Circle Of Stones,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4146,Island North,Grove Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4147,Island North,Diamond Of Grass,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4148,Island North,Small Circle Of Stones,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4149,Island North,Patch Of Sand,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4150,Dig Site,Crooked Circle Of Stones,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4151,Dig Site,Above Dig Site Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4152,Dig Site,Above Field Office Bush 1,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4153,Dig Site,Above Field Office Bush 2,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4154,Field Office,Complete Large Animal Collection,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4155,Field Office,Complete Snake Collection,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4156,Field Office,Complete Mummified Frog Collection,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4157,Field Office,Complete Mummified Bat Collection,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4158,Field Office,Purple Flowers Island Survey,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4159,Field Office,Purple Starfish Island Survey,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4160,Island North,Bush Behind Volcano Tree,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4161,Island North,Arc Of Stones,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4162,Island North,Protruding Tree Walnut,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4163,Island North,Journal Scrap #10,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4164,Island North,Northmost Point Circle Of Stones,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4165,Island North,Hidden Passage Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4166,Volcano Secret Beach,Secret Beach Bush 1,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4167,Volcano Secret Beach,Secret Beach Bush 2,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4168,Volcano - Floor 5,Volcano Rocks Walnut 1,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4169,Volcano - Floor 5,Volcano Rocks Walnut 2,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4170,Volcano - Floor 10,Volcano Rocks Walnut 3,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4171,Volcano - Floor 10,Volcano Rocks Walnut 4,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4172,Volcano - Floor 10,Volcano Rocks Walnut 5,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4173,Volcano - Floor 5,Volcano Monsters Walnut 1,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4174,Volcano - Floor 5,Volcano Monsters Walnut 2,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4175,Volcano - Floor 10,Volcano Monsters Walnut 3,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4176,Volcano - Floor 10,Volcano Monsters Walnut 4,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4177,Volcano - Floor 10,Volcano Monsters Walnut 5,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4178,Volcano - Floor 5,Volcano Crates Walnut 1,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4179,Volcano - Floor 5,Volcano Crates Walnut 2,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4180,Volcano - Floor 10,Volcano Crates Walnut 3,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4181,Volcano - Floor 10,Volcano Crates Walnut 4,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4182,Volcano - Floor 10,Volcano Crates Walnut 5,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4183,Volcano - Floor 5,Volcano Common Chest Walnut,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4184,Volcano - Floor 10,Volcano Rare Chest Walnut,"WALNUTSANITY,WALNUTSANITY_REPEATABLE",
|
||||
4185,Volcano - Floor 10,Forge Entrance Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4186,Volcano - Floor 10,Forge Exit Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4187,Island North,Cliff Over Island South Bush,"WALNUTSANITY,WALNUTSANITY_BUSH",
|
||||
4188,Island Southeast,Starfish Tide Pool,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4189,Island Southeast,Diamond Of Yellow Starfish,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
4190,Island Southeast,Mermaid Song,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4191,Pirate Cove,Pirate Darts 1,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4192,Pirate Cove,Pirate Darts 2,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4193,Pirate Cove,Pirate Darts 3,"WALNUTSANITY,WALNUTSANITY_PUZZLE",
|
||||
4194,Pirate Cove,Pirate Cove Patch Of Sand,"WALNUTSANITY,WALNUTSANITY_DIG",
|
||||
5001,Stardew Valley,Level 1 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
|
||||
5002,Stardew Valley,Level 2 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
|
||||
5003,Stardew Valley,Level 3 Luck,"LUCK_LEVEL,SKILL_LEVEL",Luck Skill
|
||||
|
@ -2578,6 +2900,7 @@ id,region,name,tags,mod_name
|
|||
7055,Abandoned Mines - 3,Abandoned Treasure - Floor 3,MANDATORY,Boarding House and Bus Stop Extension
|
||||
7056,Abandoned Mines - 4,Abandoned Treasure - Floor 4,MANDATORY,Boarding House and Bus Stop Extension
|
||||
7057,Abandoned Mines - 5,Abandoned Treasure - Floor 5,MANDATORY,Boarding House and Bus Stop Extension
|
||||
7351,Farm,Read Digging Like Worms,"BOOKSANITY,BOOKSANITY_SKILL",Archaeology
|
||||
7401,Farm,Cook Magic Elixir,COOKSANITY,Magic
|
||||
7402,Farm,Craft Travel Core,CRAFTSANITY,Magic
|
||||
7403,Farm,Craft Haste Elixir,CRAFTSANITY,Stardew Valley Expanded
|
||||
|
@ -2585,7 +2908,7 @@ id,region,name,tags,mod_name
|
|||
7405,Farm,Craft Armor Elixir,CRAFTSANITY,Stardew Valley Expanded
|
||||
7406,Witch's Swamp,Craft Ginger Tincture,"CRAFTSANITY,GINGER_ISLAND",Distant Lands - Witch Swamp Overhaul
|
||||
7407,Farm,Craft Glass Path,CRAFTSANITY,Archaeology
|
||||
7408,Farm,Craft Glass Bazier,CRAFTSANITY,Archaeology
|
||||
7408,Farm,Craft Glass Brazier,CRAFTSANITY,Archaeology
|
||||
7409,Farm,Craft Glass Fence,CRAFTSANITY,Archaeology
|
||||
7410,Farm,Craft Bone Path,CRAFTSANITY,Archaeology
|
||||
7411,Farm,Craft Water Shifter,CRAFTSANITY,Archaeology
|
||||
|
@ -2603,13 +2926,23 @@ id,region,name,tags,mod_name
|
|||
7423,Farm,Craft T-Rex Skeleton L,CRAFTSANITY,Boarding House and Bus Stop Extension
|
||||
7424,Farm,Craft T-Rex Skeleton M,CRAFTSANITY,Boarding House and Bus Stop Extension
|
||||
7425,Farm,Craft T-Rex Skeleton R,CRAFTSANITY,Boarding House and Bus Stop Extension
|
||||
7426,Farm,Craft Restoration Table,CRAFTSANITY,Archaeology
|
||||
7427,Farm,Craft Rusty Path,CRAFTSANITY,Archaeology
|
||||
7428,Farm,Craft Rusty Brazier,CRAFTSANITY,Archaeology
|
||||
7429,Farm,Craft Lucky Ring,CRAFTSANITY,Archaeology
|
||||
7430,Farm,Craft Bone Fence,CRAFTSANITY,Archaeology
|
||||
7431,Farm,Craft Bouquet,CRAFTSANITY,Socializing Skill
|
||||
7432,Farm,Craft Trash Bin,CRAFTSANITY,Binning Skill
|
||||
7433,Farm,Craft Composter,CRAFTSANITY,Binning Skill
|
||||
7434,Farm,Craft Recycling Bin,CRAFTSANITY,Binning Skill
|
||||
7435,Farm,Craft Advanced Recycling Machine,CRAFTSANITY,Binning Skill
|
||||
7451,Adventurer's Guild,Magic Elixir Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Magic
|
||||
7452,Adventurer's Guild,Travel Core Recipe,CRAFTSANITY,Magic
|
||||
7453,Alesia Shop,Haste Elixir Recipe,CRAFTSANITY,Stardew Valley Expanded
|
||||
7454,Isaac Shop,Hero Elixir Recipe,CRAFTSANITY,Stardew Valley Expanded
|
||||
7455,Alesia Shop,Armor Elixir Recipe,CRAFTSANITY,Stardew Valley Expanded
|
||||
7501,Mountain,Missing Envelope,"STORY_QUEST",Ayeisha - The Postal Worker (Custom NPC)
|
||||
7502,Forest,Lost Emerald Ring,"STORY_QUEST",Ayeisha - The Postal Worker (Custom NPC)
|
||||
7502,Forest,Ayeisha's Lost Ring,"STORY_QUEST",Ayeisha - The Postal Worker (Custom NPC)
|
||||
7503,Forest,Mr.Ginger's request,"STORY_QUEST",Mister Ginger (cat npc)
|
||||
7504,Forest,Juna's Drink Request,"STORY_QUEST",Juna - Roommate NPC
|
||||
7505,Forest,Juna's BFF Request,"STORY_QUEST",Juna - Roommate NPC
|
||||
|
@ -2648,6 +2981,11 @@ id,region,name,tags,mod_name
|
|||
7563,Kitchen,Cook Pemmican,COOKSANITY,Distant Lands - Witch Swamp Overhaul
|
||||
7564,Kitchen,Cook Void Mint Tea,COOKSANITY,Distant Lands - Witch Swamp Overhaul
|
||||
7565,Kitchen,Cook Special Pumpkin Soup,COOKSANITY,Boarding House and Bus Stop Extension
|
||||
7566,Kitchen,Cook Digger's Delight,COOKSANITY,Archaeology
|
||||
7567,Kitchen,Cook Rocky Root Coffee,COOKSANITY,Archaeology
|
||||
7568,Kitchen,Cook Ancient Jello,COOKSANITY,Archaeology
|
||||
7569,Kitchen,Cook Grilled Cheese,COOKSANITY,Binning Skill
|
||||
7570,Kitchen,Cook Fish Casserole,COOKSANITY,Binning Skill
|
||||
7601,Bear Shop,Baked Berry Oatmeal Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
|
||||
7602,Bear Shop,Flower Cookie Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Stardew Valley Expanded
|
||||
7603,Saloon,Big Bark Burger Recipe,"CHEFSANITY,CHEFSANITY_FRIENDSHIP",Stardew Valley Expanded
|
||||
|
@ -2668,6 +3006,11 @@ id,region,name,tags,mod_name
|
|||
7620,Mines Dwarf Shop,T-Rex Skeleton L Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
|
||||
7621,Mines Dwarf Shop,T-Rex Skeleton M Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
|
||||
7622,Mines Dwarf Shop,T-Rex Skeleton R Recipe,CRAFTSANITY,Boarding House and Bus Stop Extension
|
||||
7623,Farm,Digger's Delight Recipe,"CHEFSANITY,CHEFSANITY_SKILL",Archaeology
|
||||
7624,Farm,Rocky Root Coffee Recipe,"CHEFSANITY,CHEFSANITY_SKILL",Archaeology
|
||||
7625,Farm,Ancient Jello Recipe,"CHEFSANITY,CHEFSANITY_SKILL",Archaeology
|
||||
7627,Farm,Grilled Cheese Recipe,"CHEFSANITY,CHEFSANITY_SKILL",Binning Skill
|
||||
7628,Farm,Fish Casserole Recipe,"CHEFSANITY,CHEFSANITY_SKILL",Binning Skill
|
||||
7651,Alesia Shop,Tempered Galaxy Dagger,MANDATORY,Stardew Valley Expanded
|
||||
7652,Isaac Shop,Tempered Galaxy Sword,MANDATORY,Stardew Valley Expanded
|
||||
7653,Isaac Shop,Tempered Galaxy Hammer,MANDATORY,Stardew Valley Expanded
|
||||
|
@ -2697,7 +3040,6 @@ id,region,name,tags,mod_name
|
|||
7724,Mutant Bug Lair,Fishsanity: Water Grub,FISHSANITY,Stardew Valley Expanded
|
||||
7725,Crimson Badlands,Fishsanity: Undeadfish,FISHSANITY,Stardew Valley Expanded
|
||||
7726,Shearwater Bridge,Fishsanity: Kittyfish,FISHSANITY,Stardew Valley Expanded
|
||||
7727,Blue Moon Vineyard,Fishsanity: Dulse Seaweed,FISHSANITY,Stardew Valley Expanded
|
||||
7728,Witch's Swamp,Fishsanity: Void Minnow,FISHSANITY,Distant Lands - Witch Swamp Overhaul
|
||||
7729,Witch's Swamp,Fishsanity: Swamp Leech,FISHSANITY,Distant Lands - Witch Swamp Overhaul
|
||||
7730,Witch's Swamp,Fishsanity: Giant Horsehoe Crab,FISHSANITY,Distant Lands - Witch Swamp Overhaul
|
||||
|
@ -2714,15 +3056,15 @@ id,region,name,tags,mod_name
|
|||
8002,Shipping,Shipsanity: Travel Core,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Magic
|
||||
8003,Shipping,Shipsanity: Aegis Elixir,SHIPSANITY,Stardew Valley Expanded
|
||||
8004,Shipping,Shipsanity: Aged Blue Moon Wine,SHIPSANITY,Stardew Valley Expanded
|
||||
8005,Shipping,Shipsanity: Ancient Ferns Seed,SHIPSANITY,Stardew Valley Expanded
|
||||
8005,Shipping,Shipsanity: Ancient Fern Seed,SHIPSANITY,Stardew Valley Expanded
|
||||
8006,Shipping,Shipsanity: Ancient Fiber,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8007,Shipping,Shipsanity: Armor Elixir,SHIPSANITY,Stardew Valley Expanded
|
||||
8008,Shipping,Shipsanity: Baby Lunaloo,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8009,Shipping,Shipsanity: Baked Berry Oatmeal,SHIPSANITY,Stardew Valley Expanded
|
||||
8010,Shipping,Shipsanity: Barbarian Elixir,SHIPSANITY,Stardew Valley Expanded
|
||||
8011,Shipping,Shipsanity: Bearberrys,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8011,Shipping,Shipsanity: Bearberry,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8012,Shipping,Shipsanity: Big Bark Burger,SHIPSANITY,Stardew Valley Expanded
|
||||
8013,Shipping,Shipsanity: Big Conch,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8013,Shipping,Shipsanity: Conch,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8014,Shipping,Shipsanity: Blue Moon Wine,SHIPSANITY,Stardew Valley Expanded
|
||||
8015,Shipping,Shipsanity: Bonefish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
8016,Shipping,Shipsanity: Bull Trout,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
|
@ -2730,8 +3072,7 @@ id,region,name,tags,mod_name
|
|||
8018,Shipping,Shipsanity: Clownfish,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8019,Shipping,Shipsanity: Daggerfish,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8020,Shipping,Shipsanity: Dewdrop Berry,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8021,Shipping,Shipsanity: Dried Sand Dollar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8022,Shipping,Shipsanity: Dulse Seaweed,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
8021,Shipping,Shipsanity: Sand Dollar,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8023,Shipping,Shipsanity: Ferngill Primrose,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8024,Shipping,Shipsanity: Flower Cookie,SHIPSANITY,Stardew Valley Expanded
|
||||
8025,Shipping,Shipsanity: Frog,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
|
@ -2751,7 +3092,7 @@ id,region,name,tags,mod_name
|
|||
8040,Shipping,Shipsanity: King Salmon,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
8050,Shipping,Shipsanity: Kittyfish,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
8051,Shipping,Shipsanity: Lightning Elixir,SHIPSANITY,Stardew Valley Expanded
|
||||
8052,Shipping,Shipsanity: Lucky Four Leaf Clover,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8052,Shipping,Shipsanity: Four Leaf Clover,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8053,Shipping,Shipsanity: Lunaloo,"SHIPSANITY,SHIPSANITY_FISH,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8054,Shipping,Shipsanity: Meteor Carp,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
8055,Shipping,Shipsanity: Minnow,"SHIPSANITY,SHIPSANITY_FISH",Stardew Valley Expanded
|
||||
|
@ -2774,7 +3115,7 @@ id,region,name,tags,mod_name
|
|||
8072,Shipping,Shipsanity: Shrub Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8073,Shipping,Shipsanity: Slime Berry,"SHIPSANITY,SHIPSANITY_CROP,SHIPSANITY_FULL_SHIPMENT,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8074,Shipping,Shipsanity: Slime Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8075,Shipping,Shipsanity: Smelly Rafflesia,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8075,Shipping,Shipsanity: Rafflesia,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Stardew Valley Expanded
|
||||
8076,Shipping,Shipsanity: Sports Drink,SHIPSANITY,Stardew Valley Expanded
|
||||
8077,Shipping,Shipsanity: Stalk Seed,"SHIPSANITY,GINGER_ISLAND",Stardew Valley Expanded
|
||||
8078,Shipping,Shipsanity: Stamina Capsule,SHIPSANITY,Stardew Valley Expanded
|
||||
|
@ -2937,3 +3278,12 @@ id,region,name,tags,mod_name
|
|||
8235,Shipping,Shipsanity: Pterodactyl Claw,SHIPSANITY,Boarding House and Bus Stop Extension
|
||||
8236,Shipping,Shipsanity: Neanderthal Skull,SHIPSANITY,Boarding House and Bus Stop Extension
|
||||
8237,Shipping,Shipsanity: Pterodactyl R Wing Bone,SHIPSANITY,Boarding House and Bus Stop Extension
|
||||
8238,Shipping,Shipsanity: Scrap Rust,SHIPSANITY,Archaeology
|
||||
8239,Shipping,Shipsanity: Rusty Path,SHIPSANITY,Archaeology
|
||||
8240,Shipping,Shipsanity: Digging Like Worms,SHIPSANITY,Archaeology
|
||||
8241,Shipping,Shipsanity: Digger's Delight,SHIPSANITY,Archaeology
|
||||
8242,Shipping,Shipsanity: Rocky Root Coffee,SHIPSANITY,Archaeology
|
||||
8243,Shipping,Shipsanity: Ancient Jello,SHIPSANITY,Archaeology
|
||||
8244,Shipping,Shipsanity: Bone Fence,SHIPSANITY,Archaeology
|
||||
8245,Shipping,Shipsanity: Grilled Cheese,SHIPSANITY,Binning Skill
|
||||
8246,Shipping,Shipsanity: Fish Casserole,SHIPSANITY,Binning Skill
|
||||
|
|
Can't render this file because it has a wrong number of fields in line 164.
|
|
@ -76,6 +76,8 @@ def create_mineral(name: str,
|
|||
difficulty += 1.0 / 26.0 * 100
|
||||
if "Omni Geode" in geodes:
|
||||
difficulty += 31.0 / 2750.0 * 100
|
||||
if "Fishing Chest" in geodes:
|
||||
difficulty += 4.3
|
||||
|
||||
mineral_item = MuseumItem.of(name, difficulty, locations, geodes, monsters)
|
||||
all_museum_minerals.append(mineral_item)
|
||||
|
@ -95,7 +97,7 @@ class Artifact:
|
|||
geodes=Geode.artifact_trove)
|
||||
arrowhead = create_artifact("Arrowhead", 8.5, (Region.mountain, Region.forest, Region.bus_stop),
|
||||
geodes=Geode.artifact_trove)
|
||||
ancient_doll = create_artifact("Ancient Doll", 13.1, (Region.mountain, Region.forest, Region.bus_stop),
|
||||
ancient_doll = create_artifact(Artifact.ancient_doll, 13.1, (Region.mountain, Region.forest, Region.bus_stop),
|
||||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
|
||||
elvish_jewelry = create_artifact("Elvish Jewelry", 5.3, Region.forest,
|
||||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
|
||||
|
@ -103,8 +105,7 @@ class Artifact:
|
|||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
|
||||
ornamental_fan = create_artifact("Ornamental Fan", 7.4, (Region.beach, Region.forest, Region.town),
|
||||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
|
||||
dinosaur_egg = create_artifact("Dinosaur Egg", 11.4, (Region.mountain, Region.skull_cavern),
|
||||
geodes=WaterChest.fishing_chest,
|
||||
dinosaur_egg = create_artifact("Dinosaur Egg", 11.4, (Region.skull_cavern),
|
||||
monsters=Monster.pepper_rex)
|
||||
rare_disc = create_artifact("Rare Disc", 5.6, Region.stardew_valley,
|
||||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest),
|
||||
|
@ -170,18 +171,18 @@ class Artifact:
|
|||
|
||||
|
||||
class Mineral:
|
||||
quartz = create_mineral(Mineral.quartz, Region.mines_floor_20)
|
||||
quartz = create_mineral(Mineral.quartz, Region.mines_floor_20, difficulty=100.0 / 5.0)
|
||||
fire_quartz = create_mineral("Fire Quartz", Region.mines_floor_100,
|
||||
geodes=(Geode.magma, Geode.omni, WaterChest.fishing_chest),
|
||||
difficulty=1.0 / 12.0)
|
||||
difficulty=100.0 / 5.0)
|
||||
frozen_tear = create_mineral("Frozen Tear", Region.mines_floor_60,
|
||||
geodes=(Geode.frozen, Geode.omni, WaterChest.fishing_chest),
|
||||
monsters=unlikely,
|
||||
difficulty=1.0 / 12.0)
|
||||
difficulty=100.0 / 5.0)
|
||||
earth_crystal = create_mineral("Earth Crystal", Region.mines_floor_20,
|
||||
geodes=(Geode.geode, Geode.omni, WaterChest.fishing_chest),
|
||||
monsters=Monster.duggy,
|
||||
difficulty=1.0 / 12.0)
|
||||
difficulty=100.0 / 5.0)
|
||||
emerald = create_mineral("Emerald", Region.mines_floor_100,
|
||||
geodes=WaterChest.fishing_chest)
|
||||
aquamarine = create_mineral("Aquamarine", Region.mines_floor_60,
|
||||
|
|
|
@ -7,15 +7,16 @@ from ..strings.craftable_names import ModEdible, Edible
|
|||
from ..strings.crop_names import Fruit, Vegetable, SVEFruit, DistantLandsCrop
|
||||
from ..strings.fish_names import Fish, SVEFish, WaterItem, DistantLandsFish
|
||||
from ..strings.flower_names import Flower
|
||||
from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable
|
||||
from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable, Mushroom
|
||||
from ..strings.ingredient_names import Ingredient
|
||||
from ..strings.food_names import Meal, SVEMeal, Beverage, DistantLandsMeal, BoardingHouseMeal
|
||||
from ..strings.food_names import Meal, SVEMeal, Beverage, DistantLandsMeal, BoardingHouseMeal, ArchaeologyMeal, TrashyMeal
|
||||
from ..strings.material_names import Material
|
||||
from ..strings.metal_names import Fossil
|
||||
from ..strings.metal_names import Fossil, Artifact
|
||||
from ..strings.monster_drop_names import Loot
|
||||
from ..strings.region_names import Region, SVERegion
|
||||
from ..strings.season_names import Season
|
||||
from ..strings.skill_names import Skill
|
||||
from ..strings.seed_names import Seed
|
||||
from ..strings.skill_names import Skill, ModSkill
|
||||
from ..strings.villager_names import NPC, ModNPC
|
||||
|
||||
|
||||
|
@ -49,9 +50,9 @@ def friendship_and_shop_recipe(name: str, friend: str, hearts: int, region: str,
|
|||
return create_recipe(name, ingredients, source, mod_name)
|
||||
|
||||
|
||||
def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int]) -> CookingRecipe:
|
||||
def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
|
||||
source = SkillSource(skill, level)
|
||||
return create_recipe(name, ingredients, source)
|
||||
return create_recipe(name, ingredients, source, mod_name)
|
||||
|
||||
|
||||
def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int], mod_name: Optional[str] = None) -> CookingRecipe:
|
||||
|
@ -116,7 +117,7 @@ fish_taco = friendship_recipe(Meal.fish_taco, NPC.linus, 7, {Fish.tuna: 1, Meal.
|
|||
fried_calamari = friendship_recipe(Meal.fried_calamari, NPC.jodi, 3, {Fish.squid: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1})
|
||||
fried_eel = friendship_recipe(Meal.fried_eel, NPC.george, 3, {Fish.eel: 1, Ingredient.oil: 1})
|
||||
fried_egg = starter_recipe(Meal.fried_egg, {AnimalProduct.chicken_egg: 1})
|
||||
fried_mushroom = friendship_recipe(Meal.fried_mushroom, NPC.demetrius, 3, {Forageable.common_mushroom: 1, Forageable.morel: 1, Ingredient.oil: 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})
|
||||
glazed_yams = queen_of_sauce_recipe(Meal.glazed_yams, 1, Season.fall, 21, {Vegetable.yam: 1, Ingredient.sugar: 1})
|
||||
|
@ -130,6 +131,7 @@ maki_roll = queen_of_sauce_recipe(Meal.maki_roll, 1, Season.summer, 21, {Fish.an
|
|||
mango_sticky_rice = friendship_recipe(Meal.mango_sticky_rice, NPC.leo, 7, {Fruit.mango: 1, Forageable.coconut: 1, Ingredient.rice: 1})
|
||||
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})
|
||||
omelet = queen_of_sauce_recipe(Meal.omelet, 1, Season.spring, 28, {AnimalProduct.chicken_egg: 1, AnimalProduct.cow_milk: 1})
|
||||
pale_broth = friendship_recipe(Meal.pale_broth, NPC.marnie, 3, {WaterItem.white_algae: 2})
|
||||
pancakes = queen_of_sauce_recipe(Meal.pancakes, 1, Season.summer, 14, {Ingredient.wheat_flour: 1, AnimalProduct.chicken_egg: 1})
|
||||
|
@ -160,13 +162,14 @@ shrimp_cocktail = queen_of_sauce_recipe(Meal.shrimp_cocktail, 2, Season.winter,
|
|||
spaghetti = friendship_recipe(Meal.spaghetti, NPC.lewis, 3, {Vegetable.tomato: 1, Ingredient.wheat_flour: 1})
|
||||
spicy_eel = friendship_recipe(Meal.spicy_eel, NPC.george, 7, {Fish.eel: 1, Fruit.hot_pepper: 1})
|
||||
squid_ink_ravioli = skill_recipe(Meal.squid_ink_ravioli, Skill.combat, 9, {AnimalProduct.squid_ink: 1, Ingredient.wheat_flour: 1, Vegetable.tomato: 1})
|
||||
stir_fry_ingredients = {Forageable.cave_carrot: 1, Forageable.common_mushroom: 1, Vegetable.kale: 1, Ingredient.sugar: 1}
|
||||
stir_fry_ingredients = {Forageable.cave_carrot: 1, Mushroom.common: 1, Vegetable.kale: 1, Ingredient.sugar: 1}
|
||||
stir_fry_qos = queen_of_sauce_recipe(Meal.stir_fry, 1, Season.spring, 7, stir_fry_ingredients)
|
||||
strange_bun = friendship_recipe(Meal.strange_bun, NPC.shane, 7, {Ingredient.wheat_flour: 1, Fish.periwinkle: 1, ArtisanGood.void_mayonnaise: 1})
|
||||
stuffing = friendship_recipe(Meal.stuffing, NPC.pam, 7, {Meal.bread: 1, Fruit.cranberries: 1, Forageable.hazelnut: 1})
|
||||
super_meal = friendship_recipe(Meal.super_meal, NPC.kent, 7, {Vegetable.bok_choy: 1, Fruit.cranberries: 1, Vegetable.artichoke: 1})
|
||||
survival_burger = skill_recipe(Meal.survival_burger, Skill.foraging, 2, {Meal.bread: 1, Forageable.cave_carrot: 1, Vegetable.eggplant: 1})
|
||||
tom_kha_soup = friendship_recipe(Meal.tom_kha_soup, NPC.sandy, 7, {Forageable.coconut: 1, Fish.shrimp: 1, Forageable.common_mushroom: 1})
|
||||
|
||||
survival_burger = skill_recipe(Meal.survival_burger, Skill.foraging, 8, {Meal.bread: 1, Forageable.cave_carrot: 1, Vegetable.eggplant: 1})
|
||||
tom_kha_soup = friendship_recipe(Meal.tom_kha_soup, NPC.sandy, 7, {Forageable.coconut: 1, Fish.shrimp: 1, Mushroom.common: 1})
|
||||
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)
|
||||
|
@ -175,7 +178,7 @@ tropical_curry = shop_recipe(Meal.tropical_curry, Region.island_resort, 2000, {F
|
|||
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, Forageable.purple_mushroom: 1}, ModNames.magic)
|
||||
magic_elixir = shop_recipe(ModEdible.magic_elixir, Region.adventurer_guild, 3000, {Edible.life_elixir: 1, Mushroom.purple: 1}, 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)
|
||||
|
@ -188,7 +191,7 @@ frog_legs = shop_recipe(SVEMeal.frog_legs, Region.adventurer_guild, 2000, {SVEFi
|
|||
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)
|
||||
mixed_berry_pie = shop_recipe(SVEMeal.mixed_berry_pie, Region.saloon, 3500, {Fruit.strawberry: 6, SVEFruit.salal_berry: 6, Forageable.blackberry: 6,
|
||||
SVEForage.bearberrys: 6, Ingredient.sugar: 1, Ingredient.wheat_flour: 1},
|
||||
SVEForage.bearberry: 6, Ingredient.sugar: 1, Ingredient.wheat_flour: 1},
|
||||
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)
|
||||
|
@ -198,8 +201,8 @@ void_delight = friendship_and_shop_recipe(SVEMeal.void_delight, NPC.krobus, 10,
|
|||
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)
|
||||
|
||||
mushroom_kebab = friendship_recipe(DistantLandsMeal.mushroom_kebab, ModNPC.goblin, 2, {Forageable.chanterelle: 1, Forageable.common_mushroom: 1,
|
||||
Forageable.red_mushroom: 1, Material.wood: 1}, ModNames.distant_lands)
|
||||
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)
|
||||
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)
|
||||
|
@ -208,6 +211,11 @@ pemmican = friendship_recipe(DistantLandsMeal.pemmican, ModNPC.goblin, 8, {Loot.
|
|||
|
||||
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)
|
||||
diggers_delight = skill_recipe(ArchaeologyMeal.diggers_delight, ModSkill.archaeology, 3, {Forageable.cave_carrot: 2, Ingredient.sugar: 1, AnimalProduct.milk: 1}, ModNames.archaeology)
|
||||
rocky_root = skill_recipe(ArchaeologyMeal.rocky_root, ModSkill.archaeology, 7, {Forageable.cave_carrot: 3, Seed.coffee: 1, Material.stone: 1}, 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)
|
||||
|
||||
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)
|
||||
|
||||
all_cooking_recipes_by_name = {recipe.meal: recipe for recipe in all_cooking_recipes}
|
|
@ -94,6 +94,16 @@ class SkillSource(RecipeSource):
|
|||
return f"SkillSource at level {self.level} {self.skill}"
|
||||
|
||||
|
||||
class MasterySource(RecipeSource):
|
||||
skill: str
|
||||
|
||||
def __init__(self, skill: str):
|
||||
self.skill = skill
|
||||
|
||||
def __repr__(self):
|
||||
return f"MasterySource at level {self.level} {self.skill}"
|
||||
|
||||
|
||||
class ShopSource(RecipeSource):
|
||||
region: str
|
||||
price: int
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from .game_item import Requirement
|
||||
from ..strings.tool_names import ToolMaterial
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class BookRequirement(Requirement):
|
||||
book: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ToolRequirement(Requirement):
|
||||
tool: str
|
||||
tier: str = ToolMaterial.basic
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SkillRequirement(Requirement):
|
||||
skill: str
|
||||
level: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SeasonRequirement(Requirement):
|
||||
season: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class YearRequirement(Requirement):
|
||||
year: int
|
|
@ -0,0 +1,40 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Tuple, Optional
|
||||
|
||||
from .game_item import ItemSource, kw_only, Requirement
|
||||
from ..strings.season_names import Season
|
||||
|
||||
ItemPrice = Tuple[int, str]
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class ShopSource(ItemSource):
|
||||
shop_region: str
|
||||
money_price: Optional[int] = None
|
||||
items_price: Optional[Tuple[ItemPrice, ...]] = None
|
||||
seasons: Tuple[str, ...] = Season.all
|
||||
other_requirements: Tuple[Requirement, ...] = ()
|
||||
|
||||
def __post_init__(self):
|
||||
assert self.money_price or self.items_price, "At least money price or items price need to be defined."
|
||||
assert self.items_price is None or all(type(p) == tuple for p in self.items_price), "Items price should be a tuple."
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class MysteryBoxSource(ItemSource):
|
||||
amount: int
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class ArtifactTroveSource(ItemSource):
|
||||
amount: int
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class PrizeMachineSource(ItemSource):
|
||||
amount: int
|
||||
|
||||
|
||||
@dataclass(frozen=True, **kw_only)
|
||||
class FishingTreasureChestSource(ItemSource):
|
||||
amount: int
|
|
@ -0,0 +1,9 @@
|
|||
from dataclasses import dataclass, field
|
||||
|
||||
from ..data.game_item import kw_only
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Skill:
|
||||
name: str
|
||||
has_mastery: bool = field(**kw_only)
|
|
@ -1,10 +1,10 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import List, Tuple, Optional, Dict, Callable, Set
|
||||
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
|
||||
from ..strings.region_names import Region, SVERegion, AlectoRegion, BoardingHouseRegion, LaceyRegion, LogicRegion
|
||||
from ..strings.season_names import Season
|
||||
from ..strings.villager_names import NPC, ModNPC
|
||||
|
||||
|
@ -36,7 +36,7 @@ carpenter = (Region.carpenter,)
|
|||
alex_house = (Region.alex_house,)
|
||||
elliott_house = (Region.elliott_house,)
|
||||
ranch = (Region.ranch,)
|
||||
mines_dwarf_shop = (Region.mines_dwarf_shop,)
|
||||
mines_dwarf_shop = (LogicRegion.mines_dwarf_shop,)
|
||||
desert = (Region.desert,)
|
||||
oasis = (Region.oasis,)
|
||||
sewers = (Region.sewer,)
|
||||
|
@ -355,28 +355,10 @@ scarlett_loves = goat_cheese + duck_feather + goat_milk + cherry + maple_syrup +
|
|||
susan_loves = pancakes + chocolate_cake + pink_cake + ice_cream + cookie + pumpkin_pie + rhubarb_pie + \
|
||||
blueberry_tart + blackberry_cobbler + cranberry_candy + red_plate
|
||||
|
||||
all_villagers: List[Villager] = []
|
||||
villager_modifications_by_mod: Dict[str, Dict[str, Callable[[str, Villager], Villager]]] = {}
|
||||
|
||||
|
||||
def villager(name: str, bachelor: bool, locations: Tuple[str, ...], birthday: str, gifts: Tuple[str, ...],
|
||||
available: bool, mod_name: Optional[str] = None) -> Villager:
|
||||
npc = Villager(name, bachelor, locations, birthday, gifts, available, mod_name)
|
||||
all_villagers.append(npc)
|
||||
return npc
|
||||
|
||||
|
||||
def adapt_wizard_to_sve(mod_name: str, npc: Villager):
|
||||
if npc.mod_name:
|
||||
mod_name = npc.mod_name
|
||||
# The wizard leaves his tower on sunday, for like 1 hour... Good enough to meet him!
|
||||
return Villager(npc.name, True, npc.locations + forest, npc.birthday, npc.gifts, npc.available, mod_name)
|
||||
|
||||
|
||||
def register_villager_modification(mod_name: str, npc: Villager, modification_function):
|
||||
if mod_name not in villager_modifications_by_mod:
|
||||
villager_modifications_by_mod[mod_name] = {}
|
||||
villager_modifications_by_mod[mod_name][npc.name] = modification_function
|
||||
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)
|
||||
|
@ -385,18 +367,18 @@ harvey = villager(NPC.harvey, True, town + hospital, Season.winter, universal_lo
|
|||
sam = villager(NPC.sam, True, town, 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)
|
||||
best_girl = villager(NPC.abigail, True, town, Season.fall, universal_loves + abigail_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)
|
||||
hoe = villager(NPC.haley, True, town, Season.spring, universal_loves_no_prismatic_shard + haley_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)
|
||||
nerd = villager(NPC.maru, True, carpenter + hospital + town, Season.summer, universal_loves + maru_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)
|
||||
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)
|
||||
gilf = villager(NPC.evelyn, False, town, Season.winter, universal_loves + evelyn_loves, True)
|
||||
boomer = villager(NPC.george, False, town, Season.fall, universal_loves + george_loves, True)
|
||||
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)
|
||||
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)
|
||||
|
@ -408,7 +390,7 @@ linus = villager(NPC.linus, False, mountain, Season.winter, universal_loves + li
|
|||
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)
|
||||
milf = villager(NPC.robin, False, carpenter, Season.fall, universal_loves + robin_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)
|
||||
|
@ -443,54 +425,10 @@ sophia = villager(ModNPC.sophia, True, bluemoon, Season.winter, universal_loves_
|
|||
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)
|
||||
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.jasper_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.jasper_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)
|
||||
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)
|
||||
|
||||
# Modified villagers; not included in all villagers
|
||||
|
||||
register_villager_modification(ModNames.sve, wizard, adapt_wizard_to_sve)
|
||||
|
||||
all_villagers_by_name: Dict[str, Villager] = {villager.name: villager for villager in all_villagers}
|
||||
all_villagers_by_mod: Dict[str, List[Villager]] = {}
|
||||
all_villagers_by_mod_by_name: Dict[str, Dict[str, Villager]] = {}
|
||||
|
||||
for npc in all_villagers:
|
||||
mod = npc.mod_name
|
||||
name = npc.name
|
||||
if mod in all_villagers_by_mod:
|
||||
all_villagers_by_mod[mod].append(npc)
|
||||
all_villagers_by_mod_by_name[mod][name] = npc
|
||||
else:
|
||||
all_villagers_by_mod[mod] = [npc]
|
||||
all_villagers_by_mod_by_name[mod] = {}
|
||||
all_villagers_by_mod_by_name[mod][name] = npc
|
||||
|
||||
|
||||
def villager_included_for_any_mod(npc: Villager, mods: Set[str]):
|
||||
if not npc.mod_name:
|
||||
return True
|
||||
for mod in npc.mod_name.split(","):
|
||||
if mod in mods:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def get_villagers_for_mods(mods: Set[str]) -> List[Villager]:
|
||||
villagers_for_current_mods = []
|
||||
for npc in all_villagers:
|
||||
if not villager_included_for_any_mod(npc, mods):
|
||||
continue
|
||||
modified_npc = npc
|
||||
for active_mod in mods:
|
||||
if (active_mod not in villager_modifications_by_mod or
|
||||
npc.name not in villager_modifications_by_mod[active_mod]):
|
||||
continue
|
||||
modification = villager_modifications_by_mod[active_mod][npc.name]
|
||||
modified_npc = modification(active_mod, modified_npc)
|
||||
villagers_for_current_mods.append(modified_npc)
|
||||
return villagers_for_current_mods
|
||||
|
|
|
@ -17,19 +17,20 @@ may be useful to the player.
|
|||
## What is the goal of Stardew Valley?
|
||||
|
||||
The player can choose from a number of goals, using their YAML options.
|
||||
|
||||
- Complete the [Community Center](https://stardewvalleywiki.com/Bundles)
|
||||
- Succeed [Grandpa's Evaluation](https://stardewvalleywiki.com/Grandpa) with 4 lit candles
|
||||
- Reach the bottom of the [Pelican Town Mineshaft](https://stardewvalleywiki.com/The_Mines)
|
||||
- Complete the [Cryptic Note](https://stardewvalleywiki.com/Secret_Notes#Secret_Note_.2310) quest, by meeting Mr Qi on
|
||||
floor 100 of the Skull Cavern
|
||||
floor 100 of the Skull Cavern
|
||||
- Become a [Master Angler](https://stardewvalleywiki.com/Fish), which requires catching every fish in your slot
|
||||
- Restore [A Complete Collection](https://stardewvalleywiki.com/Museum), which requires donating all the artifacts and
|
||||
minerals to the museum
|
||||
minerals to the museum
|
||||
- Get the achievement [Full House](https://stardewvalleywiki.com/Children), which requires getting married and having two kids
|
||||
- Get recognized as the [Greatest Walnut Hunter](https://stardewvalleywiki.com/Golden_Walnut) by Mr Qi, which requires
|
||||
finding all 130 golden walnuts on ginger island
|
||||
finding all 130 golden walnuts on ginger island
|
||||
- Become the [Protector of the Valley](https://stardewvalleywiki.com/Adventurer%27s_Guild#Monster_Eradication_Goals) by
|
||||
completing all the monster slayer goals at the Adventure Guild
|
||||
completing all the monster slayer goals at the Adventure Guild
|
||||
- Complete a [Full Shipment](https://stardewvalleywiki.com/Shipping#Collection) by shipping every item in your slot
|
||||
- Become a [Gourmet Chef](https://stardewvalleywiki.com/Cooking) by cooking every recipe in your slot
|
||||
- Become a [Craft Master](https://stardewvalleywiki.com/Crafting) by crafting every item
|
||||
|
@ -45,25 +46,27 @@ to "Exclude Legendaries", and pick the Master Angler goal, you will not need to
|
|||
## What are location checks in Stardew Valley?
|
||||
|
||||
Location checks in Stardew Valley always include:
|
||||
|
||||
- [Community Center Bundles](https://stardewvalleywiki.com/Bundles)
|
||||
- [Mineshaft Chest Rewards](https://stardewvalleywiki.com/The_Mines#Remixed_Rewards)
|
||||
- [Traveling Merchant Items](https://stardewvalleywiki.com/Traveling_Cart)
|
||||
- Isolated objectives such as the [beach bridge](https://stardewvalleywiki.com/The_Beach#Tide_Pools),
|
||||
[Old Master Cannoli](https://stardewvalleywiki.com/Secret_Woods#Old_Master_Cannoli),
|
||||
[Grim Reaper Statue](https://stardewvalleywiki.com/Golden_Scythe), etc
|
||||
[Old Master Cannoli](https://stardewvalleywiki.com/Secret_Woods#Old_Master_Cannoli),
|
||||
[Grim Reaper Statue](https://stardewvalleywiki.com/Golden_Scythe), etc
|
||||
|
||||
There also are a number of location checks that are optional, and individual players choose to include them or not in their shuffling:
|
||||
|
||||
- [Tools and Fishing Rod Upgrades](https://stardewvalleywiki.com/Tools)
|
||||
- [Carpenter Buildings](https://stardewvalleywiki.com/Carpenter%27s_Shop#Farm_Buildings)
|
||||
- [Backpack Upgrades](https://stardewvalleywiki.com/Tools#Other_Tools)
|
||||
- [Mine Elevator Levels](https://stardewvalleywiki.com/The_Mines#Staircases)
|
||||
- [Skill Levels](https://stardewvalleywiki.com/Skills)
|
||||
- [Skill Levels](https://stardewvalleywiki.com/Skills) and [Masteries](https://stardewvalleywiki.com/Mastery_Cave#Masteries)
|
||||
- Arcade Machines
|
||||
- [Story Quests](https://stardewvalleywiki.com/Quests#List_of_Story_Quests)
|
||||
- [Help Wanted Quests](https://stardewvalleywiki.com/Quests#Help_Wanted_Quests)
|
||||
- Participating in [Festivals](https://stardewvalleywiki.com/Festivals)
|
||||
- [Special Orders](https://stardewvalleywiki.com/Quests#List_of_Special_Orders) from the town board, or from
|
||||
[Mr Qi](https://stardewvalleywiki.com/Quests#List_of_Mr._Qi.27s_Special_Orders)
|
||||
[Mr Qi](https://stardewvalleywiki.com/Quests#List_of_Mr._Qi.27s_Special_Orders)
|
||||
- [Cropsanity](https://stardewvalleywiki.com/Crops): Growing and Harvesting individual crop types
|
||||
- [Fishsanity](https://stardewvalleywiki.com/Fish): Catching individual fish
|
||||
- [Museumsanity](https://stardewvalleywiki.com/Museum): Donating individual items, or reaching milestones for museum donations
|
||||
|
@ -73,6 +76,8 @@ There also are a number of location checks that are optional, and individual pla
|
|||
- [Chefsanity](https://stardewvalleywiki.com/Cooking#Recipes): Learning cooking recipes
|
||||
- [Craftsanity](https://stardewvalleywiki.com/Crafting): Crafting individual items
|
||||
- [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
|
||||
|
||||
## Which items can be in another player's world?
|
||||
|
||||
|
@ -80,31 +85,39 @@ Every normal reward from the above locations can be in another player's world.
|
|||
For the locations which do not include a normal reward, Resource Packs and traps are instead added to the pool. Traps are optional.
|
||||
|
||||
A player can enable some options that will add some items to the pool that are relevant to progression
|
||||
|
||||
- Seasons Randomizer:
|
||||
- All 4 seasons will be items, and one of them will be selected randomly and be added to the player's start inventory.
|
||||
- At the end of each month, the player can choose the next season, instead of following the vanilla season order. On Seasons Randomizer, they can only choose from the seasons they have received.
|
||||
- At the end of each month, the player can choose the next season, instead of following the vanilla season order. On Seasons Randomizer, they can only
|
||||
choose from the seasons they have received.
|
||||
- Cropsanity:
|
||||
- Every single seed in the game starts off locked and cannot be purchased from any merchant. Their unlocks are received as multiworld items. Growing each seed and harvesting the resulting crop sends a location check
|
||||
- The way merchants sell seeds is considerably changed. Pierre sells fewer seeds at a high price, while Joja sells unlimited seeds but in huge discount packs, not individually.
|
||||
- Every single seed in the game starts off locked and cannot be purchased from any merchant. Their unlocks are received as multiworld items. Growing each
|
||||
seed and harvesting the resulting crop sends a location check
|
||||
- The way merchants sell seeds is considerably changed. Pierre sells fewer seeds at a high price, while Joja sells unlimited seeds but in huge discount
|
||||
packs, not individually.
|
||||
- Museumsanity:
|
||||
- The items that are normally obtained from museum donation milestones are added to the item pool. Some items, like the magic rock candy, are duplicated for convenience.
|
||||
- The Traveling Merchant now sells artifacts and minerals, with a bias towards undonated ones, to mitigate randomness. She will sell these items as the player receives "Traveling Merchant Metal Detector" items.
|
||||
- The items that are normally obtained from museum donation milestones are added to the item pool. Some items, like the magic rock candy, are duplicated for
|
||||
convenience.
|
||||
- The Traveling Merchant now sells artifacts and minerals, with a bias towards undonated ones, to mitigate randomness. She will sell these items as the
|
||||
player receives "Traveling Merchant Metal Detector" items.
|
||||
- TV Channels
|
||||
- Babies
|
||||
- Only if Friendsanity is enabled
|
||||
|
||||
There are a few extra vanilla items, which are added to the pool for convenience, but do not have a matching location. These include
|
||||
|
||||
- [Wizard Buildings](https://stardewvalleywiki.com/Wizard%27s_Tower#Buildings)
|
||||
- [Return Scepter](https://stardewvalleywiki.com/Return_Scepter)
|
||||
- [Qi Walnut Room QoL items](https://stardewvalleywiki.com/Qi%27s_Walnut_Room#Stock)
|
||||
|
||||
And lastly, some Archipelago-exclusive items exist in the pool, which are designed around game balance and QoL. These include:
|
||||
|
||||
- Arcade Machine buffs (Only if the arcade machines are randomized)
|
||||
- Journey of the Prairie King has drop rate increases, extra lives, and equipment
|
||||
- Junimo Kart has extra lives.
|
||||
- Permanent Movement Speed Bonuses (customizable)
|
||||
- Permanent Luck Bonuses (customizable)
|
||||
- Traveling Merchant buffs
|
||||
- Various Permanent Player Buffs (customizable)
|
||||
- Traveling Merchant modifiers
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
|
||||
|
@ -131,17 +144,14 @@ List of supported mods:
|
|||
|
||||
- General
|
||||
- [Stardew Valley Expanded](https://www.nexusmods.com/stardewvalley/mods/3753)
|
||||
- [DeepWoods](https://www.nexusmods.com/stardewvalley/mods/2571)
|
||||
- [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)
|
||||
- [Distant Lands - Witch Swamp Overhaul](https://www.nexusmods.com/stardewvalley/mods/18109)
|
||||
- Skills
|
||||
- [Magic](https://www.nexusmods.com/stardewvalley/mods/2007)
|
||||
- [Luck Skill](https://www.nexusmods.com/stardewvalley/mods/521)
|
||||
- [Socializing Skill](https://www.nexusmods.com/stardewvalley/mods/14142)
|
||||
- [Archaeology](https://www.nexusmods.com/stardewvalley/mods/15793)
|
||||
- [Cooking Skill](https://www.nexusmods.com/stardewvalley/mods/522)
|
||||
- [Archaeology](https://www.nexusmods.com/stardewvalley/mods/22199)
|
||||
- [Binning Skill](https://www.nexusmods.com/stardewvalley/mods/14073)
|
||||
- NPCs
|
||||
- [Ayeisha - The Postal Worker (Custom NPC)](https://www.nexusmods.com/stardewvalley/mods/6427)
|
||||
|
@ -149,12 +159,7 @@ List of supported mods:
|
|||
- [Juna - Roommate NPC](https://www.nexusmods.com/stardewvalley/mods/8606)
|
||||
- [Professor Jasper Thomas](https://www.nexusmods.com/stardewvalley/mods/5599)
|
||||
- [Alec Revisited](https://www.nexusmods.com/stardewvalley/mods/10697)
|
||||
- [Custom NPC - Yoba](https://www.nexusmods.com/stardewvalley/mods/14871)
|
||||
- [Custom NPC Eugene](https://www.nexusmods.com/stardewvalley/mods/9222)
|
||||
- ['Prophet' Wellwick](https://www.nexusmods.com/stardewvalley/mods/6462)
|
||||
- [Shiko - New Custom NPC](https://www.nexusmods.com/stardewvalley/mods/3732)
|
||||
- [Delores - Custom NPC](https://www.nexusmods.com/stardewvalley/mods/5510)
|
||||
- [Custom NPC - Riley](https://www.nexusmods.com/stardewvalley/mods/5811)
|
||||
- [Alecto the Witch](https://www.nexusmods.com/stardewvalley/mods/10671)
|
||||
|
||||
Some of these mods might need a patch mod to tie the randomizer with the mod. These can be found
|
||||
|
@ -164,5 +169,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 player, 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.
|
||||
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.
|
||||
|
|
|
@ -2,14 +2,10 @@
|
|||
|
||||
## Required Software
|
||||
|
||||
- Stardew Valley on PC (Recommended: [Steam version](https://store.steampowered.com/app/413150/Stardew_Valley/))
|
||||
- You need version 1.5.6. It is available in a public beta branch on Steam .
|
||||
- If your Stardew is not on Steam, you are responsible for finding a way to downgrade it.
|
||||
- This measure is temporary. We are working hard to bring the mod to Stardew 1.6 as soon as possible.
|
||||
- SMAPI 3.x.x ([Mod loader for Stardew Valley](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files))
|
||||
- Same as Stardew Valley itself, SMAPI needs a slightly older version to be compatible with Stardew Valley 1.5.6 
|
||||
- [StardewArchipelago Mod Release 5.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
|
||||
- It is important to use a mod release of version 5.x.x to play seeds that have been generated here. Later releases
|
||||
- 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
|
||||
can only be used with later releases of the world generator, that are not hosted on archipelago.gg yet.
|
||||
|
||||
## Optional Software
|
||||
|
@ -38,7 +34,7 @@ You can customize your options by visiting the [Stardew Valley Player Options Pa
|
|||
|
||||
### Installing the mod
|
||||
|
||||
- Install [SMAPI version 3.x.x](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files) by following the instructions on the mod page
|
||||
- Install [SMAPI](https://www.nexusmods.com/stardewvalley/mods/2400?tab=files) by following the instructions on the mod page
|
||||
- Download and extract the [StardewArchipelago](https://github.com/agilbert1412/StardewArchipelago/releases) mod into
|
||||
your Stardew Valley "Mods" folder
|
||||
- *OPTIONAL*: If you want to launch your game through Steam, add the following to your Stardew Valley launch options: `"[PATH TO STARDEW VALLEY]\Stardew Valley\StardewModdingAPI.exe" %command%`
|
||||
|
@ -93,7 +89,7 @@ Stardew-exclusive commands.
|
|||
|
||||
### Playing with supported mods
|
||||
|
||||
See the [Supported mods documentation](https://github.com/agilbert1412/StardewArchipelago/blob/5.x.x/Documentation/Supported%20Mods.md)
|
||||
See the [Supported mods documentation](https://github.com/agilbert1412/StardewArchipelago/blob/6.x.x/Documentation/Supported%20Mods.md)
|
||||
|
||||
### Multiplayer
|
||||
|
||||
|
|
|
@ -1,51 +1,69 @@
|
|||
from random import Random
|
||||
|
||||
from .options import BuildingProgression, StardewValleyOptions, BackpackProgression, ExcludeGingerIsland, SeasonRandomization, SpecialOrderLocations, \
|
||||
Monstersanity, ToolProgression, SkillProgression, Cooksanity, Chefsanity
|
||||
from . import options as stardew_options
|
||||
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.tv_channel_names import Channel
|
||||
from .strings.wallet_item_names import Wallet
|
||||
|
||||
early_candidate_rate = 4
|
||||
always_early_candidates = ["Greenhouse", "Desert Obelisk", "Rusty Key"]
|
||||
seasons = ["Spring", "Summer", "Fall", "Winter"]
|
||||
always_early_candidates = [Region.greenhouse, Transportation.desert_obelisk, Wallet.rusty_key]
|
||||
seasons = [Season.spring, Season.summer, Season.fall, Season.winter]
|
||||
|
||||
|
||||
def setup_early_items(multiworld, options: StardewValleyOptions, player: int, random: Random):
|
||||
def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions, player: int, random: Random):
|
||||
early_forced = []
|
||||
early_candidates = []
|
||||
early_candidates.extend(always_early_candidates)
|
||||
|
||||
add_seasonal_candidates(early_candidates, options)
|
||||
|
||||
if options.building_progression & BuildingProgression.option_progressive:
|
||||
early_forced.append("Shipping Bin")
|
||||
if options.building_progression & stardew_options.BuildingProgression.option_progressive:
|
||||
early_forced.append(Building.shipping_bin)
|
||||
if options.farm_type != stardew_options.FarmType.option_meadowlands:
|
||||
early_candidates.append("Progressive Coop")
|
||||
early_candidates.append("Progressive Barn")
|
||||
|
||||
if options.backpack_progression == BackpackProgression.option_early_progressive:
|
||||
if options.backpack_progression == stardew_options.BackpackProgression.option_early_progressive:
|
||||
early_forced.append("Progressive Backpack")
|
||||
|
||||
if options.tool_progression & ToolProgression.option_progressive:
|
||||
early_forced.append("Progressive Fishing Rod")
|
||||
if options.tool_progression & stardew_options.ToolProgression.option_progressive:
|
||||
if options.fishsanity != stardew_options.Fishsanity.option_none:
|
||||
early_candidates.append("Progressive Fishing Rod")
|
||||
early_forced.append("Progressive Pickaxe")
|
||||
|
||||
if options.skill_progression == SkillProgression.option_progressive:
|
||||
if options.skill_progression == stardew_options.SkillProgression.option_progressive:
|
||||
early_forced.append("Fishing Level")
|
||||
|
||||
if options.quest_locations >= 0:
|
||||
early_candidates.append("Magnifying Glass")
|
||||
early_candidates.append(Wallet.magnifying_glass)
|
||||
|
||||
if options.special_order_locations != SpecialOrderLocations.option_disabled:
|
||||
if options.special_order_locations & stardew_options.SpecialOrderLocations.option_board:
|
||||
early_candidates.append("Special Order Board")
|
||||
|
||||
if options.cooksanity != Cooksanity.option_none | options.chefsanity & Chefsanity.option_queen_of_sauce:
|
||||
early_candidates.append("The Queen of Sauce")
|
||||
if options.cooksanity != stardew_options.Cooksanity.option_none or options.chefsanity & stardew_options.Chefsanity.option_queen_of_sauce:
|
||||
early_candidates.append(Channel.queen_of_sauce)
|
||||
|
||||
if options.monstersanity == Monstersanity.option_none:
|
||||
early_candidates.append("Progressive Weapon")
|
||||
if options.craftsanity != stardew_options.Craftsanity.option_none:
|
||||
early_candidates.append("Furnace Recipe")
|
||||
|
||||
if options.monstersanity == stardew_options.Monstersanity.option_none:
|
||||
early_candidates.append(APWeapon.weapon)
|
||||
else:
|
||||
early_candidates.append("Progressive Sword")
|
||||
early_candidates.append(APWeapon.sword)
|
||||
|
||||
if options.exclude_ginger_island == ExcludeGingerIsland.option_false:
|
||||
early_candidates.append("Island Obelisk")
|
||||
if options.exclude_ginger_island == stardew_options.ExcludeGingerIsland.option_false:
|
||||
early_candidates.append(Transportation.island_obelisk)
|
||||
|
||||
if options.walnutsanity.value:
|
||||
early_candidates.append("Island North Turtle")
|
||||
early_candidates.append("Island West Turtle")
|
||||
|
||||
if options.museumsanity != stardew_options.Museumsanity.option_none or options.shipsanity >= stardew_options.Shipsanity.option_full_shipment:
|
||||
early_candidates.append(Wallet.metal_detector)
|
||||
|
||||
early_forced.extend(random.sample(early_candidates, len(early_candidates) // early_candidate_rate))
|
||||
|
||||
|
@ -56,10 +74,10 @@ def setup_early_items(multiworld, options: StardewValleyOptions, player: int, ra
|
|||
|
||||
|
||||
def add_seasonal_candidates(early_candidates, options):
|
||||
if options.season_randomization == SeasonRandomization.option_progressive:
|
||||
early_candidates.extend(["Progressive Season"] * 3)
|
||||
if options.season_randomization == stardew_options.SeasonRandomization.option_progressive:
|
||||
early_candidates.extend([Season.progressive] * 3)
|
||||
return
|
||||
if options.season_randomization == SeasonRandomization.option_disabled:
|
||||
if options.season_randomization == stardew_options.SeasonRandomization.option_disabled:
|
||||
return
|
||||
|
||||
early_candidates.extend(seasons)
|
||||
|
|
|
@ -8,18 +8,20 @@ from typing import Dict, List, Protocol, Union, Set, Optional
|
|||
|
||||
from BaseClasses import Item, ItemClassification
|
||||
from . import data
|
||||
from .data.villagers_data import get_villagers_for_mods
|
||||
from .content.feature import friendsanity
|
||||
from .content.game_content import StardewContent
|
||||
from .data.game_item import ItemTag
|
||||
from .logic.logic_event import all_events
|
||||
from .mods.mod_data import ModNames
|
||||
from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Cropsanity, \
|
||||
Friendsanity, Museumsanity, \
|
||||
Fishsanity, BuildingProgression, SkillProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \
|
||||
Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity
|
||||
from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Museumsanity, \
|
||||
BuildingProgression, SkillProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \
|
||||
Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity, Walnutsanity, EnabledFillerBuffs
|
||||
from .strings.ap_names.ap_option_names import OptionName
|
||||
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.event_names import Event
|
||||
from .strings.ap_names.mods.mod_items import SVEQuestItem
|
||||
from .strings.villager_names import NPC, ModNPC
|
||||
from .strings.currency_names import Currency
|
||||
from .strings.wallet_item_names import Wallet
|
||||
|
||||
ITEM_CODE_OFFSET = 717000
|
||||
|
@ -44,6 +46,7 @@ class Group(enum.Enum):
|
|||
WEAPON_SLINGSHOT = enum.auto()
|
||||
PROGRESSIVE_TOOLS = enum.auto()
|
||||
SKILL_LEVEL_UP = enum.auto()
|
||||
SKILL_MASTERY = enum.auto()
|
||||
BUILDING = enum.auto()
|
||||
WIZARD_BUILDING = enum.auto()
|
||||
ARCADE_MACHINE_BUFFS = enum.auto()
|
||||
|
@ -62,6 +65,7 @@ class Group(enum.Enum):
|
|||
FESTIVAL = enum.auto()
|
||||
RARECROW = enum.auto()
|
||||
TRAP = enum.auto()
|
||||
BONUS = enum.auto()
|
||||
MAXIMUM_ONE = enum.auto()
|
||||
EXACTLY_TWO = enum.auto()
|
||||
DEPRECATED = enum.auto()
|
||||
|
@ -80,6 +84,9 @@ class Group(enum.Enum):
|
|||
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()
|
||||
|
@ -135,11 +142,8 @@ def load_item_csv():
|
|||
|
||||
|
||||
events = [
|
||||
ItemData(None, Event.victory, ItemClassification.progression),
|
||||
ItemData(None, Event.can_construct_buildings, ItemClassification.progression),
|
||||
ItemData(None, Event.start_dark_talisman_quest, ItemClassification.progression),
|
||||
ItemData(None, Event.can_ship_items, ItemClassification.progression),
|
||||
ItemData(None, Event.can_shop_at_pierre, ItemClassification.progression),
|
||||
ItemData(None, e, ItemClassification.progression)
|
||||
for e in sorted(all_events)
|
||||
]
|
||||
|
||||
all_items: List[ItemData] = load_item_csv() + events
|
||||
|
@ -168,9 +172,9 @@ def get_too_many_items_error_message(locations_count: int, items_count: int) ->
|
|||
|
||||
|
||||
def create_items(item_factory: StardewItemFactory, item_deleter: StardewItemDeleter, locations_count: int, items_to_exclude: List[Item],
|
||||
options: StardewValleyOptions, random: Random) -> List[Item]:
|
||||
options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]:
|
||||
items = []
|
||||
unique_items = create_unique_items(item_factory, options, random)
|
||||
unique_items = create_unique_items(item_factory, options, content, random)
|
||||
|
||||
remove_items(item_deleter, items_to_exclude, unique_items)
|
||||
|
||||
|
@ -213,11 +217,12 @@ def remove_items_if_no_room_for_them(item_deleter: StardewItemDeleter, unique_it
|
|||
remove_items(item_deleter, items_to_remove, unique_items)
|
||||
|
||||
|
||||
def create_unique_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random) -> List[Item]:
|
||||
def create_unique_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]:
|
||||
items = []
|
||||
|
||||
items.extend(item_factory(item) for item in items_by_group[Group.COMMUNITY_REWARD])
|
||||
items.append(item_factory(CommunityUpgrade.movie_theater)) # It is a community reward, but we need two of them
|
||||
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)
|
||||
|
@ -233,25 +238,30 @@ def create_unique_items(item_factory: StardewItemFactory, options: StardewValley
|
|||
items.append(item_factory(CommunityUpgrade.mushroom_boxes))
|
||||
items.append(item_factory("Beach Bridge"))
|
||||
create_tv_channels(item_factory, options, items)
|
||||
create_special_quest_rewards(item_factory, options, items)
|
||||
create_stardrops(item_factory, options, items)
|
||||
create_quest_rewards(item_factory, options, items)
|
||||
create_stardrops(item_factory, options, content, items)
|
||||
create_museum_items(item_factory, options, items)
|
||||
create_arcade_machine_items(item_factory, options, items)
|
||||
create_player_buffs(item_factory, options, items)
|
||||
create_movement_buffs(item_factory, options, items)
|
||||
create_traveling_merchant_items(item_factory, items)
|
||||
items.append(item_factory("Return Scepter"))
|
||||
create_seasons(item_factory, options, items)
|
||||
create_seeds(item_factory, options, items)
|
||||
create_friendsanity_items(item_factory, options, items, random)
|
||||
create_seeds(item_factory, content, items)
|
||||
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_shipsanity_items(item_factory, options, items)
|
||||
create_booksanity_items(item_factory, content, 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)
|
||||
|
@ -259,6 +269,14 @@ def create_unique_items(item_factory: StardewItemFactory, options: StardewValley
|
|||
return items
|
||||
|
||||
|
||||
def create_raccoons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
number_progressive_raccoons = 9
|
||||
if options.quest_locations < 0:
|
||||
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):
|
||||
|
@ -310,16 +328,29 @@ def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions
|
|||
items.append(item_factory(item_data, ItemClassification.useful))
|
||||
else:
|
||||
items.extend([item_factory(item) for item in [item_data] * 4])
|
||||
items.append(item_factory("Golden Scythe"))
|
||||
if options.skill_progression == SkillProgression.option_progressive_with_masteries:
|
||||
items.append(item_factory("Progressive Scythe"))
|
||||
items.append(item_factory("Progressive Fishing Rod"))
|
||||
items.append(item_factory("Progressive Scythe"))
|
||||
|
||||
|
||||
def create_skills(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if options.skill_progression == SkillProgression.option_progressive:
|
||||
if options.skill_progression == SkillProgression.option_vanilla:
|
||||
return
|
||||
|
||||
for item in items_by_group[Group.SKILL_LEVEL_UP]:
|
||||
if item.mod_name not in options.mods and item.mod_name is not None:
|
||||
continue
|
||||
items.extend(item_factory(item) for item in [item.name] * 10)
|
||||
|
||||
if options.skill_progression != SkillProgression.option_progressive_with_masteries:
|
||||
return
|
||||
|
||||
for item in items_by_group[Group.SKILL_MASTERY]:
|
||||
if item.mod_name not in options.mods and item.mod_name is not None:
|
||||
continue
|
||||
items.append(item_factory(item))
|
||||
|
||||
|
||||
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
|
||||
|
@ -360,6 +391,13 @@ def create_carpenter_buildings(item_factory: StardewItemFactory, options: Starde
|
|||
items.append(item_factory("Tractor Garage"))
|
||||
|
||||
|
||||
def create_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
create_special_quest_rewards(item_factory, options, items)
|
||||
create_help_wanted_quest_rewards(item_factory, options, items)
|
||||
|
||||
create_quest_rewards_sve(item_factory, options, items)
|
||||
|
||||
|
||||
def create_special_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if options.quest_locations < 0:
|
||||
return
|
||||
|
@ -373,21 +411,28 @@ def create_special_quest_rewards(item_factory: StardewItemFactory, options: Star
|
|||
items.append(item_factory(Wallet.iridium_snake_milk))
|
||||
items.append(item_factory("Fairy Dust Recipe"))
|
||||
items.append(item_factory("Dark Talisman"))
|
||||
create_special_quest_rewards_sve(item_factory, options, items)
|
||||
create_distant_lands_quest_rewards(item_factory, options, items)
|
||||
create_boarding_house_quest_rewards(item_factory, options, items)
|
||||
|
||||
|
||||
def create_stardrops(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
def create_help_wanted_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if options.quest_locations <= 0:
|
||||
return
|
||||
|
||||
number_help_wanted = options.quest_locations.value
|
||||
quest_per_prize_ticket = 3
|
||||
number_prize_tickets = number_help_wanted // quest_per_prize_ticket
|
||||
items.extend(item_factory(item) for item in [Currency.prize_ticket] * number_prize_tickets)
|
||||
|
||||
|
||||
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
|
||||
if options.fishsanity != Fishsanity.option_none:
|
||||
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
|
||||
if options.friendsanity != Friendsanity.option_none:
|
||||
if content.features.friendsanity.is_enabled:
|
||||
items.append(item_factory("Stardrop", stardrops_classification)) # Spouse Stardrop
|
||||
|
||||
|
||||
|
@ -403,39 +448,23 @@ def create_museum_items(item_factory: StardewItemFactory, options: StardewValley
|
|||
items.append(item_factory(Wallet.metal_detector))
|
||||
|
||||
|
||||
def create_friendsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item], random: Random):
|
||||
island_villagers = [NPC.leo, ModNPC.lance]
|
||||
if options.friendsanity == Friendsanity.option_none:
|
||||
def create_friendsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item], random: Random):
|
||||
if not content.features.friendsanity.is_enabled:
|
||||
return
|
||||
|
||||
create_babies(item_factory, items, random)
|
||||
exclude_non_bachelors = options.friendsanity == Friendsanity.option_bachelors
|
||||
exclude_locked_villagers = options.friendsanity == Friendsanity.option_starting_npcs or \
|
||||
options.friendsanity == Friendsanity.option_bachelors
|
||||
include_post_marriage_hearts = options.friendsanity == Friendsanity.option_all_with_marriage
|
||||
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
mods = options.mods
|
||||
heart_size = options.friendsanity_heart_size
|
||||
for villager in get_villagers_for_mods(mods.value):
|
||||
if not villager.available and exclude_locked_villagers:
|
||||
continue
|
||||
if not villager.bachelor and exclude_non_bachelors:
|
||||
continue
|
||||
if villager.name in island_villagers and exclude_ginger_island:
|
||||
continue
|
||||
heart_cap = 8 if villager.bachelor else 10
|
||||
if include_post_marriage_hearts and villager.bachelor:
|
||||
heart_cap = 14
|
||||
classification = ItemClassification.progression
|
||||
for heart in range(1, 15):
|
||||
if heart > heart_cap:
|
||||
break
|
||||
if heart % heart_size == 0 or heart == heart_cap:
|
||||
items.append(item_factory(f"{villager.name} <3", classification))
|
||||
if not exclude_non_bachelors:
|
||||
|
||||
for villager in content.villagers.values():
|
||||
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
|
||||
for heart in range(1, 6):
|
||||
if heart % heart_size == 0 or heart == 5:
|
||||
items.append(item_factory(f"Pet <3", ItemClassification.progression_skip_balancing if need_pet else ItemClassification.useful))
|
||||
pet_item_classification = ItemClassification.progression_skip_balancing if need_pet else ItemClassification.useful
|
||||
|
||||
for _ in content.features.friendsanity.get_pet_randomized_hearts():
|
||||
items.append(item_factory(friendsanity.pet_heart_item_name, pet_item_classification))
|
||||
|
||||
|
||||
def create_babies(item_factory: StardewItemFactory, items: List[Item], random: Random):
|
||||
|
@ -462,26 +491,14 @@ def create_arcade_machine_items(item_factory: StardewItemFactory, options: Stard
|
|||
items.extend(item_factory(item) for item in ["Junimo Kart: Extra Life"] * 8)
|
||||
|
||||
|
||||
def create_player_buffs(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
def create_movement_buffs(item_factory, options: StardewValleyOptions, items: List[Item]):
|
||||
movement_buffs: int = options.movement_buff_number.value
|
||||
luck_buffs: int = options.luck_buff_number.value
|
||||
need_all_buffs = options.special_order_locations == SpecialOrderLocations.option_board_qi
|
||||
need_half_buffs = options.festival_locations == FestivalLocations.option_easy
|
||||
create_player_buff(item_factory, Buff.movement, movement_buffs, need_all_buffs, need_half_buffs, items)
|
||||
create_player_buff(item_factory, Buff.luck, luck_buffs, True, need_half_buffs, items)
|
||||
|
||||
|
||||
def create_player_buff(item_factory, buff: str, amount: int, need_all_buffs: bool, need_half_buffs: bool, items: List[Item]):
|
||||
progression_buffs = amount if need_all_buffs else (amount // 2 if need_half_buffs else 0)
|
||||
useful_buffs = amount - progression_buffs
|
||||
items.extend(item_factory(item) for item in [buff] * progression_buffs)
|
||||
items.extend(item_factory(item, ItemClassification.useful) for item in [buff] * useful_buffs)
|
||||
items.extend(item_factory(item) for item in [Buff.movement] * movement_buffs)
|
||||
|
||||
|
||||
def create_traveling_merchant_items(item_factory: StardewItemFactory, items: List[Item]):
|
||||
items.extend([*(item_factory(item) for item in items_by_group[Group.TRAVELING_MERCHANT_DAY]),
|
||||
*(item_factory(item) for item in ["Traveling Merchant Stock Size"] * 6),
|
||||
*(item_factory(item) for item in ["Traveling Merchant Discount"] * 8)])
|
||||
*(item_factory(item) for item in ["Traveling Merchant Stock Size"] * 6)])
|
||||
|
||||
|
||||
def create_seasons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
|
@ -495,14 +512,11 @@ def create_seasons(item_factory: StardewItemFactory, options: StardewValleyOptio
|
|||
items.extend([item_factory(item) for item in items_by_group[Group.SEASON]])
|
||||
|
||||
|
||||
def create_seeds(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if options.cropsanity == Cropsanity.option_disabled:
|
||||
def create_seeds(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
|
||||
if not content.features.cropsanity.is_enabled:
|
||||
return
|
||||
|
||||
base_seed_items = [item for item in items_by_group[Group.CROPSANITY]]
|
||||
filtered_seed_items = remove_excluded_items(base_seed_items, options)
|
||||
seed_items = [item_factory(item) for item in filtered_seed_items]
|
||||
items.extend(seed_items)
|
||||
items.extend(item_factory(item_table[seed.name]) for seed in content.find_tagged_items(ItemTag.CROPSANITY_SEED))
|
||||
|
||||
|
||||
def create_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
|
@ -514,6 +528,35 @@ def create_festival_rewards(item_factory: StardewItemFactory, options: StardewVa
|
|||
items.extend([*festival_rewards, item_factory("Stardrop", get_stardrop_classification(options))])
|
||||
|
||||
|
||||
def create_walnuts(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
walnutsanity = options.walnutsanity
|
||||
if options.exclude_ginger_island == ExcludeGingerIsland.option_true or walnutsanity == Walnutsanity.preset_none:
|
||||
return
|
||||
|
||||
# Give baseline walnuts just to be nice
|
||||
num_single_walnuts = 0
|
||||
num_triple_walnuts = 2
|
||||
num_penta_walnuts = 1
|
||||
# https://stardewvalleywiki.com/Golden_Walnut
|
||||
# Totals should be accurate, but distribution is slightly offset to make room for baseline walnuts
|
||||
if OptionName.walnutsanity_puzzles in walnutsanity: # 61
|
||||
num_single_walnuts += 6 # 6
|
||||
num_triple_walnuts += 5 # 15
|
||||
num_penta_walnuts += 8 # 40
|
||||
if OptionName.walnutsanity_bushes in walnutsanity: # 25
|
||||
num_single_walnuts += 16 # 16
|
||||
num_triple_walnuts += 3 # 9
|
||||
if OptionName.walnutsanity_dig_spots in walnutsanity: # 18
|
||||
num_single_walnuts += 18 # 18
|
||||
if OptionName.walnutsanity_repeatables in walnutsanity: # 33
|
||||
num_single_walnuts += 30 # 30
|
||||
num_triple_walnuts += 1 # 3
|
||||
|
||||
items.extend([item_factory(item) for item in ["Golden Walnut"] * num_single_walnuts])
|
||||
items.extend([item_factory(item) for item in ["3 Golden Walnuts"] * num_triple_walnuts])
|
||||
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:
|
||||
return
|
||||
|
@ -526,11 +569,8 @@ def create_walnut_purchase_rewards(item_factory: StardewItemFactory, options: St
|
|||
|
||||
|
||||
def create_special_order_board_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if options.special_order_locations == SpecialOrderLocations.option_disabled:
|
||||
return
|
||||
|
||||
if options.special_order_locations & SpecialOrderLocations.option_board:
|
||||
special_order_board_items = [item for item in items_by_group[Group.SPECIAL_ORDER_BOARD]]
|
||||
|
||||
items.extend([item_factory(item) for item in special_order_board_items])
|
||||
|
||||
|
||||
|
@ -554,7 +594,7 @@ def create_special_order_qi_rewards(item_factory: StardewItemFactory, options: S
|
|||
qi_gem_rewards.append("15 Qi Gems")
|
||||
qi_gem_rewards.append("15 Qi Gems")
|
||||
|
||||
if options.special_order_locations == SpecialOrderLocations.option_board_qi:
|
||||
if options.special_order_locations & SpecialOrderLocations.value_qi:
|
||||
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"])
|
||||
|
||||
|
@ -607,6 +647,16 @@ 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]):
|
||||
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())
|
||||
|
||||
|
||||
def create_goal_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
goal = options.goal
|
||||
if goal != Goal.option_perfection and goal != Goal.option_complete_collection:
|
||||
|
@ -643,37 +693,31 @@ def create_deepwoods_pendants(item_factory: StardewItemFactory, options: Stardew
|
|||
items.extend([item_factory(item) for item in ["Pendant of Elders", "Pendant of Community", "Pendant of Depths"]])
|
||||
|
||||
|
||||
def create_special_quest_rewards_sve(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
def create_sve_special_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if ModNames.sve not in options.mods:
|
||||
return
|
||||
|
||||
items.extend([item_factory(item) for item in items_by_group[Group.MOD_WARP] if item.mod_name == ModNames.sve])
|
||||
|
||||
if options.quest_locations < 0:
|
||||
|
||||
def create_quest_rewards_sve(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if ModNames.sve not in options.mods:
|
||||
return
|
||||
|
||||
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items])
|
||||
if not exclude_ginger_island:
|
||||
items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items_ginger_island])
|
||||
|
||||
if options.quest_locations < 0:
|
||||
return
|
||||
|
||||
items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items])
|
||||
if exclude_ginger_island:
|
||||
return
|
||||
items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items_ginger_island])
|
||||
|
||||
|
||||
def create_distant_lands_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if options.quest_locations < 0 or ModNames.distant_lands not in options.mods:
|
||||
return
|
||||
items.append(item_factory("Crayfish Soup Recipe"))
|
||||
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
|
||||
return
|
||||
items.append(item_factory("Ginger Tincture Recipe"))
|
||||
|
||||
|
||||
def create_boarding_house_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
|
||||
if options.quest_locations < 0 or ModNames.boarding_house not in options.mods:
|
||||
return
|
||||
items.append(item_factory("Special Pumpkin Soup Recipe"))
|
||||
|
||||
|
||||
def create_unique_filler_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random,
|
||||
available_item_slots: int) -> List[Item]:
|
||||
items = []
|
||||
|
@ -699,18 +743,21 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options
|
|||
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 = [pack for pack in items_by_group[Group.TRAP]
|
||||
if pack.name not in items_already_added_names and
|
||||
(pack.mod_name is None or pack.mod_name in options.mods)]
|
||||
trap_items = [trap for trap in items_by_group[Group.TRAP]
|
||||
if trap.name not in items_already_added_names and
|
||||
(trap.mod_name is None or trap.mod_name in options.mods)]
|
||||
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)
|
||||
|
@ -776,7 +823,7 @@ def remove_limited_amount_packs(packs):
|
|||
return [pack for pack in packs if Group.MAXIMUM_ONE not in pack.groups and Group.EXACTLY_TWO not in pack.groups]
|
||||
|
||||
|
||||
def get_all_filler_items(include_traps: bool, exclude_ginger_island: bool):
|
||||
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:
|
||||
|
@ -785,6 +832,33 @@ def get_all_filler_items(include_traps: bool, exclude_ginger_island: bool):
|
|||
return all_filler_items
|
||||
|
||||
|
||||
def get_allowed_player_buffs(buff_option: EnabledFillerBuffs) -> List[ItemData]:
|
||||
allowed_buffs = []
|
||||
if OptionName.buff_luck in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.luck])
|
||||
if OptionName.buff_damage in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.damage])
|
||||
if OptionName.buff_defense in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.defense])
|
||||
if OptionName.buff_immunity in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.immunity])
|
||||
if OptionName.buff_health in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.health])
|
||||
if OptionName.buff_energy in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.energy])
|
||||
if OptionName.buff_bite in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.bite_rate])
|
||||
if OptionName.buff_fish_trap in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.fish_trap])
|
||||
if OptionName.buff_fishing_bar in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.fishing_bar])
|
||||
if OptionName.buff_quality in buff_option:
|
||||
allowed_buffs.append(item_table[Buff.quality])
|
||||
if OptionName.buff_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
|
||||
|
||||
|
|
|
@ -6,17 +6,17 @@ from typing import Optional, Dict, Protocol, List, FrozenSet, Iterable
|
|||
|
||||
from . import data
|
||||
from .bundles.bundle_room import BundleRoom
|
||||
from .data.fish_data import special_fish, get_fish_for_mods
|
||||
from .content.game_content import StardewContent
|
||||
from .data.game_item import ItemTag
|
||||
from .data.museum_data import all_museum_items
|
||||
from .data.villagers_data import get_villagers_for_mods
|
||||
from .mods.mod_data import ModNames
|
||||
from .options import ExcludeGingerIsland, Friendsanity, ArcadeMachineLocations, SpecialOrderLocations, Cropsanity, Fishsanity, Museumsanity, FestivalLocations, \
|
||||
SkillProgression, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression
|
||||
from .options import ExcludeGingerIsland, ArcadeMachineLocations, SpecialOrderLocations, Museumsanity, \
|
||||
FestivalLocations, SkillProgression, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression, FarmType
|
||||
from .options import StardewValleyOptions, Craftsanity, Chefsanity, Cooksanity, Shipsanity, Monstersanity
|
||||
from .strings.goal_names import Goal
|
||||
from .strings.quest_names import ModQuest
|
||||
from .strings.region_names import Region
|
||||
from .strings.villager_names import NPC, ModNPC
|
||||
from .strings.quest_names import ModQuest, Quest
|
||||
from .strings.region_names import Region, LogicRegion
|
||||
from .strings.villager_names import NPC
|
||||
|
||||
LOCATION_CODE_OFFSET = 717000
|
||||
|
||||
|
@ -32,6 +32,7 @@ class LocationTags(enum.Enum):
|
|||
BULLETIN_BOARD_BUNDLE = enum.auto()
|
||||
VAULT_BUNDLE = enum.auto()
|
||||
COMMUNITY_CENTER_ROOM = enum.auto()
|
||||
RACCOON_BUNDLES = enum.auto()
|
||||
BACKPACK = enum.auto()
|
||||
TOOL_UPGRADE = enum.auto()
|
||||
HOE_UPGRADE = enum.auto()
|
||||
|
@ -40,6 +41,7 @@ class LocationTags(enum.Enum):
|
|||
WATERING_CAN_UPGRADE = enum.auto()
|
||||
TRASH_CAN_UPGRADE = enum.auto()
|
||||
FISHING_ROD_UPGRADE = enum.auto()
|
||||
PAN_UPGRADE = enum.auto()
|
||||
THE_MINES_TREASURE = enum.auto()
|
||||
CROPSANITY = enum.auto()
|
||||
ELEVATOR = enum.auto()
|
||||
|
@ -49,6 +51,7 @@ class LocationTags(enum.Enum):
|
|||
FORAGING_LEVEL = enum.auto()
|
||||
COMBAT_LEVEL = enum.auto()
|
||||
MINING_LEVEL = enum.auto()
|
||||
MASTERY_LEVEL = enum.auto()
|
||||
BUILDING_BLUEPRINT = enum.auto()
|
||||
STORY_QUEST = enum.auto()
|
||||
ARCADE_MACHINE = enum.auto()
|
||||
|
@ -63,11 +66,18 @@ class LocationTags(enum.Enum):
|
|||
FRIENDSANITY = enum.auto()
|
||||
FESTIVAL = enum.auto()
|
||||
FESTIVAL_HARD = enum.auto()
|
||||
DESERT_FESTIVAL_CHEF = enum.auto()
|
||||
SPECIAL_ORDER_BOARD = enum.auto()
|
||||
SPECIAL_ORDER_QI = enum.auto()
|
||||
REQUIRES_QI_ORDERS = enum.auto()
|
||||
REQUIRES_MASTERIES = enum.auto()
|
||||
GINGER_ISLAND = enum.auto()
|
||||
WALNUT_PURCHASE = enum.auto()
|
||||
WALNUTSANITY = enum.auto()
|
||||
WALNUTSANITY_PUZZLE = enum.auto()
|
||||
WALNUTSANITY_BUSH = enum.auto()
|
||||
WALNUTSANITY_DIG = enum.auto()
|
||||
WALNUTSANITY_REPEATABLE = enum.auto()
|
||||
|
||||
BABY = enum.auto()
|
||||
MONSTERSANITY = enum.auto()
|
||||
|
@ -87,6 +97,10 @@ class LocationTags(enum.Enum):
|
|||
CHEFSANITY_SKILL = enum.auto()
|
||||
CHEFSANITY_STARTER = enum.auto()
|
||||
CRAFTSANITY = enum.auto()
|
||||
BOOKSANITY = enum.auto()
|
||||
BOOKSANITY_POWER = enum.auto()
|
||||
BOOKSANITY_SKILL = enum.auto()
|
||||
BOOKSANITY_LOST = enum.auto()
|
||||
# Mods
|
||||
# Skill Mods
|
||||
LUCK_LEVEL = enum.auto()
|
||||
|
@ -143,10 +157,10 @@ events_locations = [
|
|||
LocationData(None, Region.farm_house, Goal.full_house),
|
||||
LocationData(None, Region.island_west, Goal.greatest_walnut_hunter),
|
||||
LocationData(None, Region.adventurer_guild, Goal.protector_of_the_valley),
|
||||
LocationData(None, Region.shipping, Goal.full_shipment),
|
||||
LocationData(None, Region.kitchen, Goal.gourmet_chef),
|
||||
LocationData(None, LogicRegion.shipping, Goal.full_shipment),
|
||||
LocationData(None, LogicRegion.kitchen, Goal.gourmet_chef),
|
||||
LocationData(None, Region.farm, Goal.craft_master),
|
||||
LocationData(None, Region.shipping, Goal.legend),
|
||||
LocationData(None, LogicRegion.shipping, Goal.legend),
|
||||
LocationData(None, Region.farm, Goal.mystery_of_the_stardrops),
|
||||
LocationData(None, Region.farm, Goal.allsanity),
|
||||
LocationData(None, Region.qi_walnut_room, Goal.perfection),
|
||||
|
@ -168,13 +182,13 @@ def initialize_groups():
|
|||
initialize_groups()
|
||||
|
||||
|
||||
def extend_cropsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
if options.cropsanity == Cropsanity.option_disabled:
|
||||
def extend_cropsanity_locations(randomized_locations: List[LocationData], content: StardewContent):
|
||||
cropsanity = content.features.cropsanity
|
||||
if not cropsanity.is_enabled:
|
||||
return
|
||||
|
||||
cropsanity_locations = [item for item in locations_by_tag[LocationTags.CROPSANITY] if not item.mod_name or item.mod_name in options.mods]
|
||||
cropsanity_locations = filter_ginger_island(options, cropsanity_locations)
|
||||
randomized_locations.extend(cropsanity_locations)
|
||||
randomized_locations.extend(location_table[cropsanity.to_location_name(item.name)]
|
||||
for item in content.find_tagged_items(ItemTag.CROPSANITY))
|
||||
|
||||
|
||||
def extend_quests_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
|
@ -199,32 +213,19 @@ def extend_quests_locations(randomized_locations: List[LocationData], options: S
|
|||
randomized_locations.append(location_table[f"Help Wanted: Gathering {batch + 1}"])
|
||||
|
||||
|
||||
def extend_fishsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
|
||||
prefix = "Fishsanity: "
|
||||
fishsanity = options.fishsanity
|
||||
active_fish = get_fish_for_mods(options.mods.value)
|
||||
if fishsanity == Fishsanity.option_none:
|
||||
def extend_fishsanity_locations(randomized_locations: List[LocationData], content: StardewContent, random: Random):
|
||||
fishsanity = content.features.fishsanity
|
||||
if not fishsanity.is_enabled:
|
||||
return
|
||||
elif fishsanity == Fishsanity.option_legendaries:
|
||||
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if fish.legendary]
|
||||
randomized_locations.extend(filter_disabled_locations(options, fish_locations))
|
||||
elif fishsanity == Fishsanity.option_special:
|
||||
randomized_locations.extend(location_table[f"{prefix}{special.name}"] for special in special_fish)
|
||||
elif fishsanity == Fishsanity.option_randomized:
|
||||
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if random.random() < 0.4]
|
||||
randomized_locations.extend(filter_disabled_locations(options, fish_locations))
|
||||
elif fishsanity == Fishsanity.option_all:
|
||||
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish]
|
||||
randomized_locations.extend(filter_disabled_locations(options, fish_locations))
|
||||
elif fishsanity == Fishsanity.option_exclude_legendaries:
|
||||
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if not fish.legendary]
|
||||
randomized_locations.extend(filter_disabled_locations(options, fish_locations))
|
||||
elif fishsanity == Fishsanity.option_exclude_hard_fish:
|
||||
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if fish.difficulty < 80]
|
||||
randomized_locations.extend(filter_disabled_locations(options, fish_locations))
|
||||
elif options.fishsanity == Fishsanity.option_only_easy_fish:
|
||||
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in active_fish if fish.difficulty < 50]
|
||||
randomized_locations.extend(filter_disabled_locations(options, fish_locations))
|
||||
|
||||
for fish in content.fishes.values():
|
||||
if not fishsanity.is_included(fish):
|
||||
continue
|
||||
|
||||
if fishsanity.is_randomized and random.random() >= fishsanity.randomization_ratio:
|
||||
continue
|
||||
|
||||
randomized_locations.append(location_table[fishsanity.to_location_name(fish.name)])
|
||||
|
||||
|
||||
def extend_museumsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
|
||||
|
@ -240,38 +241,20 @@ def extend_museumsanity_locations(randomized_locations: List[LocationData], opti
|
|||
randomized_locations.extend(location_table[f"{prefix}{museum_item.item_name}"] for museum_item in all_museum_items)
|
||||
|
||||
|
||||
def extend_friendsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
island_villagers = [NPC.leo, ModNPC.lance]
|
||||
if options.friendsanity == Friendsanity.option_none:
|
||||
def extend_friendsanity_locations(randomized_locations: List[LocationData], content: StardewContent):
|
||||
friendsanity = content.features.friendsanity
|
||||
if not friendsanity.is_enabled:
|
||||
return
|
||||
|
||||
randomized_locations.append(location_table[f"Spouse Stardrop"])
|
||||
extend_baby_locations(randomized_locations)
|
||||
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
exclude_non_bachelors = options.friendsanity == Friendsanity.option_bachelors
|
||||
exclude_locked_villagers = options.friendsanity == Friendsanity.option_starting_npcs or \
|
||||
options.friendsanity == Friendsanity.option_bachelors
|
||||
include_post_marriage_hearts = options.friendsanity == Friendsanity.option_all_with_marriage
|
||||
heart_size = options.friendsanity_heart_size
|
||||
for villager in get_villagers_for_mods(options.mods.value):
|
||||
if not villager.available and exclude_locked_villagers:
|
||||
continue
|
||||
if not villager.bachelor and exclude_non_bachelors:
|
||||
continue
|
||||
if villager.name in island_villagers and exclude_ginger_island:
|
||||
continue
|
||||
heart_cap = 8 if villager.bachelor else 10
|
||||
if include_post_marriage_hearts and villager.bachelor:
|
||||
heart_cap = 14
|
||||
for heart in range(1, 15):
|
||||
if heart > heart_cap:
|
||||
break
|
||||
if heart % heart_size == 0 or heart == heart_cap:
|
||||
randomized_locations.append(location_table[f"Friendsanity: {villager.name} {heart} <3"])
|
||||
if not exclude_non_bachelors:
|
||||
for heart in range(1, 6):
|
||||
if heart % heart_size == 0 or heart == 5:
|
||||
randomized_locations.append(location_table[f"Friendsanity: Pet {heart} <3"])
|
||||
|
||||
for villager in content.villagers.values():
|
||||
for heart in friendsanity.get_randomized_hearts(villager):
|
||||
randomized_locations.append(location_table[friendsanity.to_location_name(villager.name, heart)])
|
||||
|
||||
for heart in friendsanity.get_pet_randomized_hearts():
|
||||
randomized_locations.append(location_table[friendsanity.to_location_name(NPC.pet, heart)])
|
||||
|
||||
|
||||
def extend_baby_locations(randomized_locations: List[LocationData]):
|
||||
|
@ -279,16 +262,17 @@ def extend_baby_locations(randomized_locations: List[LocationData]):
|
|||
randomized_locations.extend(baby_locations)
|
||||
|
||||
|
||||
def extend_festival_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
def extend_festival_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, random: Random):
|
||||
if options.festival_locations == FestivalLocations.option_disabled:
|
||||
return
|
||||
|
||||
festival_locations = locations_by_tag[LocationTags.FESTIVAL]
|
||||
randomized_locations.extend(festival_locations)
|
||||
extend_hard_festival_locations(randomized_locations, options)
|
||||
extend_desert_festival_chef_locations(randomized_locations, options, random)
|
||||
|
||||
|
||||
def extend_hard_festival_locations(randomized_locations, options: StardewValleyOptions):
|
||||
def extend_hard_festival_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
if options.festival_locations != FestivalLocations.option_hard:
|
||||
return
|
||||
|
||||
|
@ -296,14 +280,20 @@ def extend_hard_festival_locations(randomized_locations, options: StardewValleyO
|
|||
randomized_locations.extend(hard_festival_locations)
|
||||
|
||||
|
||||
def extend_special_order_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
if options.special_order_locations == SpecialOrderLocations.option_disabled:
|
||||
return
|
||||
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)
|
||||
|
||||
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
|
||||
|
||||
def extend_special_order_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
if options.special_order_locations & SpecialOrderLocations.option_board:
|
||||
board_locations = filter_disabled_locations(options, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD])
|
||||
randomized_locations.extend(board_locations)
|
||||
if options.special_order_locations == SpecialOrderLocations.option_board_qi and include_island:
|
||||
|
||||
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
|
||||
if options.special_order_locations & SpecialOrderLocations.value_qi and include_island:
|
||||
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]
|
||||
|
@ -440,13 +430,43 @@ def extend_craftsanity_locations(randomized_locations: List[LocationData], optio
|
|||
return
|
||||
|
||||
craftsanity_locations = [craft for craft in locations_by_tag[LocationTags.CRAFTSANITY]]
|
||||
filtered_chefsanity_locations = filter_disabled_locations(options, craftsanity_locations)
|
||||
randomized_locations.extend(filtered_chefsanity_locations)
|
||||
filtered_craftsanity_locations = filter_disabled_locations(options, craftsanity_locations)
|
||||
randomized_locations.extend(filtered_craftsanity_locations)
|
||||
|
||||
|
||||
def extend_book_locations(randomized_locations: List[LocationData], content: StardewContent):
|
||||
booksanity = content.features.booksanity
|
||||
if not booksanity.is_enabled:
|
||||
return
|
||||
|
||||
book_locations = []
|
||||
for book in content.find_tagged_items(ItemTag.BOOK):
|
||||
if booksanity.is_included(book):
|
||||
book_locations.append(location_table[booksanity.to_location_name(book.name)])
|
||||
|
||||
book_locations.extend(location_table[booksanity.to_location_name(book)] for book in booksanity.get_randomized_lost_books())
|
||||
|
||||
randomized_locations.extend(book_locations)
|
||||
|
||||
|
||||
def extend_walnutsanity_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
|
||||
if not options.walnutsanity:
|
||||
return
|
||||
|
||||
if "Puzzles" in options.walnutsanity:
|
||||
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_PUZZLE])
|
||||
if "Bushes" in options.walnutsanity:
|
||||
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_BUSH])
|
||||
if "Dig Spots" in options.walnutsanity:
|
||||
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_DIG])
|
||||
if "Repeatables" in options.walnutsanity:
|
||||
randomized_locations.extend(locations_by_tag[LocationTags.WALNUTSANITY_REPEATABLE])
|
||||
|
||||
|
||||
def create_locations(location_collector: StardewLocationCollector,
|
||||
bundle_rooms: List[BundleRoom],
|
||||
options: StardewValleyOptions,
|
||||
content: StardewContent,
|
||||
random: Random):
|
||||
randomized_locations = []
|
||||
|
||||
|
@ -461,7 +481,10 @@ def create_locations(location_collector: StardewLocationCollector,
|
|||
|
||||
if not options.skill_progression == SkillProgression.option_vanilla:
|
||||
for location in locations_by_tag[LocationTags.SKILL_LEVEL]:
|
||||
if location.mod_name is None or location.mod_name in options.mods:
|
||||
if location.mod_name is not None and location.mod_name not in options.mods:
|
||||
continue
|
||||
if LocationTags.MASTERY_LEVEL in location.tags and options.skill_progression != SkillProgression.option_progressive_with_masteries:
|
||||
continue
|
||||
randomized_locations.append(location_table[location.name])
|
||||
|
||||
if options.building_progression & BuildingProgression.option_progressive:
|
||||
|
@ -475,12 +498,12 @@ def create_locations(location_collector: StardewLocationCollector,
|
|||
if options.arcade_machine_locations == ArcadeMachineLocations.option_full_shuffling:
|
||||
randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE])
|
||||
|
||||
extend_cropsanity_locations(randomized_locations, options)
|
||||
extend_fishsanity_locations(randomized_locations, options, random)
|
||||
extend_cropsanity_locations(randomized_locations, content)
|
||||
extend_fishsanity_locations(randomized_locations, content, random)
|
||||
extend_museumsanity_locations(randomized_locations, options, random)
|
||||
extend_friendsanity_locations(randomized_locations, options)
|
||||
extend_friendsanity_locations(randomized_locations, content)
|
||||
|
||||
extend_festival_locations(randomized_locations, options)
|
||||
extend_festival_locations(randomized_locations, options, random)
|
||||
extend_special_order_locations(randomized_locations, options)
|
||||
extend_walnut_purchase_locations(randomized_locations, options)
|
||||
|
||||
|
@ -490,28 +513,47 @@ def create_locations(location_collector: StardewLocationCollector,
|
|||
extend_chefsanity_locations(randomized_locations, options)
|
||||
extend_craftsanity_locations(randomized_locations, options)
|
||||
extend_quests_locations(randomized_locations, options)
|
||||
extend_book_locations(randomized_locations, content)
|
||||
extend_walnutsanity_locations(randomized_locations, options)
|
||||
|
||||
# Mods
|
||||
extend_situational_quest_locations(randomized_locations, options)
|
||||
|
||||
for location_data in randomized_locations:
|
||||
location_collector(location_data.name, location_data.code, location_data.region)
|
||||
|
||||
|
||||
def filter_farm_type(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)
|
||||
else:
|
||||
return (location for location in locations if location.name != Quest.feeding_animals)
|
||||
|
||||
|
||||
def filter_ginger_island(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
|
||||
include_island = options.exclude_ginger_island == ExcludeGingerIsland.option_false
|
||||
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.option_board_qi
|
||||
include_qi_orders = options.special_order_locations & SpecialOrderLocations.value_qi
|
||||
return (location for location in locations if include_qi_orders or LocationTags.REQUIRES_QI_ORDERS not in location.tags)
|
||||
|
||||
|
||||
def filter_masteries_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
|
||||
include_masteries = options.skill_progression == SkillProgression.option_progressive_with_masteries
|
||||
return (location for location in locations if include_masteries or 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_disabled_locations(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
|
||||
locations_island_filter = filter_ginger_island(options, locations)
|
||||
locations_farm_filter = filter_farm_type(options, locations)
|
||||
locations_island_filter = filter_ginger_island(options, locations_farm_filter)
|
||||
locations_qi_filter = filter_qi_order_locations(options, locations_island_filter)
|
||||
locations_mod_filter = filter_modded_locations(options, locations_qi_filter)
|
||||
locations_masteries_filter = filter_masteries_locations(options, locations_qi_filter)
|
||||
locations_mod_filter = filter_modded_locations(options, locations_masteries_filter)
|
||||
return locations_mod_filter
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from typing import Union
|
||||
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .cooking_logic import CookingLogicMixin
|
||||
from .mine_logic import MineLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
|
|
|
@ -5,10 +5,13 @@ from .base_logic import BaseLogic, BaseLogicMixin
|
|||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from ..stardew_rule import StardewRule, True_, Or
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..options import ToolProgression
|
||||
from ..stardew_rule import StardewRule, True_
|
||||
from ..strings.generic_names import Generic
|
||||
from ..strings.geode_names import Geode
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.tool_names import Tool
|
||||
|
||||
|
||||
class ActionLogicMixin(BaseLogicMixin):
|
||||
|
@ -17,7 +20,7 @@ class ActionLogicMixin(BaseLogicMixin):
|
|||
self.action = ActionLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class ActionLogic(BaseLogic[Union[ActionLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin]]):
|
||||
class ActionLogic(BaseLogic[Union[ActionLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin, ToolLogicMixin]]):
|
||||
|
||||
def can_watch(self, channel: str = None):
|
||||
tv_rule = True_()
|
||||
|
@ -25,16 +28,13 @@ class ActionLogic(BaseLogic[Union[ActionLogicMixin, RegionLogicMixin, ReceivedLo
|
|||
return tv_rule
|
||||
return self.logic.received(channel) & tv_rule
|
||||
|
||||
def can_pan(self) -> StardewRule:
|
||||
return self.logic.received("Glittering Boulder Removed") & self.logic.region.can_reach(Region.mountain)
|
||||
|
||||
def can_pan_at(self, region: str) -> StardewRule:
|
||||
return self.logic.region.can_reach(region) & self.logic.action.can_pan()
|
||||
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)
|
||||
|
||||
@cache_self1
|
||||
def can_open_geode(self, geode: str) -> StardewRule:
|
||||
blacksmith_access = self.logic.region.can_reach(Region.blacksmith)
|
||||
geodes = [Geode.geode, Geode.frozen, Geode.magma, Geode.omni]
|
||||
if geode == Generic.any:
|
||||
return blacksmith_access & Or(*(self.logic.has(geode_type) for geode_type in geodes))
|
||||
return blacksmith_access & self.logic.or_(*(self.logic.has(geode_type) for geode_type in geodes))
|
||||
return blacksmith_access & self.logic.has(geode)
|
||||
|
|
|
@ -3,8 +3,13 @@ from typing import Union
|
|||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from ..data.artisan import MachineSource
|
||||
from ..data.game_item import ItemTag
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.crop_names import all_vegetables, all_fruits, Vegetable, Fruit
|
||||
from ..strings.artisan_good_names import ArtisanGood
|
||||
from ..strings.crop_names import Vegetable, Fruit
|
||||
from ..strings.fish_names import Fish, all_fish
|
||||
from ..strings.forageable_names import Mushroom
|
||||
from ..strings.generic_names import Generic
|
||||
from ..strings.machine_names import Machine
|
||||
|
||||
|
@ -16,6 +21,10 @@ class ArtisanLogicMixin(BaseLogicMixin):
|
|||
|
||||
|
||||
class ArtisanLogic(BaseLogic[Union[ArtisanLogicMixin, TimeLogicMixin, HasLogicMixin]]):
|
||||
def initialize_rules(self):
|
||||
# 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})
|
||||
|
||||
def has_jelly(self) -> StardewRule:
|
||||
return self.logic.artisan.can_preserves_jar(Fruit.any)
|
||||
|
@ -23,31 +32,62 @@ class ArtisanLogic(BaseLogic[Union[ArtisanLogicMixin, TimeLogicMixin, HasLogicMi
|
|||
def has_pickle(self) -> StardewRule:
|
||||
return self.logic.artisan.can_preserves_jar(Vegetable.any)
|
||||
|
||||
def has_smoked_fish(self) -> StardewRule:
|
||||
return self.logic.artisan.can_smoke(Fish.any)
|
||||
|
||||
def has_targeted_bait(self) -> StardewRule:
|
||||
return self.logic.artisan.can_bait(Fish.any)
|
||||
|
||||
def has_dried_fruits(self) -> StardewRule:
|
||||
return self.logic.artisan.can_dehydrate(Fruit.any)
|
||||
|
||||
def has_dried_mushrooms(self) -> StardewRule:
|
||||
return self.logic.artisan.can_dehydrate(Mushroom.any_edible)
|
||||
|
||||
def has_raisins(self) -> StardewRule:
|
||||
return self.logic.artisan.can_dehydrate(Fruit.grape)
|
||||
|
||||
def can_produce_from(self, source: MachineSource) -> StardewRule:
|
||||
return self.logic.has(source.item) & self.logic.has(source.machine)
|
||||
|
||||
def can_preserves_jar(self, item: str) -> StardewRule:
|
||||
machine_rule = self.logic.has(Machine.preserves_jar)
|
||||
if item == Generic.any:
|
||||
return machine_rule
|
||||
if item == Fruit.any:
|
||||
return machine_rule & self.logic.has_any(*all_fruits)
|
||||
return machine_rule & self.logic.has_any(*(fruit.name for fruit in self.content.find_tagged_items(ItemTag.FRUIT)))
|
||||
if item == Vegetable.any:
|
||||
return machine_rule & self.logic.has_any(*all_vegetables)
|
||||
return machine_rule & self.logic.has_any(*(vege.name for vege in self.content.find_tagged_items(ItemTag.VEGETABLE)))
|
||||
return machine_rule & self.logic.has(item)
|
||||
|
||||
def has_wine(self) -> StardewRule:
|
||||
return self.logic.artisan.can_keg(Fruit.any)
|
||||
|
||||
def has_juice(self) -> StardewRule:
|
||||
return self.logic.artisan.can_keg(Vegetable.any)
|
||||
|
||||
def can_keg(self, item: str) -> StardewRule:
|
||||
machine_rule = self.logic.has(Machine.keg)
|
||||
if item == Generic.any:
|
||||
return machine_rule
|
||||
if item == Fruit.any:
|
||||
return machine_rule & self.logic.has_any(*all_fruits)
|
||||
return machine_rule & self.logic.has_any(*(fruit.name for fruit in self.content.find_tagged_items(ItemTag.FRUIT)))
|
||||
if item == Vegetable.any:
|
||||
return machine_rule & self.logic.has_any(*all_vegetables)
|
||||
return machine_rule & self.logic.has_any(*(vege.name for vege in self.content.find_tagged_items(ItemTag.VEGETABLE)))
|
||||
return machine_rule & self.logic.has(item)
|
||||
|
||||
def can_mayonnaise(self, item: str) -> StardewRule:
|
||||
return self.logic.has(Machine.mayonnaise_machine) & self.logic.has(item)
|
||||
|
||||
def can_smoke(self, item: str) -> StardewRule:
|
||||
machine_rule = self.logic.has(Machine.fish_smoker)
|
||||
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)
|
||||
|
||||
def can_dehydrate(self, item: str) -> StardewRule:
|
||||
machine_rule = self.logic.has(Machine.dehydrator)
|
||||
if item == Generic.any:
|
||||
return machine_rule
|
||||
if item == Fruit.any:
|
||||
# Grapes make raisins
|
||||
return machine_rule & self.logic.has_any(*(fruit.name for fruit in self.content.find_tagged_items(ItemTag.FRUIT) if fruit.name != Fruit.grape))
|
||||
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)
|
||||
|
|
|
@ -2,6 +2,7 @@ from __future__ import annotations
|
|||
|
||||
from typing import TypeVar, Generic, Dict, Collection
|
||||
|
||||
from ..content.game_content import StardewContent
|
||||
from ..options import StardewValleyOptions
|
||||
from ..stardew_rule import StardewRule
|
||||
|
||||
|
@ -10,12 +11,11 @@ class LogicRegistry:
|
|||
|
||||
def __init__(self):
|
||||
self.item_rules: Dict[str, StardewRule] = {}
|
||||
self.sapling_rules: Dict[str, StardewRule] = {}
|
||||
self.tree_fruit_rules: Dict[str, StardewRule] = {}
|
||||
self.seed_rules: Dict[str, StardewRule] = {}
|
||||
self.cooking_rules: Dict[str, StardewRule] = {}
|
||||
self.crafting_rules: Dict[str, StardewRule] = {}
|
||||
self.crop_rules: Dict[str, StardewRule] = {}
|
||||
self.artisan_good_rules: Dict[str, StardewRule] = {}
|
||||
self.fish_rules: Dict[str, StardewRule] = {}
|
||||
self.museum_rules: Dict[str, StardewRule] = {}
|
||||
self.festival_rules: Dict[str, StardewRule] = {}
|
||||
|
@ -38,13 +38,15 @@ class BaseLogic(BaseLogicMixin, Generic[T]):
|
|||
player: int
|
||||
registry: LogicRegistry
|
||||
options: StardewValleyOptions
|
||||
content: StardewContent
|
||||
regions: Collection[str]
|
||||
logic: T
|
||||
|
||||
def __init__(self, player: int, registry: LogicRegistry, options: StardewValleyOptions, regions: Collection[str], logic: T):
|
||||
super().__init__(player, registry, options, regions, logic)
|
||||
def __init__(self, player: int, registry: LogicRegistry, options: StardewValleyOptions, content: StardewContent, regions: Collection[str], logic: T):
|
||||
super().__init__(player, registry, options, content, regions, logic)
|
||||
self.player = player
|
||||
self.registry = registry
|
||||
self.options = options
|
||||
self.content = content
|
||||
self.regions = regions
|
||||
self.logic = logic
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
from typing import Union
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from ..stardew_rule import StardewRule
|
||||
|
||||
|
||||
class BookLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.book = BookLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class BookLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin]]):
|
||||
|
||||
@cache_self1
|
||||
def has_book_power(self, book: str) -> StardewRule:
|
||||
booksanity = self.content.features.booksanity
|
||||
if booksanity.is_included(self.content.game_items[book]):
|
||||
return self.logic.received(booksanity.to_item_name(book))
|
||||
else:
|
||||
return self.logic.has(book)
|
|
@ -1,23 +0,0 @@
|
|||
from typing import Union
|
||||
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.ap_names.buff_names import Buff
|
||||
|
||||
|
||||
class BuffLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.buff = BuffLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class BuffLogic(BaseLogic[Union[ReceivedLogicMixin]]):
|
||||
def has_max_buffs(self) -> StardewRule:
|
||||
return self.has_max_speed() & self.has_max_luck()
|
||||
|
||||
def has_max_speed(self) -> StardewRule:
|
||||
return self.logic.received(Buff.movement, self.options.movement_buff_number.value)
|
||||
|
||||
def has_max_luck(self) -> StardewRule:
|
||||
return self.logic.received(Buff.luck, self.options.luck_buff_number.value)
|
|
@ -15,6 +15,8 @@ from ..strings.fish_names import WaterItem
|
|||
from ..strings.material_names import Material
|
||||
from ..strings.metal_names import MetalBar
|
||||
|
||||
has_group = "building"
|
||||
|
||||
|
||||
class BuildingLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
@ -42,7 +44,7 @@ class BuildingLogic(BaseLogic[Union[BuildingLogicMixin, MoneyLogicMixin, RegionL
|
|||
Building.well: self.logic.money.can_spend(1000) & self.logic.has(Material.stone),
|
||||
Building.shipping_bin: self.logic.money.can_spend(250) & self.logic.has(Material.wood),
|
||||
Building.kitchen: self.logic.money.can_spend(10000) & self.logic.has(Material.wood) & self.logic.building.has_house(0),
|
||||
Building.kids_room: self.logic.money.can_spend(50000) & self.logic.has(Material.hardwood) & self.logic.building.has_house(1),
|
||||
Building.kids_room: self.logic.money.can_spend(65000) & self.logic.has(Material.hardwood) & self.logic.building.has_house(1),
|
||||
Building.cellar: self.logic.money.can_spend(100000) & self.logic.building.has_house(2),
|
||||
# @formatter:on
|
||||
})
|
||||
|
@ -60,7 +62,7 @@ class BuildingLogic(BaseLogic[Union[BuildingLogicMixin, MoneyLogicMixin, RegionL
|
|||
|
||||
carpenter_rule = self.logic.received(Event.can_construct_buildings)
|
||||
if not self.options.building_progression & BuildingProgression.option_progressive:
|
||||
return Has(building, self.registry.building_rules) & carpenter_rule
|
||||
return Has(building, self.registry.building_rules, has_group) & carpenter_rule
|
||||
|
||||
count = 1
|
||||
if building in [Building.coop, Building.barn, Building.shed]:
|
||||
|
@ -86,10 +88,10 @@ class BuildingLogic(BaseLogic[Union[BuildingLogicMixin, MoneyLogicMixin, RegionL
|
|||
return carpenter_rule & self.logic.received(f"Progressive House", upgrade_level)
|
||||
|
||||
if upgrade_level == 1:
|
||||
return carpenter_rule & Has(Building.kitchen, self.registry.building_rules)
|
||||
return carpenter_rule & Has(Building.kitchen, self.registry.building_rules, has_group)
|
||||
|
||||
if upgrade_level == 2:
|
||||
return carpenter_rule & Has(Building.kids_room, self.registry.building_rules)
|
||||
return carpenter_rule & Has(Building.kids_room, self.registry.building_rules, has_group)
|
||||
|
||||
# if upgrade_level == 3:
|
||||
return carpenter_rule & Has(Building.cellar, self.registry.building_rules)
|
||||
return carpenter_rule & Has(Building.cellar, self.registry.building_rules, has_group)
|
||||
|
|
|
@ -2,17 +2,22 @@ from functools import cached_property
|
|||
from typing import Union, List
|
||||
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .farming_logic import FarmingLogicMixin
|
||||
from .fishing_logic import FishingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .money_logic import MoneyLogicMixin
|
||||
from .quality_logic import QualityLogicMixin
|
||||
from .quest_logic import QuestLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from ..bundles.bundle import Bundle
|
||||
from ..stardew_rule import StardewRule, And, True_
|
||||
from ..stardew_rule import StardewRule, True_
|
||||
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
|
||||
from ..strings.currency_names import Currency
|
||||
from ..strings.machine_names import Machine
|
||||
from ..strings.quality_names import CropQuality, ForageQuality, FishQuality, ArtisanQuality
|
||||
from ..strings.quest_names import Quest
|
||||
from ..strings.region_names import Region
|
||||
|
||||
|
||||
|
@ -22,21 +27,26 @@ class BundleLogicMixin(BaseLogicMixin):
|
|||
self.bundle = BundleLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class BundleLogic(BaseLogic[Union[HasLogicMixin, RegionLogicMixin, MoneyLogicMixin, FarmingLogicMixin, FishingLogicMixin, SkillLogicMixin]]):
|
||||
class BundleLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, TimeLogicMixin, RegionLogicMixin, MoneyLogicMixin, QualityLogicMixin, FishingLogicMixin, SkillLogicMixin,
|
||||
QuestLogicMixin]]):
|
||||
# Should be cached
|
||||
def can_complete_bundle(self, bundle: Bundle) -> StardewRule:
|
||||
item_rules = []
|
||||
qualities = []
|
||||
time_to_grind = 0
|
||||
can_speak_junimo = self.logic.region.can_reach(Region.wizard_tower)
|
||||
for bundle_item in bundle.items:
|
||||
if Currency.is_currency(bundle_item.item_name):
|
||||
return can_speak_junimo & self.logic.money.can_trade(bundle_item.item_name, bundle_item.amount)
|
||||
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.item_name)
|
||||
item_rules.append(bundle_item.get_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)
|
||||
return can_speak_junimo & item_rules & quality_rules
|
||||
time_rule = True_() if time_to_grind <= 0 else self.logic.time.has_lived_months(time_to_grind)
|
||||
return can_speak_junimo & item_rules & quality_rules & time_rule
|
||||
|
||||
def get_quality_rules(self, qualities: List[str]) -> StardewRule:
|
||||
crop_quality = CropQuality.get_highest(qualities)
|
||||
|
@ -45,7 +55,7 @@ class BundleLogic(BaseLogic[Union[HasLogicMixin, RegionLogicMixin, MoneyLogicMix
|
|||
artisan_quality = ArtisanQuality.get_highest(qualities)
|
||||
quality_rules = []
|
||||
if crop_quality != CropQuality.basic:
|
||||
quality_rules.append(self.logic.farming.can_grow_crop_quality(crop_quality))
|
||||
quality_rules.append(self.logic.quality.can_grow_crop_quality(crop_quality))
|
||||
if fish_quality != FishQuality.basic:
|
||||
quality_rules.append(self.logic.fishing.can_catch_quality_fish(fish_quality))
|
||||
if forage_quality != ForageQuality.basic:
|
||||
|
@ -54,7 +64,7 @@ class BundleLogic(BaseLogic[Union[HasLogicMixin, RegionLogicMixin, MoneyLogicMix
|
|||
quality_rules.append(self.logic.has(Machine.cask))
|
||||
if not quality_rules:
|
||||
return True_()
|
||||
return And(*quality_rules)
|
||||
return self.logic.and_(*quality_rules)
|
||||
|
||||
@cached_property
|
||||
def can_complete_community_center(self) -> StardewRule:
|
||||
|
@ -64,3 +74,11 @@ class BundleLogic(BaseLogic[Union[HasLogicMixin, RegionLogicMixin, MoneyLogicMix
|
|||
self.logic.region.can_reach_location("Complete Bulletin Board") &
|
||||
self.logic.region.can_reach_location("Complete Vault") &
|
||||
self.logic.region.can_reach_location("Complete Boiler Room"))
|
||||
|
||||
def can_access_raccoon_bundles(self) -> StardewRule:
|
||||
if self.options.quest_locations < 0:
|
||||
return self.logic.received(CommunityUpgrade.raccoon, 1) & self.logic.quest.can_complete_quest(Quest.giant_stump)
|
||||
|
||||
# 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)
|
||||
|
|
|
@ -3,10 +3,11 @@ from typing import Union
|
|||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from ..mods.logic.magic_logic import MagicLogicMixin
|
||||
from ..stardew_rule import StardewRule, Or, False_
|
||||
from ..stardew_rule import StardewRule, False_
|
||||
from ..strings.ap_names.ap_weapon_names import APWeapon
|
||||
from ..strings.performance_names import Performance
|
||||
|
||||
|
@ -19,7 +20,7 @@ class CombatLogicMixin(BaseLogicMixin):
|
|||
self.combat = CombatLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class CombatLogic(BaseLogic[Union[CombatLogicMixin, RegionLogicMixin, ReceivedLogicMixin, MagicLogicMixin]]):
|
||||
class CombatLogic(BaseLogic[Union[HasLogicMixin, CombatLogicMixin, RegionLogicMixin, ReceivedLogicMixin, MagicLogicMixin]]):
|
||||
@cache_self1
|
||||
def can_fight_at_level(self, level: str) -> StardewRule:
|
||||
if level == Performance.basic:
|
||||
|
@ -42,16 +43,20 @@ class CombatLogic(BaseLogic[Union[CombatLogicMixin, RegionLogicMixin, ReceivedLo
|
|||
|
||||
@cached_property
|
||||
def has_decent_weapon(self) -> StardewRule:
|
||||
return Or(*(self.logic.received(weapon, 2) for weapon in valid_weapons))
|
||||
return self.logic.or_(*(self.logic.received(weapon, 2) for weapon in valid_weapons))
|
||||
|
||||
@cached_property
|
||||
def has_good_weapon(self) -> StardewRule:
|
||||
return Or(*(self.logic.received(weapon, 3) for weapon in valid_weapons))
|
||||
return self.logic.or_(*(self.logic.received(weapon, 3) for weapon in valid_weapons))
|
||||
|
||||
@cached_property
|
||||
def has_great_weapon(self) -> StardewRule:
|
||||
return Or(*(self.logic.received(weapon, 4) for weapon in valid_weapons))
|
||||
return self.logic.or_(*(self.logic.received(weapon, 4) for weapon in valid_weapons))
|
||||
|
||||
@cached_property
|
||||
def has_galaxy_weapon(self) -> StardewRule:
|
||||
return Or(*(self.logic.received(weapon, 5) for weapon in valid_weapons))
|
||||
return self.logic.or_(*(self.logic.received(weapon, 5) for weapon in valid_weapons))
|
||||
|
||||
@cached_property
|
||||
def has_slingshot(self) -> StardewRule:
|
||||
return self.logic.received(APWeapon.slingshot)
|
||||
|
|
|
@ -19,8 +19,8 @@ from ..data.recipe_source import CutsceneSource, ShopTradeSource
|
|||
from ..locations import locations_by_tag, LocationTags
|
||||
from ..options import Chefsanity
|
||||
from ..options import ExcludeGingerIsland
|
||||
from ..stardew_rule import StardewRule, True_, False_, And
|
||||
from ..strings.region_names import Region
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from ..strings.region_names import LogicRegion
|
||||
from ..strings.skill_names import Skill
|
||||
from ..strings.tv_channel_names import Channel
|
||||
|
||||
|
@ -39,7 +39,7 @@ BuildingLogicMixin, RelationshipLogicMixin, SkillLogicMixin, CookingLogicMixin]]
|
|||
|
||||
# Should be cached
|
||||
def can_cook(self, recipe: CookingRecipe = None) -> StardewRule:
|
||||
cook_rule = self.logic.region.can_reach(Region.kitchen)
|
||||
cook_rule = self.logic.region.can_reach(LogicRegion.kitchen)
|
||||
if recipe is None:
|
||||
return cook_rule
|
||||
|
||||
|
@ -65,7 +65,7 @@ BuildingLogicMixin, RelationshipLogicMixin, SkillLogicMixin, CookingLogicMixin]]
|
|||
return self.logic.cooking.received_recipe(meal_name)
|
||||
if isinstance(source, QueenOfSauceSource) and self.options.chefsanity & Chefsanity.option_queen_of_sauce:
|
||||
return self.logic.cooking.received_recipe(meal_name)
|
||||
if isinstance(source, ShopFriendshipSource) and self.options.chefsanity & Chefsanity.option_friendship:
|
||||
if isinstance(source, ShopFriendshipSource) and self.options.chefsanity & Chefsanity.option_purchases:
|
||||
return self.logic.cooking.received_recipe(meal_name)
|
||||
return self.logic.cooking.can_learn_recipe(source)
|
||||
|
||||
|
@ -105,4 +105,4 @@ BuildingLogicMixin, RelationshipLogicMixin, SkillLogicMixin, CookingLogicMixin]]
|
|||
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]
|
||||
return And(*(self.logic.cooking.can_cook(recipe) for recipe in all_recipes))
|
||||
return self.logic.and_(*(self.logic.cooking.can_cook(recipe) for recipe in all_recipes))
|
||||
|
|
|
@ -13,12 +13,11 @@ from .skill_logic import SkillLogicMixin
|
|||
from .special_order_logic import SpecialOrderLogicMixin
|
||||
from .. import options
|
||||
from ..data.craftable_data import CraftingRecipe, all_crafting_recipes_by_name
|
||||
from ..data.recipe_data import StarterSource, ShopSource, SkillSource, FriendshipSource
|
||||
from ..data.recipe_source import CutsceneSource, ShopTradeSource, ArchipelagoSource, LogicSource, SpecialOrderSource, \
|
||||
FestivalShopSource, QuestSource
|
||||
FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource
|
||||
from ..locations import locations_by_tag, LocationTags
|
||||
from ..options import Craftsanity, SpecialOrderLocations, ExcludeGingerIsland
|
||||
from ..stardew_rule import StardewRule, True_, False_, And
|
||||
from ..options import Craftsanity, SpecialOrderLocations, ExcludeGingerIsland, SkillProgression
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from ..strings.region_names import Region
|
||||
|
||||
|
||||
|
@ -58,7 +57,7 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
|
|||
if isinstance(recipe.source, StarterSource) or isinstance(recipe.source, ShopTradeSource) or isinstance(
|
||||
recipe.source, ShopSource):
|
||||
return self.logic.crafting.received_recipe(recipe.item)
|
||||
if isinstance(recipe.source, SpecialOrderSource) and self.options.special_order_locations != SpecialOrderLocations.option_disabled:
|
||||
if isinstance(recipe.source, SpecialOrderSource) and self.options.special_order_locations & SpecialOrderLocations.option_board:
|
||||
return self.logic.crafting.received_recipe(recipe.item)
|
||||
return self.logic.crafting.can_learn_recipe(recipe)
|
||||
|
||||
|
@ -74,6 +73,8 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
|
|||
return self.logic.money.can_spend_at(recipe.source.region, recipe.source.price)
|
||||
if isinstance(recipe.source, SkillSource):
|
||||
return self.logic.skill.has_level(recipe.source.skill, recipe.source.level)
|
||||
if isinstance(recipe.source, MasterySource):
|
||||
return self.logic.skill.has_mastery(recipe.source.skill)
|
||||
if isinstance(recipe.source, CutsceneSource):
|
||||
return self.logic.region.can_reach(recipe.source.region) & self.logic.relationship.has_hearts(recipe.source.friend, recipe.source.hearts)
|
||||
if isinstance(recipe.source, FriendshipSource):
|
||||
|
@ -81,9 +82,9 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
|
|||
if isinstance(recipe.source, QuestSource):
|
||||
return self.logic.quest.can_complete_quest(recipe.source.quest)
|
||||
if isinstance(recipe.source, SpecialOrderSource):
|
||||
if self.options.special_order_locations == SpecialOrderLocations.option_disabled:
|
||||
return self.logic.special_order.can_complete_special_order(recipe.source.special_order)
|
||||
if self.options.special_order_locations & SpecialOrderLocations.option_board:
|
||||
return self.logic.crafting.received_recipe(recipe.item)
|
||||
return self.logic.special_order.can_complete_special_order(recipe.source.special_order)
|
||||
if isinstance(recipe.source, LogicSource):
|
||||
if recipe.source.logic_rule == "Cellar":
|
||||
return self.logic.region.can_reach(Region.cellar)
|
||||
|
@ -99,13 +100,16 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
|
|||
craftsanity_prefix = "Craft "
|
||||
all_recipes_names = []
|
||||
exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
exclude_masteries = self.options.skill_progression != SkillProgression.option_progressive_with_masteries
|
||||
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
|
||||
if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags:
|
||||
continue
|
||||
if location.mod_name and location.mod_name not in self.options.mods:
|
||||
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]
|
||||
return And(*(self.logic.crafting.can_craft(recipe) for recipe in all_recipes))
|
||||
return self.logic.and_(*(self.logic.crafting.can_craft(recipe) for recipe in all_recipes))
|
||||
|
|
|
@ -1,72 +0,0 @@
|
|||
from typing import Union, Iterable
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .has_logic import HasLogicMixin
|
||||
from .money_logic import MoneyLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from .traveling_merchant_logic import TravelingMerchantLogicMixin
|
||||
from ..data import CropItem, SeedItem
|
||||
from ..options import Cropsanity, ExcludeGingerIsland
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from ..strings.craftable_names import Craftable
|
||||
from ..strings.forageable_names import Forageable
|
||||
from ..strings.machine_names import Machine
|
||||
from ..strings.metal_names import Fossil
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.seed_names import Seed
|
||||
from ..strings.tool_names import Tool
|
||||
|
||||
|
||||
class CropLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.crop = CropLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class CropLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, TravelingMerchantLogicMixin, SeasonLogicMixin, MoneyLogicMixin,
|
||||
ToolLogicMixin, CropLogicMixin]]):
|
||||
@cache_self1
|
||||
def can_grow(self, crop: CropItem) -> StardewRule:
|
||||
season_rule = self.logic.season.has_any(crop.farm_growth_seasons)
|
||||
seed_rule = self.logic.has(crop.seed.name)
|
||||
farm_rule = self.logic.region.can_reach(Region.farm) & season_rule
|
||||
tool_rule = self.logic.tool.has_tool(Tool.hoe) & self.logic.tool.has_tool(Tool.watering_can)
|
||||
region_rule = farm_rule | self.logic.region.can_reach(Region.greenhouse) | self.logic.crop.has_island_farm()
|
||||
if crop.name == Forageable.cactus_fruit:
|
||||
region_rule = self.logic.region.can_reach(Region.greenhouse) | self.logic.has(Craftable.garden_pot)
|
||||
return seed_rule & region_rule & tool_rule
|
||||
|
||||
def can_plant_and_grow_item(self, seasons: Union[str, Iterable[str]]) -> StardewRule:
|
||||
if isinstance(seasons, str):
|
||||
seasons = [seasons]
|
||||
season_rule = self.logic.season.has_any(seasons) | self.logic.region.can_reach(Region.greenhouse) | self.logic.crop.has_island_farm()
|
||||
farm_rule = self.logic.region.can_reach(Region.farm) | self.logic.region.can_reach(Region.greenhouse) | self.logic.crop.has_island_farm()
|
||||
return season_rule & farm_rule
|
||||
|
||||
def has_island_farm(self) -> StardewRule:
|
||||
if self.options.exclude_ginger_island == ExcludeGingerIsland.option_false:
|
||||
return self.logic.region.can_reach(Region.island_west)
|
||||
return False_()
|
||||
|
||||
@cache_self1
|
||||
def can_buy_seed(self, seed: SeedItem) -> StardewRule:
|
||||
if seed.requires_island and self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
|
||||
return False_()
|
||||
if self.options.cropsanity == Cropsanity.option_disabled or seed.name == Seed.qi_bean:
|
||||
item_rule = True_()
|
||||
else:
|
||||
item_rule = self.logic.received(seed.name)
|
||||
if seed.name == Seed.coffee:
|
||||
item_rule = item_rule & self.logic.traveling_merchant.has_days(3)
|
||||
season_rule = self.logic.season.has_any(seed.seasons)
|
||||
region_rule = self.logic.region.can_reach_all(seed.regions)
|
||||
currency_rule = self.logic.money.can_spend(1000)
|
||||
if seed.name == Seed.pineapple:
|
||||
currency_rule = self.logic.has(Forageable.magma_cap)
|
||||
if seed.name == Seed.taro:
|
||||
currency_rule = self.logic.has(Fossil.bone_fragment)
|
||||
return season_rule & region_rule & item_rule & currency_rule
|
|
@ -1,11 +1,27 @@
|
|||
from typing import Union
|
||||
from functools import cached_property
|
||||
from typing import Union, Tuple
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .has_logic import HasLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from .. import options
|
||||
from ..stardew_rule import StardewRule, True_, false_
|
||||
from ..strings.ap_names.event_names import Event
|
||||
from ..strings.fertilizer_names import Fertilizer
|
||||
from ..strings.quality_names import CropQuality
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.season_names import Season
|
||||
from ..strings.tool_names import Tool
|
||||
|
||||
farming_event_by_season = {
|
||||
Season.spring: Event.spring_farming,
|
||||
Season.summer: Event.summer_farming,
|
||||
Season.fall: Event.fall_farming,
|
||||
Season.winter: Event.winter_farming,
|
||||
}
|
||||
|
||||
|
||||
class FarmingLogicMixin(BaseLogicMixin):
|
||||
|
@ -14,7 +30,12 @@ class FarmingLogicMixin(BaseLogicMixin):
|
|||
self.farming = FarmingLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class FarmingLogic(BaseLogic[Union[HasLogicMixin, SkillLogicMixin, FarmingLogicMixin]]):
|
||||
class FarmingLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, ToolLogicMixin, FarmingLogicMixin]]):
|
||||
|
||||
@cached_property
|
||||
def has_farming_tools(self) -> StardewRule:
|
||||
return self.logic.tool.has_tool(Tool.hoe) & self.logic.tool.can_water(0)
|
||||
|
||||
def has_fertilizer(self, tier: int) -> StardewRule:
|
||||
if tier <= 0:
|
||||
return True_()
|
||||
|
@ -25,17 +46,17 @@ class FarmingLogic(BaseLogic[Union[HasLogicMixin, SkillLogicMixin, FarmingLogicM
|
|||
if tier >= 3:
|
||||
return self.logic.has(Fertilizer.deluxe)
|
||||
|
||||
def can_grow_crop_quality(self, quality: str) -> StardewRule:
|
||||
if quality == CropQuality.basic:
|
||||
return True_()
|
||||
if quality == CropQuality.silver:
|
||||
return self.logic.skill.has_farming_level(5) | (self.logic.farming.has_fertilizer(1) & self.logic.skill.has_farming_level(2)) | (
|
||||
self.logic.farming.has_fertilizer(2) & self.logic.skill.has_farming_level(1)) | self.logic.farming.has_fertilizer(3)
|
||||
if quality == CropQuality.gold:
|
||||
return self.logic.skill.has_farming_level(10) | (
|
||||
self.logic.farming.has_fertilizer(1) & self.logic.skill.has_farming_level(5)) | (
|
||||
self.logic.farming.has_fertilizer(2) & self.logic.skill.has_farming_level(3)) | (
|
||||
self.logic.farming.has_fertilizer(3) & self.logic.skill.has_farming_level(2))
|
||||
if quality == CropQuality.iridium:
|
||||
return self.logic.farming.has_fertilizer(3) & self.logic.skill.has_farming_level(4)
|
||||
return False_()
|
||||
@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
|
||||
|
||||
if isinstance(seasons, str):
|
||||
seasons = (seasons,)
|
||||
|
||||
return self.logic.or_(*(self.logic.received(farming_event_by_season[season]) for season in seasons))
|
||||
|
||||
def has_island_farm(self) -> StardewRule:
|
||||
if self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_false:
|
||||
return self.logic.region.can_reach(Region.island_west)
|
||||
return false_
|
||||
|
|
|
@ -1,18 +1,21 @@
|
|||
from typing import Union, List
|
||||
from typing import Union
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..data import FishItem, fish_data
|
||||
from ..locations import LocationTags, locations_by_tag
|
||||
from ..options import ExcludeGingerIsland, Fishsanity
|
||||
from ..data import fish_data
|
||||
from ..data.fish_data import FishItem
|
||||
from ..options import ExcludeGingerIsland
|
||||
from ..options import SpecialOrderLocations
|
||||
from ..stardew_rule import StardewRule, True_, False_, And
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from ..strings.ap_names.mods.mod_items import SVEQuestItem
|
||||
from ..strings.fish_names import SVEFish
|
||||
from ..strings.machine_names import Machine
|
||||
from ..strings.quality_names import FishQuality
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.skill_names import Skill
|
||||
|
@ -24,17 +27,16 @@ class FishingLogicMixin(BaseLogicMixin):
|
|||
self.fishing = FishingLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class FishingLogic(BaseLogic[Union[FishingLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, ToolLogicMixin, SkillLogicMixin]]):
|
||||
class FishingLogic(BaseLogic[Union[HasLogicMixin, FishingLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, ToolLogicMixin,
|
||||
SkillLogicMixin]]):
|
||||
def can_fish_in_freshwater(self) -> StardewRule:
|
||||
return self.logic.skill.can_fish() & self.logic.region.can_reach_any((Region.forest, Region.town, Region.mountain))
|
||||
|
||||
def has_max_fishing(self) -> StardewRule:
|
||||
skill_rule = self.logic.skill.has_level(Skill.fishing, 10)
|
||||
return self.logic.tool.has_fishing_rod(4) & skill_rule
|
||||
return self.logic.tool.has_fishing_rod(4) & self.logic.skill.has_level(Skill.fishing, 10)
|
||||
|
||||
def can_fish_chests(self) -> StardewRule:
|
||||
skill_rule = self.logic.skill.has_level(Skill.fishing, 6)
|
||||
return self.logic.tool.has_fishing_rod(4) & skill_rule
|
||||
return self.logic.tool.has_fishing_rod(4) & self.logic.skill.has_level(Skill.fishing, 6)
|
||||
|
||||
def can_fish_at(self, region: str) -> StardewRule:
|
||||
return self.logic.skill.can_fish() & self.logic.region.can_reach(region)
|
||||
|
@ -51,17 +53,23 @@ class FishingLogic(BaseLogic[Union[FishingLogicMixin, ReceivedLogicMixin, Region
|
|||
else:
|
||||
difficulty_rule = self.logic.skill.can_fish(difficulty=(120 if fish.legendary else fish.difficulty))
|
||||
if fish.name == SVEFish.kittyfish:
|
||||
item_rule = self.logic.received("Kittyfish Spell")
|
||||
item_rule = self.logic.received(SVEQuestItem.kittyfish_spell)
|
||||
else:
|
||||
item_rule = True_()
|
||||
return quest_rule & region_rule & season_rule & difficulty_rule & item_rule
|
||||
|
||||
def can_catch_fish_for_fishsanity(self, fish: FishItem) -> StardewRule:
|
||||
""" Rule could be different from the basic `can_catch_fish`. Imagine a fishsanity setting where you need to catch every fish with gold quality.
|
||||
"""
|
||||
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 self.options.special_order_locations != SpecialOrderLocations.option_board_qi:
|
||||
if not self.options.special_order_locations & SpecialOrderLocations.value_qi:
|
||||
return False_()
|
||||
return self.logic.region.can_reach(Region.qi_walnut_room) & And(*(self.logic.fishing.can_catch_fish(fish) for fish in fish_data.legendary_fish))
|
||||
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)))
|
||||
|
||||
def can_catch_quality_fish(self, fish_quality: str) -> StardewRule:
|
||||
if fish_quality == FishQuality.basic:
|
||||
|
@ -78,24 +86,27 @@ class FishingLogic(BaseLogic[Union[FishingLogicMixin, ReceivedLogicMixin, Region
|
|||
|
||||
def can_catch_every_fish(self) -> StardewRule:
|
||||
rules = [self.has_max_fishing()]
|
||||
exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
exclude_extended_family = self.options.special_order_locations != SpecialOrderLocations.option_board_qi
|
||||
for fish in fish_data.get_fish_for_mods(self.options.mods.value):
|
||||
if exclude_island and fish in fish_data.island_fish:
|
||||
continue
|
||||
if exclude_extended_family and fish in fish_data.extended_family:
|
||||
continue
|
||||
rules.append(self.logic.fishing.can_catch_fish(fish))
|
||||
return And(*rules)
|
||||
|
||||
def can_catch_every_fish_in_slot(self, all_location_names_in_slot: List[str]) -> StardewRule:
|
||||
if self.options.fishsanity == Fishsanity.option_none:
|
||||
rules.extend(
|
||||
self.logic.fishing.can_catch_fish(fish)
|
||||
for fish in self.content.fishes.values()
|
||||
)
|
||||
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def can_catch_every_fish_for_fishsanity(self) -> StardewRule:
|
||||
if not self.content.features.fishsanity.is_enabled:
|
||||
return self.can_catch_every_fish()
|
||||
|
||||
rules = [self.has_max_fishing()]
|
||||
|
||||
for fishsanity_location in locations_by_tag[LocationTags.FISHSANITY]:
|
||||
if fishsanity_location.name not in all_location_names_in_slot:
|
||||
continue
|
||||
rules.append(self.logic.region.can_reach_location(fishsanity_location.name))
|
||||
return And(*rules)
|
||||
rules.extend(
|
||||
self.logic.fishing.can_catch_fish_for_fishsanity(fish)
|
||||
for fish in self.content.fishes.values()
|
||||
if self.content.features.fishsanity.is_included(fish)
|
||||
)
|
||||
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def has_specific_bait(self, fish: FishItem) -> StardewRule:
|
||||
return self.can_catch_fish(fish) & self.logic.has(Machine.bait_maker)
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
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 .time_logic import TimeLogicMixin
|
||||
from ..options import Booksanity
|
||||
from ..stardew_rule import StardewRule, HasProgressionPercent
|
||||
from ..strings.book_names import Book
|
||||
from ..strings.craftable_names import Consumable
|
||||
from ..strings.currency_names import Currency
|
||||
from ..strings.fish_names import WaterChest
|
||||
from ..strings.geode_names import Geode
|
||||
from ..strings.tool_names import Tool
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .tool_logic import ToolLogicMixin
|
||||
else:
|
||||
ToolLogicMixin = object
|
||||
|
||||
MIN_ITEMS = 10
|
||||
MAX_ITEMS = 999
|
||||
PERCENT_REQUIRED_FOR_MAX_ITEM = 24
|
||||
|
||||
|
||||
class GrindLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.grind = GrindLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class GrindLogic(BaseLogic[Union[GrindLogicMixin, HasLogicMixin, ReceivedLogicMixin, BookLogicMixin, TimeLogicMixin, ToolLogicMixin]]):
|
||||
|
||||
def can_grind_mystery_boxes(self, quantity: int) -> StardewRule:
|
||||
mystery_box_rule = self.logic.has(Consumable.mystery_box)
|
||||
book_of_mysteries_rule = self.logic.true_ \
|
||||
if self.options.booksanity == Booksanity.option_none \
|
||||
else self.logic.book.has_book_power(Book.book_of_mysteries)
|
||||
# Assuming one box per day, but halved because we don't know how many months have passed before Mr. Qi's Plane Ride.
|
||||
time_rule = self.logic.time.has_lived_months(quantity // 14)
|
||||
return self.logic.and_(mystery_box_rule,
|
||||
book_of_mysteries_rule,
|
||||
time_rule)
|
||||
|
||||
def can_grind_artifact_troves(self, quantity: int) -> StardewRule:
|
||||
return self.logic.and_(self.logic.has(Geode.artifact_trove),
|
||||
# Assuming one per month if the player does not grind it.
|
||||
self.logic.time.has_lived_months(quantity))
|
||||
|
||||
def can_grind_prize_tickets(self, quantity: int) -> StardewRule:
|
||||
return self.logic.and_(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))
|
||||
|
||||
def can_grind_fishing_treasure_chests(self, quantity: int) -> StardewRule:
|
||||
return self.logic.and_(self.logic.has(WaterChest.fishing_chest),
|
||||
# Assuming one per week if the player does not grind it.
|
||||
self.logic.time.has_lived_months(quantity // 4))
|
||||
|
||||
def can_grind_artifact_spots(self, quantity: int) -> StardewRule:
|
||||
return self.logic.and_(self.logic.tool.has_tool(Tool.hoe),
|
||||
# Assuming twelve per month if the player does not grind it.
|
||||
self.logic.time.has_lived_months(quantity // 12))
|
||||
|
||||
@cache_self1
|
||||
def can_grind_item(self, quantity: int) -> StardewRule:
|
||||
if quantity <= MIN_ITEMS:
|
||||
return self.logic.true_
|
||||
|
||||
quantity = min(quantity, MAX_ITEMS)
|
||||
price = max(1, quantity * PERCENT_REQUIRED_FOR_MAX_ITEM // MAX_ITEMS)
|
||||
return HasProgressionPercent(self.player, price)
|
|
@ -0,0 +1,56 @@
|
|||
from functools import cached_property
|
||||
from typing import Union
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .farming_logic import FarmingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..data.harvest import ForagingSource, HarvestFruitTreeSource, HarvestCropSource
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
|
||||
from ..strings.region_names import Region
|
||||
|
||||
|
||||
class HarvestingLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.harvesting = HarvestingLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class HarvestingLogic(BaseLogic[Union[HarvestingLogicMixin, HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, ToolLogicMixin,
|
||||
FarmingLogicMixin, TimeLogicMixin]]):
|
||||
|
||||
@cached_property
|
||||
def can_harvest_from_fruit_bats(self) -> StardewRule:
|
||||
return self.logic.region.can_reach(Region.farm_cave) & self.logic.received(CommunityUpgrade.fruit_bats)
|
||||
|
||||
@cached_property
|
||||
def can_harvest_from_mushroom_cave(self) -> StardewRule:
|
||||
return self.logic.region.can_reach(Region.farm_cave) & self.logic.received(CommunityUpgrade.mushroom_boxes)
|
||||
|
||||
@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
|
||||
|
||||
@cache_self1
|
||||
def can_harvest_tree_from(self, source: HarvestFruitTreeSource) -> StardewRule:
|
||||
# FIXME tool not required for this
|
||||
region_to_grow_rule = self.logic.farming.can_plant_and_grow_item(source.seasons)
|
||||
sapling_rule = self.logic.has(source.sapling)
|
||||
# Because it takes 1 month to grow the sapling
|
||||
time_rule = self.logic.time.has_lived_months(1)
|
||||
|
||||
return region_to_grow_rule & sapling_rule & time_rule
|
||||
|
||||
@cache_self1
|
||||
def can_harvest_crop_from(self, source: HarvestCropSource) -> StardewRule:
|
||||
region_to_grow_rule = self.logic.farming.can_plant_and_grow_item(source.seasons)
|
||||
seed_rule = self.logic.has(source.seed)
|
||||
return region_to_grow_rule & seed_rule
|
|
@ -1,8 +1,11 @@
|
|||
from .base_logic import BaseLogic
|
||||
from ..stardew_rule import StardewRule, And, Or, Has, Count
|
||||
from ..stardew_rule import StardewRule, And, Or, Has, Count, true_, false_
|
||||
|
||||
|
||||
class HasLogicMixin(BaseLogic[None]):
|
||||
true_ = true_
|
||||
false_ = false_
|
||||
|
||||
# Should be cached
|
||||
def has(self, item: str) -> StardewRule:
|
||||
return Has(item, self.registry.item_rules)
|
||||
|
@ -10,12 +13,12 @@ class HasLogicMixin(BaseLogic[None]):
|
|||
def has_all(self, *items: str):
|
||||
assert items, "Can't have all of no items."
|
||||
|
||||
return And(*(self.has(item) for item in items))
|
||||
return self.logic.and_(*(self.has(item) for item in items))
|
||||
|
||||
def has_any(self, *items: str):
|
||||
assert items, "Can't have any of no items."
|
||||
|
||||
return Or(*(self.has(item) for item in items))
|
||||
return self.logic.or_(*(self.has(item) for item in items))
|
||||
|
||||
def has_n(self, *items: str, count: int):
|
||||
return self.count(count, *(self.has(item) for item in items))
|
||||
|
@ -24,6 +27,16 @@ class HasLogicMixin(BaseLogic[None]):
|
|||
def count(count: int, *rules: StardewRule) -> StardewRule:
|
||||
assert rules, "Can't create a Count conditions without rules"
|
||||
assert len(rules) >= count, "Count need at least as many rules as the count"
|
||||
assert count > 0, "Count can't be negative"
|
||||
|
||||
count -= sum(r is true_ for r in rules)
|
||||
rules = list(r for r in rules if r is not true_)
|
||||
|
||||
if count <= 0:
|
||||
return true_
|
||||
|
||||
if len(rules) == 1:
|
||||
return rules[0]
|
||||
|
||||
if count == 1:
|
||||
return Or(*rules)
|
||||
|
@ -31,4 +44,22 @@ class HasLogicMixin(BaseLogic[None]):
|
|||
if count == len(rules):
|
||||
return And(*rules)
|
||||
|
||||
return Count(list(rules), count)
|
||||
return Count(rules, count)
|
||||
|
||||
@staticmethod
|
||||
def and_(*rules: StardewRule) -> StardewRule:
|
||||
assert rules, "Can't create a And conditions without rules"
|
||||
|
||||
if len(rules) == 1:
|
||||
return rules[0]
|
||||
|
||||
return And(*rules)
|
||||
|
||||
@staticmethod
|
||||
def or_(*rules: StardewRule) -> StardewRule:
|
||||
assert rules, "Can't create a Or conditions without rules"
|
||||
|
||||
if len(rules) == 1:
|
||||
return rules[0]
|
||||
|
||||
return Or(*rules)
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Collection
|
||||
import logging
|
||||
from functools import cached_property
|
||||
from typing import Collection, Callable
|
||||
|
||||
from .ability_logic import AbilityLogicMixin
|
||||
from .action_logic import ActionLogicMixin
|
||||
|
@ -9,50 +10,53 @@ from .animal_logic import AnimalLogicMixin
|
|||
from .arcade_logic import ArcadeLogicMixin
|
||||
from .artisan_logic import ArtisanLogicMixin
|
||||
from .base_logic import LogicRegistry
|
||||
from .buff_logic import BuffLogicMixin
|
||||
from .book_logic import BookLogicMixin
|
||||
from .building_logic import BuildingLogicMixin
|
||||
from .bundle_logic import BundleLogicMixin
|
||||
from .combat_logic import CombatLogicMixin
|
||||
from .cooking_logic import CookingLogicMixin
|
||||
from .crafting_logic import CraftingLogicMixin
|
||||
from .crop_logic import CropLogicMixin
|
||||
from .farming_logic import FarmingLogicMixin
|
||||
from .fishing_logic import FishingLogicMixin
|
||||
from .gift_logic import GiftLogicMixin
|
||||
from .grind_logic import GrindLogicMixin
|
||||
from .harvesting_logic import HarvestingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .logic_event import all_logic_events
|
||||
from .mine_logic import MineLogicMixin
|
||||
from .money_logic import MoneyLogicMixin
|
||||
from .monster_logic import MonsterLogicMixin
|
||||
from .museum_logic import MuseumLogicMixin
|
||||
from .pet_logic import PetLogicMixin
|
||||
from .quality_logic import QualityLogicMixin
|
||||
from .quest_logic import QuestLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .relationship_logic import RelationshipLogicMixin
|
||||
from .requirement_logic import RequirementLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .shipping_logic import ShippingLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from .source_logic import SourceLogicMixin
|
||||
from .special_order_logic import SpecialOrderLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from .traveling_merchant_logic import TravelingMerchantLogicMixin
|
||||
from .wallet_logic import WalletLogicMixin
|
||||
from ..data import all_purchasable_seeds, all_crops
|
||||
from ..content.game_content import StardewContent
|
||||
from ..data.craftable_data import all_crafting_recipes
|
||||
from ..data.crops_data import crops_by_name
|
||||
from ..data.fish_data import get_fish_for_mods
|
||||
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 ..mods.mod_data import ModNames
|
||||
from ..options import Cropsanity, SpecialOrderLocations, ExcludeGingerIsland, FestivalLocations, Fishsanity, Friendsanity, StardewValleyOptions
|
||||
from ..stardew_rule import False_, Or, True_, And, StardewRule
|
||||
from ..options import SpecialOrderLocations, ExcludeGingerIsland, FestivalLocations, StardewValleyOptions, Walnutsanity
|
||||
from ..stardew_rule import False_, True_, StardewRule
|
||||
from ..strings.animal_names import Animal
|
||||
from ..strings.animal_product_names import AnimalProduct
|
||||
from ..strings.ap_names.ap_weapon_names import APWeapon
|
||||
from ..strings.ap_names.buff_names import Buff
|
||||
from ..strings.ap_names.ap_option_names import OptionName
|
||||
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
|
||||
from ..strings.ap_names.event_names import Event
|
||||
from ..strings.artisan_good_names import ArtisanGood
|
||||
from ..strings.building_names import Building
|
||||
from ..strings.craftable_names import Consumable, Furniture, Ring, Fishing, Lighting, WildSeeds
|
||||
|
@ -72,10 +76,10 @@ 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
|
||||
from ..strings.metal_names import Ore, MetalBar, Mineral, Fossil, Artifact
|
||||
from ..strings.monster_drop_names import Loot
|
||||
from ..strings.monster_names import Monster
|
||||
from ..strings.region_names import Region
|
||||
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
|
||||
|
@ -83,23 +87,26 @@ from ..strings.tool_names import Tool, ToolMaterial
|
|||
from ..strings.villager_names import NPC
|
||||
from ..strings.wallet_item_names import Wallet
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@dataclass(frozen=False, repr=False)
|
||||
class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogicMixin, TravelingMerchantLogicMixin, TimeLogicMixin,
|
||||
|
||||
class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, TravelingMerchantLogicMixin, TimeLogicMixin,
|
||||
SeasonLogicMixin, MoneyLogicMixin, ActionLogicMixin, ArcadeLogicMixin, ArtisanLogicMixin, GiftLogicMixin,
|
||||
BuildingLogicMixin, ShippingLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, WalletLogicMixin, AnimalLogicMixin,
|
||||
CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, CropLogicMixin,
|
||||
CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, QualityLogicMixin,
|
||||
SkillLogicMixin, FarmingLogicMixin, BundleLogicMixin, FishingLogicMixin, MineLogicMixin, CookingLogicMixin, AbilityLogicMixin,
|
||||
SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin):
|
||||
SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin, HarvestingLogicMixin, SourceLogicMixin,
|
||||
RequirementLogicMixin, BookLogicMixin, GrindLogicMixin):
|
||||
player: int
|
||||
options: StardewValleyOptions
|
||||
content: StardewContent
|
||||
regions: Collection[str]
|
||||
|
||||
def __init__(self, player: int, options: StardewValleyOptions, regions: Collection[str]):
|
||||
def __init__(self, player: int, options: StardewValleyOptions, content: StardewContent, regions: Collection[str]):
|
||||
self.registry = LogicRegistry()
|
||||
super().__init__(player, self.registry, options, regions, self)
|
||||
super().__init__(player, self.registry, options, content, regions, self)
|
||||
|
||||
self.registry.fish_rules.update({fish.name: self.fishing.can_catch_fish(fish) for fish in get_fish_for_mods(self.options.mods.value)})
|
||||
self.registry.fish_rules.update({fish.name: self.fishing.can_catch_fish(fish) for fish in content.fishes.values()})
|
||||
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:
|
||||
|
@ -118,37 +125,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
can_craft_rule = can_craft_rule | self.registry.crafting_rules[recipe.item]
|
||||
self.registry.crafting_rules[recipe.item] = can_craft_rule
|
||||
|
||||
self.registry.sapling_rules.update({
|
||||
Sapling.apple: self.can_buy_sapling(Fruit.apple),
|
||||
Sapling.apricot: self.can_buy_sapling(Fruit.apricot),
|
||||
Sapling.cherry: self.can_buy_sapling(Fruit.cherry),
|
||||
Sapling.orange: self.can_buy_sapling(Fruit.orange),
|
||||
Sapling.peach: self.can_buy_sapling(Fruit.peach),
|
||||
Sapling.pomegranate: self.can_buy_sapling(Fruit.pomegranate),
|
||||
Sapling.banana: self.can_buy_sapling(Fruit.banana),
|
||||
Sapling.mango: self.can_buy_sapling(Fruit.mango),
|
||||
})
|
||||
|
||||
self.registry.tree_fruit_rules.update({
|
||||
Fruit.apple: self.crop.can_plant_and_grow_item(Season.fall),
|
||||
Fruit.apricot: self.crop.can_plant_and_grow_item(Season.spring),
|
||||
Fruit.cherry: self.crop.can_plant_and_grow_item(Season.spring),
|
||||
Fruit.orange: self.crop.can_plant_and_grow_item(Season.summer),
|
||||
Fruit.peach: self.crop.can_plant_and_grow_item(Season.summer),
|
||||
Fruit.pomegranate: self.crop.can_plant_and_grow_item(Season.fall),
|
||||
Fruit.banana: self.crop.can_plant_and_grow_item(Season.summer),
|
||||
Fruit.mango: self.crop.can_plant_and_grow_item(Season.summer),
|
||||
})
|
||||
|
||||
for tree_fruit in self.registry.tree_fruit_rules:
|
||||
existing_rules = self.registry.tree_fruit_rules[tree_fruit]
|
||||
sapling = f"{tree_fruit} Sapling"
|
||||
self.registry.tree_fruit_rules[tree_fruit] = existing_rules & self.has(sapling) & self.time.has_lived_months(1)
|
||||
|
||||
self.registry.seed_rules.update({seed.name: self.crop.can_buy_seed(seed) for seed in all_purchasable_seeds})
|
||||
self.registry.crop_rules.update({crop.name: self.crop.can_grow(crop) for crop in all_crops})
|
||||
self.registry.crop_rules.update({
|
||||
Seed.coffee: (self.season.has(Season.spring) | self.season.has(Season.summer)) & self.crop.can_buy_seed(crops_by_name[Seed.coffee].seed),
|
||||
Fruit.ancient_fruit: (self.received("Ancient Seeds") | self.received("Ancient Seeds Recipe")) &
|
||||
self.region.can_reach(Region.greenhouse) & self.has(Machine.seed_maker),
|
||||
})
|
||||
|
@ -157,6 +134,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
self.registry.item_rules.update({
|
||||
"Energy Tonic": self.money.can_spend_at(Region.hospital, 1000),
|
||||
WaterChest.fishing_chest: self.fishing.can_fish_chests(),
|
||||
WaterChest.golden_fishing_chest: self.fishing.can_fish_chests() & self.skill.has_mastery(Skill.fishing),
|
||||
WaterChest.treasure: self.fishing.can_fish_chests(),
|
||||
Ring.hot_java_ring: self.region.can_reach(Region.volcano_floor_10),
|
||||
"Galaxy Soul": self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 40),
|
||||
|
@ -197,7 +175,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
AnimalProduct.large_goat_milk: self.animal.has_happy_animal(Animal.goat),
|
||||
AnimalProduct.large_milk: self.animal.has_happy_animal(Animal.cow),
|
||||
AnimalProduct.milk: self.animal.has_animal(Animal.cow),
|
||||
AnimalProduct.ostrich_egg: self.tool.can_forage(Generic.any, Region.island_north, True),
|
||||
AnimalProduct.ostrich_egg: self.tool.can_forage(Generic.any, Region.island_north, True) & self.has(Forageable.journal_scrap) & self.region.can_reach(Region.volcano_floor_5),
|
||||
AnimalProduct.rabbit_foot: self.animal.has_happy_animal(Animal.rabbit),
|
||||
AnimalProduct.roe: self.skill.can_fish() & 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)),
|
||||
|
@ -218,29 +196,35 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
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),
|
||||
ArtisanGood.green_tea: self.artisan.can_keg(Vegetable.tea_leaves),
|
||||
ArtisanGood.honey: self.money.can_spend_at(Region.oasis, 200) | (self.has(Machine.bee_house) & self.season.has_any_not_winter()),
|
||||
ArtisanGood.jelly: self.artisan.has_jelly(),
|
||||
ArtisanGood.juice: self.artisan.has_juice(),
|
||||
ArtisanGood.maple_syrup: self.has(Machine.tapper),
|
||||
ArtisanGood.mayonnaise: self.artisan.can_mayonnaise(AnimalProduct.chicken_egg),
|
||||
ArtisanGood.mead: self.artisan.can_keg(ArtisanGood.honey),
|
||||
ArtisanGood.mystic_syrup: self.has(Machine.tapper) & self.has(TreeSeed.mystic),
|
||||
ArtisanGood.oak_resin: self.has(Machine.tapper),
|
||||
ArtisanGood.pale_ale: self.artisan.can_keg(Vegetable.hops),
|
||||
ArtisanGood.pickles: self.artisan.has_pickle(),
|
||||
ArtisanGood.pine_tar: self.has(Machine.tapper),
|
||||
ArtisanGood.smoked_fish: self.artisan.has_smoked_fish(),
|
||||
ArtisanGood.targeted_bait: self.artisan.has_targeted_bait(),
|
||||
ArtisanGood.stardrop_tea: self.has(WaterChest.golden_fishing_chest),
|
||||
ArtisanGood.truffle_oil: self.has(AnimalProduct.truffle) & self.has(Machine.oil_maker),
|
||||
ArtisanGood.void_mayonnaise: (self.skill.can_fish(Region.witch_swamp)) | (self.artisan.can_mayonnaise(AnimalProduct.void_egg)),
|
||||
ArtisanGood.wine: self.artisan.has_wine(),
|
||||
Beverage.beer: self.artisan.can_keg(Vegetable.wheat) | self.money.can_spend_at(Region.saloon, 400),
|
||||
Beverage.coffee: self.artisan.can_keg(Seed.coffee) | self.has(Machine.coffee_maker) | (self.money.can_spend_at(Region.saloon, 300)) | self.has("Hot Java Ring"),
|
||||
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.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),
|
||||
Currency.golden_tag: self.region.can_reach(LogicRegion.trout_derby),
|
||||
Currency.prize_ticket: self.time.has_lived_months(2), # Time to do a few help wanted quests
|
||||
Decoration.rotten_plant: self.has(Lighting.jack_o_lantern) & self.season.has(Season.winter),
|
||||
Fertilizer.basic: self.money.can_spend_at(Region.pierre_store, 100),
|
||||
Fertilizer.quality: self.time.has_year_two & self.money.can_spend_at(Region.pierre_store, 150),
|
||||
Fertilizer.tree: self.skill.has_level(Skill.foraging, 7) & self.has(Material.fiber) & self.has(Material.stone),
|
||||
Fish.any: Or(*(self.fishing.can_catch_fish(fish) for fish in get_fish_for_mods(self.options.mods.value))),
|
||||
Fish.any: self.logic.or_(*(self.fishing.can_catch_fish(fish) for fish in content.fishes.values())),
|
||||
Fish.crab: self.skill.can_crab_pot_at(Region.beach),
|
||||
Fish.crayfish: self.skill.can_crab_pot_at(Region.town),
|
||||
Fish.lobster: self.skill.can_crab_pot_at(Region.beach),
|
||||
|
@ -252,44 +236,15 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
Fish.snail: self.skill.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.blackberry: self.tool.can_forage(Season.fall) | self.has_fruit_bats(),
|
||||
Forageable.cactus_fruit: self.tool.can_forage(Generic.any, Region.desert),
|
||||
Forageable.cave_carrot: self.tool.can_forage(Generic.any, Region.mines_floor_10, True),
|
||||
Forageable.chanterelle: self.tool.can_forage(Season.fall, Region.secret_woods) | self.has_mushroom_cave(),
|
||||
Forageable.coconut: self.tool.can_forage(Generic.any, Region.desert),
|
||||
Forageable.common_mushroom: self.tool.can_forage(Season.fall) | (self.tool.can_forage(Season.spring, Region.secret_woods)) | self.has_mushroom_cave(),
|
||||
Forageable.crocus: self.tool.can_forage(Season.winter),
|
||||
Forageable.crystal_fruit: self.tool.can_forage(Season.winter),
|
||||
Forageable.daffodil: self.tool.can_forage(Season.spring),
|
||||
Forageable.dandelion: self.tool.can_forage(Season.spring),
|
||||
Forageable.dragon_tooth: self.tool.can_forage(Generic.any, Region.volcano_floor_10),
|
||||
Forageable.fiddlehead_fern: self.tool.can_forage(Season.summer, Region.secret_woods),
|
||||
Forageable.ginger: self.tool.can_forage(Generic.any, Region.island_west, True),
|
||||
Forageable.hay: self.building.has_building(Building.silo) & self.tool.has_tool(Tool.scythe),
|
||||
Forageable.hazelnut: self.tool.can_forage(Season.fall),
|
||||
Forageable.holly: self.tool.can_forage(Season.winter),
|
||||
Forageable.journal_scrap: self.region.can_reach_all((Region.island_west, Region.island_north, Region.island_south, Region.volcano_floor_10)) & self.quest.has_magnifying_glass() & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()),
|
||||
Forageable.leek: self.tool.can_forage(Season.spring),
|
||||
Forageable.magma_cap: self.tool.can_forage(Generic.any, Region.volcano_floor_5),
|
||||
Forageable.morel: self.tool.can_forage(Season.spring, Region.secret_woods) | self.has_mushroom_cave(),
|
||||
Forageable.purple_mushroom: self.tool.can_forage(Generic.any, Region.mines_floor_95) | self.tool.can_forage(Generic.any, Region.skull_cavern_25) | self.has_mushroom_cave(),
|
||||
Forageable.rainbow_shell: self.tool.can_forage(Season.summer, Region.beach),
|
||||
Forageable.red_mushroom: self.tool.can_forage(Season.summer, Region.secret_woods) | self.tool.can_forage(Season.fall, Region.secret_woods) | self.has_mushroom_cave(),
|
||||
Forageable.salmonberry: self.tool.can_forage(Season.spring) | self.has_fruit_bats(),
|
||||
Forageable.secret_note: self.quest.has_magnifying_glass() & (self.ability.can_chop_trees() | self.mine.can_mine_in_the_mines_floor_1_40()),
|
||||
Forageable.snow_yam: self.tool.can_forage(Season.winter, Region.beach, True),
|
||||
Forageable.spice_berry: self.tool.can_forage(Season.summer) | self.has_fruit_bats(),
|
||||
Forageable.spring_onion: self.tool.can_forage(Season.spring),
|
||||
Forageable.sweet_pea: self.tool.can_forage(Season.summer),
|
||||
Forageable.wild_horseradish: self.tool.can_forage(Season.spring),
|
||||
Forageable.wild_plum: self.tool.can_forage(Season.fall) | self.has_fruit_bats(),
|
||||
Forageable.winter_root: self.tool.can_forage(Season.winter, Region.forest, True),
|
||||
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()), #
|
||||
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),
|
||||
Fossil.fossilized_skull: self.action.can_open_geode(Geode.golden_coconut),
|
||||
Fossil.fossilized_spine: self.skill.can_fish(Region.dig_site),
|
||||
Fossil.fossilized_tail: self.action.can_pan_at(Region.dig_site),
|
||||
Fossil.fossilized_tail: self.action.can_pan_at(Region.dig_site, ToolMaterial.copper),
|
||||
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.snake_skull: self.region.can_reach(Region.dig_site) & self.tool.has_tool(Tool.hoe),
|
||||
|
@ -299,10 +254,10 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
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.action.can_pan() | self.received(Wallet.rusty_key) | (self.has(Fish.octopus) & self.building.has_building(Building.fish_pond)) | self.region.can_reach(Region.volcano_floor_10),
|
||||
Gift.bouquet: self.relationship.has_hearts(Generic.bachelor, 8) & self.money.can_spend_at(Region.pierre_store, 100),
|
||||
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),
|
||||
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(Generic.bachelor, 10) & self.building.has_house(1) & self.has(Consumable.rain_totem),
|
||||
Gift.mermaid_pendant: self.region.can_reach(Region.tide_pools) & self.relationship.has_hearts_with_any_bachelor(10) & self.building.has_house(1) & self.has(Consumable.rain_totem),
|
||||
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.tea_set: self.season.has(Season.winter) & self.time.has_lived_max_months,
|
||||
|
@ -312,45 +267,27 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
Ingredient.qi_seasoning: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 10),
|
||||
Ingredient.rice: self.money.can_spend_at(Region.pierre_store, 200) | (self.building.has_building(Building.mill) & self.has(Vegetable.unmilled_rice)),
|
||||
Ingredient.sugar: self.money.can_spend_at(Region.pierre_store, 100) | (self.building.has_building(Building.mill) & self.has(Vegetable.beet)),
|
||||
Ingredient.vinegar: self.money.can_spend_at(Region.pierre_store, 200),
|
||||
Ingredient.vinegar: self.money.can_spend_at(Region.pierre_store, 200) | self.artisan.can_keg(Ingredient.rice),
|
||||
Ingredient.wheat_flour: self.money.can_spend_at(Region.pierre_store, 100) | (self.building.has_building(Building.mill) & self.has(Vegetable.wheat)),
|
||||
Loot.bat_wing: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern(),
|
||||
Loot.bug_meat: self.mine.can_mine_in_the_mines_floor_1_40(),
|
||||
Loot.slime: self.mine.can_mine_in_the_mines_floor_1_40(),
|
||||
Loot.solar_essence: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern(),
|
||||
Loot.void_essence: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern(),
|
||||
Machine.bee_house: self.skill.has_farming_level(3) & self.has(MetalBar.iron) & self.has(ArtisanGood.maple_syrup) & self.has(Material.coal) & self.has(Material.wood),
|
||||
Machine.cask: self.building.has_house(3) & self.region.can_reach(Region.cellar) & self.has(Material.wood) & self.has(Material.hardwood),
|
||||
Machine.cheese_press: self.skill.has_farming_level(6) & self.has(Material.wood) & self.has(Material.stone) & self.has(Material.hardwood) & self.has(MetalBar.copper),
|
||||
Machine.coffee_maker: self.received(Machine.coffee_maker),
|
||||
Machine.crab_pot: self.skill.has_level(Skill.fishing, 3) & (self.money.can_spend_at(Region.fish_shop, 1500) | (self.has(MetalBar.iron) & self.has(Material.wood))),
|
||||
Machine.furnace: self.has(Material.stone) & self.has(Ore.copper),
|
||||
Machine.keg: self.skill.has_farming_level(8) & self.has(Material.wood) & self.has(MetalBar.iron) & self.has(MetalBar.copper) & self.has(ArtisanGood.oak_resin),
|
||||
Machine.lightning_rod: self.skill.has_level(Skill.foraging, 6) & self.has(MetalBar.iron) & self.has(MetalBar.quartz) & self.has(Loot.bat_wing),
|
||||
Machine.loom: self.skill.has_farming_level(7) & self.has(Material.wood) & self.has(Material.fiber) & self.has(ArtisanGood.pine_tar),
|
||||
Machine.mayonnaise_machine: self.skill.has_farming_level(2) & self.has(Material.wood) & self.has(Material.stone) & self.has("Earth Crystal") & self.has(MetalBar.copper),
|
||||
Machine.ostrich_incubator: self.received("Ostrich Incubator Recipe") & self.has(Fossil.bone_fragment) & self.has(Material.hardwood) & self.has(Material.cinder_shard),
|
||||
Machine.preserves_jar: self.skill.has_farming_level(4) & self.has(Material.wood) & self.has(Material.stone) & self.has(Material.coal),
|
||||
Machine.recycling_machine: self.skill.has_level(Skill.fishing, 4) & self.has(Material.wood) & self.has(Material.stone) & self.has(MetalBar.iron),
|
||||
Machine.seed_maker: self.skill.has_farming_level(9) & self.has(Material.wood) & self.has(MetalBar.gold) & self.has(Material.coal),
|
||||
Machine.solar_panel: self.received("Solar Panel Recipe") & self.has(MetalBar.quartz) & self.has(MetalBar.iron) & self.has(MetalBar.gold),
|
||||
Machine.tapper: self.skill.has_level(Skill.foraging, 3) & self.has(Material.wood) & self.has(MetalBar.copper),
|
||||
Machine.worm_bin: self.skill.has_level(Skill.fishing, 8) & self.has(Material.hardwood) & self.has(MetalBar.gold) & self.has(MetalBar.iron) & self.has(Material.fiber),
|
||||
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),
|
||||
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.action.can_pan(),
|
||||
Material.coal: self.mine.can_mine_in_the_mines_floor_41_80() | self.tool.has_tool(Tool.pan),
|
||||
Material.fiber: True_(),
|
||||
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: True_(),
|
||||
Material.sap: self.ability.can_chop_trees(),
|
||||
Material.stone: self.tool.has_tool(Tool.pickaxe),
|
||||
Material.wood: self.tool.has_tool(Tool.axe),
|
||||
Meal.bread: self.money.can_spend_at(Region.saloon, 120),
|
||||
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.pizza: self.money.can_spend_at(Region.saloon, 600),
|
||||
Meal.salad: self.money.can_spend_at(Region.saloon, 220),
|
||||
Meal.spaghetti: self.money.can_spend_at(Region.saloon, 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),
|
||||
MetalBar.gold: self.can_smelt(Ore.gold),
|
||||
|
@ -358,15 +295,14 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
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.action.can_pan(),
|
||||
Ore.gold: self.mine.can_mine_in_the_mines_floor_81_120() | self.mine.can_mine_in_the_skull_cavern() | self.action.can_pan(),
|
||||
Ore.iridium: self.mine.can_mine_in_the_skull_cavern() | self.can_fish_pond(Fish.super_cucumber),
|
||||
Ore.iron: self.mine.can_mine_in_the_mines_floor_41_80() | self.mine.can_mine_in_the_skull_cavern() | self.action.can_pan(),
|
||||
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.iron),
|
||||
Ore.iridium: self.mine.can_mine_in_the_skull_cavern() | self.can_fish_pond(Fish.super_cucumber) | self.tool.has_tool(Tool.pan, ToolMaterial.gold),
|
||||
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.copper),
|
||||
Ore.radioactive: self.ability.can_mine_perfectly() & self.region.can_reach(Region.qi_walnut_room),
|
||||
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),
|
||||
Sapling.tea: self.relationship.has_hearts(NPC.caroline, 2) & self.has(Material.fiber) & self.has(Material.wood),
|
||||
Seed.mixed: self.tool.has_tool(Tool.scythe) & self.region.can_reach_all((Region.farm, Region.forest, Region.town)),
|
||||
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.skill.can_crab_pot,
|
||||
|
@ -380,24 +316,33 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
TreeSeed.maple: self.skill.has_level(Skill.foraging, 1) & self.ability.can_chop_trees(),
|
||||
TreeSeed.mushroom: self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 5),
|
||||
TreeSeed.pine: self.skill.has_level(Skill.foraging, 1) & self.ability.can_chop_trees(),
|
||||
Vegetable.tea_leaves: self.has(Sapling.tea) & self.time.has_lived_months(2) & self.season.has_any_not_winter(),
|
||||
TreeSeed.mossy: self.ability.can_chop_trees() & self.season.has(Season.summer),
|
||||
Fish.clam: self.tool.can_forage(Generic.any, Region.beach),
|
||||
Fish.cockle: self.tool.can_forage(Generic.any, Region.beach),
|
||||
WaterItem.coral: self.tool.can_forage(Generic.any, Region.tide_pools) | self.tool.can_forage(Season.summer, Region.beach),
|
||||
WaterItem.green_algae: self.fishing.can_fish_in_freshwater(),
|
||||
WaterItem.nautilus_shell: self.tool.can_forage(Season.winter, Region.beach),
|
||||
WaterItem.sea_urchin: self.tool.can_forage(Generic.any, Region.tide_pools),
|
||||
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.seaweed: self.skill.can_fish(Region.tide_pools),
|
||||
WaterItem.white_algae: self.skill.can_fish(Region.mines_floor_20),
|
||||
WildSeeds.grass_starter: self.money.can_spend_at(Region.pierre_store, 100),
|
||||
})
|
||||
# @formatter:on
|
||||
|
||||
content_rules = {
|
||||
item_name: self.source.has_access_to_item(game_item)
|
||||
for item_name, game_item in self.content.game_items.items()
|
||||
}
|
||||
|
||||
for item in set(content_rules.keys()).intersection(self.registry.item_rules.keys()):
|
||||
logger.warning(f"Rule for {item} already exists in the registry, overwriting it.")
|
||||
|
||||
self.registry.item_rules.update(content_rules)
|
||||
self.registry.item_rules.update(self.registry.fish_rules)
|
||||
self.registry.item_rules.update(self.registry.museum_rules)
|
||||
self.registry.item_rules.update(self.registry.sapling_rules)
|
||||
self.registry.item_rules.update(self.registry.tree_fruit_rules)
|
||||
self.registry.item_rules.update(self.registry.seed_rules)
|
||||
self.registry.item_rules.update(self.registry.crop_rules)
|
||||
self.artisan.initialize_rules()
|
||||
self.registry.item_rules.update(self.registry.artisan_good_rules)
|
||||
|
||||
self.registry.item_rules.update(self.mod.item.get_modded_item_rules())
|
||||
self.mod.item.modify_vanilla_item_rules_with_mod_additions(self.registry.item_rules) # New regions and content means new ways to obtain old items
|
||||
|
@ -423,7 +368,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
self.registry.festival_rules.update({
|
||||
FestivalCheck.egg_hunt: self.can_win_egg_hunt(),
|
||||
FestivalCheck.strawberry_seeds: self.money.can_spend(1000),
|
||||
FestivalCheck.dance: self.relationship.has_hearts(Generic.bachelor, 4),
|
||||
FestivalCheck.dance: self.relationship.has_hearts_with_any_bachelor(4),
|
||||
FestivalCheck.tub_o_flowers: self.money.can_spend(2000),
|
||||
FestivalCheck.rarecrow_5: self.money.can_spend(2500),
|
||||
FestivalCheck.luau_soup: self.can_succeed_luau_soup(),
|
||||
|
@ -457,43 +402,90 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
FestivalCheck.legend_of_the_winter_star: True_(),
|
||||
FestivalCheck.rarecrow_3: True_(),
|
||||
FestivalCheck.all_rarecrows: self.region.can_reach(Region.farm) & self.has_all_rarecrows(),
|
||||
FestivalCheck.calico_race: True_(),
|
||||
FestivalCheck.mummy_mask: True_(),
|
||||
FestivalCheck.calico_statue: True_(),
|
||||
FestivalCheck.emily_outfit_service: True_(),
|
||||
FestivalCheck.earthy_mousse: True_(),
|
||||
FestivalCheck.sweet_bean_cake: True_(),
|
||||
FestivalCheck.skull_cave_casserole: True_(),
|
||||
FestivalCheck.spicy_tacos: True_(),
|
||||
FestivalCheck.mountain_chili: True_(),
|
||||
FestivalCheck.crystal_cake: True_(),
|
||||
FestivalCheck.cave_kebab: True_(),
|
||||
FestivalCheck.hot_log: True_(),
|
||||
FestivalCheck.sour_salad: True_(),
|
||||
FestivalCheck.superfood_cake: True_(),
|
||||
FestivalCheck.warrior_smoothie: True_(),
|
||||
FestivalCheck.rumpled_fruit_skin: True_(),
|
||||
FestivalCheck.calico_pizza: True_(),
|
||||
FestivalCheck.stuffed_mushrooms: True_(),
|
||||
FestivalCheck.elf_quesadilla: True_(),
|
||||
FestivalCheck.nachos_of_the_desert: True_(),
|
||||
FestivalCheck.cloppino: True_(),
|
||||
FestivalCheck.rainforest_shrimp: True_(),
|
||||
FestivalCheck.shrimp_donut: True_(),
|
||||
FestivalCheck.smell_of_the_sea: True_(),
|
||||
FestivalCheck.desert_gumbo: True_(),
|
||||
FestivalCheck.free_cactis: True_(),
|
||||
FestivalCheck.monster_hunt: self.monster.can_kill(Monster.serpent),
|
||||
FestivalCheck.deep_dive: self.region.can_reach(Region.skull_cavern_50),
|
||||
FestivalCheck.treasure_hunt: self.region.can_reach(Region.skull_cavern_25),
|
||||
FestivalCheck.touch_calico_statue: self.region.can_reach(Region.skull_cavern_25),
|
||||
FestivalCheck.real_calico_egg_hunter: self.region.can_reach(Region.skull_cavern_100),
|
||||
FestivalCheck.willy_challenge: self.fishing.can_catch_fish(content.fishes[Fish.scorpion_carp]),
|
||||
FestivalCheck.desert_scholar: True_(),
|
||||
FestivalCheck.squidfest_day_1_copper: self.fishing.can_catch_fish(content.fishes[Fish.squid]),
|
||||
FestivalCheck.squidfest_day_1_iron: self.fishing.can_catch_fish(content.fishes[Fish.squid]) & self.has(Fishing.bait),
|
||||
FestivalCheck.squidfest_day_1_gold: self.fishing.can_catch_fish(content.fishes[Fish.squid]) & self.has(Fishing.deluxe_bait),
|
||||
FestivalCheck.squidfest_day_1_iridium: self.fishing.can_catch_fish(content.fishes[Fish.squid]) &
|
||||
self.fishing.has_specific_bait(content.fishes[Fish.squid]),
|
||||
FestivalCheck.squidfest_day_2_copper: self.fishing.can_catch_fish(content.fishes[Fish.squid]),
|
||||
FestivalCheck.squidfest_day_2_iron: self.fishing.can_catch_fish(content.fishes[Fish.squid]) & self.has(Fishing.bait),
|
||||
FestivalCheck.squidfest_day_2_gold: self.fishing.can_catch_fish(content.fishes[Fish.squid]) & self.has(Fishing.deluxe_bait),
|
||||
FestivalCheck.squidfest_day_2_iridium: self.fishing.can_catch_fish(content.fishes[Fish.squid]) &
|
||||
self.fishing.has_specific_bait(content.fishes[Fish.squid]),
|
||||
})
|
||||
for i in range(1, 11):
|
||||
self.registry.festival_rules[f"{FestivalCheck.trout_derby_reward_pattern}{i}"] = self.fishing.can_catch_fish(content.fishes[Fish.rainbow_trout])
|
||||
|
||||
self.special_order.initialize_rules()
|
||||
self.special_order.update_rules(self.mod.special_order.get_modded_special_orders_rules())
|
||||
|
||||
def can_buy_sapling(self, fruit: str) -> StardewRule:
|
||||
sapling_prices = {Fruit.apple: 4000, Fruit.apricot: 2000, Fruit.cherry: 3400, Fruit.orange: 4000,
|
||||
Fruit.peach: 6000,
|
||||
Fruit.pomegranate: 6000, Fruit.banana: 0, Fruit.mango: 0}
|
||||
received_sapling = self.received(f"{fruit} Sapling")
|
||||
if self.options.cropsanity == Cropsanity.option_disabled:
|
||||
allowed_buy_sapling = True_()
|
||||
else:
|
||||
allowed_buy_sapling = received_sapling
|
||||
can_buy_sapling = self.money.can_spend_at(Region.pierre_store, sapling_prices[fruit])
|
||||
if fruit == Fruit.banana:
|
||||
can_buy_sapling = self.has_island_trader() & self.has(Forageable.dragon_tooth)
|
||||
elif fruit == Fruit.mango:
|
||||
can_buy_sapling = self.has_island_trader() & self.has(Fish.mussel_node)
|
||||
|
||||
return allowed_buy_sapling & can_buy_sapling
|
||||
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)
|
||||
|
||||
def can_smelt(self, item: str) -> StardewRule:
|
||||
return self.has(Machine.furnace) & self.has(item)
|
||||
|
||||
def can_complete_field_office(self) -> StardewRule:
|
||||
@cached_property
|
||||
def can_start_field_office(self) -> StardewRule:
|
||||
field_office = self.region.can_reach(Region.field_office)
|
||||
professor_snail = self.received("Open Professor Snail Cave")
|
||||
tools = self.tool.has_tool(Tool.pickaxe) & self.tool.has_tool(Tool.hoe) & self.tool.has_tool(Tool.scythe)
|
||||
leg_and_snake_skull = self.has_all(Fossil.fossilized_leg, Fossil.snake_skull)
|
||||
ribs_and_spine = self.has_all(Fossil.fossilized_ribs, Fossil.fossilized_spine)
|
||||
skull = self.has(Fossil.fossilized_skull)
|
||||
tail = self.has(Fossil.fossilized_tail)
|
||||
frog = self.has(Fossil.mummified_frog)
|
||||
bat = self.has(Fossil.mummified_bat)
|
||||
snake_vertebrae = self.has(Fossil.snake_vertebrae)
|
||||
return field_office & professor_snail & tools & leg_and_snake_skull & ribs_and_spine & skull & tail & frog & bat & snake_vertebrae
|
||||
return field_office & professor_snail
|
||||
|
||||
def can_complete_large_animal_collection(self) -> StardewRule:
|
||||
fossils = self.has_all(Fossil.fossilized_leg, Fossil.fossilized_ribs, Fossil.fossilized_skull, Fossil.fossilized_spine, Fossil.fossilized_tail)
|
||||
return self.can_start_field_office & fossils
|
||||
|
||||
def can_complete_snake_collection(self) -> StardewRule:
|
||||
fossils = self.has_all(Fossil.snake_skull, Fossil.snake_vertebrae)
|
||||
return self.can_start_field_office & fossils
|
||||
|
||||
def can_complete_frog_collection(self) -> StardewRule:
|
||||
fossils = self.has_all(Fossil.mummified_frog)
|
||||
return self.can_start_field_office & fossils
|
||||
|
||||
def can_complete_bat_collection(self) -> StardewRule:
|
||||
fossils = self.has_all(Fossil.mummified_bat)
|
||||
return self.can_start_field_office & fossils
|
||||
|
||||
def can_complete_field_office(self) -> StardewRule:
|
||||
return self.can_complete_large_animal_collection() & self.can_complete_snake_collection() & \
|
||||
self.can_complete_frog_collection() & self.can_complete_bat_collection()
|
||||
|
||||
def can_finish_grandpa_evaluation(self) -> StardewRule:
|
||||
# https://stardewvalleywiki.com/Grandpa
|
||||
|
@ -511,9 +503,9 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
# Catching every fish not expected
|
||||
# Shipping every item not expected
|
||||
self.relationship.can_get_married() & self.building.has_house(2),
|
||||
self.relationship.has_hearts("5", 8), # 5 Friends
|
||||
self.relationship.has_hearts("10", 8), # 10 friends
|
||||
self.pet.has_hearts(5), # Max Pet
|
||||
self.relationship.has_hearts_with_n(5, 8), # 5 Friends
|
||||
self.relationship.has_hearts_with_n(10, 8), # 10 friends
|
||||
self.pet.has_pet_hearts(5), # Max Pet
|
||||
self.bundle.can_complete_community_center, # Community Center Completion
|
||||
self.bundle.can_complete_community_center, # CC Ceremony first point
|
||||
self.bundle.can_complete_community_center, # CC Ceremony second point
|
||||
|
@ -523,23 +515,20 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
return self.count(12, *rules_worth_a_point)
|
||||
|
||||
def can_win_egg_hunt(self) -> StardewRule:
|
||||
number_of_movement_buffs = self.options.movement_buff_number
|
||||
if self.options.festival_locations == FestivalLocations.option_hard or number_of_movement_buffs < 2:
|
||||
return True_()
|
||||
return self.received(Buff.movement, number_of_movement_buffs // 2)
|
||||
|
||||
def can_succeed_luau_soup(self) -> StardewRule:
|
||||
if self.options.festival_locations != FestivalLocations.option_hard:
|
||||
return True_()
|
||||
eligible_fish = [Fish.blobfish, Fish.crimsonfish, "Ice Pip", Fish.lava_eel, Fish.legend, Fish.angler, Fish.catfish, Fish.glacierfish, Fish.mutant_carp,
|
||||
Fish.spookfish, Fish.stingray, Fish.sturgeon, "Super Cucumber"]
|
||||
fish_rule = self.has_any(*eligible_fish)
|
||||
eligible_kegables = [Fruit.ancient_fruit, Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.melon,
|
||||
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.has_any(*(f for f in eligible_fish if f in self.content.fishes)) # To filter stingray
|
||||
eligible_kegables = (Fruit.ancient_fruit, Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.melon,
|
||||
Fruit.orange, Fruit.peach, Fruit.pineapple, Fruit.pomegranate, Fruit.rhubarb, Fruit.starfruit, Fruit.strawberry,
|
||||
Forageable.cactus_fruit, Fruit.cherry, Fruit.cranberries, Fruit.grape, Forageable.spice_berry, Forageable.wild_plum,
|
||||
Vegetable.hops, Vegetable.wheat]
|
||||
keg_rules = [self.artisan.can_keg(kegable) for kegable in eligible_kegables]
|
||||
aged_rule = self.has(Machine.cask) & Or(*keg_rules)
|
||||
Vegetable.hops, Vegetable.wheat)
|
||||
keg_rules = [self.artisan.can_keg(kegable) for kegable in eligible_kegables if kegable in self.content.game_items]
|
||||
aged_rule = self.has(Machine.cask) & self.logic.or_(*keg_rules)
|
||||
# There are a few other valid items, but I don't feel like coding them all
|
||||
return fish_rule | aged_rule
|
||||
|
||||
|
@ -553,11 +542,17 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
fish_rule = self.skill.can_fish(difficulty=50)
|
||||
forage_rule = self.region.can_reach_any((Region.forest, Region.backwoods)) # Hazelnut always available since the grange display is in fall
|
||||
mineral_rule = self.action.can_open_geode(Generic.any) # More than half the minerals are good enough
|
||||
good_fruits = [Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.orange, Fruit.peach, Fruit.pomegranate,
|
||||
Fruit.strawberry, Fruit.melon, Fruit.rhubarb, Fruit.pineapple, Fruit.ancient_fruit, Fruit.starfruit, ]
|
||||
good_fruits = (fruit
|
||||
for fruit in
|
||||
(Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.orange, Fruit.peach, Fruit.pomegranate,
|
||||
Fruit.strawberry, Fruit.melon, Fruit.rhubarb, Fruit.pineapple, Fruit.ancient_fruit, Fruit.starfruit)
|
||||
if fruit in self.content.game_items)
|
||||
fruit_rule = self.has_any(*good_fruits)
|
||||
good_vegetables = [Vegetable.amaranth, Vegetable.artichoke, Vegetable.beet, Vegetable.cauliflower, Forageable.fiddlehead_fern, Vegetable.kale,
|
||||
Vegetable.radish, Vegetable.taro_root, Vegetable.yam, Vegetable.red_cabbage, Vegetable.pumpkin]
|
||||
good_vegetables = (vegeteable
|
||||
for vegeteable in
|
||||
(Vegetable.amaranth, Vegetable.artichoke, Vegetable.beet, Vegetable.cauliflower, Forageable.fiddlehead_fern, Vegetable.kale,
|
||||
Vegetable.radish, Vegetable.taro_root, Vegetable.yam, Vegetable.red_cabbage, Vegetable.pumpkin)
|
||||
if vegeteable in self.content.game_items)
|
||||
vegetable_rule = self.has_any(*good_vegetables)
|
||||
|
||||
return animal_rule & artisan_rule & cooking_rule & fish_rule & \
|
||||
|
@ -576,6 +571,44 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
return False_()
|
||||
if number <= 0:
|
||||
return True_()
|
||||
|
||||
if self.options.walnutsanity == Walnutsanity.preset_none:
|
||||
return self.can_get_walnuts(number)
|
||||
if self.options.walnutsanity == Walnutsanity.preset_all:
|
||||
return self.has_received_walnuts(number)
|
||||
puzzle_walnuts = 61
|
||||
bush_walnuts = 25
|
||||
dig_walnuts = 18
|
||||
repeatable_walnuts = 33
|
||||
total_walnuts = puzzle_walnuts + bush_walnuts + dig_walnuts + repeatable_walnuts
|
||||
walnuts_to_receive = 0
|
||||
walnuts_to_collect = number
|
||||
if OptionName.walnutsanity_puzzles in self.options.walnutsanity:
|
||||
puzzle_walnut_rate = puzzle_walnuts / total_walnuts
|
||||
puzzle_walnuts_required = round(puzzle_walnut_rate * number)
|
||||
walnuts_to_receive += puzzle_walnuts_required
|
||||
walnuts_to_collect -= puzzle_walnuts_required
|
||||
if OptionName.walnutsanity_bushes in self.options.walnutsanity:
|
||||
bush_walnuts_rate = bush_walnuts / total_walnuts
|
||||
bush_walnuts_required = round(bush_walnuts_rate * number)
|
||||
walnuts_to_receive += bush_walnuts_required
|
||||
walnuts_to_collect -= bush_walnuts_required
|
||||
if OptionName.walnutsanity_dig_spots in self.options.walnutsanity:
|
||||
dig_walnuts_rate = dig_walnuts / total_walnuts
|
||||
dig_walnuts_required = round(dig_walnuts_rate * number)
|
||||
walnuts_to_receive += dig_walnuts_required
|
||||
walnuts_to_collect -= dig_walnuts_required
|
||||
if OptionName.walnutsanity_repeatables in self.options.walnutsanity:
|
||||
repeatable_walnuts_rate = repeatable_walnuts / total_walnuts
|
||||
repeatable_walnuts_required = round(repeatable_walnuts_rate * number)
|
||||
walnuts_to_receive += repeatable_walnuts_required
|
||||
walnuts_to_collect -= repeatable_walnuts_required
|
||||
return self.has_received_walnuts(walnuts_to_receive) & self.can_get_walnuts(walnuts_to_collect)
|
||||
|
||||
def has_received_walnuts(self, number: int) -> StardewRule:
|
||||
return self.received(Event.received_walnuts, number)
|
||||
|
||||
def can_get_walnuts(self, number: int) -> StardewRule:
|
||||
# https://stardewcommunitywiki.com/Golden_Walnut#Walnut_Locations
|
||||
reach_south = self.region.can_reach(Region.island_south)
|
||||
reach_north = self.region.can_reach(Region.island_north)
|
||||
|
@ -584,28 +617,28 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
reach_southeast = self.region.can_reach(Region.island_south_east)
|
||||
reach_field_office = self.region.can_reach(Region.field_office)
|
||||
reach_pirate_cove = self.region.can_reach(Region.pirate_cove)
|
||||
reach_outside_areas = And(reach_south, reach_north, reach_west, reach_hut)
|
||||
reach_outside_areas = self.logic.and_(reach_south, reach_north, reach_west, reach_hut)
|
||||
reach_volcano_regions = [self.region.can_reach(Region.volcano),
|
||||
self.region.can_reach(Region.volcano_secret_beach),
|
||||
self.region.can_reach(Region.volcano_floor_5),
|
||||
self.region.can_reach(Region.volcano_floor_10)]
|
||||
reach_volcano = Or(*reach_volcano_regions)
|
||||
reach_all_volcano = And(*reach_volcano_regions)
|
||||
reach_volcano = self.logic.or_(*reach_volcano_regions)
|
||||
reach_all_volcano = self.logic.and_(*reach_volcano_regions)
|
||||
reach_walnut_regions = [reach_south, reach_north, reach_west, reach_volcano, reach_field_office]
|
||||
reach_caves = And(self.region.can_reach(Region.qi_walnut_room), self.region.can_reach(Region.dig_site),
|
||||
reach_caves = self.logic.and_(self.region.can_reach(Region.qi_walnut_room), self.region.can_reach(Region.dig_site),
|
||||
self.region.can_reach(Region.gourmand_frog_cave),
|
||||
self.region.can_reach(Region.colored_crystals_cave),
|
||||
self.region.can_reach(Region.shipwreck), self.received(APWeapon.slingshot))
|
||||
reach_entire_island = And(reach_outside_areas, reach_all_volcano,
|
||||
self.region.can_reach(Region.shipwreck), self.combat.has_slingshot)
|
||||
reach_entire_island = self.logic.and_(reach_outside_areas, reach_all_volcano,
|
||||
reach_caves, reach_southeast, reach_field_office, reach_pirate_cove)
|
||||
if number <= 5:
|
||||
return Or(reach_south, reach_north, reach_west, reach_volcano)
|
||||
return self.logic.or_(reach_south, reach_north, reach_west, reach_volcano)
|
||||
if number <= 10:
|
||||
return self.count(2, *reach_walnut_regions)
|
||||
if number <= 15:
|
||||
return self.count(3, *reach_walnut_regions)
|
||||
if number <= 20:
|
||||
return And(*reach_walnut_regions)
|
||||
return self.logic.and_(*reach_walnut_regions)
|
||||
if number <= 50:
|
||||
return reach_entire_island
|
||||
gems = (Mineral.amethyst, Mineral.aquamarine, Mineral.emerald, Mineral.ruby, Mineral.topaz)
|
||||
|
@ -621,20 +654,22 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
number_of_stardrops_to_receive += 1 # Museum Stardrop
|
||||
number_of_stardrops_to_receive += 1 # Krobus Stardrop
|
||||
|
||||
if self.options.fishsanity == Fishsanity.option_none: # Master Angler Stardrop
|
||||
other_rules.append(self.fishing.can_catch_every_fish())
|
||||
else:
|
||||
# Master Angler Stardrop
|
||||
if self.content.features.fishsanity.is_enabled:
|
||||
number_of_stardrops_to_receive += 1
|
||||
else:
|
||||
other_rules.append(self.fishing.can_catch_every_fish())
|
||||
|
||||
if self.options.festival_locations == FestivalLocations.option_disabled: # Fair Stardrop
|
||||
other_rules.append(self.season.has(Season.fall))
|
||||
else:
|
||||
number_of_stardrops_to_receive += 1
|
||||
|
||||
if self.options.friendsanity == Friendsanity.option_none: # Spouse Stardrop
|
||||
other_rules.append(self.relationship.has_hearts(Generic.bachelor, 13))
|
||||
else:
|
||||
# Spouse Stardrop
|
||||
if self.content.features.friendsanity.is_enabled:
|
||||
number_of_stardrops_to_receive += 1
|
||||
else:
|
||||
other_rules.append(self.relationship.has_hearts_with_any_bachelor(13))
|
||||
|
||||
if ModNames.deepwoods in self.options.mods: # Petting the Unicorn
|
||||
number_of_stardrops_to_receive += 1
|
||||
|
@ -642,18 +677,13 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
if not other_rules:
|
||||
return self.received("Stardrop", number_of_stardrops_to_receive)
|
||||
|
||||
return self.received("Stardrop", number_of_stardrops_to_receive) & And(*other_rules)
|
||||
|
||||
def has_prismatic_jelly_reward_access(self) -> StardewRule:
|
||||
if self.options.special_order_locations == SpecialOrderLocations.option_disabled:
|
||||
return self.special_order.can_complete_special_order("Prismatic Jelly")
|
||||
return self.received("Monster Musk Recipe")
|
||||
return self.received("Stardrop", number_of_stardrops_to_receive) & self.logic.and_(*other_rules)
|
||||
|
||||
def has_all_rarecrows(self) -> StardewRule:
|
||||
rules = []
|
||||
for rarecrow_number in range(1, 9):
|
||||
rules.append(self.received(f"Rarecrow #{rarecrow_number}"))
|
||||
return And(*rules)
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def has_abandoned_jojamart(self) -> StardewRule:
|
||||
return self.received(CommunityUpgrade.movie_theater, 1)
|
||||
|
@ -664,11 +694,5 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogi
|
|||
def can_use_obelisk(self, obelisk: str) -> StardewRule:
|
||||
return self.region.can_reach(Region.farm) & self.received(obelisk)
|
||||
|
||||
def has_fruit_bats(self) -> StardewRule:
|
||||
return self.region.can_reach(Region.farm_cave) & self.received(CommunityUpgrade.fruit_bats)
|
||||
|
||||
def has_mushroom_cave(self) -> StardewRule:
|
||||
return self.region.can_reach(Region.farm_cave) & self.received(CommunityUpgrade.mushroom_boxes)
|
||||
|
||||
def can_fish_pond(self, fish: str) -> StardewRule:
|
||||
return self.building.has_building(Building.fish_pond) & self.has(fish)
|
||||
|
|
|
@ -56,3 +56,20 @@ dependencies. Vanilla would always be first, then anything that depends only on
|
|||
4. In `set_rules`, the rules are applied to the AP entrances and locations. Each content pack have to apply the proper rules for their entrances and locations.
|
||||
- (idea) To begin this step, sphere 0 could be simplified instantly as sphere 0 regions and items are already known.
|
||||
5. Nothing to do in `generate_basic`.
|
||||
|
||||
## Item Sources
|
||||
|
||||
Instead of containing rules directly, items would contain sources that would then be transformed into rules. Using a single dispatch mechanism, the sources will
|
||||
be associated to their actual logic.
|
||||
|
||||
This system is extensible and easily maintainable in the ways that it decouple the rule and the actual items. Any "type" of item could be used with any "type"
|
||||
of source (Monster drop and fish can have foraging sources).
|
||||
|
||||
- Mods requiring special rules can remove sources from vanilla content or wrap them to add their own logic (Magic add sources for some items), or change the
|
||||
rules for monster drop sources.
|
||||
- (idea) A certain difficulty level (or maybe tags) could be added to the source, to enable or disable them given settings chosen by the player. Someone with a
|
||||
high grinding tolerance can enable "hard" or "grindy" sources. Some source that are pushed back in further spheres can be replaced by less forgiving sources
|
||||
if easy logic is disabled. For instance, anything that requires money could be accessible as soon as you can sell something to someone (even wood).
|
||||
|
||||
Items are classified by their source. An item with a fishing or a crab pot source is considered a fish, an item dropping from a monster is a monster drop. An
|
||||
item with a foraging source is a forageable. Items can fit in multiple categories.
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from ..strings.ap_names import event_names
|
||||
from ..strings.metal_names import MetalBar, Ore
|
||||
from ..strings.region_names import Region
|
||||
|
||||
all_events = event_names.all_events.copy()
|
||||
all_logic_events = list()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LogicEvent:
|
||||
name: str
|
||||
region: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LogicItemEvent(LogicEvent):
|
||||
item: str
|
||||
|
||||
def __init__(self, item: str, region: str):
|
||||
super().__init__(f"{item} (Logic event)", region)
|
||||
super().__setattr__("item", item)
|
||||
|
||||
|
||||
def register_item_event(item: str, region: str = Region.farm):
|
||||
event = LogicItemEvent(item, region)
|
||||
all_logic_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):
|
||||
register_item_event(i)
|
|
@ -3,13 +3,15 @@ from typing import Union
|
|||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .combat_logic import CombatLogicMixin
|
||||
from .cooking_logic import CookingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from .. import options
|
||||
from ..options import ToolProgression
|
||||
from ..stardew_rule import StardewRule, And, True_
|
||||
from ..stardew_rule import StardewRule, True_
|
||||
from ..strings.performance_names import Performance
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.skill_names import Skill
|
||||
|
@ -22,7 +24,8 @@ class MineLogicMixin(BaseLogicMixin):
|
|||
self.mine = MineLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class MineLogic(BaseLogic[Union[MineLogicMixin, RegionLogicMixin, ReceivedLogicMixin, CombatLogicMixin, ToolLogicMixin, SkillLogicMixin]]):
|
||||
class MineLogic(BaseLogic[Union[HasLogicMixin, MineLogicMixin, RegionLogicMixin, ReceivedLogicMixin, CombatLogicMixin, ToolLogicMixin,
|
||||
SkillLogicMixin, CookingLogicMixin]]):
|
||||
# Regions
|
||||
def can_mine_in_the_mines_floor_1_40(self) -> StardewRule:
|
||||
return self.logic.region.can_reach(Region.mines_floor_5)
|
||||
|
@ -57,11 +60,13 @@ class MineLogic(BaseLogic[Union[MineLogicMixin, RegionLogicMixin, ReceivedLogicM
|
|||
rules.append(weapon_rule)
|
||||
if self.options.tool_progression & ToolProgression.option_progressive:
|
||||
rules.append(self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier]))
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive:
|
||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
||||
skill_tier = min(10, max(0, tier * 2))
|
||||
rules.append(self.logic.skill.has_level(Skill.combat, skill_tier))
|
||||
rules.append(self.logic.skill.has_level(Skill.mining, skill_tier))
|
||||
return And(*rules)
|
||||
if tier >= 4:
|
||||
rules.append(self.logic.cooking.can_cook())
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
@cache_self1
|
||||
def has_mine_elevator_to_floor(self, floor: int) -> StardewRule:
|
||||
|
@ -79,8 +84,8 @@ class MineLogic(BaseLogic[Union[MineLogicMixin, RegionLogicMixin, ReceivedLogicM
|
|||
rules.append(weapon_rule)
|
||||
if self.options.tool_progression & ToolProgression.option_progressive:
|
||||
rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2))))
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive:
|
||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
||||
skill_tier = min(10, max(0, tier * 2 + 6))
|
||||
rules.extend({self.logic.skill.has_level(Skill.combat, skill_tier),
|
||||
self.logic.skill.has_level(Skill.mining, skill_tier)})
|
||||
return And(*rules)
|
||||
return self.logic.and_(*rules)
|
||||
|
|
|
@ -2,16 +2,18 @@ from typing import Union
|
|||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .buff_logic import BuffLogicMixin
|
||||
from .grind_logic import GrindLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from ..data.shop import ShopSource
|
||||
from ..options import SpecialOrderLocations
|
||||
from ..stardew_rule import StardewRule, True_, HasProgressionPercent, False_
|
||||
from ..stardew_rule import StardewRule, True_, HasProgressionPercent, False_, true_
|
||||
from ..strings.ap_names.event_names import Event
|
||||
from ..strings.currency_names import Currency
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.region_names import Region, LogicRegion
|
||||
|
||||
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")
|
||||
|
@ -23,7 +25,8 @@ class MoneyLogicMixin(BaseLogicMixin):
|
|||
self.money = MoneyLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class MoneyLogic(BaseLogic[Union[RegionLogicMixin, MoneyLogicMixin, TimeLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin, BuffLogicMixin]]):
|
||||
class MoneyLogic(BaseLogic[Union[RegionLogicMixin, MoneyLogicMixin, TimeLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin, SeasonLogicMixin,
|
||||
GrindLogicMixin]]):
|
||||
|
||||
@cache_self1
|
||||
def can_have_earned_total(self, amount: int) -> StardewRule:
|
||||
|
@ -31,7 +34,7 @@ class MoneyLogic(BaseLogic[Union[RegionLogicMixin, MoneyLogicMixin, TimeLogicMix
|
|||
return 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, Region.fishing))
|
||||
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.received(Event.can_ship_items)
|
||||
|
@ -64,6 +67,20 @@ class MoneyLogic(BaseLogic[Union[RegionLogicMixin, MoneyLogicMixin, TimeLogicMix
|
|||
def can_spend_at(self, region: str, amount: int) -> StardewRule:
|
||||
return self.logic.region.can_reach(region) & self.logic.money.can_spend(amount)
|
||||
|
||||
@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_
|
||||
|
||||
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))
|
||||
|
||||
region_rule = self.logic.region.can_reach(source.shop_region)
|
||||
|
||||
return self.logic.and_(season_rule, money_rule, *item_rules, region_rule)
|
||||
|
||||
# Should be cached
|
||||
def can_trade(self, currency: str, amount: int) -> StardewRule:
|
||||
if amount == 0:
|
||||
|
@ -71,11 +88,11 @@ class MoneyLogic(BaseLogic[Union[RegionLogicMixin, MoneyLogicMixin, TimeLogicMix
|
|||
if currency == Currency.money:
|
||||
return self.can_spend(amount)
|
||||
if currency == Currency.star_token:
|
||||
return self.logic.region.can_reach(Region.fair)
|
||||
return self.logic.region.can_reach(LogicRegion.fair)
|
||||
if currency == Currency.qi_coin:
|
||||
return self.logic.region.can_reach(Region.casino) & self.logic.buff.has_max_luck()
|
||||
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.option_board_qi:
|
||||
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
|
||||
|
@ -84,7 +101,7 @@ class MoneyLogic(BaseLogic[Union[RegionLogicMixin, MoneyLogicMixin, TimeLogicMix
|
|||
if currency == Currency.golden_walnut:
|
||||
return self.can_spend_walnut(amount)
|
||||
|
||||
return self.logic.has(currency) & self.logic.time.has_lived_months(amount)
|
||||
return self.logic.has(currency) & self.logic.grind.can_grind_item(amount)
|
||||
|
||||
# Should be cached
|
||||
def can_trade_at(self, region: str, currency: str, amount: int) -> StardewRule:
|
||||
|
|
|
@ -4,11 +4,13 @@ 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 ..data import monster_data
|
||||
from ..stardew_rule import StardewRule, Or, And
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.generic_names import Generic
|
||||
from ..strings.region_names import Region
|
||||
|
||||
|
||||
|
@ -18,7 +20,7 @@ class MonsterLogicMixin(BaseLogicMixin):
|
|||
self.monster = MonsterLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class MonsterLogic(BaseLogic[Union[MonsterLogicMixin, RegionLogicMixin, CombatLogicMixin, TimeLogicMixin]]):
|
||||
class MonsterLogic(BaseLogic[Union[HasLogicMixin, MonsterLogicMixin, RegionLogicMixin, CombatLogicMixin, TimeLogicMixin]]):
|
||||
|
||||
@cached_property
|
||||
def all_monsters_by_name(self):
|
||||
|
@ -29,13 +31,18 @@ class MonsterLogic(BaseLogic[Union[MonsterLogicMixin, RegionLogicMixin, CombatLo
|
|||
return monster_data.all_monsters_by_category_given_mods(self.options.mods.value)
|
||||
|
||||
def can_kill(self, monster: Union[str, monster_data.StardewMonster], amount_tier: int = 0) -> StardewRule:
|
||||
if isinstance(monster, str):
|
||||
monster = self.all_monsters_by_name[monster]
|
||||
region_rule = self.logic.region.can_reach_any(monster.locations)
|
||||
combat_rule = self.logic.combat.can_fight_at_level(monster.difficulty)
|
||||
if amount_tier <= 0:
|
||||
amount_tier = 0
|
||||
time_rule = self.logic.time.has_lived_months(amount_tier)
|
||||
|
||||
if isinstance(monster, str):
|
||||
if monster == Generic.any:
|
||||
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)
|
||||
combat_rule = self.logic.combat.can_fight_at_level(monster.difficulty)
|
||||
|
||||
return region_rule & combat_rule & time_rule
|
||||
|
||||
@cache_self1
|
||||
|
@ -48,13 +55,11 @@ class MonsterLogic(BaseLogic[Union[MonsterLogicMixin, RegionLogicMixin, CombatLo
|
|||
|
||||
# Should be cached
|
||||
def can_kill_any(self, monsters: (Iterable[monster_data.StardewMonster], Hashable), amount_tier: int = 0) -> StardewRule:
|
||||
rules = [self.logic.monster.can_kill(monster, amount_tier) for monster in monsters]
|
||||
return Or(*rules)
|
||||
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:
|
||||
rules = [self.logic.monster.can_kill(monster, amount_tier) for monster in monsters]
|
||||
return And(*rules)
|
||||
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]
|
||||
|
@ -66,4 +71,4 @@ class MonsterLogic(BaseLogic[Union[MonsterLogicMixin, RegionLogicMixin, CombatLo
|
|||
continue
|
||||
rules.append(self.logic.monster.can_kill_any(self.all_monsters_by_category[category]))
|
||||
|
||||
return And(*rules)
|
||||
return self.logic.and_(*rules)
|
||||
|
|
|
@ -6,10 +6,14 @@ from .base_logic import BaseLogic, BaseLogicMixin
|
|||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from .. import options
|
||||
from ..data.museum_data import MuseumItem, all_museum_items, all_museum_artifacts, all_museum_minerals
|
||||
from ..stardew_rule import StardewRule, And, False_
|
||||
from ..stardew_rule import StardewRule, False_
|
||||
from ..strings.metal_names import Mineral
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.tool_names import Tool, ToolMaterial
|
||||
|
||||
|
||||
class MuseumLogicMixin(BaseLogicMixin):
|
||||
|
@ -18,7 +22,7 @@ class MuseumLogicMixin(BaseLogicMixin):
|
|||
self.museum = MuseumLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class MuseumLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, ActionLogicMixin, MuseumLogicMixin]]):
|
||||
class MuseumLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, TimeLogicMixin, RegionLogicMixin, ActionLogicMixin, ToolLogicMixin, MuseumLogicMixin]]):
|
||||
|
||||
def can_donate_museum_items(self, number: int) -> StardewRule:
|
||||
return self.logic.region.can_reach(Region.museum) & self.logic.museum.can_find_museum_items(number)
|
||||
|
@ -33,15 +37,16 @@ class MuseumLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, RegionLogic
|
|||
else:
|
||||
region_rule = False_()
|
||||
if item.geodes:
|
||||
geodes_rule = And(*(self.logic.action.can_open_geode(geode) for geode in item.geodes))
|
||||
geodes_rule = self.logic.and_(*(self.logic.action.can_open_geode(geode) for geode in item.geodes))
|
||||
else:
|
||||
geodes_rule = False_()
|
||||
# monster_rule = self.can_farm_monster(item.monsters)
|
||||
# extra_rule = True_()
|
||||
time_needed_to_grind = (20 - item.difficulty) / 2
|
||||
time_rule = self.logic.time.has_lived_months(time_needed_to_grind)
|
||||
pan_rule = False_()
|
||||
if item.item_name == "Earth Crystal" or item.item_name == "Fire Quartz" or item.item_name == "Frozen Tear":
|
||||
pan_rule = self.logic.action.can_pan()
|
||||
return pan_rule | region_rule | geodes_rule # & monster_rule & extra_rule
|
||||
if item.item_name == Mineral.earth_crystal or item.item_name == Mineral.fire_quartz or item.item_name == Mineral.frozen_tear:
|
||||
pan_rule = self.logic.tool.has_tool(Tool.pan, ToolMaterial.iridium)
|
||||
return (pan_rule | region_rule | geodes_rule) & time_rule # & monster_rule & extra_rule
|
||||
|
||||
def can_find_museum_artifacts(self, number: int) -> StardewRule:
|
||||
rules = []
|
||||
|
@ -74,7 +79,7 @@ class MuseumLogic(BaseLogic[Union[ReceivedLogicMixin, HasLogicMixin, RegionLogic
|
|||
|
||||
for donation in all_museum_items:
|
||||
rules.append(self.logic.museum.can_find_museum_item(donation))
|
||||
return And(*rules) & self.logic.region.can_reach(Region.museum)
|
||||
return self.logic.and_(*rules) & self.logic.region.can_reach(Region.museum)
|
||||
|
||||
def can_donate(self, item: str) -> StardewRule:
|
||||
return self.logic.has(item) & self.logic.region.can_reach(Region.museum)
|
||||
|
|
|
@ -6,11 +6,9 @@ from .received_logic import ReceivedLogicMixin
|
|||
from .region_logic import RegionLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..data.villagers_data import Villager
|
||||
from ..options import Friendsanity
|
||||
from ..content.feature.friendsanity import pet_heart_item_name
|
||||
from ..stardew_rule import StardewRule, True_
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.villager_names import NPC
|
||||
|
||||
|
||||
class PetLogicMixin(BaseLogicMixin):
|
||||
|
@ -20,21 +18,25 @@ class PetLogicMixin(BaseLogicMixin):
|
|||
|
||||
|
||||
class PetLogic(BaseLogic[Union[RegionLogicMixin, ReceivedLogicMixin, TimeLogicMixin, ToolLogicMixin]]):
|
||||
def has_hearts(self, hearts: int = 1) -> StardewRule:
|
||||
if hearts <= 0:
|
||||
def has_pet_hearts(self, hearts: int = 1) -> StardewRule:
|
||||
assert hearts >= 0, "You can't have negative hearts with a pet."
|
||||
if hearts == 0:
|
||||
return True_()
|
||||
if self.options.friendsanity == Friendsanity.option_none or self.options.friendsanity == Friendsanity.option_bachelors:
|
||||
return self.can_befriend_pet(hearts)
|
||||
return self.received_hearts(NPC.pet, hearts)
|
||||
|
||||
def received_hearts(self, npc: Union[str, Villager], hearts: int) -> StardewRule:
|
||||
if isinstance(npc, Villager):
|
||||
return self.received_hearts(npc.name, hearts)
|
||||
return self.logic.received(self.heart(npc), math.ceil(hearts / self.options.friendsanity_heart_size))
|
||||
if self.content.features.friendsanity.is_pet_randomized:
|
||||
return self.received_pet_hearts(hearts)
|
||||
|
||||
return self.can_befriend_pet(hearts)
|
||||
|
||||
def received_pet_hearts(self, hearts: int) -> StardewRule:
|
||||
return self.logic.received(pet_heart_item_name,
|
||||
math.ceil(hearts / self.content.features.friendsanity.heart_size))
|
||||
|
||||
def can_befriend_pet(self, hearts: int) -> StardewRule:
|
||||
if hearts <= 0:
|
||||
assert hearts >= 0, "You can't have negative hearts with a pet."
|
||||
if hearts == 0:
|
||||
return True_()
|
||||
|
||||
points = hearts * 200
|
||||
points_per_month = 12 * 14
|
||||
points_per_water_month = 18 * 14
|
||||
|
@ -43,8 +45,3 @@ class PetLogic(BaseLogic[Union[RegionLogicMixin, ReceivedLogicMixin, TimeLogicMi
|
|||
time_without_water_rule = self.logic.time.has_lived_months(points // points_per_month)
|
||||
time_rule = time_with_water_rule | time_without_water_rule
|
||||
return farm_rule & time_rule
|
||||
|
||||
def heart(self, npc: Union[str, Villager]) -> str:
|
||||
if isinstance(npc, str):
|
||||
return f"{npc} <3"
|
||||
return self.heart(npc.name)
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
from typing import Union
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .farming_logic import FarmingLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from ..stardew_rule import StardewRule, True_, False_
|
||||
from ..strings.quality_names import CropQuality
|
||||
|
||||
|
||||
class QualityLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.quality = QualityLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class QualityLogic(BaseLogic[Union[SkillLogicMixin, FarmingLogicMixin]]):
|
||||
|
||||
@cache_self1
|
||||
def can_grow_crop_quality(self, quality: str) -> StardewRule:
|
||||
if quality == CropQuality.basic:
|
||||
return True_()
|
||||
if quality == CropQuality.silver:
|
||||
return self.logic.skill.has_farming_level(5) | (self.logic.farming.has_fertilizer(1) & self.logic.skill.has_farming_level(2)) | (
|
||||
self.logic.farming.has_fertilizer(2) & self.logic.skill.has_farming_level(1)) | self.logic.farming.has_fertilizer(3)
|
||||
if quality == CropQuality.gold:
|
||||
return self.logic.skill.has_farming_level(10) | (
|
||||
self.logic.farming.has_fertilizer(1) & self.logic.skill.has_farming_level(5)) | (
|
||||
self.logic.farming.has_fertilizer(2) & self.logic.skill.has_farming_level(3)) | (
|
||||
self.logic.farming.has_fertilizer(3) & self.logic.skill.has_farming_level(2))
|
||||
if quality == CropQuality.iridium:
|
||||
return self.logic.farming.has_fertilizer(3) & self.logic.skill.has_farming_level(4)
|
||||
return False_()
|
|
@ -17,6 +17,7 @@ from .time_logic import TimeLogicMixin
|
|||
from .tool_logic import ToolLogicMixin
|
||||
from .wallet_logic import WalletLogicMixin
|
||||
from ..stardew_rule import StardewRule, Has, True_
|
||||
from ..strings.ap_names.community_upgrade_names import CommunityUpgrade
|
||||
from ..strings.artisan_good_names import ArtisanGood
|
||||
from ..strings.building_names import Building
|
||||
from ..strings.craftable_names import Craftable
|
||||
|
@ -43,7 +44,8 @@ class QuestLogicMixin(BaseLogicMixin):
|
|||
|
||||
|
||||
class QuestLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, MoneyLogicMixin, MineLogicMixin, RegionLogicMixin, RelationshipLogicMixin, ToolLogicMixin,
|
||||
FishingLogicMixin, CookingLogicMixin, CombatLogicMixin, SeasonLogicMixin, SkillLogicMixin, WalletLogicMixin, QuestLogicMixin, BuildingLogicMixin, TimeLogicMixin]]):
|
||||
FishingLogicMixin, CookingLogicMixin, CombatLogicMixin, SeasonLogicMixin, SkillLogicMixin, WalletLogicMixin, QuestLogicMixin,
|
||||
BuildingLogicMixin, TimeLogicMixin]]):
|
||||
|
||||
def initialize_rules(self):
|
||||
self.update_rules({
|
||||
|
@ -52,6 +54,7 @@ FishingLogicMixin, CookingLogicMixin, CombatLogicMixin, SeasonLogicMixin, SkillL
|
|||
Quest.getting_started: self.logic.has(Vegetable.parsnip),
|
||||
Quest.to_the_beach: self.logic.region.can_reach(Region.beach),
|
||||
Quest.raising_animals: self.logic.quest.can_complete_quest(Quest.getting_started) & self.logic.building.has_building(Building.coop),
|
||||
Quest.feeding_animals: self.logic.quest.can_complete_quest(Quest.getting_started) & self.logic.building.has_building(Building.silo),
|
||||
Quest.advancement: self.logic.quest.can_complete_quest(Quest.getting_started) & self.logic.has(Craftable.scarecrow),
|
||||
Quest.archaeology: self.logic.tool.has_tool(Tool.hoe) | self.logic.mine.can_mine_in_the_mines_floor_1_40() | self.logic.skill.can_fish(),
|
||||
Quest.rat_problem: self.logic.region.can_reach_all((Region.town, Region.community_center)),
|
||||
|
@ -63,7 +66,8 @@ FishingLogicMixin, CookingLogicMixin, CombatLogicMixin, SeasonLogicMixin, SkillL
|
|||
Quest.jodis_request: self.logic.season.has(Season.spring) & self.logic.has(Vegetable.cauliflower) & self.logic.relationship.can_meet(NPC.jodi),
|
||||
Quest.mayors_shorts: self.logic.season.has(Season.summer) & self.logic.relationship.has_hearts(NPC.marnie, 2) &
|
||||
self.logic.relationship.can_meet(NPC.lewis),
|
||||
Quest.blackberry_basket: self.logic.season.has(Season.fall) & self.logic.relationship.can_meet(NPC.linus),
|
||||
Quest.blackberry_basket: self.logic.season.has(Season.fall) & self.logic.relationship.can_meet(NPC.linus) & self.logic.region.can_reach(
|
||||
Region.tunnel_entrance),
|
||||
Quest.marnies_request: self.logic.relationship.has_hearts(NPC.marnie, 3) & self.logic.has(Forageable.cave_carrot),
|
||||
Quest.pam_is_thirsty: self.logic.season.has(Season.summer) & self.logic.has(ArtisanGood.pale_ale) & self.logic.relationship.can_meet(NPC.pam),
|
||||
Quest.a_dark_reagent: self.logic.season.has(Season.winter) & self.logic.has(Loot.void_essence) & self.logic.relationship.can_meet(NPC.wizard),
|
||||
|
@ -104,13 +108,14 @@ FishingLogicMixin, CookingLogicMixin, CombatLogicMixin, SeasonLogicMixin, SkillL
|
|||
Quest.the_pirates_wife: self.logic.relationship.can_meet(NPC.kent) & self.logic.relationship.can_meet(NPC.gus) &
|
||||
self.logic.relationship.can_meet(NPC.sandy) & self.logic.relationship.can_meet(NPC.george) &
|
||||
self.logic.relationship.can_meet(NPC.wizard) & self.logic.relationship.can_meet(NPC.willy),
|
||||
Quest.giant_stump: self.logic.has(Material.hardwood)
|
||||
})
|
||||
|
||||
def update_rules(self, new_rules: Dict[str, StardewRule]):
|
||||
self.registry.quest_rules.update(new_rules)
|
||||
|
||||
def can_complete_quest(self, quest: str) -> StardewRule:
|
||||
return Has(quest, self.registry.quest_rules)
|
||||
return Has(quest, self.registry.quest_rules, "quest")
|
||||
|
||||
def has_club_card(self) -> StardewRule:
|
||||
if self.options.quest_locations < 0:
|
||||
|
@ -126,3 +131,12 @@ FishingLogicMixin, CookingLogicMixin, CombatLogicMixin, SeasonLogicMixin, SkillL
|
|||
if self.options.quest_locations < 0:
|
||||
return self.logic.quest.can_complete_quest(Quest.dark_talisman)
|
||||
return self.logic.received(Wallet.dark_talisman)
|
||||
|
||||
def has_raccoon_shop(self) -> StardewRule:
|
||||
if self.options.quest_locations < 0:
|
||||
return self.logic.received(CommunityUpgrade.raccoon, 2) & self.logic.quest.can_complete_quest(Quest.giant_stump)
|
||||
|
||||
# 1 - Break the tree
|
||||
# 2 - Build the house, which summons the bundle racoon. This one is done manually if quests are turned off
|
||||
# 3 - Raccoon's wife opens the shop
|
||||
return self.logic.received(CommunityUpgrade.raccoon, 3)
|
||||
|
|
|
@ -1,26 +1,32 @@
|
|||
from typing import Optional
|
||||
|
||||
from BaseClasses import ItemClassification
|
||||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from ..stardew_rule import StardewRule, Received, And, Or, TotalReceived
|
||||
from .logic_event import all_events
|
||||
from ..items import item_table
|
||||
from ..stardew_rule import StardewRule, Received, TotalReceived
|
||||
|
||||
|
||||
class ReceivedLogicMixin(BaseLogic[HasLogicMixin], BaseLogicMixin):
|
||||
# Should be cached
|
||||
def received(self, item: str, count: Optional[int] = 1) -> StardewRule:
|
||||
assert count >= 0, "Can't receive a negative amount of item."
|
||||
|
||||
if item in all_events:
|
||||
return Received(item, self.player, count, event=True)
|
||||
|
||||
assert item_table[item].classification & ItemClassification.progression, f"Item [{item_table[item].name}] has to be progression to be used in logic"
|
||||
return Received(item, self.player, count)
|
||||
|
||||
def received_all(self, *items: str):
|
||||
assert items, "Can't receive all of no items."
|
||||
|
||||
return And(*(self.received(item) for item in items))
|
||||
return self.logic.and_(*(self.received(item) for item in items))
|
||||
|
||||
def received_any(self, *items: str):
|
||||
assert items, "Can't receive any of no items."
|
||||
|
||||
return Or(*(self.received(item) for item in items))
|
||||
return self.logic.or_(*(self.received(item) for item in items))
|
||||
|
||||
def received_once(self, *items: str, count: int):
|
||||
assert items, "Can't receive once of no items."
|
||||
|
@ -32,4 +38,7 @@ class ReceivedLogicMixin(BaseLogic[HasLogicMixin], BaseLogicMixin):
|
|||
assert items, "Can't receive n of no items."
|
||||
assert count >= 0, "Can't receive a negative amount of item."
|
||||
|
||||
for item in items:
|
||||
assert item_table[item].classification & ItemClassification.progression, f"Item [{item_table[item].name}] has to be progression to be used in logic"
|
||||
|
||||
return TotalReceived(count, items, self.player)
|
||||
|
|
|
@ -4,7 +4,7 @@ from Utils import cache_self1
|
|||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from ..options import EntranceRandomization
|
||||
from ..stardew_rule import StardewRule, And, Or, Reach, false_, true_
|
||||
from ..stardew_rule import StardewRule, Reach, false_, true_
|
||||
from ..strings.region_names import Region
|
||||
|
||||
main_outside_area = {Region.menu, Region.stardew_valley, Region.farm_house, Region.farm, Region.town, Region.beach, Region.mountain, Region.forest,
|
||||
|
@ -18,6 +18,7 @@ always_accessible_regions_without_er = {*main_outside_area, Region.community_cen
|
|||
always_regions_by_setting = {EntranceRandomization.option_disabled: always_accessible_regions_without_er,
|
||||
EntranceRandomization.option_pelican_town: always_accessible_regions_without_er,
|
||||
EntranceRandomization.option_non_progression: always_accessible_regions_without_er,
|
||||
EntranceRandomization.option_buildings_without_house: main_outside_area,
|
||||
EntranceRandomization.option_buildings: main_outside_area,
|
||||
EntranceRandomization.option_chaos: always_accessible_regions_without_er}
|
||||
|
||||
|
@ -42,11 +43,14 @@ class RegionLogic(BaseLogic[Union[RegionLogicMixin, HasLogicMixin]]):
|
|||
|
||||
@cache_self1
|
||||
def can_reach_any(self, region_names: Tuple[str, ...]) -> StardewRule:
|
||||
return Or(*(self.logic.region.can_reach(spot) for spot in region_names))
|
||||
if any(r in always_regions_by_setting[self.options.entrance_randomization] for r in region_names):
|
||||
return true_
|
||||
|
||||
return self.logic.or_(*(self.logic.region.can_reach(spot) for spot in region_names))
|
||||
|
||||
@cache_self1
|
||||
def can_reach_all(self, region_names: Tuple[str, ...]) -> StardewRule:
|
||||
return And(*(self.logic.region.can_reach(spot) for spot in region_names))
|
||||
return self.logic.and_(*(self.logic.region.can_reach(spot) for spot in region_names))
|
||||
|
||||
@cache_self1
|
||||
def can_reach_all_except_one(self, region_names: Tuple[str, ...]) -> StardewRule:
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import math
|
||||
from functools import cached_property
|
||||
from typing import Union, List
|
||||
from typing import Union
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
|
@ -11,9 +10,9 @@ from .received_logic import ReceivedLogicMixin
|
|||
from .region_logic import RegionLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from ..data.villagers_data import all_villagers_by_name, Villager, get_villagers_for_mods
|
||||
from ..options import Friendsanity
|
||||
from ..stardew_rule import StardewRule, True_, And, Or
|
||||
from ..content.feature import friendsanity
|
||||
from ..data.villagers_data import Villager
|
||||
from ..stardew_rule import StardewRule, True_, false_, true_
|
||||
from ..strings.ap_names.mods.mod_items import SVEQuestItem
|
||||
from ..strings.crop_names import Fruit
|
||||
from ..strings.generic_names import Generic
|
||||
|
@ -38,12 +37,8 @@ class RelationshipLogicMixin(BaseLogicMixin):
|
|||
self.relationship = RelationshipLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class RelationshipLogic(BaseLogic[Union[
|
||||
RelationshipLogicMixin, BuildingLogicMixin, SeasonLogicMixin, TimeLogicMixin, GiftLogicMixin, RegionLogicMixin, ReceivedLogicMixin, HasLogicMixin]]):
|
||||
|
||||
@cached_property
|
||||
def all_villagers_given_mods(self) -> List[Villager]:
|
||||
return get_villagers_for_mods(self.options.mods.value)
|
||||
class RelationshipLogic(BaseLogic[Union[RelationshipLogicMixin, BuildingLogicMixin, SeasonLogicMixin, TimeLogicMixin, GiftLogicMixin, RegionLogicMixin,
|
||||
ReceivedLogicMixin, HasLogicMixin]]):
|
||||
|
||||
def can_date(self, npc: str) -> StardewRule:
|
||||
return self.logic.relationship.has_hearts(npc, 8) & self.logic.has(Gift.bouquet)
|
||||
|
@ -52,134 +47,160 @@ class RelationshipLogic(BaseLogic[Union[
|
|||
return self.logic.relationship.has_hearts(npc, 10) & self.logic.has(Gift.mermaid_pendant)
|
||||
|
||||
def can_get_married(self) -> StardewRule:
|
||||
return self.logic.relationship.has_hearts(Generic.bachelor, 10) & self.logic.has(Gift.mermaid_pendant)
|
||||
return self.logic.relationship.has_hearts_with_any_bachelor(10) & self.logic.has(Gift.mermaid_pendant)
|
||||
|
||||
def has_children(self, number_children: int) -> StardewRule:
|
||||
if number_children <= 0:
|
||||
assert number_children >= 0, "Can't have a negative amount of children."
|
||||
if number_children == 0:
|
||||
return True_()
|
||||
if self.options.friendsanity == Friendsanity.option_none:
|
||||
|
||||
if not self.content.features.friendsanity.is_enabled:
|
||||
return self.logic.relationship.can_reproduce(number_children)
|
||||
|
||||
return self.logic.received_n(*possible_kids, count=number_children) & self.logic.building.has_house(2)
|
||||
|
||||
def can_reproduce(self, number_children: int = 1) -> StardewRule:
|
||||
if number_children <= 0:
|
||||
assert number_children >= 0, "Can't have a negative amount of children."
|
||||
if number_children == 0:
|
||||
return True_()
|
||||
baby_rules = [self.logic.relationship.can_get_married(), self.logic.building.has_house(2), self.logic.relationship.has_hearts(Generic.bachelor, 12),
|
||||
|
||||
baby_rules = [self.logic.relationship.can_get_married(),
|
||||
self.logic.building.has_house(2),
|
||||
self.logic.relationship.has_hearts_with_any_bachelor(12),
|
||||
self.logic.relationship.has_children(number_children - 1)]
|
||||
return And(*baby_rules)
|
||||
|
||||
return self.logic.and_(*baby_rules)
|
||||
|
||||
@cache_self1
|
||||
def has_hearts_with_any_bachelor(self, hearts: int = 1) -> StardewRule:
|
||||
assert hearts >= 0, f"Can't have a negative hearts with any bachelor."
|
||||
if hearts == 0:
|
||||
return True_()
|
||||
|
||||
return self.logic.or_(*(self.logic.relationship.has_hearts(name, hearts)
|
||||
for name, villager in self.content.villagers.items()
|
||||
if villager.bachelor))
|
||||
|
||||
@cache_self1
|
||||
def has_hearts_with_any(self, hearts: int = 1) -> StardewRule:
|
||||
assert hearts >= 0, f"Can't have a negative hearts with any npc."
|
||||
if hearts == 0:
|
||||
return True_()
|
||||
|
||||
return self.logic.or_(*(self.logic.relationship.has_hearts(name, hearts)
|
||||
for name, villager in self.content.villagers.items()))
|
||||
|
||||
def has_hearts_with_n(self, amount: int, hearts: int = 1) -> StardewRule:
|
||||
assert hearts >= 0, f"Can't have a negative hearts with any npc."
|
||||
assert amount >= 0, f"Can't have a negative amount of npc."
|
||||
if hearts == 0 or amount == 0:
|
||||
return True_()
|
||||
|
||||
return self.logic.count(amount, *(self.logic.relationship.has_hearts(name, hearts)
|
||||
for name, villager in self.content.villagers.items()))
|
||||
|
||||
# Should be cached
|
||||
def has_hearts(self, npc: str, hearts: int = 1) -> StardewRule:
|
||||
if hearts <= 0:
|
||||
return True_()
|
||||
if self.options.friendsanity == Friendsanity.option_none:
|
||||
return self.logic.relationship.can_earn_relationship(npc, hearts)
|
||||
if npc not in all_villagers_by_name:
|
||||
if npc == Generic.any or npc == Generic.bachelor:
|
||||
possible_friends = []
|
||||
for name in all_villagers_by_name:
|
||||
if not self.npc_is_in_current_slot(name):
|
||||
continue
|
||||
if npc == Generic.any or all_villagers_by_name[name].bachelor:
|
||||
possible_friends.append(self.logic.relationship.has_hearts(name, hearts))
|
||||
return Or(*possible_friends)
|
||||
if npc == Generic.all:
|
||||
mandatory_friends = []
|
||||
for name in all_villagers_by_name:
|
||||
if not self.npc_is_in_current_slot(name):
|
||||
continue
|
||||
mandatory_friends.append(self.logic.relationship.has_hearts(name, hearts))
|
||||
return And(*mandatory_friends)
|
||||
if npc.isnumeric():
|
||||
possible_friends = []
|
||||
for name in all_villagers_by_name:
|
||||
if not self.npc_is_in_current_slot(name):
|
||||
continue
|
||||
possible_friends.append(self.logic.relationship.has_hearts(name, hearts))
|
||||
return self.logic.count(int(npc), *possible_friends)
|
||||
return self.can_earn_relationship(npc, hearts)
|
||||
assert hearts >= 0, f"Can't have a negative hearts with {npc}."
|
||||
|
||||
if not self.npc_is_in_current_slot(npc):
|
||||
return True_()
|
||||
villager = all_villagers_by_name[npc]
|
||||
if self.options.friendsanity == Friendsanity.option_bachelors and not villager.bachelor:
|
||||
villager = self.content.villagers.get(npc)
|
||||
if villager is None:
|
||||
return false_
|
||||
|
||||
if hearts == 0:
|
||||
return true_
|
||||
|
||||
heart_steps = self.content.features.friendsanity.get_randomized_hearts(villager)
|
||||
if not heart_steps or hearts > heart_steps[-1]: # Hearts are sorted, bigger is the last one.
|
||||
return self.logic.relationship.can_earn_relationship(npc, hearts)
|
||||
if self.options.friendsanity == Friendsanity.option_starting_npcs and not villager.available:
|
||||
return self.logic.relationship.can_earn_relationship(npc, hearts)
|
||||
is_capped_at_8 = villager.bachelor and self.options.friendsanity != Friendsanity.option_all_with_marriage
|
||||
if is_capped_at_8 and hearts > 8:
|
||||
return self.logic.relationship.received_hearts(villager.name, 8) & self.logic.relationship.can_earn_relationship(npc, hearts)
|
||||
return self.logic.relationship.received_hearts(villager.name, hearts)
|
||||
|
||||
return self.logic.relationship.received_hearts(villager, hearts)
|
||||
|
||||
# Should be cached
|
||||
def received_hearts(self, npc: str, hearts: int) -> StardewRule:
|
||||
heart_item = heart_item_name(npc)
|
||||
number_required = math.ceil(hearts / self.options.friendsanity_heart_size)
|
||||
return self.logic.received(heart_item, number_required)
|
||||
def received_hearts(self, villager: Villager, hearts: int) -> StardewRule:
|
||||
heart_item = friendsanity.to_item_name(villager.name)
|
||||
|
||||
number_required = math.ceil(hearts / self.content.features.friendsanity.heart_size)
|
||||
return self.logic.received(heart_item, number_required) & self.can_meet(villager.name)
|
||||
|
||||
@cache_self1
|
||||
def can_meet(self, npc: str) -> StardewRule:
|
||||
if npc not in all_villagers_by_name or not self.npc_is_in_current_slot(npc):
|
||||
return True_()
|
||||
villager = all_villagers_by_name[npc]
|
||||
villager = self.content.villagers.get(npc)
|
||||
if villager is None:
|
||||
return false_
|
||||
|
||||
rules = [self.logic.region.can_reach_any(villager.locations)]
|
||||
|
||||
if npc == NPC.kent:
|
||||
rules.append(self.logic.time.has_year_two)
|
||||
|
||||
elif npc == NPC.leo:
|
||||
rules.append(self.logic.received("Island West Turtle"))
|
||||
rules.append(self.logic.received("Island North Turtle"))
|
||||
|
||||
elif npc == ModNPC.lance:
|
||||
rules.append(self.logic.region.can_reach(Region.volcano_floor_10))
|
||||
|
||||
elif npc == ModNPC.apples:
|
||||
rules.append(self.logic.has(Fruit.starfruit))
|
||||
|
||||
elif npc == ModNPC.scarlett:
|
||||
scarlett_job = self.logic.received(SVEQuestItem.scarlett_job_offer)
|
||||
scarlett_spring = self.logic.season.has(Season.spring) & self.can_meet(ModNPC.andy)
|
||||
scarlett_summer = self.logic.season.has(Season.summer) & self.can_meet(ModNPC.susan)
|
||||
scarlett_fall = self.logic.season.has(Season.fall) & self.can_meet(ModNPC.sophia)
|
||||
rules.append(scarlett_job & (scarlett_spring | scarlett_summer | scarlett_fall))
|
||||
|
||||
elif npc == ModNPC.morgan:
|
||||
rules.append(self.logic.received(SVEQuestItem.morgan_schooling))
|
||||
|
||||
elif npc == ModNPC.goblin:
|
||||
rules.append(self.logic.region.can_reach_all((Region.witch_hut, Region.wizard_tower)))
|
||||
|
||||
return And(*rules)
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def can_give_loved_gifts_to_everyone(self) -> StardewRule:
|
||||
rules = []
|
||||
for npc in all_villagers_by_name:
|
||||
if not self.npc_is_in_current_slot(npc):
|
||||
continue
|
||||
|
||||
for npc in self.content.villagers:
|
||||
meet_rule = self.logic.relationship.can_meet(npc)
|
||||
rules.append(meet_rule)
|
||||
|
||||
rules.append(self.logic.gifts.has_any_universal_love)
|
||||
return And(*rules)
|
||||
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
# Should be cached
|
||||
def can_earn_relationship(self, npc: str, hearts: int = 0) -> StardewRule:
|
||||
if hearts <= 0:
|
||||
assert hearts >= 0, f"Can't have a negative hearts with {npc}."
|
||||
|
||||
villager = self.content.villagers.get(npc)
|
||||
if villager is None:
|
||||
return false_
|
||||
|
||||
if hearts == 0:
|
||||
return True_()
|
||||
|
||||
previous_heart = hearts - self.options.friendsanity_heart_size
|
||||
previous_heart_rule = self.logic.relationship.has_hearts(npc, previous_heart)
|
||||
rules = [self.logic.relationship.can_meet(npc)]
|
||||
|
||||
if npc not in all_villagers_by_name or not self.npc_is_in_current_slot(npc):
|
||||
return previous_heart_rule
|
||||
heart_size = self.content.features.friendsanity.heart_size
|
||||
max_randomized_hearts = self.content.features.friendsanity.get_randomized_hearts(villager)
|
||||
if max_randomized_hearts:
|
||||
if hearts > max_randomized_hearts[-1]:
|
||||
rules.append(self.logic.relationship.has_hearts(npc, hearts - 1))
|
||||
else:
|
||||
previous_heart = max(hearts - heart_size, 0)
|
||||
rules.append(self.logic.relationship.has_hearts(npc, previous_heart))
|
||||
|
||||
rules = [previous_heart_rule, self.logic.relationship.can_meet(npc)]
|
||||
villager = all_villagers_by_name[npc]
|
||||
if hearts > 2 or hearts > self.options.friendsanity_heart_size:
|
||||
if hearts > 2 or hearts > heart_size:
|
||||
rules.append(self.logic.season.has(villager.birthday))
|
||||
|
||||
if villager.birthday == Generic.any:
|
||||
rules.append(self.logic.season.has_all() | self.logic.time.has_year_three) # push logic back for any birthday-less villager
|
||||
|
||||
if villager.bachelor:
|
||||
if hearts > 8:
|
||||
rules.append(self.logic.relationship.can_date(npc))
|
||||
if hearts > 10:
|
||||
rules.append(self.logic.relationship.can_marry(npc))
|
||||
elif hearts > 8:
|
||||
rules.append(self.logic.relationship.can_date(npc))
|
||||
|
||||
return And(*rules)
|
||||
|
||||
@cache_self1
|
||||
def npc_is_in_current_slot(self, name: str) -> bool:
|
||||
npc = all_villagers_by_name[name]
|
||||
return npc in self.all_villagers_given_mods
|
||||
return self.logic.and_(*rules)
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
import functools
|
||||
from typing import Union, Iterable
|
||||
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .book_logic import BookLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .season_logic import SeasonLogicMixin
|
||||
from .skill_logic import SkillLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..data.game_item import Requirement
|
||||
from ..data.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement, YearRequirement
|
||||
|
||||
|
||||
class RequirementLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.requirement = RequirementLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class RequirementLogic(BaseLogic[Union[RequirementLogicMixin, HasLogicMixin, ReceivedLogicMixin, ToolLogicMixin, SkillLogicMixin, BookLogicMixin,
|
||||
SeasonLogicMixin, TimeLogicMixin]]):
|
||||
|
||||
def meet_all_requirements(self, requirements: Iterable[Requirement]):
|
||||
if not requirements:
|
||||
return self.logic.true_
|
||||
return self.logic.and_(*(self.logic.requirement.meet_requirement(requirement) for requirement in requirements))
|
||||
|
||||
@functools.singledispatchmethod
|
||||
def meet_requirement(self, requirement: Requirement):
|
||||
raise ValueError(f"Requirements of type{type(requirement)} have no rule registered.")
|
||||
|
||||
@meet_requirement.register
|
||||
def _(self, requirement: ToolRequirement):
|
||||
return self.logic.tool.has_tool(requirement.tool, requirement.tier)
|
||||
|
||||
@meet_requirement.register
|
||||
def _(self, requirement: SkillRequirement):
|
||||
return self.logic.skill.has_level(requirement.skill, requirement.level)
|
||||
|
||||
@meet_requirement.register
|
||||
def _(self, requirement: BookRequirement):
|
||||
return self.logic.book.has_book_power(requirement.book)
|
||||
|
||||
@meet_requirement.register
|
||||
def _(self, requirement: SeasonRequirement):
|
||||
return self.logic.season.has(requirement.season)
|
||||
|
||||
@meet_requirement.register
|
||||
def _(self, requirement: YearRequirement):
|
||||
return self.logic.time.has_year(requirement.year)
|
|
@ -1,11 +1,13 @@
|
|||
from functools import cached_property
|
||||
from typing import Iterable, Union
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from ..options import SeasonRandomization
|
||||
from ..stardew_rule import StardewRule, True_, Or, And
|
||||
from ..stardew_rule import StardewRule, True_, true_
|
||||
from ..strings.generic_names import Generic
|
||||
from ..strings.season_names import Season
|
||||
|
||||
|
@ -16,7 +18,23 @@ class SeasonLogicMixin(BaseLogicMixin):
|
|||
self.season = SeasonLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class SeasonLogic(BaseLogic[Union[SeasonLogicMixin, TimeLogicMixin, ReceivedLogicMixin]]):
|
||||
class SeasonLogic(BaseLogic[Union[HasLogicMixin, SeasonLogicMixin, TimeLogicMixin, ReceivedLogicMixin]]):
|
||||
|
||||
@cached_property
|
||||
def has_spring(self) -> StardewRule:
|
||||
return self.logic.season.has(Season.spring)
|
||||
|
||||
@cached_property
|
||||
def has_summer(self) -> StardewRule:
|
||||
return self.logic.season.has(Season.summer)
|
||||
|
||||
@cached_property
|
||||
def has_fall(self) -> StardewRule:
|
||||
return self.logic.season.has(Season.fall)
|
||||
|
||||
@cached_property
|
||||
def has_winter(self) -> StardewRule:
|
||||
return self.logic.season.has(Season.winter)
|
||||
|
||||
@cache_self1
|
||||
def has(self, season: str) -> StardewRule:
|
||||
|
@ -32,13 +50,16 @@ class SeasonLogic(BaseLogic[Union[SeasonLogicMixin, TimeLogicMixin, ReceivedLogi
|
|||
return self.logic.received(season)
|
||||
|
||||
def has_any(self, seasons: Iterable[str]):
|
||||
if seasons == Season.all:
|
||||
return true_
|
||||
if not seasons:
|
||||
# That should be false, but I'm scared.
|
||||
return True_()
|
||||
return Or(*(self.logic.season.has(season) for season in seasons))
|
||||
return self.logic.or_(*(self.logic.season.has(season) for season in seasons))
|
||||
|
||||
def has_any_not_winter(self):
|
||||
return self.logic.season.has_any([Season.spring, Season.summer, Season.fall])
|
||||
|
||||
def has_all(self):
|
||||
seasons = [Season.spring, Season.summer, Season.fall, Season.winter]
|
||||
return And(*(self.logic.season.has(season) for season in seasons))
|
||||
return self.logic.and_(*(self.logic.season.has(season) for season in seasons))
|
||||
|
|
|
@ -10,7 +10,7 @@ from .region_logic import RegionLogicMixin
|
|||
from ..locations import LocationTags, locations_by_tag
|
||||
from ..options import ExcludeGingerIsland, Shipsanity
|
||||
from ..options import SpecialOrderLocations
|
||||
from ..stardew_rule import StardewRule, And
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.ap_names.event_names import Event
|
||||
from ..strings.building_names import Building
|
||||
|
||||
|
@ -35,7 +35,7 @@ class ShippingLogic(BaseLogic[Union[ReceivedLogicMixin, ShippingLogicMixin, Buil
|
|||
shipsanity_prefix = "Shipsanity: "
|
||||
all_items_to_ship = []
|
||||
exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true
|
||||
exclude_qi = self.options.special_order_locations != SpecialOrderLocations.option_board_qi
|
||||
exclude_qi = not (self.options.special_order_locations & SpecialOrderLocations.value_qi)
|
||||
mod_list = self.options.mods.value
|
||||
for location in locations_by_tag[LocationTags.SHIPSANITY_FULL_SHIPMENT]:
|
||||
if exclude_island and LocationTags.GINGER_ISLAND in location.tags:
|
||||
|
@ -57,4 +57,4 @@ class ShippingLogic(BaseLogic[Union[ReceivedLogicMixin, ShippingLogicMixin, Buil
|
|||
if shipsanity_location.name not in all_location_names_in_slot:
|
||||
continue
|
||||
rules.append(self.logic.region.can_reach_location(shipsanity_location.name))
|
||||
return And(*rules)
|
||||
return self.logic.and_(*rules)
|
||||
|
|
|
@ -4,7 +4,7 @@ from typing import Union, Tuple
|
|||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .combat_logic import CombatLogicMixin
|
||||
from .crop_logic import CropLogicMixin
|
||||
from .harvesting_logic import HarvestingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
|
@ -12,10 +12,10 @@ from .season_logic import SeasonLogicMixin
|
|||
from .time_logic import TimeLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from .. import options
|
||||
from ..data import all_crops
|
||||
from ..data.harvest import HarvestCropSource
|
||||
from ..mods.logic.magic_logic import MagicLogicMixin
|
||||
from ..mods.logic.mod_skills_levels import get_mod_skill_levels
|
||||
from ..stardew_rule import StardewRule, True_, Or, False_
|
||||
from ..stardew_rule import StardewRule, True_, False_, true_, And
|
||||
from ..strings.craftable_names import Fishing
|
||||
from ..strings.machine_names import Machine
|
||||
from ..strings.performance_names import Performance
|
||||
|
@ -23,8 +23,10 @@ from ..strings.quality_names import ForageQuality
|
|||
from ..strings.region_names import Region
|
||||
from ..strings.skill_names import Skill, all_mod_skills
|
||||
from ..strings.tool_names import ToolMaterial, Tool
|
||||
from ..strings.wallet_item_names import Wallet
|
||||
|
||||
fishing_regions = (Region.beach, Region.town, Region.forest, Region.mountain, Region.island_south, Region.island_west)
|
||||
vanilla_skill_items = ("Farming Level", "Mining Level", "Foraging Level", "Fishing Level", "Combat Level")
|
||||
|
||||
|
||||
class SkillLogicMixin(BaseLogicMixin):
|
||||
|
@ -34,7 +36,8 @@ class SkillLogicMixin(BaseLogicMixin):
|
|||
|
||||
|
||||
class SkillLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, TimeLogicMixin, ToolLogicMixin, SkillLogicMixin,
|
||||
CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
||||
CombatLogicMixin, MagicLogicMixin, HarvestingLogicMixin]]):
|
||||
|
||||
# Should be cached
|
||||
def can_earn_level(self, skill: str, level: int) -> StardewRule:
|
||||
if level <= 0:
|
||||
|
@ -48,14 +51,15 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
if self.options.skill_progression != options.SkillProgression.option_vanilla:
|
||||
previous_level_rule = self.logic.skill.has_level(skill, level - 1)
|
||||
else:
|
||||
previous_level_rule = True_()
|
||||
previous_level_rule = true_
|
||||
|
||||
if skill == Skill.fishing:
|
||||
xp_rule = self.logic.tool.has_fishing_rod(max(tool_level, 1))
|
||||
xp_rule = self.logic.tool.has_fishing_rod(max(tool_level, 3))
|
||||
elif skill == Skill.farming:
|
||||
xp_rule = self.logic.tool.has_tool(Tool.hoe, tool_material) & self.logic.tool.can_water(tool_level)
|
||||
xp_rule = self.can_get_farming_xp & self.logic.tool.has_tool(Tool.hoe, tool_material) & self.logic.tool.can_water(tool_level)
|
||||
elif skill == Skill.foraging:
|
||||
xp_rule = self.logic.tool.has_tool(Tool.axe, tool_material) | self.logic.magic.can_use_clear_debris_instead_of_tool_level(tool_level)
|
||||
xp_rule = (self.can_get_foraging_xp & self.logic.tool.has_tool(Tool.axe, tool_material)) |\
|
||||
self.logic.magic.can_use_clear_debris_instead_of_tool_level(tool_level)
|
||||
elif skill == Skill.mining:
|
||||
xp_rule = self.logic.tool.has_tool(Tool.pickaxe, tool_material) | \
|
||||
self.logic.magic.can_use_clear_debris_instead_of_tool_level(tool_level)
|
||||
|
@ -66,7 +70,7 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
xp_rule = xp_rule & self.logic.region.can_reach(Region.mines_floor_5)
|
||||
elif skill in all_mod_skills:
|
||||
# Ideal solution would be to add a logic registry, but I'm too lazy.
|
||||
return self.logic.mod.skill.can_earn_mod_skill_level(skill, level)
|
||||
return previous_level_rule & months_rule & self.logic.mod.skill.can_earn_mod_skill_level(skill, level)
|
||||
else:
|
||||
raise Exception(f"Unknown skill: {skill}")
|
||||
|
||||
|
@ -77,11 +81,11 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
if level <= 0:
|
||||
return True_()
|
||||
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive:
|
||||
return self.logic.received(f"{skill} Level", level)
|
||||
|
||||
if self.options.skill_progression == options.SkillProgression.option_vanilla:
|
||||
return self.logic.skill.can_earn_level(skill, level)
|
||||
|
||||
return self.logic.received(f"{skill} Level", level)
|
||||
|
||||
@cache_self1
|
||||
def has_farming_level(self, level: int) -> StardewRule:
|
||||
return self.logic.skill.has_level(Skill.farming, level)
|
||||
|
@ -91,8 +95,8 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
if level <= 0:
|
||||
return True_()
|
||||
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive:
|
||||
skills_items = ("Farming Level", "Mining Level", "Foraging Level", "Fishing Level", "Combat Level")
|
||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
||||
skills_items = vanilla_skill_items
|
||||
if allow_modded_skills:
|
||||
skills_items += get_mod_skill_levels(self.options.mods)
|
||||
return self.logic.received_n(*skills_items, count=level)
|
||||
|
@ -104,12 +108,26 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
return rule_with_fishing
|
||||
return self.logic.time.has_lived_months(months_with_4_skills) | rule_with_fishing
|
||||
|
||||
def has_all_skills_maxed(self, included_modded_skills: bool = True) -> StardewRule:
|
||||
if self.options.skill_progression == options.SkillProgression.option_vanilla:
|
||||
return self.has_total_level(50)
|
||||
skills_items = vanilla_skill_items
|
||||
if included_modded_skills:
|
||||
skills_items += get_mod_skill_levels(self.options.mods)
|
||||
return And(*[self.logic.received(skill, 10) for skill in skills_items])
|
||||
|
||||
def can_enter_mastery_cave(self) -> StardewRule:
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive_with_masteries:
|
||||
return self.logic.received(Wallet.mastery_of_the_five_ways)
|
||||
return self.has_all_skills_maxed()
|
||||
|
||||
@cached_property
|
||||
def can_get_farming_xp(self) -> StardewRule:
|
||||
sources = self.content.find_sources_of_type(HarvestCropSource)
|
||||
crop_rules = []
|
||||
for crop in all_crops:
|
||||
crop_rules.append(self.logic.crop.can_grow(crop))
|
||||
return Or(*crop_rules)
|
||||
for crop_source in sources:
|
||||
crop_rules.append(self.logic.harvesting.can_harvest_crop_from(crop_source))
|
||||
return self.logic.or_(*crop_rules)
|
||||
|
||||
@cached_property
|
||||
def can_get_foraging_xp(self) -> StardewRule:
|
||||
|
@ -132,7 +150,7 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
|
||||
@cached_property
|
||||
def can_get_fishing_xp(self) -> StardewRule:
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive:
|
||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
||||
return self.logic.skill.can_fish() | self.logic.skill.can_crab_pot
|
||||
|
||||
return self.logic.skill.can_fish()
|
||||
|
@ -162,7 +180,7 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
@cached_property
|
||||
def can_crab_pot(self) -> StardewRule:
|
||||
crab_pot_rule = self.logic.has(Fishing.bait)
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive:
|
||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
||||
crab_pot_rule = crab_pot_rule & self.logic.has(Machine.crab_pot)
|
||||
else:
|
||||
crab_pot_rule = crab_pot_rule & self.logic.skill.can_get_fishing_xp
|
||||
|
@ -178,3 +196,14 @@ CombatLogicMixin, CropLogicMixin, MagicLogicMixin]]):
|
|||
if quality == ForageQuality.gold:
|
||||
return self.has_level(Skill.foraging, 9)
|
||||
return False_()
|
||||
|
||||
@cached_property
|
||||
def can_earn_mastery_experience(self) -> StardewRule:
|
||||
if self.options.skill_progression != options.SkillProgression.option_progressive_with_masteries:
|
||||
return self.has_all_skills_maxed() & self.logic.time.has_lived_max_months
|
||||
return self.logic.time.has_lived_max_months
|
||||
|
||||
def has_mastery(self, skill: str) -> StardewRule:
|
||||
if self.options.skill_progression != options.SkillProgression.option_progressive_with_masteries:
|
||||
return self.can_earn_mastery_experience and self.logic.region.can_reach(Region.mastery_cave)
|
||||
return self.logic.received(f"{skill} Mastery")
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
import functools
|
||||
from typing import Union, Any, Iterable
|
||||
|
||||
from .artisan_logic import ArtisanLogicMixin
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .grind_logic import GrindLogicMixin
|
||||
from .harvesting_logic import HarvestingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .money_logic import MoneyLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .requirement_logic import RequirementLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..data.artisan import MachineSource
|
||||
from ..data.game_item import GenericSource, ItemSource, GameItem, CustomRuleSource
|
||||
from ..data.harvest import ForagingSource, FruitBatsSource, MushroomCaveSource, SeasonalForagingSource, \
|
||||
HarvestCropSource, HarvestFruitTreeSource, ArtifactSpotSource
|
||||
from ..data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource
|
||||
|
||||
|
||||
class SourceLogicMixin(BaseLogicMixin):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
self.source = SourceLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class SourceLogic(BaseLogic[Union[SourceLogicMixin, HasLogicMixin, ReceivedLogicMixin, HarvestingLogicMixin, MoneyLogicMixin, RegionLogicMixin,
|
||||
ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
|
||||
|
||||
def has_access_to_item(self, item: GameItem):
|
||||
rules = []
|
||||
|
||||
if self.content.features.cropsanity.is_included(item):
|
||||
rules.append(self.logic.received(item.name))
|
||||
|
||||
rules.append(self.logic.source.has_access_to_any(item.sources))
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def has_access_to_any(self, sources: Iterable[ItemSource]):
|
||||
return self.logic.or_(*(self.logic.source.has_access_to(source) & self.logic.requirement.meet_all_requirements(source.other_requirements)
|
||||
for source in sources))
|
||||
|
||||
@functools.singledispatchmethod
|
||||
def has_access_to(self, source: Any):
|
||||
raise ValueError(f"Sources of type{type(source)} have no rule registered.")
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: GenericSource):
|
||||
return self.logic.region.can_reach_any(source.regions) if source.regions else self.logic.true_
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: CustomRuleSource):
|
||||
return source.create_rule(self.logic)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: ForagingSource):
|
||||
return self.logic.harvesting.can_forage_from(source)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: SeasonalForagingSource):
|
||||
# Implementation could be different with some kind of "calendar shuffle"
|
||||
return self.logic.harvesting.can_forage_from(source.as_foraging_source())
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, _: FruitBatsSource):
|
||||
return self.logic.harvesting.can_harvest_from_fruit_bats
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, _: MushroomCaveSource):
|
||||
return self.logic.harvesting.can_harvest_from_mushroom_cave
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: ShopSource):
|
||||
return self.logic.money.can_shop_from(source)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: HarvestFruitTreeSource):
|
||||
return self.logic.harvesting.can_harvest_tree_from(source)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: HarvestCropSource):
|
||||
return self.logic.harvesting.can_harvest_crop_from(source)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: MachineSource):
|
||||
return self.logic.artisan.can_produce_from(source)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: MysteryBoxSource):
|
||||
return self.logic.grind.can_grind_mystery_boxes(source.amount)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: ArtifactTroveSource):
|
||||
return self.logic.grind.can_grind_artifact_troves(source.amount)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: PrizeMachineSource):
|
||||
return self.logic.grind.can_grind_prize_tickets(source.amount)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: FishingTreasureChestSource):
|
||||
return self.logic.grind.can_grind_fishing_treasure_chests(source.amount)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: ArtifactSpotSource):
|
||||
return self.logic.grind.can_grind_artifact_spots(source.amount)
|
|
@ -4,7 +4,6 @@ from .ability_logic import AbilityLogicMixin
|
|||
from .arcade_logic import ArcadeLogicMixin
|
||||
from .artisan_logic import ArtisanLogicMixin
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .buff_logic import BuffLogicMixin
|
||||
from .cooking_logic import CookingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .mine_logic import MineLogicMixin
|
||||
|
@ -18,7 +17,9 @@ from .shipping_logic import ShippingLogicMixin
|
|||
from .skill_logic import SkillLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..stardew_rule import StardewRule, Has
|
||||
from ..content.vanilla.ginger_island import ginger_island_content_pack
|
||||
from ..content.vanilla.qi_board import qi_board_content_pack
|
||||
from ..stardew_rule import StardewRule, Has, false_
|
||||
from ..strings.animal_product_names import AnimalProduct
|
||||
from ..strings.ap_names.event_names import Event
|
||||
from ..strings.ap_names.transport_names import Transportation
|
||||
|
@ -35,7 +36,6 @@ from ..strings.monster_names import Monster
|
|||
from ..strings.region_names import Region
|
||||
from ..strings.season_names import Season
|
||||
from ..strings.special_order_names import SpecialOrder
|
||||
from ..strings.tool_names import Tool
|
||||
from ..strings.villager_names import NPC
|
||||
|
||||
|
||||
|
@ -47,14 +47,11 @@ class SpecialOrderLogicMixin(BaseLogicMixin):
|
|||
|
||||
class SpecialOrderLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, TimeLogicMixin, MoneyLogicMixin,
|
||||
ShippingLogicMixin, ArcadeLogicMixin, ArtisanLogicMixin, RelationshipLogicMixin, ToolLogicMixin, SkillLogicMixin,
|
||||
MineLogicMixin, CookingLogicMixin, BuffLogicMixin,
|
||||
MineLogicMixin, CookingLogicMixin,
|
||||
AbilityLogicMixin, SpecialOrderLogicMixin, MonsterLogicMixin]]):
|
||||
|
||||
def initialize_rules(self):
|
||||
self.update_rules({
|
||||
SpecialOrder.island_ingredients: self.logic.relationship.can_meet(NPC.caroline) & self.logic.special_order.has_island_transport() &
|
||||
self.logic.ability.can_farm_perfectly() & self.logic.shipping.can_ship(Vegetable.taro_root) &
|
||||
self.logic.shipping.can_ship(Fruit.pineapple) & self.logic.shipping.can_ship(Forageable.ginger),
|
||||
SpecialOrder.cave_patrol: self.logic.relationship.can_meet(NPC.clint),
|
||||
SpecialOrder.aquatic_overpopulation: self.logic.relationship.can_meet(NPC.demetrius) & self.logic.ability.can_fish_perfectly(),
|
||||
SpecialOrder.biome_balance: self.logic.relationship.can_meet(NPC.demetrius) & self.logic.ability.can_fish_perfectly(),
|
||||
|
@ -66,46 +63,63 @@ AbilityLogicMixin, SpecialOrderLogicMixin, MonsterLogicMixin]]):
|
|||
SpecialOrder.gus_famous_omelet: self.logic.has(AnimalProduct.any_egg),
|
||||
SpecialOrder.crop_order: self.logic.ability.can_farm_perfectly() & self.logic.received(Event.can_ship_items),
|
||||
SpecialOrder.community_cleanup: self.logic.skill.can_crab_pot,
|
||||
SpecialOrder.the_strong_stuff: self.logic.artisan.can_keg(Vegetable.potato),
|
||||
SpecialOrder.the_strong_stuff: self.logic.has(ArtisanGood.specific_juice(Vegetable.potato)),
|
||||
SpecialOrder.pierres_prime_produce: self.logic.ability.can_farm_perfectly(),
|
||||
SpecialOrder.robins_project: self.logic.relationship.can_meet(NPC.robin) & self.logic.ability.can_chop_perfectly() &
|
||||
self.logic.has(Material.hardwood),
|
||||
SpecialOrder.robins_resource_rush: self.logic.relationship.can_meet(NPC.robin) & self.logic.ability.can_chop_perfectly() &
|
||||
self.logic.has(Fertilizer.tree) & self.logic.ability.can_mine_perfectly(),
|
||||
SpecialOrder.juicy_bugs_wanted: self.logic.has(Loot.bug_meat),
|
||||
SpecialOrder.a_curious_substance: self.logic.region.can_reach(Region.wizard_tower),
|
||||
SpecialOrder.prismatic_jelly: self.logic.region.can_reach(Region.wizard_tower),
|
||||
|
||||
})
|
||||
|
||||
if ginger_island_content_pack.name in self.content.registered_packs:
|
||||
self.update_rules({
|
||||
SpecialOrder.island_ingredients: self.logic.relationship.can_meet(NPC.caroline) & self.logic.special_order.has_island_transport() &
|
||||
self.logic.ability.can_farm_perfectly() & self.logic.shipping.can_ship(Vegetable.taro_root) &
|
||||
self.logic.shipping.can_ship(Fruit.pineapple) & self.logic.shipping.can_ship(Forageable.ginger),
|
||||
SpecialOrder.tropical_fish: self.logic.relationship.can_meet(NPC.willy) & self.logic.received("Island Resort") &
|
||||
self.logic.special_order.has_island_transport() &
|
||||
self.logic.has(Fish.stingray) & self.logic.has(Fish.blue_discus) & self.logic.has(Fish.lionfish),
|
||||
SpecialOrder.a_curious_substance: self.logic.region.can_reach(Region.wizard_tower),
|
||||
SpecialOrder.prismatic_jelly: self.logic.region.can_reach(Region.wizard_tower),
|
||||
})
|
||||
else:
|
||||
self.update_rules({
|
||||
SpecialOrder.island_ingredients: false_,
|
||||
SpecialOrder.tropical_fish: false_,
|
||||
})
|
||||
|
||||
if qi_board_content_pack.name in self.content.registered_packs:
|
||||
self.update_rules({
|
||||
SpecialOrder.qis_crop: self.logic.ability.can_farm_perfectly() & self.logic.region.can_reach(Region.greenhouse) &
|
||||
self.logic.region.can_reach(Region.island_west) & self.logic.skill.has_total_level(50) &
|
||||
self.logic.has(Machine.seed_maker) & self.logic.received(Event.can_ship_items),
|
||||
SpecialOrder.lets_play_a_game: self.logic.arcade.has_junimo_kart_max_level(),
|
||||
SpecialOrder.four_precious_stones: self.logic.time.has_lived_max_months & self.logic.has("Prismatic Shard") &
|
||||
self.logic.ability.can_mine_perfectly_in_the_skull_cavern(),
|
||||
SpecialOrder.qis_hungry_challenge: self.logic.ability.can_mine_perfectly_in_the_skull_cavern() & self.logic.buff.has_max_buffs(),
|
||||
SpecialOrder.qis_hungry_challenge: self.logic.ability.can_mine_perfectly_in_the_skull_cavern(),
|
||||
SpecialOrder.qis_cuisine: self.logic.cooking.can_cook() & self.logic.received(Event.can_ship_items) &
|
||||
(self.logic.money.can_spend_at(Region.saloon, 205000) | self.logic.money.can_spend_at(Region.pierre_store, 170000)),
|
||||
SpecialOrder.qis_kindness: self.logic.relationship.can_give_loved_gifts_to_everyone(),
|
||||
SpecialOrder.extended_family: self.logic.ability.can_fish_perfectly() & self.logic.has(Fish.angler) & self.logic.has(Fish.glacierfish) &
|
||||
self.logic.has(Fish.crimsonfish) & self.logic.has(Fish.mutant_carp) & self.logic.has(Fish.legend),
|
||||
SpecialOrder.danger_in_the_deep: self.logic.ability.can_mine_perfectly() & self.logic.mine.has_mine_elevator_to_floor(120),
|
||||
SpecialOrder.skull_cavern_invasion: self.logic.ability.can_mine_perfectly_in_the_skull_cavern() & self.logic.buff.has_max_buffs(),
|
||||
SpecialOrder.skull_cavern_invasion: self.logic.ability.can_mine_perfectly_in_the_skull_cavern(),
|
||||
SpecialOrder.qis_prismatic_grange: self.logic.has(Loot.bug_meat) & # 100 Bug Meat
|
||||
self.logic.money.can_spend_at(Region.saloon, 24000) & # 100 Spaghetti
|
||||
self.logic.money.can_spend_at(Region.blacksmith, 15000) & # 100 Copper Ore
|
||||
self.logic.money.can_spend_at(Region.ranch, 5000) & # 100 Hay
|
||||
self.logic.money.can_spend_at(Region.saloon, 22000) & # 100 Salads
|
||||
self.logic.money.can_spend_at(Region.saloon, 7500) & # 100 Joja Cola
|
||||
self.logic.money.can_spend(80000), # I need this extra rule because money rules aren't additive...
|
||||
self.logic.money.can_spend(80000), # I need this extra rule because money rules aren't additive...)
|
||||
})
|
||||
|
||||
def update_rules(self, new_rules: Dict[str, StardewRule]):
|
||||
self.registry.special_order_rules.update(new_rules)
|
||||
|
||||
def can_complete_special_order(self, special_order: str) -> StardewRule:
|
||||
return Has(special_order, self.registry.special_order_rules)
|
||||
return Has(special_order, self.registry.special_order_rules, "special order")
|
||||
|
||||
def has_island_transport(self) -> StardewRule:
|
||||
return self.logic.received(Transportation.island_obelisk) | self.logic.received(Transportation.boat_repair)
|
||||
|
|
|
@ -3,11 +3,17 @@ from typing import Union
|
|||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogic, BaseLogicMixin
|
||||
from .received_logic import ReceivedLogicMixin
|
||||
from ..stardew_rule import StardewRule, HasProgressionPercent, True_
|
||||
from .has_logic import HasLogicMixin
|
||||
from ..stardew_rule import StardewRule, HasProgressionPercent
|
||||
|
||||
MAX_MONTHS = 12
|
||||
MONTH_COEFFICIENT = 24 // MAX_MONTHS
|
||||
ONE_YEAR = 4
|
||||
MAX_MONTHS = 3 * ONE_YEAR
|
||||
PERCENT_REQUIRED_FOR_MAX_MONTHS = 48
|
||||
MONTH_COEFFICIENT = PERCENT_REQUIRED_FOR_MAX_MONTHS // MAX_MONTHS
|
||||
|
||||
MIN_ITEMS = 10
|
||||
MAX_ITEMS = 999
|
||||
PERCENT_REQUIRED_FOR_MAX_ITEM = 24
|
||||
|
||||
|
||||
class TimeLogicMixin(BaseLogicMixin):
|
||||
|
@ -16,12 +22,12 @@ class TimeLogicMixin(BaseLogicMixin):
|
|||
self.time = TimeLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class TimeLogic(BaseLogic[Union[TimeLogicMixin, ReceivedLogicMixin]]):
|
||||
class TimeLogic(BaseLogic[Union[TimeLogicMixin, HasLogicMixin]]):
|
||||
|
||||
@cache_self1
|
||||
def has_lived_months(self, number: int) -> StardewRule:
|
||||
if number <= 0:
|
||||
return True_()
|
||||
return self.logic.true_
|
||||
number = min(number, MAX_MONTHS)
|
||||
return HasProgressionPercent(self.player, number * MONTH_COEFFICIENT)
|
||||
|
||||
|
@ -29,10 +35,18 @@ class TimeLogic(BaseLogic[Union[TimeLogicMixin, ReceivedLogicMixin]]):
|
|||
def has_lived_max_months(self) -> StardewRule:
|
||||
return self.logic.time.has_lived_months(MAX_MONTHS)
|
||||
|
||||
@cache_self1
|
||||
def has_lived_year(self, number: int) -> StardewRule:
|
||||
return self.logic.time.has_lived_months(number * ONE_YEAR)
|
||||
|
||||
@cache_self1
|
||||
def has_year(self, number: int) -> StardewRule:
|
||||
return self.logic.time.has_lived_year(number - 1)
|
||||
|
||||
@cached_property
|
||||
def has_year_two(self) -> StardewRule:
|
||||
return self.logic.time.has_lived_months(4)
|
||||
return self.logic.time.has_year(2)
|
||||
|
||||
@cached_property
|
||||
def has_year_three(self) -> StardewRule:
|
||||
return self.logic.time.has_lived_months(8)
|
||||
return self.logic.time.has_year(3)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from typing import Union, Iterable
|
||||
from typing import Union, Iterable, Tuple
|
||||
|
||||
from Utils import cache_self1
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
|
@ -42,9 +42,17 @@ class ToolLogicMixin(BaseLogicMixin):
|
|||
|
||||
|
||||
class ToolLogic(BaseLogic[Union[ToolLogicMixin, HasLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, MoneyLogicMixin, MagicLogicMixin]]):
|
||||
|
||||
def has_all_tools(self, tools: Iterable[Tuple[str, str]]):
|
||||
return self.logic.and_(*(self.logic.tool.has_tool(tool, material) for tool, material in tools))
|
||||
|
||||
# Should be cached
|
||||
def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule:
|
||||
assert tool != Tool.fishing_rod, "Use `has_fishing_rod` instead of `has_tool`."
|
||||
if tool == Tool.fishing_rod:
|
||||
return self.logic.tool.has_fishing_rod(tool_materials[material])
|
||||
|
||||
if tool == Tool.pan and material == ToolMaterial.basic:
|
||||
material = ToolMaterial.copper # The first Pan is the copper one, so the basic one does not exist
|
||||
|
||||
if material == ToolMaterial.basic or tool == Tool.scythe:
|
||||
return True_()
|
||||
|
@ -52,7 +60,14 @@ class ToolLogic(BaseLogic[Union[ToolLogicMixin, HasLogicMixin, ReceivedLogicMixi
|
|||
if self.options.tool_progression & ToolProgression.option_progressive:
|
||||
return self.logic.received(f"Progressive {tool}", tool_materials[material])
|
||||
|
||||
return self.logic.has(f"{material} Bar") & self.logic.money.can_spend_at(Region.blacksmith, tool_upgrade_prices[material])
|
||||
can_upgrade_rule = self.logic.has(f"{material} Bar") & self.logic.money.can_spend_at(Region.blacksmith, tool_upgrade_prices[material])
|
||||
if tool == Tool.pan:
|
||||
has_base_pan = self.logic.received("Glittering Boulder Removed") & self.logic.region.can_reach(Region.mountain)
|
||||
if material == ToolMaterial.copper:
|
||||
return has_base_pan
|
||||
return has_base_pan & can_upgrade_rule
|
||||
|
||||
return can_upgrade_rule
|
||||
|
||||
def can_use_tool_at(self, tool: str, material: str, region: str) -> StardewRule:
|
||||
return self.has_tool(tool, material) & self.logic.region.can_reach(region)
|
||||
|
|
|
@ -10,13 +10,13 @@ from ...logic.skill_logic import SkillLogicMixin
|
|||
from ...logic.tool_logic import ToolLogicMixin
|
||||
from ...mods.mod_data import ModNames
|
||||
from ...options import ElevatorProgression
|
||||
from ...stardew_rule import StardewRule, True_, And, true_
|
||||
from ...strings.ap_names.mods.mod_items import DeepWoodsItem, SkillLevel
|
||||
from ...stardew_rule import StardewRule, True_, true_
|
||||
from ...strings.ap_names.mods.mod_items import DeepWoodsItem
|
||||
from ...strings.ap_names.transport_names import ModTransportation
|
||||
from ...strings.craftable_names import Bomb
|
||||
from ...strings.food_names import Meal
|
||||
from ...strings.performance_names import Performance
|
||||
from ...strings.skill_names import Skill
|
||||
from ...strings.skill_names import Skill, ModSkill
|
||||
from ...strings.tool_names import Tool, ToolMaterial
|
||||
|
||||
|
||||
|
@ -45,11 +45,11 @@ CookingLogicMixin]]):
|
|||
self.logic.received(ModTransportation.woods_obelisk))
|
||||
|
||||
tier = int(depth / 25) + 1
|
||||
if self.options.skill_progression == options.SkillProgression.option_progressive:
|
||||
if self.options.skill_progression >= options.SkillProgression.option_progressive:
|
||||
combat_tier = min(10, max(0, tier + 5))
|
||||
rules.append(self.logic.skill.has_level(Skill.combat, combat_tier))
|
||||
|
||||
return And(*rules)
|
||||
return self.logic.and_(*rules)
|
||||
|
||||
def has_woods_rune_to_depth(self, floor: int) -> StardewRule:
|
||||
if self.options.elevator_progression == ElevatorProgression.option_vanilla:
|
||||
|
@ -66,8 +66,8 @@ CookingLogicMixin]]):
|
|||
self.logic.received(DeepWoodsItem.pendant_elder),
|
||||
self.logic.skill.has_total_level(40)]
|
||||
if ModNames.luck_skill in self.options.mods:
|
||||
rules.append(self.logic.received(SkillLevel.luck, 7))
|
||||
rules.append(self.logic.skill.has_level(ModSkill.luck, 7))
|
||||
else:
|
||||
rules.append(
|
||||
self.logic.has(Meal.magic_rock_candy)) # You need more luck than this, but it'll push the logic down a ways; you can get the rest there.
|
||||
return And(*rules)
|
||||
return self.logic.and_(*rules)
|
||||
|
|
|
@ -7,7 +7,7 @@ from ...logic.base_logic import BaseLogicMixin, BaseLogic
|
|||
from ...logic.combat_logic import CombatLogicMixin
|
||||
from ...logic.cooking_logic import CookingLogicMixin
|
||||
from ...logic.crafting_logic import CraftingLogicMixin
|
||||
from ...logic.crop_logic import CropLogicMixin
|
||||
from ...logic.farming_logic import FarmingLogicMixin
|
||||
from ...logic.fishing_logic import FishingLogicMixin
|
||||
from ...logic.has_logic import HasLogicMixin
|
||||
from ...logic.money_logic import MoneyLogicMixin
|
||||
|
@ -24,11 +24,10 @@ from ...options import Cropsanity
|
|||
from ...stardew_rule import StardewRule, True_
|
||||
from ...strings.artisan_good_names import ModArtisanGood
|
||||
from ...strings.craftable_names import ModCraftable, ModEdible, ModMachine
|
||||
from ...strings.crop_names import SVEVegetable, SVEFruit, DistantLandsCrop, Fruit
|
||||
from ...strings.fish_names import WaterItem
|
||||
from ...strings.flower_names import Flower
|
||||
from ...strings.crop_names import SVEVegetable, SVEFruit, DistantLandsCrop
|
||||
from ...strings.fish_names import ModTrash, SVEFish
|
||||
from ...strings.food_names import SVEMeal, SVEBeverage
|
||||
from ...strings.forageable_names import SVEForage, DistantLandsForageable, Forageable
|
||||
from ...strings.forageable_names import SVEForage, DistantLandsForageable
|
||||
from ...strings.gift_names import SVEGift
|
||||
from ...strings.ingredient_names import Ingredient
|
||||
from ...strings.material_names import Material
|
||||
|
@ -53,8 +52,9 @@ class ModItemLogicMixin(BaseLogicMixin):
|
|||
self.item = ModItemLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class ModItemLogic(BaseLogic[Union[CombatLogicMixin, ReceivedLogicMixin, CropLogicMixin, CookingLogicMixin, FishingLogicMixin, HasLogicMixin, MoneyLogicMixin,
|
||||
RegionLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, ToolLogicMixin, CraftingLogicMixin, SkillLogicMixin, TimeLogicMixin, QuestLogicMixin]]):
|
||||
class ModItemLogic(BaseLogic[Union[CombatLogicMixin, ReceivedLogicMixin, CookingLogicMixin, FishingLogicMixin, HasLogicMixin, MoneyLogicMixin,
|
||||
RegionLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, ToolLogicMixin, CraftingLogicMixin, SkillLogicMixin, TimeLogicMixin, QuestLogicMixin,
|
||||
FarmingLogicMixin]]):
|
||||
|
||||
def get_modded_item_rules(self) -> Dict[str, StardewRule]:
|
||||
items = dict()
|
||||
|
@ -78,53 +78,53 @@ RegionLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, To
|
|||
def get_sve_item_rules(self):
|
||||
return {SVEGift.aged_blue_moon_wine: self.logic.money.can_spend_at(SVERegion.sophias_house, 28000),
|
||||
SVEGift.blue_moon_wine: self.logic.money.can_spend_at(SVERegion.sophias_house, 3000),
|
||||
SVESeed.fungus_seed: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon,
|
||||
SVESeed.fungus: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon,
|
||||
ModLoot.green_mushroom: self.logic.region.can_reach(SVERegion.highlands_outside) &
|
||||
self.logic.tool.has_tool(Tool.axe, ToolMaterial.iron) & self.logic.season.has_any_not_winter(),
|
||||
SVEFruit.monster_fruit: self.logic.season.has(Season.summer) & self.logic.has(SVESeed.stalk_seed),
|
||||
SVEVegetable.monster_mushroom: self.logic.season.has(Season.fall) & self.logic.has(SVESeed.fungus_seed),
|
||||
SVEForage.ornate_treasure_chest: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_galaxy_weapon &
|
||||
SVEFruit.monster_fruit: self.logic.season.has(Season.summer) & self.logic.has(SVESeed.stalk),
|
||||
SVEVegetable.monster_mushroom: self.logic.season.has(Season.fall) & self.logic.has(SVESeed.fungus),
|
||||
ModLoot.ornate_treasure_chest: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_galaxy_weapon &
|
||||
self.logic.tool.has_tool(Tool.axe, ToolMaterial.iron),
|
||||
SVEFruit.slime_berry: self.logic.season.has(Season.spring) & self.logic.has(SVESeed.slime_seed),
|
||||
SVESeed.slime_seed: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon,
|
||||
SVESeed.stalk_seed: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon,
|
||||
SVEForage.swirl_stone: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
|
||||
SVEVegetable.void_root: self.logic.season.has(Season.winter) & self.logic.has(SVESeed.void_seed),
|
||||
SVESeed.void_seed: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon,
|
||||
SVEForage.void_soul: self.logic.region.can_reach(
|
||||
SVEFruit.slime_berry: self.logic.season.has(Season.spring) & self.logic.has(SVESeed.slime),
|
||||
SVESeed.slime: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon,
|
||||
SVESeed.stalk: self.logic.region.can_reach(SVERegion.highlands_outside) & self.logic.combat.has_good_weapon,
|
||||
ModLoot.swirl_stone: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
|
||||
SVEVegetable.void_root: self.logic.season.has(Season.winter) & self.logic.has(SVESeed.void),
|
||||
SVESeed.void: self.logic.region.can_reach(SVERegion.highlands_cavern) & self.logic.combat.has_good_weapon,
|
||||
ModLoot.void_soul: self.logic.region.can_reach(
|
||||
SVERegion.crimson_badlands) & self.logic.combat.has_good_weapon & self.logic.cooking.can_cook(),
|
||||
SVEForage.winter_star_rose: self.logic.region.can_reach(SVERegion.summit) & self.logic.season.has(Season.winter),
|
||||
SVEForage.bearberrys: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has(Season.winter),
|
||||
SVEForage.bearberry: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has(Season.winter),
|
||||
SVEForage.poison_mushroom: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has_any([Season.summer, Season.fall]),
|
||||
SVEForage.red_baneberry: self.logic.region.can_reach(Region.secret_woods) & self.logic.season.has(Season.summer),
|
||||
SVEForage.ferngill_primrose: self.logic.region.can_reach(SVERegion.summit) & self.logic.season.has(Season.spring),
|
||||
SVEForage.goldenrod: self.logic.region.can_reach(SVERegion.summit) & (
|
||||
self.logic.season.has(Season.summer) | self.logic.season.has(Season.fall)),
|
||||
SVESeed.shrub_seed: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic),
|
||||
SVEFruit.salal_berry: self.logic.crop.can_plant_and_grow_item([Season.spring, Season.summer]) & self.logic.has(SVESeed.shrub_seed),
|
||||
SVESeed.shrub: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic),
|
||||
SVEFruit.salal_berry: self.logic.farming.can_plant_and_grow_item((Season.spring, Season.summer)) & self.logic.has(SVESeed.shrub),
|
||||
ModEdible.aegis_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 28000),
|
||||
ModEdible.lightning_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 12000),
|
||||
ModEdible.barbarian_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 22000),
|
||||
ModEdible.gravity_elixir: self.logic.money.can_spend_at(SVERegion.galmoran_outpost, 4000),
|
||||
SVESeed.ancient_ferns_seed: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic),
|
||||
SVEVegetable.ancient_fiber: self.logic.crop.can_plant_and_grow_item(Season.summer) & self.logic.has(SVESeed.ancient_ferns_seed),
|
||||
SVEForage.big_conch: self.logic.region.can_reach_any((Region.beach, SVERegion.fable_reef)),
|
||||
SVESeed.ancient_fern: self.logic.region.can_reach(Region.secret_woods) & self.logic.tool.has_tool(Tool.hoe, ToolMaterial.basic),
|
||||
SVEVegetable.ancient_fiber: self.logic.farming.can_plant_and_grow_item(Season.summer) & self.logic.has(SVESeed.ancient_fern),
|
||||
SVEForage.conch: self.logic.region.can_reach_any((Region.beach, SVERegion.fable_reef)),
|
||||
SVEForage.dewdrop_berry: self.logic.region.can_reach(SVERegion.enchanted_grove),
|
||||
SVEForage.dried_sand_dollar: self.logic.region.can_reach(SVERegion.fable_reef) | (self.logic.region.can_reach(Region.beach) &
|
||||
SVEForage.sand_dollar: self.logic.region.can_reach(SVERegion.fable_reef) | (self.logic.region.can_reach(Region.beach) &
|
||||
self.logic.season.has_any([Season.summer, Season.fall])),
|
||||
SVEForage.golden_ocean_flower: self.logic.region.can_reach(SVERegion.fable_reef),
|
||||
SVEMeal.grampleton_orange_chicken: self.logic.money.can_spend_at(Region.saloon, 650) & self.logic.relationship.has_hearts(ModNPC.sophia, 6),
|
||||
ModEdible.hero_elixir: self.logic.money.can_spend_at(SVERegion.isaac_shop, 8000),
|
||||
SVEForage.lucky_four_leaf_clover: self.logic.region.can_reach_any((Region.secret_woods, SVERegion.forest_west)) &
|
||||
SVEForage.four_leaf_clover: self.logic.region.can_reach_any((Region.secret_woods, SVERegion.forest_west)) &
|
||||
self.logic.season.has_any([Season.spring, Season.summer]),
|
||||
SVEForage.mushroom_colony: self.logic.region.can_reach_any((Region.secret_woods, SVERegion.junimo_woods, SVERegion.forest_west)) &
|
||||
self.logic.season.has(Season.fall),
|
||||
SVEForage.rusty_blade: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
|
||||
SVEForage.smelly_rafflesia: self.logic.region.can_reach(Region.secret_woods),
|
||||
SVEForage.rafflesia: self.logic.region.can_reach(Region.secret_woods),
|
||||
SVEBeverage.sports_drink: self.logic.money.can_spend_at(Region.hospital, 750),
|
||||
"Stamina Capsule": self.logic.money.can_spend_at(Region.hospital, 4000),
|
||||
SVEForage.thistle: self.logic.region.can_reach(SVERegion.summit),
|
||||
SVEForage.void_pebble: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
|
||||
ModLoot.void_pebble: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_great_weapon,
|
||||
ModLoot.void_shard: self.logic.region.can_reach(SVERegion.crimson_badlands) & self.logic.combat.has_galaxy_weapon &
|
||||
self.logic.skill.has_level(Skill.combat, 10) & self.logic.region.can_reach(Region.saloon) & self.logic.time.has_year_three
|
||||
}
|
||||
|
@ -135,49 +135,17 @@ RegionLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, To
|
|||
Loot.void_essence: items[Loot.void_essence] | self.logic.region.can_reach(SVERegion.highlands_cavern) | self.logic.region.can_reach(
|
||||
SVERegion.crimson_badlands),
|
||||
Loot.solar_essence: items[Loot.solar_essence] | self.logic.region.can_reach(SVERegion.crimson_badlands),
|
||||
Flower.tulip: items[Flower.tulip] | self.logic.tool.can_forage(Season.spring, SVERegion.sprite_spring),
|
||||
Flower.blue_jazz: items[Flower.blue_jazz] | self.logic.tool.can_forage(Season.spring, SVERegion.sprite_spring),
|
||||
Flower.summer_spangle: items[Flower.summer_spangle] | self.logic.tool.can_forage(Season.summer, SVERegion.sprite_spring),
|
||||
Flower.sunflower: items[Flower.sunflower] | self.logic.tool.can_forage((Season.summer, Season.fall), SVERegion.sprite_spring),
|
||||
Flower.fairy_rose: items[Flower.fairy_rose] | self.logic.tool.can_forage(Season.fall, SVERegion.sprite_spring),
|
||||
Fruit.ancient_fruit: items[Fruit.ancient_fruit] | (
|
||||
self.logic.tool.can_forage((Season.spring, Season.summer, Season.fall), SVERegion.sprite_spring) &
|
||||
self.logic.time.has_year_three) | self.logic.region.can_reach(SVERegion.sprite_spring_cave),
|
||||
Fruit.sweet_gem_berry: items[Fruit.sweet_gem_berry] | (
|
||||
self.logic.tool.can_forage((Season.spring, Season.summer, Season.fall), SVERegion.sprite_spring) &
|
||||
self.logic.time.has_year_three),
|
||||
WaterItem.coral: items[WaterItem.coral] | self.logic.region.can_reach(SVERegion.fable_reef),
|
||||
Forageable.rainbow_shell: items[Forageable.rainbow_shell] | self.logic.region.can_reach(SVERegion.fable_reef),
|
||||
WaterItem.sea_urchin: items[WaterItem.sea_urchin] | self.logic.region.can_reach(SVERegion.fable_reef),
|
||||
Forageable.red_mushroom: items[Forageable.red_mushroom] | self.logic.tool.can_forage((Season.summer, Season.fall), SVERegion.forest_west) |
|
||||
self.logic.region.can_reach(SVERegion.sprite_spring_cave),
|
||||
Forageable.purple_mushroom: items[Forageable.purple_mushroom] | self.logic.tool.can_forage(Season.fall, SVERegion.forest_west) |
|
||||
self.logic.region.can_reach(SVERegion.sprite_spring_cave),
|
||||
Forageable.morel: items[Forageable.morel] | self.logic.tool.can_forage(Season.fall, SVERegion.forest_west),
|
||||
Forageable.chanterelle: items[Forageable.chanterelle] | self.logic.tool.can_forage(Season.fall, SVERegion.forest_west) |
|
||||
self.logic.region.can_reach(SVERegion.sprite_spring_cave),
|
||||
Ore.copper: items[Ore.copper] | (self.logic.tool.can_use_tool_at(Tool.pickaxe, ToolMaterial.basic, SVERegion.highlands_cavern) &
|
||||
self.logic.combat.can_fight_at_level(Performance.great)),
|
||||
Ore.iron: items[Ore.iron] | (self.logic.tool.can_use_tool_at(Tool.pickaxe, ToolMaterial.basic, SVERegion.highlands_cavern) &
|
||||
self.logic.combat.can_fight_at_level(Performance.great)),
|
||||
Ore.iridium: items[Ore.iridium] | (self.logic.tool.can_use_tool_at(Tool.pickaxe, ToolMaterial.basic, SVERegion.crimson_badlands) &
|
||||
self.logic.combat.can_fight_at_level(Performance.maximum)),
|
||||
SVEFish.dulse_seaweed: self.logic.fishing.can_fish_at(Region.beach) & self.logic.season.has_any([Season.spring, Season.summer, Season.winter])
|
||||
}
|
||||
|
||||
def get_modified_item_rules_for_deep_woods(self, items: Dict[str, StardewRule]):
|
||||
options_to_update = {
|
||||
Fruit.apple: items[Fruit.apple] | self.logic.region.can_reach(DeepWoodsRegion.floor_10), # Deep enough to have seen such a tree at least once
|
||||
Fruit.apricot: items[Fruit.apricot] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Fruit.cherry: items[Fruit.cherry] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Fruit.orange: items[Fruit.orange] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Fruit.peach: items[Fruit.peach] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Fruit.pomegranate: items[Fruit.pomegranate] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Fruit.mango: items[Fruit.mango] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Flower.tulip: items[Flower.tulip] | self.logic.tool.can_forage(Season.not_winter, DeepWoodsRegion.floor_10),
|
||||
Flower.blue_jazz: items[Flower.blue_jazz] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Flower.summer_spangle: items[Flower.summer_spangle] | self.logic.tool.can_forage(Season.not_winter, DeepWoodsRegion.floor_10),
|
||||
Flower.poppy: items[Flower.poppy] | self.logic.tool.can_forage(Season.not_winter, DeepWoodsRegion.floor_10),
|
||||
Flower.fairy_rose: items[Flower.fairy_rose] | self.logic.region.can_reach(DeepWoodsRegion.floor_10),
|
||||
Material.hardwood: items[Material.hardwood] | self.logic.tool.can_use_tool_at(Tool.axe, ToolMaterial.iron, DeepWoodsRegion.floor_10),
|
||||
Ingredient.sugar: items[Ingredient.sugar] | self.logic.tool.can_use_tool_at(Tool.axe, ToolMaterial.gold, DeepWoodsRegion.floor_50),
|
||||
# Gingerbread House
|
||||
|
@ -207,6 +175,7 @@ RegionLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, To
|
|||
archaeology_item_rules[location_name] = display_item_rule & preservation_chamber_rule
|
||||
else:
|
||||
archaeology_item_rules[location_name] = display_item_rule & hardwood_preservation_chamber_rule
|
||||
archaeology_item_rules[ModTrash.rusty_scrap] = self.logic.has(ModMachine.grinder) & self.logic.has_any(*all_artifacts)
|
||||
return archaeology_item_rules
|
||||
|
||||
def get_distant_lands_item_rules(self):
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue