Archipelago/worlds/stardew_valley/logic/logic.py

675 lines
49 KiB
Python

from __future__ import annotations
from dataclasses import dataclass
from typing import Collection
from .ability_logic import AbilityLogicMixin
from .action_logic import ActionLogicMixin
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 .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 .has_logic import HasLogicMixin
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 .quest_logic import QuestLogicMixin
from .received_logic import ReceivedLogicMixin
from .region_logic import RegionLogicMixin
from .relationship_logic import RelationshipLogicMixin
from .season_logic import SeasonLogicMixin
from .shipping_logic import ShippingLogicMixin
from .skill_logic import SkillLogicMixin
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 ..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 ..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.community_upgrade_names import CommunityUpgrade
from ..strings.artisan_good_names import ArtisanGood
from ..strings.building_names import Building
from ..strings.craftable_names import Consumable, Furniture, Ring, Fishing, Lighting, WildSeeds
from ..strings.crop_names import Fruit, Vegetable
from ..strings.currency_names import Currency
from ..strings.decoration_names import Decoration
from ..strings.fertilizer_names import Fertilizer, SpeedGro, RetainingSoil
from ..strings.festival_check_names import FestivalCheck
from ..strings.fish_names import Fish, Trash, WaterItem, WaterChest
from ..strings.flower_names import Flower
from ..strings.food_names import Meal, Beverage
from ..strings.forageable_names import Forageable
from ..strings.fruit_tree_names import Sapling
from ..strings.generic_names import Generic
from ..strings.geode_names import Geode
from ..strings.gift_names import Gift
from ..strings.ingredient_names import Ingredient
from ..strings.machine_names import Machine
from ..strings.material_names import Material
from ..strings.metal_names import Ore, MetalBar, Mineral, Fossil
from ..strings.monster_drop_names import Loot
from ..strings.monster_names import Monster
from ..strings.region_names import Region
from ..strings.season_names import Season
from ..strings.seed_names import Seed, TreeSeed
from ..strings.skill_names import Skill
from ..strings.tool_names import Tool, ToolMaterial
from ..strings.villager_names import NPC
from ..strings.wallet_item_names import Wallet
@dataclass(frozen=False, repr=False)
class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, BuffLogicMixin, TravelingMerchantLogicMixin, TimeLogicMixin,
SeasonLogicMixin, MoneyLogicMixin, ActionLogicMixin, ArcadeLogicMixin, ArtisanLogicMixin, GiftLogicMixin,
BuildingLogicMixin, ShippingLogicMixin, RelationshipLogicMixin, MuseumLogicMixin, WalletLogicMixin, AnimalLogicMixin,
CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, CropLogicMixin,
SkillLogicMixin, FarmingLogicMixin, BundleLogicMixin, FishingLogicMixin, MineLogicMixin, CookingLogicMixin, AbilityLogicMixin,
SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin):
player: int
options: StardewValleyOptions
regions: Collection[str]
def __init__(self, player: int, options: StardewValleyOptions, regions: Collection[str]):
self.registry = LogicRegistry()
super().__init__(player, self.registry, options, 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.museum_rules.update({donation.item_name: self.museum.can_find_museum_item(donation) for donation in all_museum_items})
for recipe in all_cooking_recipes:
if recipe.mod_name and recipe.mod_name not in self.options.mods:
continue
can_cook_rule = self.cooking.can_cook(recipe)
if recipe.meal in self.registry.cooking_rules:
can_cook_rule = can_cook_rule | self.registry.cooking_rules[recipe.meal]
self.registry.cooking_rules[recipe.meal] = can_cook_rule
for recipe in all_crafting_recipes:
if recipe.mod_name and recipe.mod_name not in self.options.mods:
continue
can_craft_rule = self.crafting.can_craft(recipe)
if recipe.item in self.registry.crafting_rules:
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),
})
# @formatter:off
self.registry.item_rules.update({
"Energy Tonic": self.money.can_spend_at(Region.hospital, 1000),
WaterChest.fishing_chest: self.fishing.can_fish_chests(),
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),
"JotPK Big Buff": self.arcade.has_jotpk_power_level(7),
"JotPK Max Buff": self.arcade.has_jotpk_power_level(9),
"JotPK Medium Buff": self.arcade.has_jotpk_power_level(4),
"JotPK Small Buff": self.arcade.has_jotpk_power_level(2),
"Junimo Kart Big Buff": self.arcade.has_junimo_kart_power_level(6),
"Junimo Kart Max Buff": self.arcade.has_junimo_kart_power_level(8),
"Junimo Kart Medium Buff": self.arcade.has_junimo_kart_power_level(4),
"Junimo Kart Small Buff": self.arcade.has_junimo_kart_power_level(2),
"Magic Rock Candy": self.region.can_reach(Region.desert) & self.has("Prismatic Shard"),
"Muscle Remedy": self.money.can_spend_at(Region.hospital, 1000),
# self.has(Ingredient.vinegar)),
# self.received("Deluxe Fertilizer Recipe") & self.has(MetalBar.iridium) & self.has(SVItem.sap),
# | (self.ability.can_cook() & self.relationship.has_hearts(NPC.emily, 3) & self.has(Forageable.leek) & self.has(Forageable.dandelion) &
# | (self.ability.can_cook() & self.relationship.has_hearts(NPC.jodi, 7) & self.has(AnimalProduct.cow_milk) & self.has(Ingredient.sugar)),
Animal.chicken: self.animal.can_buy_animal(Animal.chicken),
Animal.cow: self.animal.can_buy_animal(Animal.cow),
Animal.dinosaur: self.building.has_building(Building.big_coop) & self.has(AnimalProduct.dinosaur_egg),
Animal.duck: self.animal.can_buy_animal(Animal.duck),
Animal.goat: self.animal.can_buy_animal(Animal.goat),
Animal.ostrich: self.building.has_building(Building.barn) & self.has(AnimalProduct.ostrich_egg) & self.has(Machine.ostrich_incubator),
Animal.pig: self.animal.can_buy_animal(Animal.pig),
Animal.rabbit: self.animal.can_buy_animal(Animal.rabbit),
Animal.sheep: self.animal.can_buy_animal(Animal.sheep),
AnimalProduct.any_egg: self.has_any(AnimalProduct.chicken_egg, AnimalProduct.duck_egg),
AnimalProduct.brown_egg: self.animal.has_animal(Animal.chicken),
AnimalProduct.chicken_egg: self.has_any(AnimalProduct.egg, AnimalProduct.brown_egg, AnimalProduct.large_egg, AnimalProduct.large_brown_egg),
AnimalProduct.cow_milk: self.has_any(AnimalProduct.milk, AnimalProduct.large_milk),
AnimalProduct.duck_egg: self.animal.has_animal(Animal.duck),
AnimalProduct.duck_feather: self.animal.has_happy_animal(Animal.duck),
AnimalProduct.egg: self.animal.has_animal(Animal.chicken),
AnimalProduct.goat_milk: self.has(Animal.goat),
AnimalProduct.golden_egg: self.received(AnimalProduct.golden_egg) & (self.money.can_spend_at(Region.ranch, 100000) | self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 100)),
AnimalProduct.large_brown_egg: self.animal.has_happy_animal(Animal.chicken),
AnimalProduct.large_egg: self.animal.has_happy_animal(Animal.chicken),
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.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)),
AnimalProduct.sturgeon_roe: self.has(Fish.sturgeon) & self.building.has_building(Building.fish_pond),
AnimalProduct.truffle: self.animal.has_animal(Animal.pig) & self.season.has_any_not_winter(),
AnimalProduct.void_egg: self.money.can_spend_at(Region.sewer, 5000) | (self.building.has_building(Building.fish_pond) & self.has(Fish.void_salmon)),
AnimalProduct.wool: self.animal.has_animal(Animal.rabbit) | self.animal.has_animal(Animal.sheep),
AnimalProduct.slime_egg_green: self.has(Machine.slime_egg_press) & self.has(Loot.slime),
AnimalProduct.slime_egg_blue: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(3),
AnimalProduct.slime_egg_red: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(6),
AnimalProduct.slime_egg_purple: self.has(Machine.slime_egg_press) & self.has(Loot.slime) & self.time.has_lived_months(9),
AnimalProduct.slime_egg_tiger: self.has(Fish.lionfish) & self.building.has_building(Building.fish_pond),
ArtisanGood.aged_roe: self.artisan.can_preserves_jar(AnimalProduct.roe),
ArtisanGood.battery_pack: (self.has(Machine.lightning_rod) & self.season.has_any_not_winter()) | self.has(Machine.solar_panel),
ArtisanGood.caviar: self.artisan.can_preserves_jar(AnimalProduct.sturgeon_roe),
ArtisanGood.cheese: (self.has(AnimalProduct.cow_milk) & self.has(Machine.cheese_press)) | (self.region.can_reach(Region.desert) & self.has(Mineral.emerald)),
ArtisanGood.cloth: (self.has(AnimalProduct.wool) & self.has(Machine.loom)) | (self.region.can_reach(Region.desert) & self.has(Mineral.aquamarine)),
ArtisanGood.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.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.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"),
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.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),
Fish.mussel: self.tool.can_forage(Generic.any, Region.beach) or self.has(Fish.mussel_node),
Fish.mussel_node: self.region.can_reach(Region.island_west),
Fish.oyster: self.tool.can_forage(Generic.any, Region.beach),
Fish.periwinkle: self.skill.can_crab_pot_at(Region.town),
Fish.shrimp: self.skill.can_crab_pot_at(Region.beach),
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),
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.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),
Fossil.snake_vertebrae: self.region.can_reach(Region.island_west) & self.tool.has_tool(Tool.hoe),
Geode.artifact_trove: self.has(Geode.omni) & self.region.can_reach(Region.desert),
Geode.frozen: self.mine.can_mine_in_the_mines_floor_41_80(),
Geode.geode: self.mine.can_mine_in_the_mines_floor_1_40(),
Geode.golden_coconut: self.region.can_reach(Region.island_north),
Geode.magma: self.mine.can_mine_in_the_mines_floor_81_120() | (self.has(Fish.lava_eel) & self.building.has_building(Building.fish_pond)),
Geode.omni: self.mine.can_mine_in_the_mines_floor_41_80() | self.region.can_reach(Region.desert) | self.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),
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.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,
Gift.void_ghost_pendant: self.money.can_trade_at(Region.desert, Loot.void_essence, 200) & self.relationship.has_hearts(NPC.krobus, 10),
Gift.wilted_bouquet: self.has(Machine.furnace) & self.has(Gift.bouquet) & self.has(Material.coal),
Ingredient.oil: self.money.can_spend_at(Region.pierre_store, 200) | (self.has(Machine.oil_maker) & (self.has(Vegetable.corn) | self.has(Flower.sunflower) | self.has(Seed.sunflower))),
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.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.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.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.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),
MetalBar.iridium: self.can_smelt(Ore.iridium),
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.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,
Trash.broken_glasses: self.skill.can_crab_pot,
Trash.driftwood: self.skill.can_crab_pot,
Trash.joja_cola: self.money.can_spend_at(Region.saloon, 75),
Trash.soggy_newspaper: self.skill.can_crab_pot,
Trash.trash: self.skill.can_crab_pot,
TreeSeed.acorn: self.skill.has_level(Skill.foraging, 1) & self.ability.can_chop_trees(),
TreeSeed.mahogany: self.region.can_reach(Region.secret_woods) & self.tool.has_tool(Tool.axe, ToolMaterial.iron) & self.skill.has_level(Skill.foraging, 1),
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(),
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.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
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.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
# For some recipes, the cooked item can be obtained directly, so we either cook it or get it
for recipe in self.registry.cooking_rules:
cooking_rule = self.registry.cooking_rules[recipe]
obtention_rule = self.registry.item_rules[recipe] if recipe in self.registry.item_rules else False_()
self.registry.item_rules[recipe] = obtention_rule | cooking_rule
# For some recipes, the crafted item can be obtained directly, so we either craft it or get it
for recipe in self.registry.crafting_rules:
crafting_rule = self.registry.crafting_rules[recipe]
obtention_rule = self.registry.item_rules[recipe] if recipe in self.registry.item_rules else False_()
self.registry.item_rules[recipe] = obtention_rule | crafting_rule
self.building.initialize_rules()
self.building.update_rules(self.mod.building.get_modded_building_rules())
self.quest.initialize_rules()
self.quest.update_rules(self.mod.quest.get_modded_quest_rules())
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.tub_o_flowers: self.money.can_spend(2000),
FestivalCheck.rarecrow_5: self.money.can_spend(2500),
FestivalCheck.luau_soup: self.can_succeed_luau_soup(),
FestivalCheck.moonlight_jellies: True_(),
FestivalCheck.moonlight_jellies_banner: self.money.can_spend(800),
FestivalCheck.starport_decal: self.money.can_spend(1000),
FestivalCheck.smashing_stone: True_(),
FestivalCheck.grange_display: self.can_succeed_grange_display(),
FestivalCheck.rarecrow_1: True_(), # only cost star tokens
FestivalCheck.fair_stardrop: True_(), # only cost star tokens
FestivalCheck.spirit_eve_maze: True_(),
FestivalCheck.jack_o_lantern: self.money.can_spend(2000),
FestivalCheck.rarecrow_2: self.money.can_spend(5000),
FestivalCheck.fishing_competition: self.can_win_fishing_competition(),
FestivalCheck.rarecrow_4: self.money.can_spend(5000),
FestivalCheck.mermaid_pearl: self.has(Forageable.secret_note),
FestivalCheck.cone_hat: self.money.can_spend(2500),
FestivalCheck.iridium_fireplace: self.money.can_spend(15000),
FestivalCheck.rarecrow_7: self.money.can_spend(5000) & self.museum.can_donate_museum_artifacts(20),
FestivalCheck.rarecrow_8: self.money.can_spend(5000) & self.museum.can_donate_museum_items(40),
FestivalCheck.lupini_red_eagle: self.money.can_spend(1200),
FestivalCheck.lupini_portrait_mermaid: self.money.can_spend(1200),
FestivalCheck.lupini_solar_kingdom: self.money.can_spend(1200),
FestivalCheck.lupini_clouds: self.time.has_year_two & self.money.can_spend(1200),
FestivalCheck.lupini_1000_years: self.time.has_year_two & self.money.can_spend(1200),
FestivalCheck.lupini_three_trees: self.time.has_year_two & self.money.can_spend(1200),
FestivalCheck.lupini_the_serpent: self.time.has_year_three & self.money.can_spend(1200),
FestivalCheck.lupini_tropical_fish: self.time.has_year_three & self.money.can_spend(1200),
FestivalCheck.lupini_land_of_clay: self.time.has_year_three & self.money.can_spend(1200),
FestivalCheck.secret_santa: self.gifts.has_any_universal_love,
FestivalCheck.legend_of_the_winter_star: True_(),
FestivalCheck.rarecrow_3: True_(),
FestivalCheck.all_rarecrows: self.region.can_reach(Region.farm) & self.has_all_rarecrows(),
})
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 can_smelt(self, item: str) -> StardewRule:
return self.has(Machine.furnace) & self.has(item)
def can_complete_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
def can_finish_grandpa_evaluation(self) -> StardewRule:
# https://stardewvalleywiki.com/Grandpa
rules_worth_a_point = [
self.money.can_have_earned_total(50000), # 50 000g
self.money.can_have_earned_total(100000), # 100 000g
self.money.can_have_earned_total(200000), # 200 000g
self.money.can_have_earned_total(300000), # 300 000g
self.money.can_have_earned_total(500000), # 500 000g
self.money.can_have_earned_total(1000000), # 1 000 000g first point
self.money.can_have_earned_total(1000000), # 1 000 000g second point
self.skill.has_total_level(30), # Total Skills: 30
self.skill.has_total_level(50), # Total Skills: 50
self.museum.can_complete_museum(), # Completing the museum for a point
# 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.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
self.received(Wallet.skull_key), # Skull Key obtained
self.wallet.has_rusty_key(), # Rusty key obtained
]
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,
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)
# There are a few other valid items, but I don't feel like coding them all
return fish_rule | aged_rule
def can_succeed_grange_display(self) -> StardewRule:
if self.options.festival_locations != FestivalLocations.option_hard:
return True_()
animal_rule = self.animal.has_animal(Generic.any)
artisan_rule = self.artisan.can_keg(Generic.any) | self.artisan.can_preserves_jar(Generic.any)
cooking_rule = self.money.can_spend_at(Region.saloon, 220) # Salads at the bar are good enough
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, ]
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]
vegetable_rule = self.has_any(*good_vegetables)
return animal_rule & artisan_rule & cooking_rule & fish_rule & \
forage_rule & fruit_rule & mineral_rule & vegetable_rule
def can_win_fishing_competition(self) -> StardewRule:
return self.skill.can_fish(difficulty=60)
def has_island_trader(self) -> StardewRule:
if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return False_()
return self.region.can_reach(Region.island_trader)
def has_walnut(self, number: int) -> StardewRule:
if self.options.exclude_ginger_island == ExcludeGingerIsland.option_true:
return False_()
if number <= 0:
return True_()
# 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)
reach_west = self.region.can_reach(Region.island_west)
reach_hut = self.region.can_reach(Region.leo_hut)
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_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_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),
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,
reach_caves, reach_southeast, reach_field_office, reach_pirate_cove)
if number <= 5:
return 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)
if number <= 50:
return reach_entire_island
gems = (Mineral.amethyst, Mineral.aquamarine, Mineral.emerald, Mineral.ruby, Mineral.topaz)
return reach_entire_island & self.has(Fruit.banana) & self.has_all(*gems) & self.ability.can_mine_perfectly() & \
self.ability.can_fish_perfectly() & self.has(Furniture.flute_block) & self.has(Seed.melon) & self.has(Seed.wheat) & self.has(Seed.garlic) & \
self.can_complete_field_office()
def has_all_stardrops(self) -> StardewRule:
other_rules = []
number_of_stardrops_to_receive = 0
number_of_stardrops_to_receive += 1 # The Mines level 100
number_of_stardrops_to_receive += 1 # Old Master Cannoli
number_of_stardrops_to_receive += 1 # Museum Stardrop
number_of_stardrops_to_receive += 1 # Krobus Stardrop
if self.options.fishsanity == Fishsanity.option_none: # Master Angler Stardrop
other_rules.append(self.fishing.can_catch_every_fish())
else:
number_of_stardrops_to_receive += 1
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:
number_of_stardrops_to_receive += 1
if ModNames.deepwoods in self.options.mods: # Petting the Unicorn
number_of_stardrops_to_receive += 1
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")
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)
def has_abandoned_jojamart(self) -> StardewRule:
return self.received(CommunityUpgrade.movie_theater, 1)
def has_movie_theater(self) -> StardewRule:
return self.received(CommunityUpgrade.movie_theater, 2)
def can_use_obelisk(self, obelisk: str) -> StardewRule:
return self.region.can_reach(Region.farm) & self.received(obelisk)
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)