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 ..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)),
|
||||||
|
|
|
@ -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, ...]
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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