| 
									
										
										
											
												Stardew Valley 6.x.x: The Content Update (#3478)
Focus of the Update: Compatibility with Stardew Valley 1.6 Released on March 19th 2024
This includes randomization for pretty much all of the new content, including but not limited to
- Raccoon Bundles
- Booksanity
- Skill Masteries
- New Recipes, Craftables, Fish, Maps, Farm Type, Festivals and Quests
This also includes a significant reorganisation of the code into "Content Packs", to allow for easier modularity of various game mechanics between the settings and the supported mods. This improves maintainability quite a bit.
In addition to that, a few **very** requested new features have been introduced, although they weren't the focus of this update
- Walnutsanity
- Player Buffs
- More customizability in settings, such as shorter special orders, ER without farmhouse
- New Remixed Bundles
											
										 
											2024-07-07 16:04:25 +03:00
										 |  |  | import unittest | 
					
						
							|  |  |  | from collections import Counter | 
					
						
							|  |  |  | from typing import ClassVar, Set | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-13 03:58:03 -04:00
										 |  |  | from .bases import SVTestBase | 
					
						
							| 
									
										
										
											
												Stardew Valley 6.x.x: The Content Update (#3478)
Focus of the Update: Compatibility with Stardew Valley 1.6 Released on March 19th 2024
This includes randomization for pretty much all of the new content, including but not limited to
- Raccoon Bundles
- Booksanity
- Skill Masteries
- New Recipes, Craftables, Fish, Maps, Farm Type, Festivals and Quests
This also includes a significant reorganisation of the code into "Content Packs", to allow for easier modularity of various game mechanics between the settings and the supported mods. This improves maintainability quite a bit.
In addition to that, a few **very** requested new features have been introduced, although they weren't the focus of this update
- Walnutsanity
- Player Buffs
- More customizability in settings, such as shorter special orders, ER without farmhouse
- New Remixed Bundles
											
										 
											2024-07-07 16:04:25 +03:00
										 |  |  | from ..content.feature import friendsanity | 
					
						
							|  |  |  | from ..options import Friendsanity, FriendsanityHeartSize | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | all_vanilla_bachelor = { | 
					
						
							|  |  |  |     "Harvey", "Elliott", "Sam", "Alex", "Shane", "Sebastian", "Emily", "Haley", "Leah", "Abigail", "Penny", "Maru" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | all_vanilla_starting_npc = { | 
					
						
							|  |  |  |     "Alex", "Elliott", "Harvey", "Sam", "Sebastian", "Shane", "Abigail", "Emily", "Haley", "Leah", "Maru", "Penny", "Caroline", "Clint", "Demetrius", "Evelyn", | 
					
						
							|  |  |  |     "George", "Gus", "Jas", "Jodi", "Lewis", "Linus", "Marnie", "Pam", "Pierre", "Robin", "Vincent", "Willy", "Wizard", "Pet", | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | all_vanilla_npc = { | 
					
						
							|  |  |  |     "Alex", "Elliott", "Harvey", "Sam", "Sebastian", "Shane", "Abigail", "Emily", "Haley", "Leah", "Maru", "Penny", "Caroline", "Clint", "Demetrius", "Evelyn", | 
					
						
							|  |  |  |     "George", "Gus", "Jas", "Jodi", "Lewis", "Linus", "Marnie", "Pam", "Pierre", "Robin", "Vincent", "Willy", "Wizard", "Pet", "Sandy", "Dwarf", "Kent", "Leo", | 
					
						
							|  |  |  |     "Krobus" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SVFriendsanityTestBase(SVTestBase): | 
					
						
							|  |  |  |     expected_npcs: ClassVar[Set[str]] = set() | 
					
						
							|  |  |  |     expected_pet_heart_size: ClassVar[Set[str]] = set() | 
					
						
							|  |  |  |     expected_bachelor_heart_size: ClassVar[Set[str]] = set() | 
					
						
							|  |  |  |     expected_other_heart_size: ClassVar[Set[str]] = set() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @classmethod | 
					
						
							|  |  |  |     def setUpClass(cls) -> None: | 
					
						
							|  |  |  |         if cls is SVFriendsanityTestBase: | 
					
						
							|  |  |  |             raise unittest.SkipTest("Base tests disabled") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         super().setUpClass() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def test_friendsanity(self): | 
					
						
							|  |  |  |         with self.subTest("Items are valid"): | 
					
						
							|  |  |  |             self.check_all_items_match_expected_npcs() | 
					
						
							|  |  |  |         with self.subTest("Correct number of items"): | 
					
						
							|  |  |  |             self.check_correct_number_of_items() | 
					
						
							|  |  |  |         with self.subTest("Locations are valid"): | 
					
						
							|  |  |  |             self.check_all_locations_match_expected_npcs() | 
					
						
							|  |  |  |         with self.subTest("Locations heart size are valid"): | 
					
						
							|  |  |  |             self.check_all_locations_match_heart_size() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_all_items_match_expected_npcs(self): | 
					
						
							|  |  |  |         npc_names = { | 
					
						
							|  |  |  |             name | 
					
						
							|  |  |  |             for item in self.multiworld.itempool | 
					
						
							|  |  |  |             if (name := friendsanity.extract_npc_from_item_name(item.name)) is not None | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(npc_names, self.expected_npcs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_correct_number_of_items(self): | 
					
						
							|  |  |  |         item_by_npc = Counter() | 
					
						
							|  |  |  |         for item in self.multiworld.itempool: | 
					
						
							|  |  |  |             name = friendsanity.extract_npc_from_item_name(item.name) | 
					
						
							|  |  |  |             if name is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             item_by_npc[name] += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for name, count in item_by_npc.items(): | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if name == "Pet": | 
					
						
							|  |  |  |                 self.assertEqual(count, len(self.expected_pet_heart_size)) | 
					
						
							|  |  |  |             elif self.world.content.villagers[name].bachelor: | 
					
						
							|  |  |  |                 self.assertEqual(count, len(self.expected_bachelor_heart_size)) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.assertEqual(count, len(self.expected_other_heart_size)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_all_locations_match_expected_npcs(self): | 
					
						
							|  |  |  |         npc_names = { | 
					
						
							|  |  |  |             name_and_heart[0] | 
					
						
							|  |  |  |             for location_name in self.get_real_location_names() | 
					
						
							|  |  |  |             if (name_and_heart := friendsanity.extract_npc_from_location_name(location_name))[0] is not None | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.assertEqual(npc_names, self.expected_npcs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_all_locations_match_heart_size(self): | 
					
						
							|  |  |  |         for location_name in self.get_real_location_names(): | 
					
						
							|  |  |  |             name, heart_size = friendsanity.extract_npc_from_location_name(location_name) | 
					
						
							|  |  |  |             if name is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if name == "Pet": | 
					
						
							|  |  |  |                 self.assertIn(heart_size, self.expected_pet_heart_size) | 
					
						
							|  |  |  |             elif self.world.content.villagers[name].bachelor: | 
					
						
							|  |  |  |                 self.assertIn(heart_size, self.expected_bachelor_heart_size) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 self.assertIn(heart_size, self.expected_other_heart_size) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestFriendsanityNone(SVFriendsanityTestBase): | 
					
						
							|  |  |  |     options = { | 
					
						
							|  |  |  |         Friendsanity: Friendsanity.option_none, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def run_default_tests(self) -> bool: | 
					
						
							|  |  |  |         # None is default | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestFriendsanityBachelors(SVFriendsanityTestBase): | 
					
						
							|  |  |  |     options = { | 
					
						
							|  |  |  |         Friendsanity: Friendsanity.option_bachelors, | 
					
						
							|  |  |  |         FriendsanityHeartSize: 1, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expected_npcs = all_vanilla_bachelor | 
					
						
							|  |  |  |     expected_bachelor_heart_size = {1, 2, 3, 4, 5, 6, 7, 8} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestFriendsanityStartingNpcs(SVFriendsanityTestBase): | 
					
						
							|  |  |  |     options = { | 
					
						
							|  |  |  |         Friendsanity: Friendsanity.option_starting_npcs, | 
					
						
							|  |  |  |         FriendsanityHeartSize: 1, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expected_npcs = all_vanilla_starting_npc | 
					
						
							|  |  |  |     expected_pet_heart_size = {1, 2, 3, 4, 5} | 
					
						
							|  |  |  |     expected_bachelor_heart_size = {1, 2, 3, 4, 5, 6, 7, 8} | 
					
						
							|  |  |  |     expected_other_heart_size = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestFriendsanityAllNpcs(SVFriendsanityTestBase): | 
					
						
							|  |  |  |     options = { | 
					
						
							|  |  |  |         Friendsanity: Friendsanity.option_all, | 
					
						
							|  |  |  |         FriendsanityHeartSize: 4, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expected_npcs = all_vanilla_npc | 
					
						
							|  |  |  |     expected_pet_heart_size = {4, 5} | 
					
						
							|  |  |  |     expected_bachelor_heart_size = {4, 8} | 
					
						
							|  |  |  |     expected_other_heart_size = {4, 8, 10} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestFriendsanityHeartSize3(SVFriendsanityTestBase): | 
					
						
							|  |  |  |     options = { | 
					
						
							|  |  |  |         Friendsanity: Friendsanity.option_all_with_marriage, | 
					
						
							|  |  |  |         FriendsanityHeartSize: 3, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expected_npcs = all_vanilla_npc | 
					
						
							|  |  |  |     expected_pet_heart_size = {3, 5} | 
					
						
							|  |  |  |     expected_bachelor_heart_size = {3, 6, 9, 12, 14} | 
					
						
							|  |  |  |     expected_other_heart_size = {3, 6, 9, 10} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TestFriendsanityHeartSize5(SVFriendsanityTestBase): | 
					
						
							|  |  |  |     options = { | 
					
						
							|  |  |  |         Friendsanity: Friendsanity.option_all_with_marriage, | 
					
						
							|  |  |  |         FriendsanityHeartSize: 5, | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     expected_npcs = all_vanilla_npc | 
					
						
							|  |  |  |     expected_pet_heart_size = {5} | 
					
						
							|  |  |  |     expected_bachelor_heart_size = {5, 10, 14} | 
					
						
							|  |  |  |     expected_other_heart_size = {5, 10} |