Stardew valley: Add Marlon bedroom entrance rule (#3735)

* - Created a test for the "Mapping Cave Systems" book

* - Added missing rule to marlon's bedroom

* - Can kill any monster, not just green slime

* - Added a compound source structure, but I ended up deciding to not use it here. Still keeping it as it will probably be useful eventually

* - Use the compound source of the monster compoundium (ironic, I know)

* - Add required elevators

---------

Co-authored-by: NewSoupVi <57900059+NewSoupVi@users.noreply.github.com>
This commit is contained in:
agilbert1412 2024-09-18 01:03:33 +03:00 committed by GitHub
parent 4e60f3cc54
commit a7c96436d9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 50 additions and 7 deletions

View File

@ -1,6 +1,6 @@
from ..game_content import ContentPack from ..game_content import ContentPack
from ...data import villagers_data, fish_data from ...data import villagers_data, fish_data
from ...data.game_item import GenericSource, ItemTag, Tag, CustomRuleSource from ...data.game_item import GenericSource, ItemTag, Tag, CustomRuleSource, CompoundSource
from ...data.harvest import ForagingSource, SeasonalForagingSource, ArtifactSpotSource from ...data.harvest import ForagingSource, SeasonalForagingSource, ArtifactSpotSource
from ...data.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement from ...data.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement
from ...data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource from ...data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource
@ -229,8 +229,10 @@ pelican_town = ContentPack(
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),), ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
Book.mapping_cave_systems: ( Book.mapping_cave_systems: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER), Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
GenericSource(regions=(Region.adventurer_guild_bedroom,)), CompoundSource(sources=(
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),), GenericSource(regions=(Region.adventurer_guild_bedroom,)),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),
))),
Book.monster_compendium: ( Book.monster_compendium: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER), Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
CustomRuleSource(create_rule=lambda logic: logic.monster.can_kill_many(Generic.any)), CustomRuleSource(create_rule=lambda logic: logic.monster.can_kill_many(Generic.any)),

View File

@ -59,6 +59,11 @@ class CustomRuleSource(ItemSource):
create_rule: Callable[[Any], StardewRule] create_rule: Callable[[Any], StardewRule]
@dataclass(frozen=True, **kw_only)
class CompoundSource(ItemSource):
sources: Tuple[ItemSource, ...] = ()
class Tag(ItemSource): class Tag(ItemSource):
"""Not a real source, just a way to add tags to an item. Will be removed from the item sources during unpacking.""" """Not a real source, just a way to add tags to an item. Will be removed from the item sources during unpacking."""
tag: Tuple[ItemTag, ...] tag: Tuple[ItemTag, ...]

View File

@ -12,7 +12,7 @@ from .region_logic import RegionLogicMixin
from .requirement_logic import RequirementLogicMixin from .requirement_logic import RequirementLogicMixin
from .tool_logic import ToolLogicMixin from .tool_logic import ToolLogicMixin
from ..data.artisan import MachineSource from ..data.artisan import MachineSource
from ..data.game_item import GenericSource, ItemSource, GameItem, CustomRuleSource from ..data.game_item import GenericSource, ItemSource, GameItem, CustomRuleSource, CompoundSource
from ..data.harvest import ForagingSource, FruitBatsSource, MushroomCaveSource, SeasonalForagingSource, \ from ..data.harvest import ForagingSource, FruitBatsSource, MushroomCaveSource, SeasonalForagingSource, \
HarvestCropSource, HarvestFruitTreeSource, ArtifactSpotSource HarvestCropSource, HarvestFruitTreeSource, ArtifactSpotSource
from ..data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource from ..data.shop import ShopSource, MysteryBoxSource, ArtifactTroveSource, PrizeMachineSource, FishingTreasureChestSource
@ -25,7 +25,7 @@ class SourceLogicMixin(BaseLogicMixin):
class SourceLogic(BaseLogic[Union[SourceLogicMixin, HasLogicMixin, ReceivedLogicMixin, HarvestingLogicMixin, MoneyLogicMixin, RegionLogicMixin, class SourceLogic(BaseLogic[Union[SourceLogicMixin, HasLogicMixin, ReceivedLogicMixin, HarvestingLogicMixin, MoneyLogicMixin, RegionLogicMixin,
ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]): ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
def has_access_to_item(self, item: GameItem): def has_access_to_item(self, item: GameItem):
rules = [] rules = []
@ -40,6 +40,10 @@ ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
return self.logic.or_(*(self.logic.source.has_access_to(source) & self.logic.requirement.meet_all_requirements(source.other_requirements) return self.logic.or_(*(self.logic.source.has_access_to(source) & self.logic.requirement.meet_all_requirements(source.other_requirements)
for source in sources)) for source in sources))
def has_access_to_all(self, sources: Iterable[ItemSource]):
return self.logic.and_(*(self.logic.source.has_access_to(source) & self.logic.requirement.meet_all_requirements(source.other_requirements)
for source in sources))
@functools.singledispatchmethod @functools.singledispatchmethod
def has_access_to(self, source: Any): def has_access_to(self, source: Any):
raise ValueError(f"Sources of type{type(source)} have no rule registered.") raise ValueError(f"Sources of type{type(source)} have no rule registered.")
@ -52,6 +56,10 @@ ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
def _(self, source: CustomRuleSource): def _(self, source: CustomRuleSource):
return source.create_rule(self.logic) return source.create_rule(self.logic)
@has_access_to.register
def _(self, source: CompoundSource):
return self.logic.source.has_access_to_all(source.sources)
@has_access_to.register @has_access_to.register
def _(self, source: ForagingSource): def _(self, source: ForagingSource):
return self.logic.harvesting.can_forage_from(source) return self.logic.harvesting.can_forage_from(source)

View File

@ -39,6 +39,7 @@ from .strings.crop_names import Fruit, Vegetable
from .strings.entrance_names import dig_to_mines_floor, dig_to_skull_floor, Entrance, move_to_woods_depth, DeepWoodsEntrance, AlecEntrance, \ from .strings.entrance_names import dig_to_mines_floor, dig_to_skull_floor, Entrance, move_to_woods_depth, DeepWoodsEntrance, AlecEntrance, \
SVEEntrance, LaceyEntrance, BoardingHouseEntrance, LogicEntrance SVEEntrance, LaceyEntrance, BoardingHouseEntrance, LogicEntrance
from .strings.forageable_names import Forageable from .strings.forageable_names import Forageable
from .strings.generic_names import Generic
from .strings.geode_names import Geode from .strings.geode_names import Geode
from .strings.material_names import Material from .strings.material_names import Material
from .strings.metal_names import MetalBar, Mineral from .strings.metal_names import MetalBar, Mineral
@ -263,6 +264,7 @@ def set_entrance_rules(logic: StardewLogic, multiworld, player, world_options: S
set_entrance_rule(multiworld, player, LogicEntrance.buy_experience_books, logic.time.has_lived_months(2)) set_entrance_rule(multiworld, player, LogicEntrance.buy_experience_books, logic.time.has_lived_months(2))
set_entrance_rule(multiworld, player, LogicEntrance.buy_year1_books, logic.time.has_year_two) set_entrance_rule(multiworld, player, LogicEntrance.buy_year1_books, logic.time.has_year_two)
set_entrance_rule(multiworld, player, LogicEntrance.buy_year3_books, logic.time.has_year_three) set_entrance_rule(multiworld, player, LogicEntrance.buy_year3_books, logic.time.has_year_three)
set_entrance_rule(multiworld, player, Entrance.adventurer_guild_to_bedroom, logic.monster.can_kill_max(Generic.any))
def set_dangerous_mine_rules(logic, multiworld, player, world_options: StardewValleyOptions): def set_dangerous_mine_rules(logic, multiworld, player, world_options: StardewValleyOptions):

View File

@ -256,10 +256,10 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
return False return False
return super().run_default_tests return super().run_default_tests
def collect_lots_of_money(self): def collect_lots_of_money(self, percent: float = 0.25):
self.multiworld.state.collect(self.world.create_item("Shipping Bin"), prevent_sweep=False) self.multiworld.state.collect(self.world.create_item("Shipping Bin"), prevent_sweep=False)
real_total_prog_items = self.multiworld.worlds[self.player].total_progression_items real_total_prog_items = self.multiworld.worlds[self.player].total_progression_items
required_prog_items = int(round(real_total_prog_items * 0.25)) required_prog_items = int(round(real_total_prog_items * percent))
for i in range(required_prog_items): for i in range(required_prog_items):
self.multiworld.state.collect(self.world.create_item("Stardrop"), prevent_sweep=False) self.multiworld.state.collect(self.world.create_item("Stardrop"), prevent_sweep=False)
self.multiworld.worlds[self.player].total_progression_items = real_total_prog_items self.multiworld.worlds[self.player].total_progression_items = real_total_prog_items

View File

@ -0,0 +1,26 @@
from ... import options
from ...test import SVTestBase
class TestBooksLogic(SVTestBase):
options = {
options.Booksanity.internal_name: options.Booksanity.option_all,
}
def test_need_weapon_for_mapping_cave_systems(self):
self.collect_lots_of_money(0.5)
location = self.multiworld.get_location("Read Mapping Cave Systems", self.player)
self.assert_reach_location_false(location, self.multiworld.state)
self.collect("Progressive Mine Elevator")
self.collect("Progressive Mine Elevator")
self.collect("Progressive Mine Elevator")
self.collect("Progressive Mine Elevator")
self.assert_reach_location_false(location, self.multiworld.state)
self.collect("Progressive Weapon")
self.assert_reach_location_true(location, self.multiworld.state)