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:
parent
50d9ab041a
commit
ea03c90152
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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.")
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue