Stardew Valley: Fix a logic bug and a documentation typo (#1722)

Typo in the documentation
Logic error for help wanted quests
This commit is contained in:
agilbert1412 2023-04-16 05:22:33 -04:00 committed by GitHub
parent 50d9ab041a
commit ea03c90152
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 103 additions and 75 deletions

View File

@ -26,6 +26,16 @@ class BundleItem:
def as_quality(self, quality: int):
return BundleItem.item_bundle(self.item.name, self.item.item_id, self.amount, quality)
def as_gold_quality(self):
return self.as_quality(2)
def as_quality_crop(self):
amount = 5
difficult_crops = ["Sweet Gem Berry", "Ancient Fruit"]
if self.item.name in difficult_crops:
amount = 1
return self.as_gold_quality().as_amount(amount)
def __repr__(self):
return f"{self.amount} {quality_dict[self.quality]} {self.item.name}"
@ -281,7 +291,7 @@ summer_crops_items = [blueberry, corn, hops, hot_pepper, melon, poppy,
fall_crops_items = [corn, sunflower, wheat, amaranth, bok_choy, cranberries,
eggplant, fairy_rose, grape, pumpkin, yam, sweet_gem_berry]
all_crops_items = sorted({*spring_crop_items, *summer_crops_items, *fall_crops_items})
quality_crops_items = [item.as_quality(2).as_amount(5) for item in all_crops_items]
quality_crops_items = [item.as_quality_crop() for item in all_crops_items]
# TODO void_egg, dinosaur_egg, ostrich_egg, golden_egg
animal_product_items = [egg, large_egg, brown_egg, large_brown_egg, wool, milk, large_milk,
goat_milk, large_goat_milk, truffle, duck_feather, duck_egg, rabbit_foot]

View File

@ -4,7 +4,7 @@
- Stardew Valley on PC (Recommended: [Steam version](https://store.steampowered.com/app/413150/Stardew_Valley/))
- SMAPI ([Mod loader for Stardew Valley](https://smapi.io/))
- [StardewArchipelago Mod Release 2.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- [StardewArchipelago Mod Release 3.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- It is important to use a mod release of version 3.x.x to play seeds that have been generated here. Later releases can only be used with later releases of the world generator, that are not hosted on archipelago.gg yet.
## Optional Software

View File

@ -124,21 +124,22 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: StardewOption
# Help Wanted Quests
desired_number_help_wanted: int = world_options[options.HelpWantedLocations] // 7
for i in range(1, desired_number_help_wanted + 1):
for i in range(0, desired_number_help_wanted):
prefix = "Help Wanted:"
delivery = "Item Delivery"
rule = logic.received("Month End", i - 1)
rule = logic.received("Month End", i)
fishing_rule = rule & logic.can_fish()
slay_rule = rule & logic.has_any_weapon()
for j in range(i, i + 4):
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} {delivery} {j}", player),
rule.simplify())
item_delivery_index = (i * 4) + 1
for j in range(item_delivery_index, item_delivery_index + 4):
location_name = f"{prefix} {delivery} {j}"
MultiWorldRules.set_rule(multi_world.get_location(location_name, player), rule.simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Gathering {i}", player),
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Gathering {i+1}", player),
rule.simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Fishing {i}", player),
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Fishing {i+1}", player),
fishing_rule.simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Slay Monsters {i}", player),
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Slay Monsters {i+1}", player),
slay_rule.simplify())
set_fishsanity_rules(all_location_names, logic, multi_world, player)

View File

@ -1,16 +1,30 @@
import unittest
from ..data.bundle_data import all_bundle_items
from ..data.bundle_data import all_bundle_items, quality_crops_items
class TestBundles(unittest.TestCase):
def test_all_bundle_items_have_3_parts(self):
for bundle_item in all_bundle_items:
name = bundle_item.item.name
assert len(name) > 0
id = bundle_item.item.item_id
assert (id > 0 or id == -1)
amount = bundle_item.amount
assert amount > 0
quality = bundle_item.quality
assert quality >= 0
with self.subTest(bundle_item.item.name):
self.assertGreater(len(bundle_item.item.name), 0)
id = bundle_item.item.item_id
self.assertGreaterEqual(id, -1)
self.assertNotEqual(id, 0)
self.assertGreater(bundle_item.amount, 0)
self.assertGreaterEqual(bundle_item.quality, 0)
def test_quality_crops_have_correct_amounts(self):
for bundle_item in quality_crops_items:
with self.subTest(bundle_item.item.name):
name = bundle_item.item.name
if name == "Sweet Gem Berry" or name == "Ancient Fruit":
self.assertEqual(bundle_item.amount, 1)
else:
self.assertEqual(bundle_item.amount, 5)
def test_quality_crops_have_correct_quality(self):
for bundle_item in quality_crops_items:
with self.subTest(bundle_item.item.name):
self.assertEqual(bundle_item.quality, 2)

View File

@ -9,12 +9,12 @@ class TestCsvIntegrity(unittest.TestCase):
items = load_item_csv()
for item in items:
assert item.code_without_offset is not None, \
"Some item do not have an id. Run the script `update_data.py` to generate them."
self.assertIsNotNone(item.code_without_offset, "Some item do not have an id."
" Run the script `update_data.py` to generate them.")
def test_locations_integrity(self):
locations = load_location_csv()
for location in locations:
assert location.code_without_offset is not None, \
"Some location do not have an id. Run the script `update_data.py` to generate them."
self.assertIsNotNone(location.code_without_offset, "Some location do not have an id."
" Run the script `update_data.py` to generate them.")

View File

@ -17,54 +17,57 @@ class TestBaseItemGeneration(SVTestBase):
if item.classification is classification}
for item in all_classified_items:
assert item in self.multiworld.itempool
self.assertIn(item, self.multiworld.itempool)
def test_creates_as_many_item_as_non_event_locations(self):
non_event_locations = [location for location in self.multiworld.get_locations(self.player) if
not location.event]
assert len(non_event_locations), len(self.multiworld.itempool)
self.assertEqual(len(non_event_locations), len(self.multiworld.itempool))
class TestGivenProgressiveBackpack(SVTestBase):
options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive}
def test_when_generate_world_then_two_progressive_backpack_are_added(self):
assert self.multiworld.itempool.count(self.world.create_item("Progressive Backpack")) == 2
self.assertEqual(self.multiworld.itempool.count(self.world.create_item("Progressive Backpack")), 2)
def test_when_generate_world_then_backpack_locations_are_added(self):
created_locations = {location.name for location in self.multiworld.get_locations(1)}
assert all(location.name in created_locations for location in locations.locations_by_tag[LocationTags.BACKPACK])
backpacks_exist = [location.name in created_locations
for location in locations.locations_by_tag[LocationTags.BACKPACK]]
all_exist = all(backpacks_exist)
self.assertTrue(all_exist)
class TestRemixedMineRewards(SVTestBase):
def test_when_generate_world_then_one_reward_is_added_per_chest(self):
# assert self.world.create_item("Rusty Sword") in self.multiworld.itempool
assert any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_10])
assert any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_20])
assert self.world.create_item("Slingshot") in self.multiworld.itempool
assert any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_50])
assert any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_60])
assert self.world.create_item("Master Slingshot") in self.multiworld.itempool
assert any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_80])
assert any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_90])
assert self.world.create_item("Stardrop") in self.multiworld.itempool
assert any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_110])
assert self.world.create_item("Skull Key") in self.multiworld.itempool
self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_10]))
self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_20]))
self.assertIn(self.world.create_item("Slingshot"), self.multiworld.itempool)
self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_50]))
self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_60]))
self.assertIn(self.world.create_item("Master Slingshot"), self.multiworld.itempool)
self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_80]))
self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_90]))
self.assertIn(self.world.create_item("Stardrop"), self.multiworld.itempool)
self.assertTrue(any(self.world.create_item(item) in self.multiworld.itempool
for item in items_by_group[Group.MINES_FLOOR_110]))
self.assertIn(self.world.create_item("Skull Key"), self.multiworld.itempool)
# This test as 1 over 90,000 changes to fail... Sorry in advance
# This test has a 1/90,000 chance to fail... Sorry in advance
def test_when_generate_world_then_rewards_are_not_all_vanilla(self):
assert not all(self.world.create_item(item) in self.multiworld.itempool
self.assertFalse(all(self.world.create_item(item) in self.multiworld.itempool
for item in
["Leather Boots", "Steel Smallsword", "Tundra Boots", "Crystal Dagger", "Firewalker Boots",
"Obsidian Edge", "Space Boots"])
"Obsidian Edge", "Space Boots"]))
class TestProgressiveElevator(SVTestBase):
@ -81,11 +84,11 @@ class TestProgressiveElevator(SVTestBase):
self.collect([self.get_item_by_name("Combat Level")] * 4)
self.collect(self.get_item_by_name("Adventurer's Guild"))
assert not self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state)
self.assertFalse(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
self.collect(self.get_item_by_name("Progressive Mine Elevator"))
assert self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state)
self.assertTrue(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
def test_given_access_to_floor_115_when_find_another_pickaxe_and_sword_then_has_access_to_floor_120(self):
self.collect([self.get_item_by_name("Progressive Pickaxe")] * 2)
@ -94,14 +97,14 @@ class TestProgressiveElevator(SVTestBase):
self.collect([self.get_item_by_name("Combat Level")] * 4)
self.collect(self.get_item_by_name("Adventurer's Guild"))
assert not self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state)
self.assertFalse(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
self.collect(self.get_item_by_name("Progressive Pickaxe"))
self.collect(self.multiworld.create_item("Steel Falchion", self.player))
self.collect(self.get_item_by_name("Combat Level"))
self.collect(self.get_item_by_name("Combat Level"))
assert self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state)
self.assertTrue(self.multiworld.get_region("The Mines - Floor 120", self.player).can_reach(self.multiworld.state))
class TestLocationGeneration(SVTestBase):
@ -109,7 +112,7 @@ class TestLocationGeneration(SVTestBase):
def test_all_location_created_are_in_location_table(self):
for location in self.multiworld.get_locations(self.player):
if not location.event:
assert location.name in location_table
self.assertIn(location.name, location_table)
class TestLocationAndItemCount(SVTestBase):
@ -130,7 +133,7 @@ class TestLocationAndItemCount(SVTestBase):
}
def test_minimal_location_maximal_items_still_valid(self):
assert len(self.multiworld.get_locations()) >= len(self.multiworld.get_items())
self.assertGreaterEqual(len(self.multiworld.get_locations()), len(self.multiworld.get_items()))
class TestFriendsanityNone(SVTestBase):
@ -140,11 +143,11 @@ class TestFriendsanityNone(SVTestBase):
def test_no_friendsanity_items(self):
for item in self.multiworld.get_items():
assert not item.name.endswith(": 1 <3")
self.assertFalse(item.name.endswith(": 1 <3"))
def test_no_friendsanity_locations(self):
for location in self.multiworld.get_locations():
assert not location.name.startswith("Friendsanity")
self.assertFalse(location.name.startswith("Friendsanity"))
class TestFriendsanityBachelors(SVTestBase):
@ -159,7 +162,7 @@ class TestFriendsanityBachelors(SVTestBase):
for item in self.multiworld.get_items():
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
assert villager_name in self.bachelors
self.assertIn(villager_name, self.bachelors)
def test_friendsanity_only_bachelor_locations(self):
prefix = "Friendsanity: "
@ -171,8 +174,8 @@ class TestFriendsanityBachelors(SVTestBase):
parts = name_trimmed.split(" ")
name = parts[0]
hearts = parts[1]
assert name in self.bachelors
assert int(hearts) <= 8
self.assertIn(name, self.bachelors)
self.assertLessEqual(int(hearts), 8)
class TestFriendsanityStartingNpcs(SVTestBase):
@ -186,7 +189,7 @@ class TestFriendsanityStartingNpcs(SVTestBase):
for item in self.multiworld.get_items():
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
assert villager_name not in self.excluded_npcs
self.assertNotIn(villager_name, self.excluded_npcs)
def test_friendsanity_only_starting_npcs_locations(self):
prefix = "Friendsanity: "
@ -198,14 +201,14 @@ class TestFriendsanityStartingNpcs(SVTestBase):
parts = name_trimmed.split(" ")
name = parts[0]
hearts = parts[1]
assert name not in self.excluded_npcs
assert name in all_villagers_by_name or name == "Pet"
self.assertNotIn(name, self.excluded_npcs)
self.assertTrue(name in all_villagers_by_name or name == "Pet")
if name == "Pet":
assert int(hearts) <= 5
self.assertLessEqual(int(hearts), 5)
elif all_villagers_by_name[name].bachelor:
assert int(hearts) <= 8
self.assertLessEqual(int(hearts), 8)
else:
assert int(hearts) <= 10
self.assertLessEqual(int(hearts), 10)
class TestFriendsanityAllNpcs(SVTestBase):
@ -218,7 +221,7 @@ class TestFriendsanityAllNpcs(SVTestBase):
for item in self.multiworld.get_items():
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
assert villager_name in all_villagers_by_name or villager_name == "Pet"
self.assertTrue(villager_name in all_villagers_by_name or villager_name == "Pet")
def test_friendsanity_all_locations(self):
prefix = "Friendsanity: "
@ -230,13 +233,13 @@ class TestFriendsanityAllNpcs(SVTestBase):
parts = name_trimmed.split(" ")
name = parts[0]
hearts = parts[1]
assert name in all_villagers_by_name or name == "Pet"
self.assertTrue(name in all_villagers_by_name or name == "Pet")
if name == "Pet":
assert int(hearts) <= 5
self.assertLessEqual(int(hearts), 5)
elif all_villagers_by_name[name].bachelor:
assert int(hearts) <= 8
self.assertLessEqual(int(hearts), 8)
else:
assert int(hearts) <= 10
self.assertLessEqual(int(hearts), 10)
class TestFriendsanityAllNpcsWithMarriage(SVTestBase):
@ -249,7 +252,7 @@ class TestFriendsanityAllNpcsWithMarriage(SVTestBase):
for item in self.multiworld.get_items():
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
assert villager_name in all_villagers_by_name or villager_name == "Pet"
self.assertTrue(villager_name in all_villagers_by_name or villager_name == "Pet")
def test_friendsanity_all_with_marriage_locations(self):
prefix = "Friendsanity: "
@ -261,10 +264,10 @@ class TestFriendsanityAllNpcsWithMarriage(SVTestBase):
parts = name_trimmed.split(" ")
name = parts[0]
hearts = parts[1]
assert name in all_villagers_by_name or name == "Pet"
self.assertTrue(name in all_villagers_by_name or name == "Pet")
if name == "Pet":
assert int(hearts) <= 5
self.assertLessEqual(int(hearts), 5)
elif all_villagers_by_name[name].bachelor:
assert int(hearts) <= 14
self.assertLessEqual(int(hearts), 14)
else:
assert int(hearts) <= 10
self.assertLessEqual(int(hearts), 10)