mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
Stardew Valley: Refactor Animals to use Content Packs (#4320)
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, Iterable, Set, Any, Mapping, Type, Tuple, Union
|
||||
from typing import Iterable, Set, Any, Mapping, Type, Tuple, Union
|
||||
|
||||
from .feature import booksanity, cropsanity, fishsanity, friendsanity, skill_progression, building_progression, tool_progression
|
||||
from ..data.animal import Animal
|
||||
from ..data.building import Building
|
||||
from ..data.fish_data import FishItem
|
||||
from ..data.game_item import GameItem, Source, ItemTag
|
||||
@@ -18,12 +19,13 @@ class StardewContent:
|
||||
|
||||
# regions -> To be used with can reach rule
|
||||
|
||||
game_items: Dict[str, GameItem] = field(default_factory=dict)
|
||||
fishes: Dict[str, FishItem] = field(default_factory=dict)
|
||||
villagers: Dict[str, Villager] = field(default_factory=dict)
|
||||
farm_buildings: Dict[str, Building] = field(default_factory=dict)
|
||||
skills: Dict[str, Skill] = field(default_factory=dict)
|
||||
quests: Dict[str, Any] = field(default_factory=dict)
|
||||
game_items: dict[str, GameItem] = field(default_factory=dict)
|
||||
fishes: dict[str, FishItem] = field(default_factory=dict)
|
||||
villagers: dict[str, Villager] = field(default_factory=dict)
|
||||
farm_buildings: dict[str, Building] = field(default_factory=dict)
|
||||
animals: dict[str, Animal] = field(default_factory=dict)
|
||||
skills: dict[str, Skill] = field(default_factory=dict)
|
||||
quests: dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
def find_sources_of_type(self, types: Union[Type[Source], Tuple[Type[Source]]]) -> Iterable[Source]:
|
||||
for item in self.game_items.values():
|
||||
@@ -109,6 +111,11 @@ class ContentPack:
|
||||
def farm_building_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
animals: Iterable[Animal] = ()
|
||||
|
||||
def animal_hook(self, content: StardewContent):
|
||||
...
|
||||
|
||||
skills: Iterable[Skill] = ()
|
||||
|
||||
def skill_hook(self, content: StardewContent):
|
||||
|
@@ -65,6 +65,10 @@ def register_pack(content: StardewContent, pack: ContentPack):
|
||||
content.farm_buildings[building.name] = building
|
||||
pack.farm_building_hook(content)
|
||||
|
||||
for animal in pack.animals:
|
||||
content.animals[animal.name] = animal
|
||||
pack.animal_hook(content)
|
||||
|
||||
for skill in pack.skills:
|
||||
content.skills[skill.name] = skill
|
||||
pack.skill_hook(content)
|
||||
|
@@ -1,15 +1,19 @@
|
||||
from .pelican_town import pelican_town as pelican_town_content_pack
|
||||
from ..game_content import ContentPack, StardewContent
|
||||
from ...data import villagers_data, fish_data
|
||||
from ...data.game_item import ItemTag, Tag
|
||||
from ...data.animal import Animal, AnimalName, OstrichIncubatorSource
|
||||
from ...data.game_item import ItemTag, Tag, CustomRuleSource
|
||||
from ...data.harvest import ForagingSource, HarvestFruitTreeSource, HarvestCropSource
|
||||
from ...data.requirement import WalnutRequirement
|
||||
from ...data.shop import ShopSource
|
||||
from ...strings.animal_product_names import AnimalProduct
|
||||
from ...strings.book_names import Book
|
||||
from ...strings.building_names import Building
|
||||
from ...strings.crop_names import Fruit, Vegetable
|
||||
from ...strings.fish_names import Fish
|
||||
from ...strings.forageable_names import Forageable, Mushroom
|
||||
from ...strings.fruit_tree_names import Sapling
|
||||
from ...strings.generic_names import Generic
|
||||
from ...strings.metal_names import Fossil, Mineral
|
||||
from ...strings.region_names import Region, LogicRegion
|
||||
from ...strings.season_names import Season
|
||||
@@ -51,6 +55,13 @@ ginger_island_content_pack = GingerIslandContentPack(
|
||||
Vegetable.taro_root: (HarvestCropSource(seed=Seed.taro, seasons=(Season.summer,)),),
|
||||
Fruit.pineapple: (HarvestCropSource(seed=Seed.pineapple, seasons=(Season.summer,)),),
|
||||
|
||||
# Temporary animal stuff, will be moved once animal products are properly content-packed
|
||||
AnimalProduct.ostrich_egg_starter: (CustomRuleSource(lambda logic: logic.tool.can_forage(Generic.any, Region.island_north, True)
|
||||
& logic.has(Forageable.journal_scrap)
|
||||
& logic.region.can_reach(Region.volcano_floor_5)),),
|
||||
AnimalProduct.ostrich_egg: (CustomRuleSource(lambda logic: logic.has(AnimalProduct.ostrich_egg_starter)
|
||||
| logic.animal.has_animal(AnimalName.ostrich)),),
|
||||
|
||||
},
|
||||
shop_sources={
|
||||
Seed.taro: (ShopSource(items_price=((2, Fossil.bone_fragment),), shop_region=Region.island_trader),),
|
||||
@@ -81,5 +92,12 @@ ginger_island_content_pack = GingerIslandContentPack(
|
||||
),
|
||||
villagers=(
|
||||
villagers_data.leo,
|
||||
),
|
||||
animals=(
|
||||
Animal(AnimalName.ostrich,
|
||||
required_building=Building.barn,
|
||||
sources=(
|
||||
OstrichIncubatorSource(AnimalProduct.ostrich_egg_starter),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
@@ -1,7 +1,12 @@
|
||||
from .pelican_town import pelican_town as pelican_town_content_pack
|
||||
from ..game_content import ContentPack
|
||||
from ...data.animal import IncubatorSource, Animal, AnimalName
|
||||
from ...data.harvest import FruitBatsSource, MushroomCaveSource
|
||||
from ...data.shop import ShopSource
|
||||
from ...strings.animal_product_names import AnimalProduct
|
||||
from ...strings.building_names import Building
|
||||
from ...strings.forageable_names import Forageable, Mushroom
|
||||
from ...strings.region_names import Region
|
||||
|
||||
the_farm = ContentPack(
|
||||
"The Farm (Vanilla)",
|
||||
@@ -39,5 +44,64 @@ the_farm = ContentPack(
|
||||
Mushroom.red: (
|
||||
MushroomCaveSource(),
|
||||
),
|
||||
}
|
||||
},
|
||||
animals=(
|
||||
Animal(AnimalName.chicken,
|
||||
required_building=Building.coop,
|
||||
sources=(
|
||||
ShopSource(shop_region=Region.ranch, money_price=800),
|
||||
# For now there is no way to obtain the starter item, so this adds additional rules in the system for nothing.
|
||||
# IncubatorSource(AnimalProduct.egg_starter)
|
||||
)),
|
||||
Animal(AnimalName.cow,
|
||||
required_building=Building.barn,
|
||||
sources=(
|
||||
ShopSource(shop_region=Region.ranch, money_price=1500),
|
||||
)),
|
||||
Animal(AnimalName.goat,
|
||||
required_building=Building.big_barn,
|
||||
sources=(
|
||||
ShopSource(shop_region=Region.ranch, money_price=4000),
|
||||
)),
|
||||
Animal(AnimalName.duck,
|
||||
required_building=Building.big_coop,
|
||||
sources=(
|
||||
ShopSource(shop_region=Region.ranch, money_price=1200),
|
||||
# For now there is no way to obtain the starter item, so this adds additional rules in the system for nothing.
|
||||
# IncubatorSource(AnimalProduct.duck_egg_starter)
|
||||
)),
|
||||
Animal(AnimalName.sheep,
|
||||
required_building=Building.deluxe_barn,
|
||||
sources=(
|
||||
ShopSource(shop_region=Region.ranch, money_price=8000),
|
||||
)),
|
||||
Animal(AnimalName.rabbit,
|
||||
required_building=Building.deluxe_coop,
|
||||
sources=(
|
||||
ShopSource(shop_region=Region.ranch, money_price=8000),
|
||||
)),
|
||||
Animal(AnimalName.pig,
|
||||
required_building=Building.deluxe_barn,
|
||||
sources=(
|
||||
ShopSource(shop_region=Region.ranch, money_price=16000),
|
||||
)),
|
||||
Animal(AnimalName.void_chicken,
|
||||
required_building=Building.big_coop,
|
||||
sources=(
|
||||
IncubatorSource(AnimalProduct.void_egg_starter),
|
||||
)),
|
||||
Animal(AnimalName.golden_chicken,
|
||||
required_building=Building.big_coop,
|
||||
sources=(
|
||||
IncubatorSource(AnimalProduct.golden_egg_starter),
|
||||
)),
|
||||
Animal(AnimalName.dinosaur,
|
||||
required_building=Building.big_coop,
|
||||
sources=(
|
||||
# We should use the starter item here, but since the dinosaur egg is also an artifact, it's part of the museum rules
|
||||
# and I do not want to touch it yet.
|
||||
# IncubatorSource(AnimalProduct.dinosaur_egg_starter),
|
||||
IncubatorSource(AnimalProduct.dinosaur_egg),
|
||||
)),
|
||||
)
|
||||
)
|
||||
|
23
worlds/stardew_valley/data/animal.py
Normal file
23
worlds/stardew_valley/data/animal.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from dataclasses import dataclass, field
|
||||
|
||||
from .game_item import Source
|
||||
from ..strings.animal_names import Animal as AnimalName
|
||||
|
||||
assert AnimalName
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Animal:
|
||||
name: str
|
||||
required_building: str = field(kw_only=True)
|
||||
sources: tuple[Source, ...] = field(kw_only=True)
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class IncubatorSource(Source):
|
||||
egg_item: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class OstrichIncubatorSource(Source):
|
||||
egg_item: str
|
@@ -143,7 +143,7 @@ duck_egg = BundleItem(AnimalProduct.duck_egg)
|
||||
rabbit_foot = BundleItem(AnimalProduct.rabbit_foot)
|
||||
dinosaur_egg = BundleItem(AnimalProduct.dinosaur_egg)
|
||||
void_egg = BundleItem(AnimalProduct.void_egg)
|
||||
ostrich_egg = BundleItem(AnimalProduct.ostrich_egg, source=BundleItem.Sources.island, )
|
||||
ostrich_egg = BundleItem(AnimalProduct.ostrich_egg, source=BundleItem.Sources.content)
|
||||
golden_egg = BundleItem(AnimalProduct.golden_egg)
|
||||
|
||||
truffle_oil = BundleItem(ArtisanGood.truffle_oil)
|
||||
@@ -832,7 +832,7 @@ calico_items = [calico_egg.as_amount(200), calico_egg.as_amount(200), calico_egg
|
||||
magic_rock_candy, mega_bomb.as_amount(10), mystery_box.as_amount(10), mixed_seeds.as_amount(50),
|
||||
strawberry_seeds.as_amount(20),
|
||||
spicy_eel.as_amount(5), crab_cakes.as_amount(5), eggplant_parmesan.as_amount(5),
|
||||
pumpkin_soup.as_amount(5), lucky_lunch.as_amount(5)]
|
||||
pumpkin_soup.as_amount(5), lucky_lunch.as_amount(5) ]
|
||||
calico_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.calico, calico_items, 2, 2)
|
||||
|
||||
raccoon_bundle = BundleTemplate(CCRoom.bulletin_board, BundleName.raccoon, raccoon_foraging_items, 4, 4)
|
||||
|
@@ -3,12 +3,13 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Tuple, Union, Optional
|
||||
|
||||
from ..strings.monster_names import Monster
|
||||
from ..strings.animal_product_names import AnimalProduct
|
||||
from ..strings.fish_names import WaterChest
|
||||
from ..strings.forageable_names import Forageable
|
||||
from ..strings.metal_names import Mineral, Artifact, Fossil
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.geode_names import Geode
|
||||
from ..strings.metal_names import Mineral, Artifact, Fossil
|
||||
from ..strings.monster_names import Monster
|
||||
from ..strings.region_names import Region
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
@@ -105,7 +106,7 @@ class Artifact:
|
||||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
|
||||
ornamental_fan = create_artifact("Ornamental Fan", 7.4, (Region.beach, Region.forest, Region.town),
|
||||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest))
|
||||
dinosaur_egg = create_artifact("Dinosaur Egg", 11.4, (Region.skull_cavern),
|
||||
dinosaur_egg = create_artifact(AnimalProduct.dinosaur_egg, 11.4, (Region.skull_cavern),
|
||||
monsters=Monster.pepper_rex)
|
||||
rare_disc = create_artifact("Rare Disc", 5.6, Region.stardew_valley,
|
||||
geodes=(Geode.artifact_trove, WaterChest.fishing_chest),
|
||||
|
@@ -1,25 +1,15 @@
|
||||
from typing import Union
|
||||
import typing
|
||||
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .building_logic import BuildingLogicMixin
|
||||
from .has_logic import HasLogicMixin
|
||||
from .money_logic import MoneyLogicMixin
|
||||
from ..stardew_rule import StardewRule, true_
|
||||
from ..strings.animal_names import Animal, coop_animals, barn_animals
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.building_names import Building
|
||||
from ..strings.forageable_names import Forageable
|
||||
from ..strings.generic_names import Generic
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.machine_names import Machine
|
||||
|
||||
cost_and_building_by_animal = {
|
||||
Animal.chicken: (800, Building.coop),
|
||||
Animal.cow: (1500, Building.barn),
|
||||
Animal.goat: (4000, Building.big_barn),
|
||||
Animal.duck: (1200, Building.big_coop),
|
||||
Animal.sheep: (8000, Building.deluxe_barn),
|
||||
Animal.rabbit: (8000, Building.deluxe_coop),
|
||||
Animal.pig: (16000, Building.deluxe_barn)
|
||||
}
|
||||
if typing.TYPE_CHECKING:
|
||||
from .logic import StardewLogic
|
||||
else:
|
||||
StardewLogic = object
|
||||
|
||||
|
||||
class AnimalLogicMixin(BaseLogicMixin):
|
||||
@@ -28,32 +18,19 @@ class AnimalLogicMixin(BaseLogicMixin):
|
||||
self.animal = AnimalLogic(*args, **kwargs)
|
||||
|
||||
|
||||
class AnimalLogic(BaseLogic[Union[HasLogicMixin, MoneyLogicMixin, BuildingLogicMixin]]):
|
||||
class AnimalLogic(BaseLogic[StardewLogic]):
|
||||
|
||||
def can_buy_animal(self, animal: str) -> StardewRule:
|
||||
try:
|
||||
price, building = cost_and_building_by_animal[animal]
|
||||
except KeyError:
|
||||
return true_
|
||||
return self.logic.money.can_spend_at(Region.ranch, price) & self.logic.building.has_building(building)
|
||||
def can_incubate(self, egg_item: str) -> StardewRule:
|
||||
return self.logic.building.has_building(Building.coop) & self.logic.has(egg_item)
|
||||
|
||||
def has_animal(self, animal: str) -> StardewRule:
|
||||
if animal == Generic.any:
|
||||
return self.has_any_animal()
|
||||
elif animal == Building.coop:
|
||||
return self.has_any_coop_animal()
|
||||
elif animal == Building.barn:
|
||||
return self.has_any_barn_animal()
|
||||
return self.logic.has(animal)
|
||||
def can_ostrich_incubate(self, egg_item: str) -> StardewRule:
|
||||
return self.logic.building.has_building(Building.barn) & self.logic.has(Machine.ostrich_incubator) & self.logic.has(egg_item)
|
||||
|
||||
def has_happy_animal(self, animal: str) -> StardewRule:
|
||||
return self.has_animal(animal) & self.logic.has(Forageable.hay)
|
||||
def has_animal(self, animal_name: str) -> StardewRule:
|
||||
animal = self.content.animals.get(animal_name)
|
||||
assert animal is not None, f"Animal {animal_name} not found."
|
||||
|
||||
def has_any_animal(self) -> StardewRule:
|
||||
return self.has_any_coop_animal() | self.has_any_barn_animal()
|
||||
return self.logic.source.has_access_to_any(animal.sources) & self.logic.building.has_building(animal.required_building)
|
||||
|
||||
def has_any_coop_animal(self) -> StardewRule:
|
||||
return self.logic.has_any(*coop_animals)
|
||||
|
||||
def has_any_barn_animal(self) -> StardewRule:
|
||||
return self.logic.has_any(*barn_animals)
|
||||
def has_happy_animal(self, animal_name: str) -> StardewRule:
|
||||
return self.logic.animal.has_animal(animal_name) & self.logic.has(Forageable.hay)
|
||||
|
@@ -17,6 +17,7 @@ from .skill_logic import SkillLogicMixin
|
||||
from .time_logic import TimeLogicMixin
|
||||
from ..options import FestivalLocations
|
||||
from ..stardew_rule import StardewRule
|
||||
from ..strings.animal_product_names import AnimalProduct
|
||||
from ..strings.book_names import Book
|
||||
from ..strings.craftable_names import Fishing
|
||||
from ..strings.crop_names import Fruit, Vegetable
|
||||
@@ -154,18 +155,37 @@ SkillLogicMixin, RegionLogicMixin, ActionLogicMixin, MonsterLogicMixin, Relation
|
||||
if self.options.festival_locations != FestivalLocations.option_hard:
|
||||
return self.logic.true_
|
||||
|
||||
animal_rule = self.logic.animal.has_animal(Generic.any)
|
||||
# Other animal products are not counted in the animal product category
|
||||
good_animal_products = [
|
||||
AnimalProduct.duck_egg, AnimalProduct.duck_feather, AnimalProduct.egg, AnimalProduct.goat_milk, AnimalProduct.golden_egg, AnimalProduct.large_egg,
|
||||
AnimalProduct.large_goat_milk, AnimalProduct.large_milk, AnimalProduct.milk, AnimalProduct.ostrich_egg, AnimalProduct.rabbit_foot,
|
||||
AnimalProduct.void_egg, AnimalProduct.wool
|
||||
]
|
||||
if AnimalProduct.ostrich_egg not in self.content.game_items:
|
||||
# When ginger island is excluded, ostrich egg is not available
|
||||
good_animal_products.remove(AnimalProduct.ostrich_egg)
|
||||
animal_rule = self.logic.has_any(*good_animal_products)
|
||||
|
||||
artisan_rule = self.logic.artisan.can_keg(Generic.any) | self.logic.artisan.can_preserves_jar(Generic.any)
|
||||
cooking_rule = self.logic.money.can_spend_at(Region.saloon, 220) # Salads at the bar are good enough
|
||||
|
||||
# Salads at the bar are good enough
|
||||
cooking_rule = self.logic.money.can_spend_at(Region.saloon, 220)
|
||||
|
||||
fish_rule = self.logic.skill.can_fish(difficulty=50)
|
||||
forage_rule = self.logic.region.can_reach_any((Region.forest, Region.backwoods)) # Hazelnut always available since the grange display is in fall
|
||||
mineral_rule = self.logic.action.can_open_geode(Generic.any) # More than half the minerals are good enough
|
||||
|
||||
# Hazelnut always available since the grange display is in fall
|
||||
forage_rule = self.logic.region.can_reach_any((Region.forest, Region.backwoods))
|
||||
|
||||
# More than half the minerals are good enough
|
||||
mineral_rule = self.logic.action.can_open_geode(Generic.any)
|
||||
|
||||
good_fruits = (fruit
|
||||
for fruit in
|
||||
(Fruit.apple, Fruit.banana, Forageable.coconut, Forageable.crystal_fruit, Fruit.mango, Fruit.orange, Fruit.peach, Fruit.pomegranate,
|
||||
Fruit.strawberry, Fruit.melon, Fruit.rhubarb, Fruit.pineapple, Fruit.ancient_fruit, Fruit.starfruit)
|
||||
if fruit in self.content.game_items)
|
||||
fruit_rule = self.logic.has_any(*good_fruits)
|
||||
|
||||
good_vegetables = (vegeteable
|
||||
for vegeteable in
|
||||
(Vegetable.amaranth, Vegetable.artichoke, Vegetable.beet, Vegetable.cauliflower, Forageable.fiddlehead_fern, Vegetable.kale,
|
||||
@@ -173,8 +193,7 @@ SkillLogicMixin, RegionLogicMixin, ActionLogicMixin, MonsterLogicMixin, Relation
|
||||
if vegeteable in self.content.game_items)
|
||||
vegetable_rule = self.logic.has_any(*good_vegetables)
|
||||
|
||||
return animal_rule & artisan_rule & cooking_rule & fish_rule & \
|
||||
forage_rule & fruit_rule & mineral_rule & vegetable_rule
|
||||
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.logic.skill.can_fish(difficulty=60)
|
||||
|
@@ -149,42 +149,37 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin
|
||||
# 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_egg: self.animal.has_animal(Animal.duck), # Should also check starter
|
||||
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.egg: self.animal.has_animal(Animal.chicken), # Should also check starter
|
||||
AnimalProduct.goat_milk: self.animal.has_animal(Animal.goat),
|
||||
AnimalProduct.golden_egg: self.has(AnimalProduct.golden_egg_starter), # Should also check golden chicken if there was an alternative to obtain it without golden egg
|
||||
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) & self.has(Forageable.journal_scrap) & self.region.can_reach(Region.volcano_floor_5),
|
||||
AnimalProduct.rabbit_foot: self.animal.has_happy_animal(Animal.rabbit),
|
||||
AnimalProduct.roe: self.skill.can_fish() & self.building.has_building(Building.fish_pond),
|
||||
AnimalProduct.squid_ink: self.mine.can_mine_in_the_mines_floor_81_120() | (self.building.has_building(Building.fish_pond) & self.has(Fish.squid)),
|
||||
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.void_egg: self.has(AnimalProduct.void_egg_starter), # Should also check void chicken if there was an alternative to obtain it without void egg
|
||||
AnimalProduct.wool: self.animal.has_animal(Animal.rabbit) | self.animal.has_animal(Animal.sheep),
|
||||
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),
|
||||
AnimalProduct.duck_egg_starter: self.logic.false_, # It could be purchased at the Feast of the Winter Star, but it's random every year, so not considering it yet...
|
||||
AnimalProduct.dinosaur_egg_starter: self.logic.false_, # Dinosaur eggs are also part of the museum rules, and I don't want to touch them yet.
|
||||
AnimalProduct.egg_starter: self.logic.false_, # It could be purchased at the Desert Festival, but festival logic is quite a mess, so not considering it yet...
|
||||
AnimalProduct.golden_egg_starter: self.received(AnimalProduct.golden_egg) & (self.money.can_spend_at(Region.ranch, 100000) | self.money.can_trade_at(Region.qi_walnut_room, Currency.qi_gem, 100)),
|
||||
AnimalProduct.void_egg_starter: self.money.can_spend_at(Region.sewer, 5000) | (self.building.has_building(Building.fish_pond) & self.has(Fish.void_salmon)),
|
||||
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),
|
||||
|
@@ -72,4 +72,16 @@ of source (Monster drop and fish can have foraging sources).
|
||||
if easy logic is disabled. For instance, anything that requires money could be accessible as soon as you can sell something to someone (even wood).
|
||||
|
||||
Items are classified by their source. An item with a fishing or a crab pot source is considered a fish, an item dropping from a monster is a monster drop. An
|
||||
item with a foraging source is a forageable. Items can fit in multiple categories.
|
||||
item with a foraging source is a forageable. Items can fit in multiple categories.
|
||||
|
||||
## Prefer rich class to anemic list of sources
|
||||
|
||||
For game mechanic that might need more logic/interaction than a simple game item, prefer creating a class than just listing the sources and adding generic
|
||||
requirements to them. This will simplify the implementation of more complex mechanics and increase cohesion.
|
||||
|
||||
For instance, `Building` can be upgraded. Instead of having a simple source for the `Big Coop` being a shop source with an additional requirement being having
|
||||
the previous building, the `Building` class has knowledge of the upgrade system and know from which building it can be upgraded.
|
||||
|
||||
Another example is `Animal`. Instead of a shopping source with a requirement of having a `Coop`, the `Chicken` knows that a building is required. This way, a
|
||||
potential source of chicken from incubating an egg would not require an additional requirement of having a coop (assuming the incubator could be obtained
|
||||
without a big coop).
|
||||
|
@@ -1,6 +1,7 @@
|
||||
import functools
|
||||
from typing import Union, Any, Iterable
|
||||
|
||||
from .animal_logic import AnimalLogicMixin
|
||||
from .artisan_logic import ArtisanLogicMixin
|
||||
from .base_logic import BaseLogicMixin, BaseLogic
|
||||
from .grind_logic import GrindLogicMixin
|
||||
@@ -11,6 +12,7 @@ from .received_logic import ReceivedLogicMixin
|
||||
from .region_logic import RegionLogicMixin
|
||||
from .requirement_logic import RequirementLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from ..data.animal import IncubatorSource, OstrichIncubatorSource
|
||||
from ..data.artisan import MachineSource
|
||||
from ..data.game_item import GenericSource, Source, GameItem, CustomRuleSource
|
||||
from ..data.harvest import ForagingSource, FruitBatsSource, MushroomCaveSource, SeasonalForagingSource, \
|
||||
@@ -25,7 +27,7 @@ class SourceLogicMixin(BaseLogicMixin):
|
||||
|
||||
|
||||
class SourceLogic(BaseLogic[Union[SourceLogicMixin, HasLogicMixin, ReceivedLogicMixin, HarvestingLogicMixin, MoneyLogicMixin, RegionLogicMixin,
|
||||
ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
|
||||
ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin, AnimalLogicMixin]]):
|
||||
|
||||
def has_access_to_item(self, item: GameItem):
|
||||
rules = []
|
||||
@@ -81,6 +83,14 @@ ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
|
||||
def _(self, source: HarvestCropSource):
|
||||
return self.logic.harvesting.can_harvest_crop_from(source)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: IncubatorSource):
|
||||
return self.logic.animal.can_incubate(source.egg_item)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: OstrichIncubatorSource):
|
||||
return self.logic.animal.can_ostrich_incubate(source.egg_item)
|
||||
|
||||
@has_access_to.register
|
||||
def _(self, source: MachineSource):
|
||||
return self.logic.artisan.can_produce_from(source)
|
||||
|
@@ -8,6 +8,5 @@ class Animal:
|
||||
rabbit = "Rabbit"
|
||||
goat = "Goat"
|
||||
ostrich = "Ostrich"
|
||||
|
||||
coop_animals = [Animal.chicken, "Rabbit", "Duck", "Dinosaur"]
|
||||
barn_animals = [Animal.cow, "Sheep", "Pig", "Ostrich"]
|
||||
void_chicken = "Void Chicken"
|
||||
golden_chicken = "Golden Chicken"
|
||||
|
@@ -3,17 +3,32 @@ class AnimalProduct:
|
||||
brown_egg = "Egg (Brown)"
|
||||
chicken_egg = "Chicken Egg"
|
||||
cow_milk = "Cow Milk"
|
||||
dinosaur_egg_starter = "Dinosaur Egg (Starter)"
|
||||
"""This item does not really exist and should never end up being displayed.
|
||||
It's there to patch the loop in logic because of the Dinosaur-and-egg problem."""
|
||||
dinosaur_egg = "Dinosaur Egg"
|
||||
duck_egg_starter = "Duck Egg (Starter)"
|
||||
"""This item does not really exist and should never end up being displayed.
|
||||
It's there to patch the loop in logic because of the Chicken-and-egg problem."""
|
||||
duck_egg = "Duck Egg"
|
||||
duck_feather = "Duck Feather"
|
||||
egg_starter = "Egg (Starter)"
|
||||
"""This item does not really exist and should never end up being displayed.
|
||||
It's there to patch the loop in logic because of the Chicken-and-egg problem."""
|
||||
egg = "Egg"
|
||||
goat_milk = "Goat Milk"
|
||||
golden_egg_starter = "Golden Egg (Starter)"
|
||||
"""This item does not really exist and should never end up being displayed.
|
||||
It's there to patch the loop in logic because of the Chicken-and-egg problem."""
|
||||
golden_egg = "Golden Egg"
|
||||
large_brown_egg = "Large Egg (Brown)"
|
||||
large_egg = "Large Egg"
|
||||
large_goat_milk = "Large Goat Milk"
|
||||
large_milk = "Large Milk"
|
||||
milk = "Milk"
|
||||
ostrich_egg_starter = "Ostrich Egg (Starter)"
|
||||
"""This item does not really exist and should never end up being displayed.
|
||||
It's there to patch the loop in logic because of the Chicken-and-egg problem."""
|
||||
ostrich_egg = "Ostrich Egg"
|
||||
rabbit_foot = "Rabbit's Foot"
|
||||
roe = "Roe"
|
||||
@@ -25,6 +40,8 @@ class AnimalProduct:
|
||||
squid_ink = "Squid Ink"
|
||||
sturgeon_roe = "Sturgeon Roe"
|
||||
truffle = "Truffle"
|
||||
void_egg_starter = "Void Egg (Starter)"
|
||||
"""This item does not really exist and should never end up being displayed.
|
||||
It's there to patch the loop in logic because of the Chicken-and-egg problem."""
|
||||
void_egg = "Void Egg"
|
||||
wool = "Wool"
|
||||
|
||||
|
@@ -142,5 +142,3 @@ class ModFossil:
|
||||
pterodactyl_phalange = "Pterodactyl Phalange"
|
||||
pterodactyl_vertebra = "Pterodactyl Vertebra"
|
||||
pterodactyl_claw = "Pterodactyl Claw"
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user