From 1656e6cfd84e6141f242273bdb5e17ddd800151b Mon Sep 17 00:00:00 2001
From: Alex Gilbert <alexgilbert@yahoo.com>
Date: Mon, 5 Aug 2024 16:40:01 -0400
Subject: [PATCH 1/6] - Created a test for the "Mapping Cave Systems" book

---
 worlds/stardew_valley/test/rules/TestBooks.py | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 worlds/stardew_valley/test/rules/TestBooks.py

diff --git a/worlds/stardew_valley/test/rules/TestBooks.py b/worlds/stardew_valley/test/rules/TestBooks.py
new file mode 100644
index 000000000000..41eb86b90f19
--- /dev/null
+++ b/worlds/stardew_valley/test/rules/TestBooks.py
@@ -0,0 +1,21 @@
+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_all_the_money()
+
+        location = self.multiworld.get_location("Read Mapping Cave Systems", self.player)
+
+        self.assert_reach_location_false(location, self.multiworld.state)
+
+        self.collect("Progressive Weapon")
+
+        self.assert_reach_location_true(location, self.multiworld.state)
+
+

From 25a6754eca638bcd86a1584a1995fcddd54a7132 Mon Sep 17 00:00:00 2001
From: Alex Gilbert <alexgilbert@yahoo.com>
Date: Mon, 5 Aug 2024 16:40:13 -0400
Subject: [PATCH 2/6] - Added missing rule to marlon's bedroom

---
 worlds/stardew_valley/rules.py | 1 +
 1 file changed, 1 insertion(+)

diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py
index 89b1cf87c3c1..dfe4bb3d1930 100644
--- a/worlds/stardew_valley/rules.py
+++ b/worlds/stardew_valley/rules.py
@@ -261,6 +261,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(Monster.green_slime))
 
 
 def set_dangerous_mine_rules(logic, multiworld, player, world_options: StardewValleyOptions):

From 6d9e86b2feede051895c3b678c94cd3f25f89e43 Mon Sep 17 00:00:00 2001
From: Alex Gilbert <alexgilbert@yahoo.com>
Date: Mon, 5 Aug 2024 16:41:56 -0400
Subject: [PATCH 3/6] - Can kill any monster, not just green slime

---
 worlds/stardew_valley/rules.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py
index dfe4bb3d1930..2394fc76df1e 100644
--- a/worlds/stardew_valley/rules.py
+++ b/worlds/stardew_valley/rules.py
@@ -39,6 +39,7 @@
 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
@@ -261,7 +262,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(Monster.green_slime))
+    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):

From c1bcf263de8fa0e98a7f4a4f04658497a2448f56 Mon Sep 17 00:00:00 2001
From: Alex Gilbert <alexgilbert@yahoo.com>
Date: Mon, 5 Aug 2024 16:44:30 -0400
Subject: [PATCH 4/6] - 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

---
 worlds/stardew_valley/data/game_item.py     |  5 +++++
 worlds/stardew_valley/logic/source_logic.py | 12 ++++++++++--
 2 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/worlds/stardew_valley/data/game_item.py b/worlds/stardew_valley/data/game_item.py
index 2107ca30d33a..6c8d30ed8e6f 100644
--- a/worlds/stardew_valley/data/game_item.py
+++ b/worlds/stardew_valley/data/game_item.py
@@ -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, ...]
diff --git a/worlds/stardew_valley/logic/source_logic.py b/worlds/stardew_valley/logic/source_logic.py
index 0e9b8e976f5b..9ef68a020eef 100644
--- a/worlds/stardew_valley/logic/source_logic.py
+++ b/worlds/stardew_valley/logic/source_logic.py
@@ -12,7 +12,7 @@
 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 @@ def __init__(self, *args, **kwargs):
 
 
 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 @@ def has_access_to_any(self, sources: Iterable[ItemSource]):
         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 @@ def _(self, source: GenericSource):
     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)

From 3ae94355f6a0c9ac9db60d0f680276425d2aa9b0 Mon Sep 17 00:00:00 2001
From: Alex Gilbert <alexgilbert@yahoo.com>
Date: Mon, 5 Aug 2024 17:06:39 -0400
Subject: [PATCH 5/6] - Use the compound source of the monster compoundium
 (ironic, I know)

---
 worlds/stardew_valley/content/vanilla/pelican_town.py | 8 +++++---
 worlds/stardew_valley/test/__init__.py                | 4 ++--
 worlds/stardew_valley/test/rules/TestBooks.py         | 2 +-
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/worlds/stardew_valley/content/vanilla/pelican_town.py b/worlds/stardew_valley/content/vanilla/pelican_town.py
index 220b46eae2a4..73cc8f119a3e 100644
--- a/worlds/stardew_valley/content/vanilla/pelican_town.py
+++ b/worlds/stardew_valley/content/vanilla/pelican_town.py
@@ -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 @@
             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)),
diff --git a/worlds/stardew_valley/test/__init__.py b/worlds/stardew_valley/test/__init__.py
index 4dee0ebf6d66..74ade9696f7d 100644
--- a/worlds/stardew_valley/test/__init__.py
+++ b/worlds/stardew_valley/test/__init__.py
@@ -256,10 +256,10 @@ def run_default_tests(self) -> bool:
             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
diff --git a/worlds/stardew_valley/test/rules/TestBooks.py b/worlds/stardew_valley/test/rules/TestBooks.py
index 41eb86b90f19..c7872daec6c5 100644
--- a/worlds/stardew_valley/test/rules/TestBooks.py
+++ b/worlds/stardew_valley/test/rules/TestBooks.py
@@ -8,7 +8,7 @@ class TestBooksLogic(SVTestBase):
     }
 
     def test_need_weapon_for_mapping_cave_systems(self):
-        self.collect_all_the_money()
+        self.collect_lots_of_money(0.5)
 
         location = self.multiworld.get_location("Read Mapping Cave Systems", self.player)
 

From abad4ba0304084774d20b71c122bfdfed5ad956f Mon Sep 17 00:00:00 2001
From: Alex Gilbert <alexgilbert@yahoo.com>
Date: Mon, 5 Aug 2024 17:26:27 -0400
Subject: [PATCH 6/6] - Add required elevators

---
 worlds/stardew_valley/test/rules/TestBooks.py | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/worlds/stardew_valley/test/rules/TestBooks.py b/worlds/stardew_valley/test/rules/TestBooks.py
index c7872daec6c5..6605e7e645e3 100644
--- a/worlds/stardew_valley/test/rules/TestBooks.py
+++ b/worlds/stardew_valley/test/rules/TestBooks.py
@@ -14,8 +14,13 @@ def test_need_weapon_for_mapping_cave_systems(self):
 
         self.assert_reach_location_false(location, self.multiworld.state)
 
-        self.collect("Progressive Weapon")
+        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)