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:
parent
4e60f3cc54
commit
a7c96436d9
|
@ -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)),
|
||||
|
|
|
@ -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, ...]
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
Loading…
Reference in New Issue