Stardew Valley: Added rules requiring museum access to make donations (#2107)
This commit is contained in:
parent
6c7a7d2be5
commit
7ce9f20bc7
|
@ -43,8 +43,8 @@ class MuseumItem(GameItem):
|
|||
|
||||
unlikely = ()
|
||||
|
||||
all_artifact_items: List[MuseumItem] = []
|
||||
all_mineral_items: List[MuseumItem] = []
|
||||
all_museum_artifacts: List[MuseumItem] = []
|
||||
all_museum_minerals: List[MuseumItem] = []
|
||||
|
||||
all_museum_items: List[MuseumItem] = []
|
||||
|
||||
|
@ -56,7 +56,7 @@ def create_artifact(name: str,
|
|||
geodes: Union[str, Tuple[str, ...]] = (),
|
||||
monsters: Union[str, Tuple[str, ...]] = ()) -> MuseumItem:
|
||||
artifact_item = MuseumItem.of(name, item_id, difficulty, locations, geodes, monsters)
|
||||
all_artifact_items.append(artifact_item)
|
||||
all_museum_artifacts.append(artifact_item)
|
||||
all_museum_items.append(artifact_item)
|
||||
return artifact_item
|
||||
|
||||
|
@ -79,7 +79,7 @@ def create_mineral(name: str,
|
|||
difficulty += 31.0 / 2750.0 * 100
|
||||
|
||||
mineral_item = MuseumItem.of(name, item_id, difficulty, locations, geodes, monsters)
|
||||
all_mineral_items.append(mineral_item)
|
||||
all_museum_minerals.append(mineral_item)
|
||||
all_museum_items.append(mineral_item)
|
||||
return mineral_item
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from .data import all_fish, FishItem, all_purchasable_seeds, SeedItem, all_crops
|
|||
from .data.bundle_data import BundleItem
|
||||
from .data.crops_data import crops_by_name
|
||||
from .data.fish_data import island_fish
|
||||
from .data.museum_data import all_museum_items, MuseumItem, all_artifact_items, dwarf_scrolls
|
||||
from .data.museum_data import all_museum_items, MuseumItem, all_museum_artifacts, dwarf_scrolls, all_museum_minerals
|
||||
from .data.recipe_data import all_cooking_recipes, CookingRecipe, RecipeSource, FriendshipSource, QueenOfSauceSource, \
|
||||
StarterSource, ShopSource, SkillSource
|
||||
from .data.villagers_data import all_villagers_by_name, Villager
|
||||
|
@ -475,8 +475,8 @@ class StardewLogic:
|
|||
FestivalCheck.mermaid_pearl: self.has_season(Season.winter) & self.can_reach_region(Region.beach),
|
||||
FestivalCheck.cone_hat: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(2500),
|
||||
FestivalCheck.iridium_fireplace: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(15000),
|
||||
FestivalCheck.rarecrow_7: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(5000) & self.can_find_museum_artifacts(20),
|
||||
FestivalCheck.rarecrow_8: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(5000) & self.can_find_museum_items(40),
|
||||
FestivalCheck.rarecrow_7: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(5000) & self.can_donate_museum_artifacts(20),
|
||||
FestivalCheck.rarecrow_8: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(5000) & self.can_donate_museum_items(40),
|
||||
FestivalCheck.lupini_red_eagle: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(1200),
|
||||
FestivalCheck.lupini_portrait_mermaid: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(1200),
|
||||
FestivalCheck.lupini_solar_kingdom: self.has_season(Season.winter) & self.can_reach_region(Region.beach) & self.can_spend_money(1200),
|
||||
|
@ -1213,7 +1213,7 @@ class StardewLogic:
|
|||
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
|
||||
self.can_complete_museum(), # Completing the museum for a point
|
||||
# Catching every fish not expected
|
||||
# Shipping every item not expected
|
||||
self.can_get_married() & self.has_house(2),
|
||||
|
@ -1224,7 +1224,7 @@ class StardewLogic:
|
|||
self.can_complete_community_center(), # CC Ceremony first point
|
||||
self.can_complete_community_center(), # CC Ceremony second point
|
||||
self.received(Wallet.skull_key), # Skull Key obtained
|
||||
self.has_rusty_key(), # Rusty key not expected
|
||||
self.has_rusty_key(), # Rusty key obtained
|
||||
]
|
||||
return Count(12, rules_worth_a_point)
|
||||
|
||||
|
@ -1266,9 +1266,21 @@ class StardewLogic:
|
|||
|
||||
def can_speak_dwarf(self) -> StardewRule:
|
||||
if self.options[options.Museumsanity] == options.Museumsanity.option_none:
|
||||
return self.has([item.name for item in dwarf_scrolls])
|
||||
return And([self.can_donate_museum_item(item) for item in dwarf_scrolls])
|
||||
return self.received("Dwarvish Translation Guide")
|
||||
|
||||
def can_donate_museum_item(self, item: MuseumItem) -> StardewRule:
|
||||
return self.can_reach_region(Region.museum) & self.can_find_museum_item(item)
|
||||
|
||||
def can_donate_museum_items(self, number: int) -> StardewRule:
|
||||
return self.can_reach_region(Region.museum) & self.can_find_museum_items(number)
|
||||
|
||||
def can_donate_museum_artifacts(self, number: int) -> StardewRule:
|
||||
return self.can_reach_region(Region.museum) & self.can_find_museum_artifacts(number)
|
||||
|
||||
def can_donate_museum_minerals(self, number: int) -> StardewRule:
|
||||
return self.can_reach_region(Region.museum) & self.can_find_museum_minerals(number)
|
||||
|
||||
def can_find_museum_item(self, item: MuseumItem) -> StardewRule:
|
||||
region_rule = self.can_reach_all_regions_except_one(item.locations)
|
||||
geodes_rule = And([self.can_open_geode(geode) for geode in item.geodes])
|
||||
|
@ -1281,9 +1293,15 @@ class StardewLogic:
|
|||
|
||||
def can_find_museum_artifacts(self, number: int) -> StardewRule:
|
||||
rules = []
|
||||
for donation in all_museum_items:
|
||||
if donation in all_artifact_items:
|
||||
rules.append(self.can_find_museum_item(donation))
|
||||
for artifact in all_museum_artifacts:
|
||||
rules.append(self.can_find_museum_item(artifact))
|
||||
|
||||
return Count(number, rules)
|
||||
|
||||
def can_find_museum_minerals(self, number: int) -> StardewRule:
|
||||
rules = []
|
||||
for mineral in all_museum_minerals:
|
||||
rules.append(self.can_find_museum_item(mineral))
|
||||
|
||||
return Count(number, rules)
|
||||
|
||||
|
@ -1295,7 +1313,7 @@ class StardewLogic:
|
|||
return Count(number, rules)
|
||||
|
||||
def can_complete_museum(self) -> StardewRule:
|
||||
rules = [self.can_mine_perfectly()]
|
||||
rules = [self.can_reach_region(Region.museum), self.can_mine_perfectly()]
|
||||
|
||||
if self.options[options.Museumsanity] != options.Museumsanity.option_none:
|
||||
rules.append(self.received("Traveling Merchant Metal Detector", 4))
|
||||
|
|
|
@ -5,15 +5,14 @@ from BaseClasses import MultiWorld
|
|||
from worlds.generic import Rules as MultiWorldRules
|
||||
from . import options, locations
|
||||
from .bundles import Bundle
|
||||
from .data.crops_data import crops_by_name
|
||||
from .strings.entrance_names import dig_to_mines_floor, dig_to_skull_floor, Entrance, move_to_woods_depth, \
|
||||
DeepWoodsEntrance, AlecEntrance, MagicEntrance
|
||||
from .data.museum_data import all_museum_items, all_mineral_items, all_artifact_items, \
|
||||
from .data.museum_data import all_museum_items, all_museum_minerals, all_museum_artifacts, \
|
||||
dwarf_scrolls, skeleton_front, \
|
||||
skeleton_middle, skeleton_back, all_museum_items_by_name
|
||||
skeleton_middle, skeleton_back, all_museum_items_by_name, Artifact
|
||||
from .strings.region_names import Region
|
||||
from .mods.mod_data import ModNames
|
||||
from .mods.logic import magic, skills, deepwoods
|
||||
from .mods.logic import magic, deepwoods
|
||||
from .locations import LocationTags
|
||||
from .logic import StardewLogic, And, tool_upgrade_prices
|
||||
from .options import StardewOptions
|
||||
|
@ -23,7 +22,6 @@ from .strings.calendar_names import Weekday
|
|||
from .strings.craftable_names import Craftable
|
||||
from .strings.material_names import Material
|
||||
from .strings.metal_names import MetalBar
|
||||
from .strings.spells import MagicSpell
|
||||
from .strings.skill_names import ModSkill, Skill
|
||||
from .strings.tool_names import Tool, ToolMaterial
|
||||
from .strings.villager_names import NPC, ModNPC
|
||||
|
@ -432,7 +430,7 @@ def set_museum_individual_donations_rules(all_location_names, logic: StardewLogi
|
|||
if museum_location.name in all_location_names:
|
||||
donation_name = museum_location.name[len(museum_prefix):]
|
||||
required_detectors = counter * 5 // number_donations
|
||||
rule = logic.has(donation_name) & logic.received("Traveling Merchant Metal Detector", required_detectors)
|
||||
rule = logic.can_donate_museum_item(all_museum_items_by_name[donation_name]) & logic.received("Traveling Merchant Metal Detector", required_detectors)
|
||||
MultiWorldRules.set_rule(multi_world.get_location(museum_location.name, player),
|
||||
rule.simplify())
|
||||
counter += 1
|
||||
|
@ -447,31 +445,31 @@ def set_museum_milestone_rule(logic: StardewLogic, multi_world: MultiWorld, muse
|
|||
metal_detector = "Traveling Merchant Metal Detector"
|
||||
rule = None
|
||||
if milestone_name.endswith(donations_suffix):
|
||||
rule = get_museum_item_count_rule(logic, donations_suffix, milestone_name, all_museum_items)
|
||||
rule = get_museum_item_count_rule(logic, donations_suffix, milestone_name, all_museum_items, logic.can_donate_museum_items)
|
||||
elif milestone_name.endswith(minerals_suffix):
|
||||
rule = get_museum_item_count_rule(logic, minerals_suffix, milestone_name, all_mineral_items)
|
||||
rule = get_museum_item_count_rule(logic, minerals_suffix, milestone_name, all_museum_minerals, logic.can_donate_museum_minerals)
|
||||
elif milestone_name.endswith(artifacts_suffix):
|
||||
rule = get_museum_item_count_rule(logic, artifacts_suffix, milestone_name, all_artifact_items)
|
||||
rule = get_museum_item_count_rule(logic, artifacts_suffix, milestone_name, all_museum_artifacts, logic.can_donate_museum_artifacts)
|
||||
elif milestone_name == "Dwarf Scrolls":
|
||||
rule = logic.has([item.name for item in dwarf_scrolls]) & logic.received(metal_detector, 4)
|
||||
rule = And([logic.can_donate_museum_item(item) for item in dwarf_scrolls]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Skeleton Front":
|
||||
rule = logic.has([item.name for item in skeleton_front]) & logic.received(metal_detector, 4)
|
||||
rule = And([logic.can_donate_museum_item(item) for item in skeleton_front]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Skeleton Middle":
|
||||
rule = logic.has([item.name for item in skeleton_middle]) & logic.received(metal_detector, 4)
|
||||
rule = And([logic.can_donate_museum_item(item) for item in skeleton_middle]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Skeleton Back":
|
||||
rule = logic.has([item.name for item in skeleton_back]) & logic.received(metal_detector, 4)
|
||||
rule = And([logic.can_donate_museum_item(item) for item in skeleton_back]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Ancient Seed":
|
||||
rule = logic.has("Ancient Seed") & logic.received(metal_detector, 4)
|
||||
rule = logic.can_donate_museum_item(Artifact.ancient_seed) & logic.received(metal_detector, 4)
|
||||
if rule is None:
|
||||
return
|
||||
MultiWorldRules.set_rule(multi_world.get_location(museum_milestone.name, player), rule.simplify())
|
||||
|
||||
|
||||
def get_museum_item_count_rule(logic: StardewLogic, suffix, milestone_name, accepted_items):
|
||||
def get_museum_item_count_rule(logic: StardewLogic, suffix, milestone_name, accepted_items, donation_func):
|
||||
metal_detector = "Traveling Merchant Metal Detector"
|
||||
num = int(milestone_name[:milestone_name.index(suffix)])
|
||||
required_detectors = (num - 1) * 5 // len(accepted_items)
|
||||
rule = logic.has([item.name for item in accepted_items], num) & logic.received(metal_detector, required_detectors)
|
||||
rule = donation_func(num) & logic.received(metal_detector, required_detectors)
|
||||
return rule
|
||||
|
||||
|
||||
|
|
|
@ -2,10 +2,12 @@ from collections import Counter
|
|||
|
||||
from . import SVTestBase
|
||||
from .. import options
|
||||
from ..locations import locations_by_tag, LocationTags, location_table
|
||||
from ..strings.animal_names import Animal
|
||||
from ..strings.animal_product_names import AnimalProduct
|
||||
from ..strings.artisan_good_names import ArtisanGood
|
||||
from ..strings.crop_names import Vegetable
|
||||
from ..strings.entrance_names import Entrance
|
||||
from ..strings.food_names import Meal
|
||||
from ..strings.ingredient_names import Ingredient
|
||||
from ..strings.machine_names import Machine
|
||||
|
@ -369,3 +371,76 @@ class TestRecipeLogic(SVTestBase):
|
|||
# self.assertTrue(logic.has(Machine.cheese_press)(self.multiworld.state))
|
||||
# self.assertTrue(logic.has(ArtisanGood.cheese)(self.multiworld.state))
|
||||
# self.assertTrue(logic.has(Meal.pizza)(self.multiworld.state))
|
||||
|
||||
|
||||
class TestDonationLogicAll(SVTestBase):
|
||||
options = {
|
||||
options.Museumsanity.internal_name: options.Museumsanity.option_all
|
||||
}
|
||||
|
||||
def test_cannot_make_any_donation_without_museum_access(self):
|
||||
guild_item = "Adventurer's Guild"
|
||||
swap_museum_and_guild(self.multiworld, self.player)
|
||||
collect_all_except(self.multiworld, guild_item)
|
||||
|
||||
for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
|
||||
self.assertFalse(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item(guild_item), event=True)
|
||||
|
||||
for donation in locations_by_tag[LocationTags.MUSEUM_DONATIONS]:
|
||||
self.assertTrue(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
|
||||
|
||||
|
||||
class TestDonationLogicRandomized(SVTestBase):
|
||||
options = {
|
||||
options.Museumsanity.internal_name: options.Museumsanity.option_randomized
|
||||
}
|
||||
|
||||
def test_cannot_make_any_donation_without_museum_access(self):
|
||||
guild_item = "Adventurer's Guild"
|
||||
swap_museum_and_guild(self.multiworld, self.player)
|
||||
collect_all_except(self.multiworld, guild_item)
|
||||
donation_locations = [location for location in self.multiworld.get_locations() if not location.event and LocationTags.MUSEUM_DONATIONS in location_table[location.name].tags]
|
||||
|
||||
for donation in donation_locations:
|
||||
self.assertFalse(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item(guild_item), event=True)
|
||||
|
||||
for donation in donation_locations:
|
||||
self.assertTrue(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
|
||||
|
||||
|
||||
class TestDonationLogicMilestones(SVTestBase):
|
||||
options = {
|
||||
options.Museumsanity.internal_name: options.Museumsanity.option_milestones
|
||||
}
|
||||
|
||||
def test_cannot_make_any_donation_without_museum_access(self):
|
||||
guild_item = "Adventurer's Guild"
|
||||
swap_museum_and_guild(self.multiworld, self.player)
|
||||
collect_all_except(self.multiworld, guild_item)
|
||||
|
||||
for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
|
||||
self.assertFalse(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item(guild_item), event=True)
|
||||
|
||||
for donation in locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
|
||||
self.assertTrue(self.world.logic.can_reach_location(donation.name)(self.multiworld.state))
|
||||
|
||||
|
||||
def swap_museum_and_guild(multiworld, player):
|
||||
museum_region = multiworld.get_region(Region.museum, player)
|
||||
guild_region = multiworld.get_region(Region.adventurer_guild, player)
|
||||
museum_entrance = multiworld.get_entrance(Entrance.town_to_museum, player)
|
||||
guild_entrance = multiworld.get_entrance(Entrance.mountain_to_adventurer_guild, player)
|
||||
museum_entrance.connect(guild_region)
|
||||
guild_entrance.connect(museum_region)
|
||||
|
||||
|
||||
def collect_all_except(multiworld, item_to_not_collect: str):
|
||||
for item in multiworld.get_items():
|
||||
if item.name != item_to_not_collect:
|
||||
multiworld.state.collect(item)
|
||||
|
|
Loading…
Reference in New Issue