675 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			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)
 |