1149 lines
51 KiB
Python
1149 lines
51 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
from typing import Dict, Union, Optional, Iterable, Sized, Tuple, List, FrozenSet
|
|
|
|
from BaseClasses import CollectionState, ItemClassification
|
|
from . import options
|
|
from .bundle_data import BundleItem
|
|
from .fish_data import all_fish_items
|
|
from .game_item import FishItem
|
|
from .items import all_items, Group, item_table
|
|
from .options import StardewOptions
|
|
|
|
MISSING_ITEM = "THIS ITEM IS MISSING"
|
|
|
|
tool_materials = {
|
|
"Copper": 1,
|
|
"Iron": 2,
|
|
"Gold": 3,
|
|
"Iridium": 4
|
|
}
|
|
|
|
tool_prices = {
|
|
"Copper": 2000,
|
|
"Iron": 5000,
|
|
"Gold": 10000,
|
|
"Iridium": 25000
|
|
}
|
|
|
|
skill_level_per_season = {
|
|
"Spring": {
|
|
"Farming": 2,
|
|
"Fishing": 2,
|
|
"Foraging": 2,
|
|
"Mining": 2,
|
|
"Combat": 2,
|
|
},
|
|
"Summer": {
|
|
"Farming": 4,
|
|
"Fishing": 4,
|
|
"Foraging": 4,
|
|
"Mining": 4,
|
|
"Combat": 3,
|
|
},
|
|
"Fall": {
|
|
"Farming": 7,
|
|
"Fishing": 5,
|
|
"Foraging": 5,
|
|
"Mining": 5,
|
|
"Combat": 4,
|
|
},
|
|
"Winter": {
|
|
"Farming": 7,
|
|
"Fishing": 7,
|
|
"Foraging": 6,
|
|
"Mining": 7,
|
|
"Combat": 5,
|
|
},
|
|
"Year Two": {
|
|
"Farming": 10,
|
|
"Fishing": 10,
|
|
"Foraging": 10,
|
|
"Mining": 10,
|
|
"Combat": 10,
|
|
},
|
|
}
|
|
season_per_skill_level: Dict[Tuple[str, int], str] = {}
|
|
season_per_total_level: Dict[int, str] = {}
|
|
|
|
|
|
def initialize_season_per_skill_level():
|
|
current_level = {
|
|
"Farming": 0,
|
|
"Fishing": 0,
|
|
"Foraging": 0,
|
|
"Mining": 0,
|
|
"Combat": 0,
|
|
}
|
|
for season, skills in skill_level_per_season.items():
|
|
for skill, expected_level in skills.items():
|
|
for level_up in range(current_level[skill] + 1, expected_level + 1):
|
|
skill_level = (skill, level_up)
|
|
if skill_level not in season_per_skill_level:
|
|
season_per_skill_level[skill_level] = season
|
|
level_up = 0
|
|
for level_up in range(level_up + 1, sum(skills.values()) + 1):
|
|
if level_up not in season_per_total_level:
|
|
season_per_total_level[level_up] = season
|
|
|
|
|
|
initialize_season_per_skill_level()
|
|
week_days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]
|
|
|
|
|
|
class StardewRule:
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
raise NotImplementedError
|
|
|
|
def __or__(self, other) -> StardewRule:
|
|
if isinstance(other, _Or):
|
|
return _Or(self, *other.rules)
|
|
|
|
return _Or(self, other)
|
|
|
|
def __and__(self, other) -> StardewRule:
|
|
if isinstance(other, _And):
|
|
return _And(other.rules.union({self}))
|
|
|
|
return _And(self, other)
|
|
|
|
def get_difficulty(self):
|
|
raise NotImplementedError
|
|
|
|
def simplify(self) -> StardewRule:
|
|
return self
|
|
|
|
|
|
class _True(StardewRule):
|
|
|
|
def __new__(cls, _cache=[]): # noqa
|
|
if not _cache:
|
|
_cache.append(super(_True, cls).__new__(cls))
|
|
return _cache[0]
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
return True
|
|
|
|
def __or__(self, other) -> StardewRule:
|
|
return self
|
|
|
|
def __and__(self, other) -> StardewRule:
|
|
return other
|
|
|
|
def __repr__(self):
|
|
return "True"
|
|
|
|
def get_difficulty(self):
|
|
return 0
|
|
|
|
|
|
class _False(StardewRule):
|
|
|
|
def __new__(cls, _cache=[]): # noqa
|
|
if not _cache:
|
|
_cache.append(super(_False, cls).__new__(cls))
|
|
return _cache[0]
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
return False
|
|
|
|
def __or__(self, other) -> StardewRule:
|
|
return other
|
|
|
|
def __and__(self, other) -> StardewRule:
|
|
return self
|
|
|
|
def __repr__(self):
|
|
return "False"
|
|
|
|
def get_difficulty(self):
|
|
return 999999999
|
|
|
|
|
|
class _Or(StardewRule):
|
|
rules: FrozenSet[StardewRule]
|
|
|
|
def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
|
|
rules_list = set()
|
|
if isinstance(rule, Iterable):
|
|
rules_list.update(rule)
|
|
else:
|
|
rules_list.add(rule)
|
|
|
|
if rules is not None:
|
|
rules_list.update(rules)
|
|
|
|
assert rules_list, "Can't create a Or conditions without rules"
|
|
|
|
new_rules = set()
|
|
for rule in rules_list:
|
|
if isinstance(rule, _Or):
|
|
new_rules.update(rule.rules)
|
|
else:
|
|
new_rules.add(rule)
|
|
rules_list = new_rules
|
|
|
|
self.rules = frozenset(rules_list)
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
return any(rule(state) for rule in self.rules)
|
|
|
|
def __repr__(self):
|
|
return f"({' | '.join(repr(rule) for rule in self.rules)})"
|
|
|
|
def __or__(self, other):
|
|
if isinstance(other, _True):
|
|
return other
|
|
if isinstance(other, _False):
|
|
return self
|
|
if isinstance(other, _Or):
|
|
return _Or(self.rules.union(other.rules))
|
|
|
|
return _Or(self.rules.union({other}))
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, self.__class__) and other.rules == self.rules
|
|
|
|
def __hash__(self):
|
|
return hash(self.rules)
|
|
|
|
def get_difficulty(self):
|
|
return min(rule.get_difficulty() for rule in self.rules)
|
|
|
|
def simplify(self) -> StardewRule:
|
|
if any(isinstance(rule, _True) for rule in self.rules):
|
|
return _True()
|
|
|
|
simplified_rules = {rule.simplify() for rule in self.rules}
|
|
simplified_rules = {rule for rule in simplified_rules if rule is not _False()}
|
|
|
|
if not simplified_rules:
|
|
return _False()
|
|
|
|
if len(simplified_rules) == 1:
|
|
return next(iter(simplified_rules))
|
|
|
|
return _Or(simplified_rules)
|
|
|
|
|
|
class _And(StardewRule):
|
|
rules: frozenset[StardewRule]
|
|
|
|
def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
|
|
rules_list = set()
|
|
if isinstance(rule, Iterable):
|
|
rules_list.update(rule)
|
|
else:
|
|
rules_list.add(rule)
|
|
|
|
if rules is not None:
|
|
rules_list.update(rules)
|
|
|
|
assert rules_list, "Can't create a And conditions without rules"
|
|
|
|
new_rules = set()
|
|
for rule in rules_list:
|
|
if isinstance(rule, _And):
|
|
new_rules.update(rule.rules)
|
|
else:
|
|
new_rules.add(rule)
|
|
rules_list = new_rules
|
|
|
|
self.rules = frozenset(rules_list)
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
return all(rule(state) for rule in self.rules)
|
|
|
|
def __repr__(self):
|
|
return f"({' & '.join(repr(rule) for rule in self.rules)})"
|
|
|
|
def __and__(self, other):
|
|
if isinstance(other, _True):
|
|
return self
|
|
if isinstance(other, _False):
|
|
return other
|
|
if isinstance(other, _And):
|
|
return _And(self.rules.union(other.rules))
|
|
|
|
return _And(self.rules.union({other}))
|
|
|
|
def __eq__(self, other):
|
|
return isinstance(other, self.__class__) and other.rules == self.rules
|
|
|
|
def __hash__(self):
|
|
return hash(self.rules)
|
|
|
|
def get_difficulty(self):
|
|
return max(rule.get_difficulty() for rule in self.rules)
|
|
|
|
def simplify(self) -> StardewRule:
|
|
if any(isinstance(rule, _False) for rule in self.rules):
|
|
return _False()
|
|
|
|
simplified_rules = {rule.simplify() for rule in self.rules}
|
|
simplified_rules = {rule for rule in simplified_rules if rule is not _True()}
|
|
|
|
if not simplified_rules:
|
|
return _True()
|
|
|
|
if len(simplified_rules) == 1:
|
|
return next(iter(simplified_rules))
|
|
|
|
return _And(simplified_rules)
|
|
|
|
|
|
class _Count(StardewRule):
|
|
count: int
|
|
rules: List[StardewRule]
|
|
|
|
def __init__(self, count: int, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
|
|
rules_list = []
|
|
if isinstance(rule, Iterable):
|
|
rules_list.extend(rule)
|
|
else:
|
|
rules_list.append(rule)
|
|
|
|
if rules is not None:
|
|
rules_list.extend(rules)
|
|
|
|
assert rules_list, "Can't create a Count conditions without rules"
|
|
assert len(rules_list) >= count, "Count need at least as many rules at the count"
|
|
|
|
self.rules = rules_list
|
|
self.count = count
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
c = 0
|
|
for r in self.rules:
|
|
if r(state):
|
|
c += 1
|
|
if c >= self.count:
|
|
return True
|
|
return False
|
|
|
|
def __repr__(self):
|
|
return f"Received {self.count} {repr(self.rules)}"
|
|
|
|
def get_difficulty(self):
|
|
rules_sorted_by_difficulty = sorted(self.rules, key=lambda x: x.get_difficulty())
|
|
easiest_n_rules = rules_sorted_by_difficulty[0:self.count]
|
|
return max(rule.get_difficulty() for rule in easiest_n_rules)
|
|
|
|
def simplify(self):
|
|
return _Count(self.count, [rule.simplify() for rule in self.rules])
|
|
|
|
|
|
class _TotalReceived(StardewRule):
|
|
count: int
|
|
items: Iterable[str]
|
|
player: int
|
|
|
|
def __init__(self, count: int, items: Union[str, Iterable[str]], player: int):
|
|
items_list = []
|
|
if isinstance(items, Iterable):
|
|
items_list.extend(items)
|
|
else:
|
|
items_list.append(items)
|
|
|
|
assert items_list, "Can't create a Total Received conditions without items"
|
|
for item in items_list:
|
|
assert item_table[item].classification & ItemClassification.progression, \
|
|
"Item has to be progression to be used in logic"
|
|
|
|
self.player = player
|
|
self.items = items_list
|
|
self.count = count
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
c = 0
|
|
for item in self.items:
|
|
c += state.count(item, self.player)
|
|
if c >= self.count:
|
|
return True
|
|
return False
|
|
|
|
def __repr__(self):
|
|
return f"Received {self.count} {self.items}"
|
|
|
|
def get_difficulty(self):
|
|
return self.count
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class _Received(StardewRule):
|
|
item: str
|
|
player: int
|
|
count: int
|
|
|
|
def __post_init__(self):
|
|
assert item_table[self.item].classification & ItemClassification.progression, \
|
|
"Item has to be progression to be used in logic"
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
return state.has(self.item, self.player, self.count)
|
|
|
|
def __repr__(self):
|
|
if self.count == 1:
|
|
return f"Received {self.item}"
|
|
return f"Received {self.count} {self.item}"
|
|
|
|
def get_difficulty(self):
|
|
if self.item == "Spring":
|
|
return 0
|
|
if self.item == "Summer":
|
|
return 1
|
|
if self.item == "Fall":
|
|
return 2
|
|
if self.item == "Winter":
|
|
return 3
|
|
if self.item == "Year Two":
|
|
return 4
|
|
return self.count
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class _Reach(StardewRule):
|
|
spot: str
|
|
resolution_hint: str
|
|
player: int
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
return state.can_reach(self.spot, self.resolution_hint, self.player)
|
|
|
|
def __repr__(self):
|
|
return f"Reach {self.resolution_hint} {self.spot}"
|
|
|
|
def get_difficulty(self):
|
|
return 1
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class _Has(StardewRule):
|
|
item: str
|
|
# For sure there is a better way than just passing all the rules everytime
|
|
other_rules: Dict[str, StardewRule]
|
|
|
|
def __call__(self, state: CollectionState) -> bool:
|
|
if isinstance(self.item, str):
|
|
return self.other_rules[self.item](state)
|
|
|
|
def __repr__(self):
|
|
if not self.item in self.other_rules:
|
|
return f"Has {self.item} -> {MISSING_ITEM}"
|
|
return f"Has {self.item} -> {repr(self.other_rules[self.item])}"
|
|
|
|
def get_difficulty(self):
|
|
return self.other_rules[self.item].get_difficulty() + 1
|
|
|
|
def __hash__(self):
|
|
return hash(self.item)
|
|
|
|
def simplify(self) -> StardewRule:
|
|
return self.other_rules[self.item].simplify()
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class StardewLogic:
|
|
player: int
|
|
options: StardewOptions
|
|
|
|
item_rules: Dict[str, StardewRule] = field(default_factory=dict)
|
|
fish_rules: Dict[str, StardewRule] = field(default_factory=dict)
|
|
building_rules: Dict[str, StardewRule] = field(default_factory=dict)
|
|
quest_rules: Dict[str, StardewRule] = field(default_factory=dict)
|
|
|
|
def __post_init__(self):
|
|
self.fish_rules.update({fish.name: self.can_catch_fish(fish) for fish in all_fish_items})
|
|
|
|
self.item_rules.update({
|
|
"Aged Roe": self.has("Preserves Jar") & self.has("Roe"),
|
|
"Algae Soup": self.can_cook() & self.has("Green Algae") & self.can_have_relationship("Clint", 3),
|
|
"Amaranth": self.received("Fall"),
|
|
"Amethyst": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Ancient Drum": self.has("Frozen Geode"),
|
|
"Any Egg": self.has("Chicken Egg") | self.has("Duck Egg"),
|
|
"Apple": self.received("Fall"),
|
|
"Apricot": self.received("Year Two"),
|
|
"Aquamarine": self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
|
|
"Artichoke": self.received("Year Two") & self.received("Fall"),
|
|
"Bait": self.has_skill_level("Fishing", 2),
|
|
"Bat Wing": self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
|
|
"Battery Pack": self.has("Lightning Rod"),
|
|
"Bee House": self.has_skill_level("Farming", 3) & self.has("Iron Bar") & self.has("Maple Syrup"),
|
|
"Beer": (self.has("Keg") & self.has("Wheat")) | self.can_spend_money(400),
|
|
"Beet": self.received("Fall") & self.can_reach_region("The Desert"),
|
|
"Blackberry": self.received("Fall"),
|
|
"Blue Jazz": self.received("Spring"),
|
|
"Blueberry": self.received("Summer"),
|
|
"Blueberry Tart": self.has("Blueberry") & self.has("Any Egg") & self.can_have_relationship("Pierre", 3),
|
|
"Bok Choy": self.received("Fall"),
|
|
"Bouquet": self.can_have_relationship("Any", 8),
|
|
"Bread": self.can_spend_money(120) | (self.can_spend_money(100) & self.can_cook()),
|
|
"Broken CD": self.can_crab_pot(),
|
|
"Broken Glasses": self.can_crab_pot(),
|
|
"Bug Meat": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Cactus Fruit": self.can_reach_region("The Desert"),
|
|
"Cauliflower": self.received("Spring"),
|
|
"Cave Carrot": self.has_mine_elevator_to_floor(10),
|
|
"Caviar": self.has("Preserves Jar") & self.has("Sturgeon Roe"),
|
|
"Chanterelle": self.received("Fall") & self.can_reach_region("Secret Woods"),
|
|
"Cheese Press": self.has_skill_level("Farming", 6) & self.has("Hardwood") & self.has("Copper Bar"),
|
|
"Cheese": (self.has("Cow Milk") & self.has("Cheese Press")) |
|
|
(self.can_reach_region("The Desert") & self.has("Emerald")),
|
|
"Cheese Cauliflower": self.has(["Cheese", "Cauliflower"]) & self.can_have_relationship("Pam", 3) &
|
|
self.can_cook(),
|
|
"Cherry": self.received("Year Two"),
|
|
"Chicken": self.has_building("Coop"),
|
|
"Chicken Egg": self.has(["Egg", "Egg (Brown)", "Large Egg", "Large Egg (Brown)"], 1),
|
|
"Chowder": self.can_cook() & self.can_have_relationship("Willy", 3) & self.has(["Clam", "Cow Milk"]),
|
|
"Clam": _True(),
|
|
"Clay": _True(),
|
|
"Cloth": (self.has("Wool") & self.has("Loom")) |
|
|
(self.can_reach_region("The Desert") & self.has("Aquamarine")),
|
|
"Coal": _True(),
|
|
"Cockle": _True(),
|
|
"Coconut": self.can_reach_region("The Desert"),
|
|
"Coffee": (self.has("Keg") & self.has("Coffee Bean")) | self.has("Coffee Maker") |
|
|
self.can_spend_money(300) | self.has("Hot Java Ring"),
|
|
"Coffee Bean": (self.received("Spring") | self.received("Summer")) &
|
|
(self.can_mine_in_the_mines_floor_41_80() | _True()), # Travelling merchant
|
|
"Coffee Maker": _False(),
|
|
"Common Mushroom": self.received("Fall") |
|
|
(self.received("Spring") & self.can_reach_region("Secret Woods")),
|
|
"Copper Bar": self.can_smelt("Copper Ore"),
|
|
"Copper Ore": self.can_mine_in_the_mines_floor_1_40() | self.can_mine_in_the_skull_cavern(),
|
|
"Coral": self.can_reach_region("Tide Pools") | self.received("Summer"),
|
|
"Corn": self.received("Summer") | self.received("Fall"),
|
|
"Cow": self.has_building("Barn"),
|
|
"Cow Milk": self.has("Milk") | self.has("Large Milk"),
|
|
"Crab": self.can_crab_pot(),
|
|
"Crab Pot": self.has_skill_level("Fishing", 3),
|
|
"Cranberries": self.received("Fall"),
|
|
"Crayfish": self.can_crab_pot(),
|
|
"Crocus": self.received("Winter"),
|
|
"Crystal Fruit": self.received("Winter"),
|
|
"Daffodil": self.received("Spring"),
|
|
"Dandelion": self.received("Spring"),
|
|
"Dish O' The Sea": self.can_cook() & self.has_skill_level("Fishing", 3) &
|
|
self.has(["Sardine", "Hashbrowns"]),
|
|
"Dorado": self.can_fish(78) & self.received("Summer"),
|
|
"Dried Starfish": self.can_fish() & self.can_reach_region("Beach"),
|
|
"Driftwood": self.can_crab_pot(),
|
|
"Duck Egg": self.has("Duck"),
|
|
"Duck Feather": self.has("Duck"),
|
|
"Duck": self.has_building("Big Coop"),
|
|
"Dwarf Scroll I": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Dwarf Scroll II": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Dwarf Scroll III": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Dwarf Scroll IV": self.can_mine_in_the_mines_floor_81_120(),
|
|
"Earth Crystal": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Egg": self.has("Chicken"),
|
|
"Egg (Brown)": self.has("Chicken"),
|
|
"Eggplant": self.received("Fall"),
|
|
"Elvish Jewelry": self.can_fish() & self.can_reach_region("Forest"),
|
|
"Emerald": self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
|
|
"Fairy Rose": self.received("Fall"),
|
|
"Farmer's Lunch": self.can_cook() & self.has_skill_level("Farming", 3) & self.has("Omelet") & self.has(
|
|
"Parsnip"),
|
|
"Fiber": _True(),
|
|
"Fiddlehead Fern": self.can_reach_region("Secret Woods") & self.received("Summer"),
|
|
"Fire Quartz": self.can_mine_in_the_mines_floor_81_120(),
|
|
"Fried Egg": self.can_cook() & self.has("Any Egg"),
|
|
"Fried Mushroom": self.can_cook() & self.can_have_relationship("Demetrius", 3) & self.has(
|
|
"Morel") & self.has("Common Mushroom"),
|
|
"Frozen Geode": self.can_mine_in_the_mines_floor_41_80(),
|
|
"Frozen Tear": self.can_mine_in_the_mines_floor_41_80(),
|
|
"Furnace": self.has("Stone") & self.has("Copper Ore"),
|
|
"Geode": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Goat Cheese": self.has("Goat Milk") & self.has("Cheese Press"),
|
|
"Goat Milk": self.has("Goat"),
|
|
"Goat": self.has_building("Big Barn"),
|
|
"Gold Bar": self.can_smelt("Gold Ore"),
|
|
"Gold Ore": self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
|
|
"Grape": self.received("Summer"),
|
|
"Green Algae": self.can_fish(),
|
|
"Green Bean": self.received("Spring"),
|
|
"Hardwood": self.has_tool("Axe", "Copper"),
|
|
"Hashbrowns": self.can_cook() & self.can_spend_money(50) & self.has("Potato"),
|
|
"Hazelnut": self.received("Fall"),
|
|
"Holly": self.received("Winter"),
|
|
"Honey": self.can_reach_region("The Desert") |
|
|
(self.has("Bee House") &
|
|
(self.received("Spring") | self.received("Summer") | self.received("Fall"))),
|
|
"Hops": self.received("Summer"),
|
|
"Hot Java Ring": self.can_reach_region("Ginger Island"),
|
|
"Hot Pepper": self.received("Summer"),
|
|
"Iridium Bar": self.can_smelt("Iridium Ore"),
|
|
"Iridium Ore": self.can_mine_in_the_skull_cavern(),
|
|
"Iron Bar": self.can_smelt("Iron Ore"),
|
|
"Iron Ore": self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
|
|
"Jade": self.can_mine_in_the_mines_floor_41_80(),
|
|
"Jelly": self.has("Preserves Jar"),
|
|
"JotPK Small Buff": self.has_jotpk_power_level(2),
|
|
"JotPK Medium Buff": self.has_jotpk_power_level(4),
|
|
"JotPK Big Buff": self.has_jotpk_power_level(7),
|
|
"JotPK Max Buff": self.has_jotpk_power_level(9),
|
|
"Juice": self.has("Keg"),
|
|
"Junimo Kart Small Buff": self.has_junimo_kart_power_level(2),
|
|
"Junimo Kart Medium Buff": self.has_junimo_kart_power_level(4),
|
|
"Junimo Kart Big Buff": self.has_junimo_kart_power_level(6),
|
|
"Junimo Kart Max Buff": self.has_junimo_kart_power_level(8),
|
|
"Kale": self.received("Spring"),
|
|
"Keg": self.has_skill_level("Farming", 8) & self.has("Iron Bar") & self.has("Copper Bar") & self.has(
|
|
"Oak Resin"),
|
|
"Large Egg": self.has("Chicken"),
|
|
"Large Egg (Brown)": self.has("Chicken"),
|
|
"Large Goat Milk": self.has("Goat"),
|
|
"Large Milk": self.has("Cow"),
|
|
"Leek": self.received("Spring"),
|
|
"Lightning Rod": self.has_skill_level("Foraging", 6),
|
|
"Lobster": self.can_crab_pot(),
|
|
"Loom": self.has_skill_level("Farming", 7) & self.has("Pine Tar"),
|
|
"Magma Geode": self.can_mine_in_the_mines_floor_81_120() |
|
|
(self.has("Lava Eel") & self.has_building("Fish Pond")),
|
|
"Maki Roll": self.can_cook() & self.can_fish(),
|
|
"Maple Syrup": self.has("Tapper"),
|
|
"Mead": self.has("Keg") & self.has("Honey"),
|
|
"Melon": self.received("Summer"),
|
|
"Milk": self.has("Cow"),
|
|
"Miner's Treat": self.can_cook() & self.has_skill_level("Mining", 3) & self.has("Cow Milk") & self.has(
|
|
"Cave Carrot"),
|
|
"Morel": self.can_reach_region("Secret Woods") & self.received("Year Two"),
|
|
"Mussel": _True(),
|
|
"Nautilus Shell": self.received("Winter"),
|
|
"Oak Resin": self.has("Tapper"),
|
|
"Oil Maker": self.has_skill_level("Farming", 8) & self.has("Hardwood") & self.has("Gold Bar"),
|
|
"Omelet": self.can_cook() & self.can_spend_money(100) & self.has("Any Egg") & self.has("Cow Milk"),
|
|
"Omni Geode": self.can_mine_in_the_mines_floor_41_80() |
|
|
self.can_reach_region("The Desert") |
|
|
self.can_do_panning() |
|
|
self.received("Rusty Key") |
|
|
(self.has("Octopus") & self.has_building("Fish Pond")) |
|
|
self.can_reach_region("Ginger Island"),
|
|
"Orange": self.received("Summer"),
|
|
"Ostrich": self.has_building("Barn"),
|
|
"Oyster": _True(),
|
|
"Pale Ale": self.has("Keg") & self.has("Hops"),
|
|
"Pale Broth": self.can_cook() & self.can_have_relationship("Marnie", 3) & self.has("White Algae"),
|
|
"Pancakes": self.can_cook() & self.can_spend_money(100) & self.has("Any Egg"),
|
|
"Parsnip": self.received("Spring"),
|
|
"Parsnip Soup": self.can_cook() & self.can_have_relationship("Caroline", 3) & self.has(
|
|
"Parsnip") & self.has("Cow Milk"),
|
|
"Peach": self.received("Summer"),
|
|
"Pepper Poppers": self.can_cook() & self.has("Cheese") & self.has(
|
|
"Hot Pepper") & self.can_have_relationship("Shane", 3),
|
|
"Periwinkle": self.can_crab_pot(),
|
|
"Pickles": self.has("Preserves Jar"),
|
|
"Pig": self.has_building("Deluxe Barn"),
|
|
"Pine Tar": self.has("Tapper"),
|
|
"Pizza": self.can_spend_money(600),
|
|
"Pomegranate": self.received("Fall"),
|
|
"Poppy": self.received("Summer"),
|
|
"Potato": self.received("Spring"),
|
|
"Preserves Jar": self.has_skill_level("Farming", 4),
|
|
"Prismatic Shard": self.received("Year Two"),
|
|
"Pumpkin": self.received("Fall"),
|
|
"Purple Mushroom": self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
|
|
"Quartz": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Rabbit": self.has_building("Deluxe Coop"),
|
|
"Rabbit's Foot": self.has("Rabbit"),
|
|
"Radish": self.received("Summer"),
|
|
"Rainbow Shell": self.received("Summer"),
|
|
"Rain Totem": self.has_skill_level("Foraging", 9),
|
|
"Recycling Machine": self.has_skill_level("Fishing", 4) & self.has("Wood") &
|
|
self.has("Stone") & self.has("Iron Bar"),
|
|
"Red Cabbage": self.received("Year Two"),
|
|
"Red Mushroom": self.can_reach_region("Secret Woods") & (self.received("Summer") | self.received("Fall")),
|
|
"Refined Quartz": self.has("Quartz") | self.has("Fire Quartz") |
|
|
(self.has("Recycling Machine") & (self.has("Broken CD") | self.has("Broken Glasses"))),
|
|
"Rhubarb": self.received("Spring") & self.can_reach_region("The Desert"),
|
|
"Roe": self.can_fish() & self.has_building("Fish Pond"),
|
|
"Roots Platter": self.can_cook() & self.has_skill_level("Combat", 3) &
|
|
self.has("Cave Carrot") & self.has("Winter Root"),
|
|
"Ruby": self.can_mine_in_the_mines_floor_81_120() | self.can_do_panning(),
|
|
"Salad": self.can_spend_money(220) | (
|
|
self.can_cook() & self.can_have_relationship("Emily", 3) & self.has("Leek") & self.has(
|
|
"Dandelion")),
|
|
"Salmonberry": self.received("Spring"),
|
|
"Salmon Dinner": self.can_cook() & self.can_have_relationship("Gus", 3) & self.has("Salmon") & self.has(
|
|
"Amaranth") & self.has("Kale"),
|
|
"Sashimi": self.can_fish() & self.can_cook() & self.can_have_relationship("Linus", 3),
|
|
"Sea Urchin": self.can_reach_region("Tide Pools") | self.received("Summer"),
|
|
"Seaweed": self.can_fish() | self.can_reach_region("Tide Pools"),
|
|
"Sheep": self.has_building("Deluxe Barn"),
|
|
"Shrimp": self.can_crab_pot(),
|
|
"Slime": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Snail": self.can_crab_pot(),
|
|
"Snow Yam": self.received("Winter"),
|
|
"Soggy Newspaper": self.can_crab_pot(),
|
|
"Solar Essence": self.can_mine_in_the_mines_floor_41_80() | self.can_mine_in_the_skull_cavern(),
|
|
"Spaghetti": self.can_spend_money(240),
|
|
"Spice Berry": self.received("Summer"),
|
|
"Spring Onion": self.received("Spring"),
|
|
"Staircase": self.has_skill_level("Mining", 2),
|
|
"Starfruit": (self.received("Summer") | self.received("Greenhouse")) & self.can_reach_region("The Desert"),
|
|
"Stone": self.has_tool("Pickaxe"),
|
|
"Strawberry": self.received("Spring"),
|
|
"Sturgeon Roe": self.has("Sturgeon") & self.has_building("Fish Pond"),
|
|
"Summer Spangle": self.received("Summer"),
|
|
"Sunflower": self.received("Summer") | self.received("Fall"),
|
|
"Survival Burger": self.can_cook() & self.has_skill_level("Foraging", 2) &
|
|
self.has(["Bread", "Cave Carrot", "Eggplant"]),
|
|
"Sweet Gem Berry": (self.received("Fall") | self.received("Greenhouse")) & self.has_traveling_merchant(),
|
|
"Sweet Pea": self.received("Summer"),
|
|
"Tapper": self.has_skill_level("Foraging", 3),
|
|
"Tomato": self.received("Summer"),
|
|
"Topaz": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Tortilla": self.can_cook() & self.can_spend_money(100) & self.has("Corn"),
|
|
"Trash": self.can_crab_pot(),
|
|
"Triple Shot Espresso": (self.has("Hot Java Ring") |
|
|
(self.can_cook() & self.can_spend_money(5000) & self.has("Coffee"))),
|
|
"Truffle Oil": self.has("Truffle") & self.has("Oil Maker"),
|
|
"Truffle": self.has("Pig") & self.received("Year Two"),
|
|
"Tulip": self.received("Spring"),
|
|
"Unmilled Rice": self.received("Year Two"),
|
|
"Void Essence": self.can_mine_in_the_mines_floor_81_120() | self.can_mine_in_the_skull_cavern(),
|
|
"Wheat": self.received("Summer") | self.received("Fall"),
|
|
"White Algae": self.can_fish() & self.can_mine_in_the_mines_floor_1_40(),
|
|
"Wild Horseradish": self.received("Spring"),
|
|
"Wild Plum": self.received("Fall"),
|
|
"Wilted Bouquet": self.has("Furnace") & self.has("Bouquet") & self.has("Coal"),
|
|
"Wine": self.has("Keg"),
|
|
"Winter Root": self.received("Winter"),
|
|
"Wood": self.has_tool("Axe"),
|
|
"Wool": self.has("Rabbit") | self.has("Sheep"),
|
|
"Yam": self.received("Fall"),
|
|
"Hay": self.has_building("Silo"),
|
|
})
|
|
self.item_rules.update(self.fish_rules)
|
|
|
|
self.building_rules.update({
|
|
"Barn": self.can_spend_money(6000) & self.has(["Wood", "Stone"]),
|
|
"Big Barn": self.can_spend_money(12000) & self.has(["Wood", "Stone"]) & self.has_building("Barn"),
|
|
"Deluxe Barn": self.can_spend_money(25000) & self.has(["Wood", "Stone"]) & self.has_building("Big Barn"),
|
|
"Coop": self.can_spend_money(4000) & self.has(["Wood", "Stone"]),
|
|
"Big Coop": self.can_spend_money(10000) & self.has(["Wood", "Stone"]) & self.has_building("Coop"),
|
|
"Deluxe Coop": self.can_spend_money(20000) & self.has(["Wood", "Stone"]) & self.has_building("Big Coop"),
|
|
"Fish Pond": self.can_spend_money(5000) & self.has(["Stone", "Seaweed", "Green Algae"]),
|
|
"Mill": self.can_spend_money(2500) & self.has(["Stone", "Wood", "Cloth"]),
|
|
"Shed": self.can_spend_money(15000) & self.has("Wood"),
|
|
"Big Shed": self.can_spend_money(20000) & self.has(["Wood", "Stone"]) & self.has_building("Shed"),
|
|
"Silo": self.can_spend_money(100) & self.has(["Stone", "Clay", "Copper Bar"]),
|
|
"Slime Hutch": self.can_spend_money(10000) & self.has(["Stone", "Refined Quartz", "Iridium Bar"]),
|
|
"Stable": self.can_spend_money(10000) & self.has(["Hardwood", "Iron Bar"]),
|
|
"Well": self.can_spend_money(1000) & self.has("Stone"),
|
|
"Shipping Bin": self.can_spend_money(250) & self.has("Wood"),
|
|
"Kitchen": self.can_spend_money(10000) & self.has("Wood") & self.has_house(0),
|
|
"Kids Room": self.can_spend_money(50000) & self.has("Hardwood") & self.has_house(1),
|
|
"Cellar": self.can_spend_money(100000) & self.has_house(2),
|
|
})
|
|
|
|
self.quest_rules.update({
|
|
"Introductions": _True(),
|
|
"How To Win Friends": self.can_complete_quest("Introductions"),
|
|
"Getting Started": self.received("Spring") & self.has_tool("Hoe") & self.has_tool("Watering Can"),
|
|
"To The Beach": self.received("Spring"),
|
|
"Raising Animals": self.can_complete_quest("Getting Started") & self.has_building("Coop"),
|
|
"Advancement": self.can_complete_quest("Getting Started") & self.has_skill_level("Farming", 1),
|
|
"Archaeology": self.has_tool("Hoe") | self.can_mine_in_the_mines_floor_1_40() | self.can_fish(),
|
|
"Meet The Wizard": self.received("Spring") & self.can_reach_region("Community Center"),
|
|
"Forging Ahead": self.has("Copper Ore") & self.has("Furnace"),
|
|
"Smelting": self.has("Copper Bar"),
|
|
"Initiation": self.can_mine_in_the_mines_floor_1_40(),
|
|
"Robin's Lost Axe": self.received("Spring"),
|
|
"Jodi's Request": self.received("Spring") & self.has("Cauliflower"),
|
|
"Mayor's \"Shorts\"": self.received("Summer") & self.can_have_relationship("Marnie", 4),
|
|
"Blackberry Basket": self.received("Fall"),
|
|
"Marnie's Request": self.can_have_relationship("Marnie", 3) & self.has("Cave Carrot"),
|
|
"Pam Is Thirsty": self.received("Summer") & self.has("Pale Ale"),
|
|
"A Dark Reagent": self.received("Winter") & self.has("Void Essence"),
|
|
"Cow's Delight": self.received("Fall") & self.has("Amaranth"),
|
|
"The Skull Key": self.received("Skull Key") & self.can_reach_region("The Desert"),
|
|
"Crop Research": self.received("Summer") & self.has("Melon"),
|
|
"Knee Therapy": self.received("Summer") & self.has("Hot Pepper"),
|
|
"Robin's Request": self.received("Winter") & self.has("Hardwood"),
|
|
"Qi's Challenge": self.can_mine_in_the_skull_cavern(),
|
|
"The Mysterious Qi": self.has("Battery Pack") & self.can_reach_region("The Desert") & self.has(
|
|
"Rainbow Shell") & self.has("Beet") & self.has("Solar Essence"),
|
|
"Carving Pumpkins": self.received("Fall") & self.has("Pumpkin"),
|
|
"A Winter Mystery": self.received("Winter"),
|
|
"Strange Note": self.received("Magnifying Glass") & self.can_reach_region("Secret Woods") & self.has(
|
|
"Maple Syrup"),
|
|
"Cryptic Note": self.received("Magnifying Glass") & self.can_mine_perfectly_in_the_skull_cavern(),
|
|
"Fresh Fruit": self.received("Year Two") & self.has("Apricot"),
|
|
"Aquatic Research": self.received("Year Two") & self.has("Pufferfish"),
|
|
"A Soldier's Star": self.received("Year Two") & self.has("Starfruit"),
|
|
"Mayor's Need": self.received("Year Two") & self.has("Truffle Oil"),
|
|
"Wanted: Lobster": self.received("Year Two") & self.has("Lobster"),
|
|
"Pam Needs Juice": self.received("Year Two") & self.has("Battery Pack"),
|
|
"Fish Casserole": self.received("Year Two") & self.can_have_relationship("Jodi", 4) & self.has(
|
|
"Largemouth Bass"),
|
|
"Catch A Squid": self.received("Year Two") & self.has("Squid"),
|
|
"Fish Stew": self.received("Year Two") & self.has("Albacore"),
|
|
"Pierre's Notice": self.received("Year Two") & self.has("Sashimi"),
|
|
"Clint's Attempt": self.received("Year Two") & self.has("Amethyst"),
|
|
"A Favor For Clint": self.received("Year Two") & self.has("Iron Bar"),
|
|
"Staff Of Power": self.received("Year Two") & self.has("Iridium Bar"),
|
|
"Granny's Gift": self.received("Year Two") & self.has("Leek"),
|
|
"Exotic Spirits": self.received("Year Two") & self.has("Coconut"),
|
|
"Catch a Lingcod": self.received("Year Two") & self.has("Lingcod"),
|
|
})
|
|
|
|
def has(self, items: Union[str, (Iterable[str], Sized)], count: Optional[int] = None) -> StardewRule:
|
|
if isinstance(items, str):
|
|
return _Has(items, self.item_rules)
|
|
|
|
if count is None or count == len(items):
|
|
return _And(self.has(item) for item in items)
|
|
|
|
if count == 1:
|
|
return _Or(self.has(item) for item in items)
|
|
|
|
return _Count(count, (self.has(item) for item in items))
|
|
|
|
def received(self, items: Union[str, Iterable[str]], count: Optional[int] = 1) -> StardewRule:
|
|
if isinstance(items, str):
|
|
return _Received(items, self.player, count)
|
|
|
|
if count is None:
|
|
return _And(self.received(item) for item in items)
|
|
|
|
if count == 1:
|
|
return _Or(self.received(item) for item in items)
|
|
|
|
return _TotalReceived(count, items, self.player)
|
|
|
|
def can_reach_region(self, spot: str) -> StardewRule:
|
|
return _Reach(spot, "Region", self.player)
|
|
|
|
def can_reach_any_region(self, spots: Iterable[str]) -> StardewRule:
|
|
return _Or(self.can_reach_region(spot) for spot in spots)
|
|
|
|
def can_reach_location(self, spot: str) -> StardewRule:
|
|
return _Reach(spot, "Location", self.player)
|
|
|
|
def can_reach_entrance(self, spot: str) -> StardewRule:
|
|
return _Reach(spot, "Entrance", self.player)
|
|
|
|
def can_have_earned_total_money(self, amount: int) -> StardewRule:
|
|
if amount <= 10000:
|
|
return self.received("Spring")
|
|
elif amount <= 30000:
|
|
return self.received("Summer")
|
|
elif amount <= 60000:
|
|
return self.received("Fall")
|
|
elif amount <= 70000:
|
|
return self.received("Winter")
|
|
return self.received("Year Two")
|
|
|
|
def can_spend_money(self, amount: int) -> StardewRule:
|
|
if amount <= 2000:
|
|
return self.received("Spring")
|
|
elif amount <= 8000:
|
|
return self.received("Summer")
|
|
elif amount <= 15000:
|
|
return self.received("Fall")
|
|
elif amount <= 18000:
|
|
return self.received("Winter")
|
|
return self.received("Year Two")
|
|
|
|
def has_tool(self, tool: str, material: str = "Basic") -> StardewRule:
|
|
if material == "Basic":
|
|
return _True()
|
|
|
|
if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
|
return self.received(f"Progressive {tool}", count=tool_materials[material])
|
|
|
|
return self.has(f"{material} Bar") & self.can_spend_money(tool_prices[material])
|
|
|
|
def has_skill_level(self, skill: str, level: int) -> StardewRule:
|
|
if level == 0:
|
|
return _True()
|
|
|
|
if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
|
|
return self.received(f"{skill} Level", count=level)
|
|
|
|
if skill == "Fishing" and self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
|
return self.can_get_fishing_xp()
|
|
|
|
return self.received(season_per_skill_level[(skill, level)])
|
|
|
|
def has_total_skill_level(self, level: int) -> StardewRule:
|
|
if level == 0:
|
|
return _True()
|
|
|
|
if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
|
|
skills_items = ["Farming Level", "Mining Level", "Foraging Level",
|
|
"Fishing Level", "Combat Level"]
|
|
return self.received(skills_items, count=level)
|
|
|
|
if level > 40 and self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
|
return self.received(season_per_total_level[level]) & self.can_get_fishing_xp()
|
|
|
|
return self.received(season_per_total_level[level])
|
|
|
|
def has_building(self, building: str) -> StardewRule:
|
|
if not self.options[options.BuildingProgression] == options.BuildingProgression.option_vanilla:
|
|
count = 1
|
|
if building in ["Coop", "Barn", "Shed"]:
|
|
building = f"Progressive {building}"
|
|
elif building.startswith("Big"):
|
|
count = 2
|
|
building = " ".join(["Progressive", *building.split(" ")[1:]])
|
|
elif building.startswith("Deluxe"):
|
|
count = 3
|
|
building = " ".join(["Progressive", *building.split(" ")[1:]])
|
|
return self.received(f"{building}", count)
|
|
|
|
return _Has(building, self.building_rules)
|
|
|
|
def has_house(self, upgrade_level: int) -> StardewRule:
|
|
if upgrade_level < 1:
|
|
return _True()
|
|
|
|
if upgrade_level > 3:
|
|
return _False()
|
|
|
|
if not self.options[options.BuildingProgression] == options.BuildingProgression.option_vanilla:
|
|
return self.received(f"Progressive House", upgrade_level)
|
|
|
|
if upgrade_level == 1:
|
|
return _Has("Kitchen", self.building_rules)
|
|
|
|
if upgrade_level == 2:
|
|
return _Has("Kids Room", self.building_rules)
|
|
|
|
# if upgrade_level == 3:
|
|
return _Has("Cellar", self.building_rules)
|
|
|
|
def can_complete_quest(self, quest: str) -> StardewRule:
|
|
return _Has(quest, self.quest_rules)
|
|
|
|
def can_get_fishing_xp(self) -> StardewRule:
|
|
if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
|
|
return self.can_fish() | self.can_crab_pot()
|
|
|
|
return self.can_fish()
|
|
|
|
def can_fish(self, difficulty: int = 0) -> StardewRule:
|
|
skill_required = max(0, int((difficulty / 10) - 1))
|
|
if difficulty <= 40:
|
|
skill_required = 0
|
|
skill_rule = self.has_skill_level("Fishing", skill_required)
|
|
number_fishing_rod_required = 1 if difficulty < 50 else 2
|
|
if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
|
return self.received("Progressive Fishing Rod", number_fishing_rod_required) & skill_rule
|
|
|
|
return skill_rule
|
|
|
|
def can_catch_fish(self, fish: FishItem) -> StardewRule:
|
|
region_rule = self.can_reach_any_region(fish.locations)
|
|
season_rule = self.received(fish.seasons)
|
|
difficulty_rule = self.can_fish(fish.difficulty)
|
|
if fish.difficulty == -1:
|
|
difficulty_rule = self.can_crab_pot()
|
|
return region_rule & season_rule & difficulty_rule
|
|
|
|
def can_catch_every_fish(self) -> StardewRule:
|
|
rules = [self.has_skill_level("Fishing", 10), self.has_max_fishing_rod()]
|
|
for fish in all_fish_items:
|
|
rules.append(self.can_catch_fish(fish))
|
|
return _And(rules)
|
|
|
|
def has_max_fishing_rod(self) -> StardewRule:
|
|
if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
|
return self.received("Progressive Fishing Rod", 4)
|
|
return self.can_get_fishing_xp()
|
|
|
|
def can_cook(self) -> StardewRule:
|
|
return self.has_house(1) or self.has_skill_level("Foraging", 9)
|
|
|
|
def can_smelt(self, item: str) -> StardewRule:
|
|
return self.has("Furnace") & self.has(item)
|
|
|
|
def can_crab_pot(self) -> StardewRule:
|
|
if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
|
|
return self.has("Crab Pot")
|
|
|
|
return _True()
|
|
|
|
def can_do_panning(self) -> StardewRule:
|
|
return self.received("Glittering Boulder Removed")
|
|
|
|
# Regions
|
|
def can_mine_in_the_mines_floor_1_40(self) -> StardewRule:
|
|
return self.can_reach_region("The Mines - Floor 5")
|
|
|
|
def can_mine_in_the_mines_floor_41_80(self) -> StardewRule:
|
|
return self.can_reach_region("The Mines - Floor 45")
|
|
|
|
def can_mine_in_the_mines_floor_81_120(self) -> StardewRule:
|
|
return self.can_reach_region("The Mines - Floor 85")
|
|
|
|
def can_mine_in_the_skull_cavern(self) -> StardewRule:
|
|
return (self.can_progress_in_the_mines_from_floor(120) &
|
|
self.can_reach_region("Skull Cavern"))
|
|
|
|
def can_mine_perfectly_in_the_skull_cavern(self) -> StardewRule:
|
|
return (self.can_progress_in_the_mines_from_floor(160) &
|
|
self.can_reach_region("Skull Cavern"))
|
|
|
|
def get_weapon_rule_for_floor_tier(self, tier: int):
|
|
if tier >= 4:
|
|
return self.has_galaxy_weapon()
|
|
if tier >= 3:
|
|
return self.has_great_weapon()
|
|
if tier >= 2:
|
|
return self.has_good_weapon()
|
|
if tier >= 1:
|
|
return self.has_decent_weapon()
|
|
return self.has_any_weapon()
|
|
|
|
def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule:
|
|
tier = int(floor / 40)
|
|
rules = []
|
|
weapon_rule = self.get_weapon_rule_for_floor_tier(tier)
|
|
rules.append(weapon_rule)
|
|
if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
|
rules.append(self.received("Progressive Pickaxe", tier))
|
|
if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
|
|
combat_tier = min(10, max(0, tier * 2))
|
|
rules.append(self.has_skill_level("Combat", combat_tier))
|
|
return _And(rules)
|
|
|
|
def can_progress_easily_in_the_mines_from_floor(self, floor: int) -> StardewRule:
|
|
tier = int(floor / 40) + 1
|
|
rules = []
|
|
weapon_rule = self.get_weapon_rule_for_floor_tier(tier)
|
|
rules.append(weapon_rule)
|
|
if self.options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
|
rules.append(self.received("Progressive Pickaxe", count=tier))
|
|
if self.options[options.SkillProgression] == options.SkillProgression.option_progressive:
|
|
combat_tier = min(10, max(0, tier * 2))
|
|
rules.append(self.has_skill_level("Combat", combat_tier))
|
|
return _And(rules)
|
|
|
|
def has_mine_elevator_to_floor(self, floor: int) -> StardewRule:
|
|
if (self.options[options.TheMinesElevatorsProgression] ==
|
|
options.TheMinesElevatorsProgression.option_progressive or
|
|
self.options[options.TheMinesElevatorsProgression] ==
|
|
options.TheMinesElevatorsProgression.option_progressive_from_previous_floor):
|
|
return self.received("Progressive Mine Elevator", count=int(floor / 5))
|
|
return _True()
|
|
|
|
def can_mine_to_floor(self, floor: int) -> StardewRule:
|
|
previous_elevator = max(floor - 5, 0)
|
|
previous_previous_elevator = max(floor - 10, 0)
|
|
return ((self.has_mine_elevator_to_floor(previous_elevator) &
|
|
self.can_progress_in_the_mines_from_floor(previous_elevator)) |
|
|
(self.has_mine_elevator_to_floor(previous_previous_elevator) &
|
|
self.can_progress_easily_in_the_mines_from_floor(previous_previous_elevator)))
|
|
|
|
def has_jotpk_power_level(self, power_level: int) -> StardewRule:
|
|
if self.options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling:
|
|
return _True()
|
|
jotpk_buffs = ["JotPK: Progressive Boots", "JotPK: Progressive Gun",
|
|
"JotPK: Progressive Ammo", "JotPK: Extra Life", "JotPK: Increased Drop Rate"]
|
|
return self.received(jotpk_buffs, power_level)
|
|
|
|
def has_junimo_kart_power_level(self, power_level: int) -> StardewRule:
|
|
if self.options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling:
|
|
return _True()
|
|
return self.received("Junimo Kart: Extra Life", power_level)
|
|
|
|
def has_traveling_merchant(self, tier: int = 1):
|
|
traveling_merchant_days = [f"Traveling Merchant: {day}" for day in week_days]
|
|
return self.received(traveling_merchant_days, tier)
|
|
|
|
def can_get_married(self) -> StardewRule:
|
|
return self.can_reach_region("Tide Pools") & self.can_have_relationship("Bachelor", 10) & self.has_house(1)
|
|
|
|
def can_have_relationship(self, npc: str, hearts: int) -> StardewRule:
|
|
if npc == "Leo":
|
|
return self.can_reach_region("Ginger Island")
|
|
|
|
if npc == "Sandy":
|
|
return self.can_reach_region("The Desert")
|
|
|
|
if npc == "Kent":
|
|
return self.received("Year Two")
|
|
|
|
if hearts <= 3:
|
|
return self.received("Spring")
|
|
if hearts <= 6:
|
|
return self.received("Summer")
|
|
if hearts <= 9:
|
|
return self.received("Fall")
|
|
return self.received("Winter")
|
|
|
|
def can_complete_bundle(self, bundle_requirements: List[BundleItem], number_required: int) -> StardewRule:
|
|
item_rules = []
|
|
for bundle_item in bundle_requirements:
|
|
if bundle_item.item.item_id == -1:
|
|
return self.can_spend_money(bundle_item.amount)
|
|
else:
|
|
item_rules.append(bundle_item.item.name)
|
|
return self.has(item_rules, number_required)
|
|
|
|
def can_complete_community_center(self) -> StardewRule:
|
|
return (self.can_reach_location("Complete Crafts Room") &
|
|
self.can_reach_location("Complete Pantry") &
|
|
self.can_reach_location("Complete Fish Tank") &
|
|
self.can_reach_location("Complete Bulletin Board") &
|
|
self.can_reach_location("Complete Vault") &
|
|
self.can_reach_location("Complete Boiler Room"))
|
|
|
|
def can_finish_grandpa_evaluation(self) -> StardewRule:
|
|
# https://stardewvalleywiki.com/Grandpa
|
|
rules_worth_a_point = [self.can_have_earned_total_money(50000), # 50 000g
|
|
self.can_have_earned_total_money(100000), # 100 000g
|
|
self.can_have_earned_total_money(200000), # 200 000g
|
|
self.can_have_earned_total_money(300000), # 300 000g
|
|
self.can_have_earned_total_money(500000), # 500 000g
|
|
self.can_have_earned_total_money(1000000), # 1 000 000g first point
|
|
self.can_have_earned_total_money(1000000), # 1 000 000g second point
|
|
self.has_total_skill_level(30), # Total Skills: 30
|
|
self.has_total_skill_level(50), # Total Skills: 50
|
|
# Completing the museum not expected
|
|
# Catching every fish not expected
|
|
# Shipping every item not expected
|
|
self.can_get_married() & self.has_house(2),
|
|
self.received("Fall"), # 5 Friends (TODO)
|
|
self.received("Winter"), # 10 friends (TODO)
|
|
self.received("Fall"), # Max Pet takes 56 days min
|
|
self.can_complete_community_center(), # Community Center Completion
|
|
self.can_complete_community_center(), # CC Ceremony first point
|
|
self.can_complete_community_center(), # CC Ceremony second point
|
|
self.received("Skull Key"), # Skull Key obtained
|
|
# Rusty key not expected
|
|
]
|
|
return _Count(12, rules_worth_a_point)
|
|
|
|
def has_any_weapon(self) -> StardewRule:
|
|
return self.has_decent_weapon() | self.received(item.name for item in all_items if Group.WEAPON in item.groups)
|
|
|
|
def has_decent_weapon(self) -> StardewRule:
|
|
return (self.has_good_weapon() |
|
|
self.received(item.name for item in all_items
|
|
if Group.WEAPON in item.groups and
|
|
(Group.MINES_FLOOR_50 in item.groups or Group.MINES_FLOOR_60 in item.groups)))
|
|
|
|
def has_good_weapon(self) -> StardewRule:
|
|
return ((self.has_great_weapon() |
|
|
self.received(item.name for item in all_items
|
|
if Group.WEAPON in item.groups and
|
|
(Group.MINES_FLOOR_80 in item.groups or Group.MINES_FLOOR_90 in item.groups))) &
|
|
self.received("Adventurer's Guild"))
|
|
|
|
def has_great_weapon(self) -> StardewRule:
|
|
return ((self.has_galaxy_weapon() |
|
|
self.received(item.name for item in all_items
|
|
if Group.WEAPON in item.groups and Group.MINES_FLOOR_110 in item.groups)) &
|
|
self.received("Adventurer's Guild"))
|
|
|
|
def has_galaxy_weapon(self) -> StardewRule:
|
|
return (self.received(item.name for item in all_items
|
|
if Group.WEAPON in item.groups and Group.GALAXY_WEAPONS in item.groups) &
|
|
self.received("Adventurer's Guild"))
|