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 ...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.requirement import ToolRequirement, BookRequirement, SkillRequirement, SeasonRequirement
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),),
Book.mapping_cave_systems: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
GenericSource(regions=(Region.adventurer_guild_bedroom,)),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),),
CompoundSource(sources=(
GenericSource(regions=(Region.adventurer_guild_bedroom,)),
ShopSource(money_price=20000, shop_region=LogicRegion.bookseller_3),
))),
Book.monster_compendium: (
Tag(ItemTag.BOOK, ItemTag.BOOK_POWER),
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]
@dataclass(frozen=True, **kw_only)
class CompoundSource(ItemSource):
sources: Tuple[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."""
tag: Tuple[ItemTag, ...]

View File

@ -12,7 +12,7 @@ from .region_logic import RegionLogicMixin
from .requirement_logic import RequirementLogicMixin
from .tool_logic import ToolLogicMixin
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, \
HarvestCropSource, HarvestFruitTreeSource, ArtifactSpotSource
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,
ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
def has_access_to_item(self, item: GameItem):
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)
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
def has_access_to(self, source: Any):
raise ValueError(f"Sources of type{type(source)} have no rule registered.")
@ -52,6 +56,10 @@ ArtisanLogicMixin, ToolLogicMixin, RequirementLogicMixin, GrindLogicMixin]]):
def _(self, source: CustomRuleSource):
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
def _(self, source: ForagingSource):
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, \
SVEEntrance, LaceyEntrance, BoardingHouseEntrance, LogicEntrance
from .strings.forageable_names import Forageable
from .strings.generic_names import Generic
from .strings.geode_names import Geode
from .strings.material_names import Material
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_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, Entrance.adventurer_guild_to_bedroom, logic.monster.can_kill_max(Generic.any))
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 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)
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):
self.multiworld.state.collect(self.world.create_item("Stardrop"), prevent_sweep=False)
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)