From 9de49aa419202700669d9557d371ebd7e42234bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=A9mie=20Bolduc?= <16137441+Jouramie@users.noreply.github.com> Date: Sat, 22 Mar 2025 15:29:16 -0400 Subject: [PATCH] Stardew Valley: Move all the goal logic into its own file (#4383) --- worlds/stardew_valley/__init__.py | 32 ++-- worlds/stardew_valley/logic/cooking_logic.py | 19 +- worlds/stardew_valley/logic/crafting_logic.py | 29 +-- worlds/stardew_valley/logic/fishing_logic.py | 16 +- worlds/stardew_valley/logic/goal_logic.py | 173 ++++++++++++++++++ worlds/stardew_valley/logic/has_logic.py | 28 ++- worlds/stardew_valley/logic/logic.py | 66 +------ worlds/stardew_valley/logic/shipping_logic.py | 16 +- 8 files changed, 223 insertions(+), 156 deletions(-) create mode 100644 worlds/stardew_valley/logic/goal_logic.py diff --git a/worlds/stardew_valley/__init__.py b/worlds/stardew_valley/__init__.py index fe69030882..83a1c11c18 100644 --- a/worlds/stardew_valley/__init__.py +++ b/worlds/stardew_valley/__init__.py @@ -214,67 +214,67 @@ class StardewValleyWorld(World): def setup_victory(self): if self.options.goal == Goal.option_community_center: self.create_event_location(location_table[GoalName.community_center], - self.logic.bundle.can_complete_community_center, + self.logic.goal.can_complete_community_center(), Event.victory) elif self.options.goal == Goal.option_grandpa_evaluation: self.create_event_location(location_table[GoalName.grandpa_evaluation], - self.logic.can_finish_grandpa_evaluation(), + self.logic.goal.can_finish_grandpa_evaluation(), Event.victory) elif self.options.goal == Goal.option_bottom_of_the_mines: self.create_event_location(location_table[GoalName.bottom_of_the_mines], - True_(), + self.logic.goal.can_complete_bottom_of_the_mines(), Event.victory) elif self.options.goal == Goal.option_cryptic_note: self.create_event_location(location_table[GoalName.cryptic_note], - self.logic.quest.can_complete_quest("Cryptic Note"), + self.logic.goal.can_complete_cryptic_note(), Event.victory) elif self.options.goal == Goal.option_master_angler: self.create_event_location(location_table[GoalName.master_angler], - self.logic.fishing.can_catch_every_fish_for_fishsanity(), + self.logic.goal.can_complete_master_angler(), Event.victory) elif self.options.goal == Goal.option_complete_collection: self.create_event_location(location_table[GoalName.complete_museum], - self.logic.museum.can_complete_museum(), + self.logic.goal.can_complete_complete_collection(), Event.victory) elif self.options.goal == Goal.option_full_house: self.create_event_location(location_table[GoalName.full_house], - (self.logic.relationship.has_children(2) & self.logic.relationship.can_reproduce()), + self.logic.goal.can_complete_full_house(), Event.victory) elif self.options.goal == Goal.option_greatest_walnut_hunter: self.create_event_location(location_table[GoalName.greatest_walnut_hunter], - self.logic.walnut.has_walnut(130), + self.logic.goal.can_complete_greatest_walnut_hunter(), Event.victory) elif self.options.goal == Goal.option_protector_of_the_valley: self.create_event_location(location_table[GoalName.protector_of_the_valley], - self.logic.monster.can_complete_all_monster_slaying_goals(), + self.logic.goal.can_complete_protector_of_the_valley(), Event.victory) elif self.options.goal == Goal.option_full_shipment: self.create_event_location(location_table[GoalName.full_shipment], - self.logic.shipping.can_ship_everything_in_slot(self.get_all_location_names()), + self.logic.goal.can_complete_full_shipment(self.get_all_location_names()), Event.victory) elif self.options.goal == Goal.option_gourmet_chef: self.create_event_location(location_table[GoalName.gourmet_chef], - self.logic.cooking.can_cook_everything, + self.logic.goal.can_complete_gourmet_chef(), Event.victory) elif self.options.goal == Goal.option_craft_master: self.create_event_location(location_table[GoalName.craft_master], - self.logic.crafting.can_craft_everything, + self.logic.goal.can_complete_craft_master(), Event.victory) elif self.options.goal == Goal.option_legend: self.create_event_location(location_table[GoalName.legend], - self.logic.money.can_have_earned_total(10_000_000), + self.logic.goal.can_complete_legend(), Event.victory) elif self.options.goal == Goal.option_mystery_of_the_stardrops: self.create_event_location(location_table[GoalName.mystery_of_the_stardrops], - self.logic.has_all_stardrops(), + self.logic.goal.can_complete_mystery_of_the_stardrop(), Event.victory) elif self.options.goal == Goal.option_allsanity: self.create_event_location(location_table[GoalName.allsanity], - HasProgressionPercent(self.player, 100), + self.logic.goal.can_complete_allsanity(), Event.victory) elif self.options.goal == Goal.option_perfection: self.create_event_location(location_table[GoalName.perfection], - HasProgressionPercent(self.player, 100), + self.logic.goal.can_complete_perfection(), Event.victory) self.multiworld.completion_condition[self.player] = lambda state: state.has(Event.victory, self.player) diff --git a/worlds/stardew_valley/logic/cooking_logic.py b/worlds/stardew_valley/logic/cooking_logic.py index 46f3bdc93f..8c8f716afb 100644 --- a/worlds/stardew_valley/logic/cooking_logic.py +++ b/worlds/stardew_valley/logic/cooking_logic.py @@ -13,12 +13,9 @@ from .relationship_logic import RelationshipLogicMixin from .season_logic import SeasonLogicMixin from .skill_logic import SkillLogicMixin from ..data.recipe_data import RecipeSource, StarterSource, ShopSource, SkillSource, FriendshipSource, \ - QueenOfSauceSource, CookingRecipe, ShopFriendshipSource, \ - all_cooking_recipes_by_name + QueenOfSauceSource, CookingRecipe, ShopFriendshipSource from ..data.recipe_source import CutsceneSource, ShopTradeSource -from ..locations import locations_by_tag, LocationTags from ..options import Chefsanity -from ..options import ExcludeGingerIsland from ..stardew_rule import StardewRule, True_, False_ from ..strings.region_names import LogicRegion from ..strings.skill_names import Skill @@ -92,17 +89,3 @@ BuildingLogicMixin, RelationshipLogicMixin, SkillLogicMixin, CookingLogicMixin]] @cache_self1 def received_recipe(self, meal_name: str): return self.logic.received(f"{meal_name} Recipe") - - @cached_property - def can_cook_everything(self) -> StardewRule: - cooksanity_prefix = "Cook " - all_recipes_names = [] - exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true - for location in locations_by_tag[LocationTags.COOKSANITY]: - if exclude_island and LocationTags.GINGER_ISLAND in location.tags: - continue - if location.mod_name and location.mod_name not in self.options.mods: - continue - all_recipes_names.append(location.name[len(cooksanity_prefix):]) - all_recipes = [all_cooking_recipes_by_name[recipe_name] for recipe_name in all_recipes_names] - return self.logic.and_(*(self.logic.cooking.can_cook(recipe) for recipe in all_recipes)) diff --git a/worlds/stardew_valley/logic/crafting_logic.py b/worlds/stardew_valley/logic/crafting_logic.py index bd839707ef..b768a74b92 100644 --- a/worlds/stardew_valley/logic/crafting_logic.py +++ b/worlds/stardew_valley/logic/crafting_logic.py @@ -1,4 +1,3 @@ -from functools import cached_property from typing import Union from Utils import cache_self1 @@ -12,11 +11,10 @@ from .relationship_logic import RelationshipLogicMixin from .skill_logic import SkillLogicMixin from .special_order_logic import SpecialOrderLogicMixin from .. import options -from ..data.craftable_data import CraftingRecipe, all_crafting_recipes_by_name +from ..data.craftable_data import CraftingRecipe from ..data.recipe_source import CutsceneSource, ShopTradeSource, ArchipelagoSource, LogicSource, SpecialOrderSource, \ FestivalShopSource, QuestSource, StarterSource, ShopSource, SkillSource, MasterySource, FriendshipSource, SkillCraftsanitySource -from ..locations import locations_by_tag, LocationTags -from ..options import Craftsanity, SpecialOrderLocations, ExcludeGingerIsland +from ..options import Craftsanity, SpecialOrderLocations from ..stardew_rule import StardewRule, True_, False_ from ..strings.region_names import Region @@ -71,7 +69,8 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]): if isinstance(recipe.source, ShopSource): return self.logic.money.can_spend_at(recipe.source.region, recipe.source.price) if isinstance(recipe.source, SkillCraftsanitySource): - return self.logic.skill.has_level(recipe.source.skill, recipe.source.level) & self.logic.skill.can_earn_level(recipe.source.skill, recipe.source.level) + return self.logic.skill.has_level(recipe.source.skill, recipe.source.level) & self.logic.skill.can_earn_level(recipe.source.skill, + recipe.source.level) if isinstance(recipe.source, SkillSource): return self.logic.skill.has_level(recipe.source.skill, recipe.source.level) if isinstance(recipe.source, MasterySource): @@ -95,23 +94,3 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]): @cache_self1 def received_recipe(self, item_name: str): return self.logic.received(f"{item_name} Recipe") - - @cached_property - def can_craft_everything(self) -> StardewRule: - craftsanity_prefix = "Craft " - all_recipes_names = [] - exclude_island = self.options.exclude_ginger_island == ExcludeGingerIsland.option_true - exclude_masteries = not self.content.features.skill_progression.are_masteries_shuffled - for location in locations_by_tag[LocationTags.CRAFTSANITY]: - if not location.name.startswith(craftsanity_prefix): - continue - if exclude_island and LocationTags.GINGER_ISLAND in location.tags: - continue - # FIXME Remove when recipes are in content packs - if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags: - continue - if location.mod_name and location.mod_name not in self.options.mods: - continue - all_recipes_names.append(location.name[len(craftsanity_prefix):]) - all_recipes = [all_crafting_recipes_by_name[recipe_name] for recipe_name in all_recipes_names] - return self.logic.and_(*(self.logic.crafting.can_craft(recipe) for recipe in all_recipes)) diff --git a/worlds/stardew_valley/logic/fishing_logic.py b/worlds/stardew_valley/logic/fishing_logic.py index 63798a92fe..1bb4cccea6 100644 --- a/worlds/stardew_valley/logic/fishing_logic.py +++ b/worlds/stardew_valley/logic/fishing_logic.py @@ -29,7 +29,7 @@ class FishingLogicMixin(BaseLogicMixin): class FishingLogic(BaseLogic[Union[HasLogicMixin, FishingLogicMixin, ReceivedLogicMixin, RegionLogicMixin, SeasonLogicMixin, ToolLogicMixin, - SkillLogicMixin]]): +SkillLogicMixin]]): def can_fish_in_freshwater(self) -> StardewRule: return self.logic.skill.can_fish() & self.logic.region.can_reach_any((Region.forest, Region.town, Region.mountain)) @@ -97,19 +97,5 @@ class FishingLogic(BaseLogic[Union[HasLogicMixin, FishingLogicMixin, ReceivedLog return self.logic.and_(*rules) - def can_catch_every_fish_for_fishsanity(self) -> StardewRule: - if not self.content.features.fishsanity.is_enabled: - return self.can_catch_every_fish() - - rules = [self.has_max_fishing()] - - rules.extend( - self.logic.fishing.can_catch_fish_for_fishsanity(fish) - for fish in self.content.fishes.values() - if self.content.features.fishsanity.is_included(fish) - ) - - return self.logic.and_(*rules) - def has_specific_bait(self, fish: FishItem) -> StardewRule: return self.can_catch_fish(fish) & self.logic.has(Machine.bait_maker) diff --git a/worlds/stardew_valley/logic/goal_logic.py b/worlds/stardew_valley/logic/goal_logic.py new file mode 100644 index 0000000000..0ad3eb4f37 --- /dev/null +++ b/worlds/stardew_valley/logic/goal_logic.py @@ -0,0 +1,173 @@ +import typing + +from .base_logic import BaseLogic, BaseLogicMixin +from ..data.craftable_data import all_crafting_recipes_by_name +from ..data.recipe_data import all_cooking_recipes_by_name +from ..locations import LocationTags, locations_by_tag +from ..mods.mod_data import ModNames +from ..options import options +from ..stardew_rule import StardewRule +from ..strings.building_names import Building +from ..strings.quest_names import Quest +from ..strings.season_names import Season +from ..strings.wallet_item_names import Wallet + +if typing.TYPE_CHECKING: + from .logic import StardewLogic +else: + StardewLogic = object + + +class GoalLogicMixin(BaseLogicMixin): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.goal = GoalLogic(*args, **kwargs) + + +class GoalLogic(BaseLogic[StardewLogic]): + + def can_complete_community_center(self) -> StardewRule: + return self.logic.bundle.can_complete_community_center + + def can_finish_grandpa_evaluation(self) -> StardewRule: + # https://stardewvalleywiki.com/Grandpa + rules_worth_a_point = [ + self.logic.money.can_have_earned_total(50_000), + self.logic.money.can_have_earned_total(100_000), + self.logic.money.can_have_earned_total(200_000), + self.logic.money.can_have_earned_total(300_000), + self.logic.money.can_have_earned_total(500_000), + self.logic.money.can_have_earned_total(1_000_000), # first point + self.logic.money.can_have_earned_total(1_000_000), # second point + self.logic.skill.has_total_level(30), + self.logic.skill.has_total_level(50), + self.logic.museum.can_complete_museum(), + # Catching every fish not expected + # Shipping every item not expected + self.logic.relationship.can_get_married() & self.logic.building.has_house(2), + self.logic.relationship.has_hearts_with_n(5, 8), # 5 Friends + self.logic.relationship.has_hearts_with_n(10, 8), # 10 friends + self.logic.pet.has_pet_hearts(5), # Max Pet + self.logic.bundle.can_complete_community_center, # 1 point for Community Center Completion + self.logic.bundle.can_complete_community_center, # Ceremony first point + self.logic.bundle.can_complete_community_center, # Ceremony second point + self.logic.received(Wallet.skull_key), + self.logic.wallet.has_rusty_key(), + ] + return self.logic.count(12, *rules_worth_a_point) + + def can_complete_bottom_of_the_mines(self) -> StardewRule: + # The location is in the bottom of the mines region, so no actual rule is required + return self.logic.true_ + + def can_complete_cryptic_note(self) -> StardewRule: + return self.logic.quest.can_complete_quest(Quest.cryptic_note) + + def can_complete_master_angler(self) -> StardewRule: + if not self.content.features.fishsanity.is_enabled: + return self.logic.fishing.can_catch_every_fish() + + rules = [self.logic.fishing.has_max_fishing()] + + rules.extend( + self.logic.fishing.can_catch_fish_for_fishsanity(fish) + for fish in self.content.fishes.values() + if self.content.features.fishsanity.is_included(fish) + ) + + return self.logic.and_(*rules) + + def can_complete_complete_collection(self) -> StardewRule: + return self.logic.museum.can_complete_museum() + + def can_complete_full_house(self) -> StardewRule: + return self.logic.relationship.has_children(2) & self.logic.relationship.can_reproduce() + + def can_complete_greatest_walnut_hunter(self) -> StardewRule: + return self.logic.walnut.has_walnut(130) + + def can_complete_protector_of_the_valley(self) -> StardewRule: + return self.logic.monster.can_complete_all_monster_slaying_goals() + + def can_complete_full_shipment(self, all_location_names_in_slot: list[str]) -> StardewRule: + if self.options.shipsanity == options.Shipsanity.option_none: + return self.logic.shipping.can_ship_everything() + + rules = [self.logic.building.has_building(Building.shipping_bin)] + + for shipsanity_location in locations_by_tag[LocationTags.SHIPSANITY]: + if shipsanity_location.name not in all_location_names_in_slot: + continue + rules.append(self.logic.region.can_reach_location(shipsanity_location.name)) + return self.logic.and_(*rules) + + def can_complete_gourmet_chef(self) -> StardewRule: + cooksanity_prefix = "Cook " + all_recipes_names = [] + exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true + for location in locations_by_tag[LocationTags.COOKSANITY]: + if exclude_island and LocationTags.GINGER_ISLAND in location.tags: + continue + if location.mod_name and location.mod_name not in self.options.mods: + continue + all_recipes_names.append(location.name[len(cooksanity_prefix):]) + all_recipes = [all_cooking_recipes_by_name[recipe_name] for recipe_name in all_recipes_names] + return self.logic.and_(*(self.logic.cooking.can_cook(recipe) for recipe in all_recipes)) + + def can_complete_craft_master(self) -> StardewRule: + craftsanity_prefix = "Craft " + all_recipes_names = [] + exclude_island = self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_true + exclude_masteries = not self.content.features.skill_progression.are_masteries_shuffled + for location in locations_by_tag[LocationTags.CRAFTSANITY]: + if not location.name.startswith(craftsanity_prefix): + continue + if exclude_island and LocationTags.GINGER_ISLAND in location.tags: + continue + # FIXME Remove when recipes are in content packs + if exclude_masteries and LocationTags.REQUIRES_MASTERIES in location.tags: + continue + if location.mod_name and location.mod_name not in self.options.mods: + continue + all_recipes_names.append(location.name[len(craftsanity_prefix):]) + all_recipes = [all_crafting_recipes_by_name[recipe_name] for recipe_name in all_recipes_names] + return self.logic.and_(*(self.logic.crafting.can_craft(recipe) for recipe in all_recipes)) + + def can_complete_legend(self) -> StardewRule: + return self.logic.money.can_have_earned_total(10_000_000) + + def can_complete_mystery_of_the_stardrop(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 + + # Master Angler Stardrop + if self.content.features.fishsanity.is_enabled: + number_of_stardrops_to_receive += 1 + else: + other_rules.append(self.logic.fishing.can_catch_every_fish()) + + if self.options.festival_locations == options.FestivalLocations.option_disabled: # Fair Stardrop + other_rules.append(self.logic.season.has(Season.fall)) + else: + number_of_stardrops_to_receive += 1 + + # Spouse Stardrop + if self.content.features.friendsanity.is_enabled: + number_of_stardrops_to_receive += 1 + else: + other_rules.append(self.logic.relationship.has_hearts_with_any_bachelor(13)) + + if ModNames.deepwoods in self.options.mods: # Petting the Unicorn + number_of_stardrops_to_receive += 1 + + return self.logic.received("Stardrop", number_of_stardrops_to_receive) & self.logic.and_(*other_rules, allow_empty=True) + + def can_complete_allsanity(self) -> StardewRule: + return self.logic.has_progress_percent(100) + + def can_complete_perfection(self) -> StardewRule: + return self.logic.has_progress_percent(100) diff --git a/worlds/stardew_valley/logic/has_logic.py b/worlds/stardew_valley/logic/has_logic.py index 4331780dc0..5d4b700e3b 100644 --- a/worlds/stardew_valley/logic/has_logic.py +++ b/worlds/stardew_valley/logic/has_logic.py @@ -1,5 +1,5 @@ from .base_logic import BaseLogic -from ..stardew_rule import StardewRule, And, Or, Has, Count, true_, false_ +from ..stardew_rule import StardewRule, And, Or, Has, Count, true_, false_, HasProgressionPercent class HasLogicMixin(BaseLogic[None]): @@ -23,6 +23,12 @@ class HasLogicMixin(BaseLogic[None]): def has_n(self, *items: str, count: int): return self.count(count, *(self.has(item) for item in items)) + def has_progress_percent(self, percent: int): + assert percent >= 0, "Can't have a negative progress percent" + assert percent <= 100, "Can't have a progress percent over 100" + + return HasProgressionPercent(self.player, percent) + @staticmethod def count(count: int, *rules: StardewRule) -> StardewRule: assert rules, "Can't create a Count conditions without rules" @@ -47,8 +53,14 @@ class HasLogicMixin(BaseLogic[None]): return Count(rules, count) @staticmethod - def and_(*rules: StardewRule) -> StardewRule: - assert rules, "Can't create a And conditions without rules" + def and_(*rules: StardewRule, allow_empty: bool = False) -> StardewRule: + """ + :param rules: The rules to combine + :param allow_empty: If True, return true_ when no rules are given. Otherwise, raise an error. + """ + if not rules: + assert allow_empty, "Can't create a And conditions without rules" + return true_ if len(rules) == 1: return rules[0] @@ -56,8 +68,14 @@ class HasLogicMixin(BaseLogic[None]): return And(*rules) @staticmethod - def or_(*rules: StardewRule) -> StardewRule: - assert rules, "Can't create a Or conditions without rules" + def or_(*rules: StardewRule, allow_empty: bool = False) -> StardewRule: + """ + :param rules: The rules to combine + :param allow_empty: If True, return false_ when no rules are given. Otherwise, raise an error. + """ + if not rules: + assert allow_empty, "Can't create a Or conditions without rules" + return false_ if len(rules) == 1: return rules[0] diff --git a/worlds/stardew_valley/logic/logic.py b/worlds/stardew_valley/logic/logic.py index 34f609c0e2..dd95165417 100644 --- a/worlds/stardew_valley/logic/logic.py +++ b/worlds/stardew_valley/logic/logic.py @@ -19,6 +19,7 @@ from .farming_logic import FarmingLogicMixin from .festival_logic import FestivalLogicMixin from .fishing_logic import FishingLogicMixin from .gift_logic import GiftLogicMixin +from .goal_logic import GoalLogicMixin from .grind_logic import GrindLogicMixin from .harvesting_logic import HarvestingLogicMixin from .has_logic import HasLogicMixin @@ -50,8 +51,7 @@ 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 ExcludeGingerIsland, FestivalLocations, StardewValleyOptions +from ..options import ExcludeGingerIsland, StardewValleyOptions from ..stardew_rule import False_, True_, StardewRule from ..strings.animal_names import Animal from ..strings.animal_product_names import AnimalProduct @@ -93,7 +93,7 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin CombatLogicMixin, MagicLogicMixin, MonsterLogicMixin, ToolLogicMixin, PetLogicMixin, QualityLogicMixin, SkillLogicMixin, FarmingLogicMixin, BundleLogicMixin, FishingLogicMixin, MineLogicMixin, CookingLogicMixin, AbilityLogicMixin, SpecialOrderLogicMixin, QuestLogicMixin, CraftingLogicMixin, ModLogicMixin, HarvestingLogicMixin, SourceLogicMixin, - RequirementLogicMixin, BookLogicMixin, GrindLogicMixin, FestivalLogicMixin, WalnutLogicMixin): + RequirementLogicMixin, BookLogicMixin, GrindLogicMixin, FestivalLogicMixin, WalnutLogicMixin, GoalLogicMixin): player: int options: StardewValleyOptions content: StardewContent @@ -375,71 +375,11 @@ class StardewLogic(ReceivedLogicMixin, HasLogicMixin, RegionLogicMixin, Travelin def can_smelt(self, item: str) -> StardewRule: return self.has(Machine.furnace) & self.has(item) - 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_with_n(5, 8), # 5 Friends - self.relationship.has_hearts_with_n(10, 8), # 10 friends - self.pet.has_pet_hearts(5), # Max Pet - self.bundle.can_complete_community_center, # Community Center Completion - self.bundle.can_complete_community_center, # CC Ceremony first point - self.bundle.can_complete_community_center, # CC Ceremony second point - 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 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_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 - - # Master Angler Stardrop - if self.content.features.fishsanity.is_enabled: - number_of_stardrops_to_receive += 1 - else: - other_rules.append(self.fishing.can_catch_every_fish()) - - if self.options.festival_locations == FestivalLocations.option_disabled: # Fair Stardrop - other_rules.append(self.season.has(Season.fall)) - else: - number_of_stardrops_to_receive += 1 - - # Spouse Stardrop - if self.content.features.friendsanity.is_enabled: - number_of_stardrops_to_receive += 1 - else: - other_rules.append(self.relationship.has_hearts_with_any_bachelor(13)) - - if ModNames.deepwoods in self.options.mods: # Petting the Unicorn - number_of_stardrops_to_receive += 1 - - if not other_rules: - return self.received("Stardrop", number_of_stardrops_to_receive) - - return self.received("Stardrop", number_of_stardrops_to_receive) & self.logic.and_(*other_rules) - def has_abandoned_jojamart(self) -> StardewRule: return self.received(CommunityUpgrade.movie_theater, 1) diff --git a/worlds/stardew_valley/logic/shipping_logic.py b/worlds/stardew_valley/logic/shipping_logic.py index e9f2258172..d509cc4167 100644 --- a/worlds/stardew_valley/logic/shipping_logic.py +++ b/worlds/stardew_valley/logic/shipping_logic.py @@ -1,5 +1,5 @@ from functools import cached_property -from typing import Union, List +from typing import Union from Utils import cache_self1 from .base_logic import BaseLogic, BaseLogicMixin @@ -8,7 +8,7 @@ from .has_logic import HasLogicMixin from .received_logic import ReceivedLogicMixin from .region_logic import RegionLogicMixin from ..locations import LocationTags, locations_by_tag -from ..options import ExcludeGingerIsland, Shipsanity +from ..options import ExcludeGingerIsland from ..options import SpecialOrderLocations from ..stardew_rule import StardewRule from ..strings.building_names import Building @@ -45,15 +45,3 @@ class ShippingLogic(BaseLogic[Union[ReceivedLogicMixin, ShippingLogicMixin, Buil continue all_items_to_ship.append(location.name[len(shipsanity_prefix):]) return self.logic.building.has_building(Building.shipping_bin) & self.logic.has_all(*all_items_to_ship) - - def can_ship_everything_in_slot(self, all_location_names_in_slot: List[str]) -> StardewRule: - if self.options.shipsanity == Shipsanity.option_none: - return self.logic.shipping.can_ship_everything() - - rules = [self.logic.building.has_building(Building.shipping_bin)] - - for shipsanity_location in locations_by_tag[LocationTags.SHIPSANITY]: - if shipsanity_location.name not in all_location_names_in_slot: - continue - rules.append(self.logic.region.can_reach_location(shipsanity_location.name)) - return self.logic.and_(*rules)