Stardew valley: Fix Aurora Vineyard Tablet logic (#4512)

* - Add requirement on Aurora Vineyard tablet to start the quest

* - Add rule for using the aurora vineyard staircase

* - Added a test for the tablet

* - Add a few missing items to the test

* - Introduce a new item to split the quest from the door and avoir ER issues

* - Optimize imports

* - Forgot to generate the item

* fix Aurora mess

# Conflicts:
#	worlds/stardew_valley/rules.py
#	worlds/stardew_valley/test/mods/TestMods.py

* fix a couple errors in the cherry picked commit, added a method to improve readability and reduce chance of human error on story quest conditions

* - remove blank line

* - Code review comments

* - fixed weird assert name

* - fixed accidentally surviving line

* - Fixed imports

---------

Co-authored-by: Jouramie <16137441+Jouramie@users.noreply.github.com>
This commit is contained in:
agilbert1412
2025-03-10 18:39:35 +03:00
committed by GitHub
parent be550ff6fb
commit d83294efa7
15 changed files with 120 additions and 52 deletions

View File

@@ -928,6 +928,7 @@ id,name,classification,groups,mod_name
10518,Aurora Vineyard Tablet,progression,,Stardew Valley Expanded 10518,Aurora Vineyard Tablet,progression,,Stardew Valley Expanded
10519,Scarlett's Job Offer,progression,,Stardew Valley Expanded 10519,Scarlett's Job Offer,progression,,Stardew Valley Expanded
10520,Morgan's Schooling,progression,,Stardew Valley Expanded 10520,Morgan's Schooling,progression,,Stardew Valley Expanded
10521,Aurora Vineyard Reclamation,progression,,Stardew Valley Expanded
10601,Magic Elixir Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Magic 10601,Magic Elixir Recipe,progression,"CHEFSANITY,CHEFSANITY_PURCHASE",Magic
10602,Travel Core Recipe,progression,CRAFTSANITY,Magic 10602,Travel Core Recipe,progression,CRAFTSANITY,Magic
10603,Haste Elixir Recipe,progression,CRAFTSANITY,Stardew Valley Expanded 10603,Haste Elixir Recipe,progression,CRAFTSANITY,Stardew Valley Expanded
1 id name classification groups mod_name
928 10518 Aurora Vineyard Tablet progression Stardew Valley Expanded
929 10519 Scarlett's Job Offer progression Stardew Valley Expanded
930 10520 Morgan's Schooling progression Stardew Valley Expanded
931 10521 Aurora Vineyard Reclamation progression Stardew Valley Expanded
932 10601 Magic Elixir Recipe progression CHEFSANITY,CHEFSANITY_PURCHASE Magic
933 10602 Travel Core Recipe progression CRAFTSANITY Magic
934 10603 Haste Elixir Recipe progression CRAFTSANITY Stardew Valley Expanded

View File

@@ -41,7 +41,7 @@ def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions,
if fishing is not None and content.features.skill_progression.is_progressive: if fishing is not None and content.features.skill_progression.is_progressive:
early_forced.append(fishing.level_name) early_forced.append(fishing.level_name)
if options.quest_locations >= 0: if options.quest_locations.has_story_quests():
early_candidates.append(Wallet.magnifying_glass) early_candidates.append(Wallet.magnifying_glass)
if options.special_order_locations & stardew_options.SpecialOrderLocations.option_board: if options.special_order_locations & stardew_options.SpecialOrderLocations.option_board:

View File

@@ -264,7 +264,7 @@ def create_unique_items(item_factory: StardewItemFactory, options: StardewValley
def create_raccoons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): def create_raccoons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
number_progressive_raccoons = 9 number_progressive_raccoons = 9
if options.quest_locations < 0: if options.quest_locations.has_no_story_quests():
number_progressive_raccoons = number_progressive_raccoons - 1 number_progressive_raccoons = number_progressive_raccoons - 1
items.extend(item_factory(item) for item in [CommunityUpgrade.raccoon] * number_progressive_raccoons) items.extend(item_factory(item) for item in [CommunityUpgrade.raccoon] * number_progressive_raccoons)
@@ -387,7 +387,7 @@ def create_quest_rewards(item_factory: StardewItemFactory, options: StardewValle
def create_special_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]): def create_special_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
if options.quest_locations < 0: if options.quest_locations.has_no_story_quests():
return return
# items.append(item_factory("Adventurer's Guild")) # Now unlocked always! # items.append(item_factory("Adventurer's Guild")) # Now unlocked always!
items.append(item_factory(Wallet.club_card)) items.append(item_factory(Wallet.club_card))
@@ -698,7 +698,7 @@ def create_quest_rewards_sve(item_factory: StardewItemFactory, options: StardewV
if not exclude_ginger_island: if not exclude_ginger_island:
items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items_ginger_island]) items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items_ginger_island])
if options.quest_locations < 0: if options.quest_locations.has_no_story_quests():
return return
items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items]) items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items])

View File

@@ -191,7 +191,7 @@ def extend_cropsanity_locations(randomized_locations: List[LocationData], conten
def extend_quests_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent): def extend_quests_locations(randomized_locations: List[LocationData], options: StardewValleyOptions, content: StardewContent):
if options.quest_locations < 0: if options.quest_locations.has_no_story_quests():
return return
story_quest_locations = locations_by_tag[LocationTags.STORY_QUEST] story_quest_locations = locations_by_tag[LocationTags.STORY_QUEST]
@@ -317,7 +317,7 @@ def extend_mandatory_locations(randomized_locations: List[LocationData], options
def extend_situational_quest_locations(randomized_locations: List[LocationData], options: StardewValleyOptions): def extend_situational_quest_locations(randomized_locations: List[LocationData], options: StardewValleyOptions):
if options.quest_locations < 0: if options.quest_locations.has_no_story_quests():
return return
if ModNames.distant_lands in options.mods: if ModNames.distant_lands in options.mods:
if ModNames.alecto in options.mods: if ModNames.alecto in options.mods:

View File

@@ -76,7 +76,7 @@ SkillLogicMixin, QuestLogicMixin]]):
self.logic.region.can_reach_location("Complete Boiler Room")) self.logic.region.can_reach_location("Complete Boiler Room"))
def can_access_raccoon_bundles(self) -> StardewRule: def can_access_raccoon_bundles(self) -> StardewRule:
if self.options.quest_locations < 0: if self.options.quest_locations.has_no_story_quests():
return self.logic.received(CommunityUpgrade.raccoon, 1) & self.logic.quest.can_complete_quest(Quest.giant_stump) return self.logic.received(CommunityUpgrade.raccoon, 1) & self.logic.quest.can_complete_quest(Quest.giant_stump)
# 1 - Break the tree # 1 - Break the tree

View File

@@ -48,7 +48,7 @@ SkillLogicMixin, SpecialOrderLogicMixin, CraftingLogicMixin, QuestLogicMixin]]):
else: else:
return self.logic.crafting.received_recipe(recipe.item) return self.logic.crafting.received_recipe(recipe.item)
if isinstance(recipe.source, QuestSource): if isinstance(recipe.source, QuestSource):
if self.options.quest_locations < 0: if self.options.quest_locations.has_no_story_quests():
return self.logic.crafting.can_learn_recipe(recipe) return self.logic.crafting.can_learn_recipe(recipe)
else: else:
return self.logic.crafting.received_recipe(recipe.item) return self.logic.crafting.received_recipe(recipe.item)

View File

@@ -118,25 +118,24 @@ class QuestLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, MoneyLogicMi
return Has(quest, self.registry.quest_rules, "quest") return Has(quest, self.registry.quest_rules, "quest")
def has_club_card(self) -> StardewRule: def has_club_card(self) -> StardewRule:
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.quest.can_complete_quest(Quest.the_mysterious_qi) return self.logic.received(Wallet.club_card)
return self.logic.received(Wallet.club_card) return self.logic.quest.can_complete_quest(Quest.the_mysterious_qi)
def has_magnifying_glass(self) -> StardewRule: def has_magnifying_glass(self) -> StardewRule:
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.quest.can_complete_quest(Quest.a_winter_mystery) return self.logic.received(Wallet.magnifying_glass)
return self.logic.received(Wallet.magnifying_glass) return self.logic.quest.can_complete_quest(Quest.a_winter_mystery)
def has_dark_talisman(self) -> StardewRule: def has_dark_talisman(self) -> StardewRule:
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.quest.can_complete_quest(Quest.dark_talisman) return self.logic.received(Wallet.dark_talisman)
return self.logic.received(Wallet.dark_talisman) return self.logic.quest.can_complete_quest(Quest.dark_talisman)
def has_raccoon_shop(self) -> StardewRule: def has_raccoon_shop(self) -> StardewRule:
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.received(CommunityUpgrade.raccoon, 2) & self.logic.quest.can_complete_quest(Quest.giant_stump) # 1 - Break the tree
# 2 - Build the house, which summons the bundle racoon. This one is done manually if quests are turned off
# 1 - Break the tree # 3 - Raccoon's wife opens the shop
# 2 - Build the house, which summons the bundle racoon. This one is done manually if quests are turned off return self.logic.received(CommunityUpgrade.raccoon, 3)
# 3 - Raccoon's wife opens the shop return self.logic.received(CommunityUpgrade.raccoon, 2) & self.logic.quest.can_complete_quest(Quest.giant_stump)
return self.logic.received(CommunityUpgrade.raccoon, 3)

View File

@@ -1,4 +1,5 @@
import math import math
import typing
from typing import Union from typing import Union
from Utils import cache_self1 from Utils import cache_self1
@@ -14,13 +15,18 @@ from ..content.feature import friendsanity
from ..data.villagers_data import Villager from ..data.villagers_data import Villager
from ..stardew_rule import StardewRule, True_, false_, true_ from ..stardew_rule import StardewRule, True_, false_, true_
from ..strings.ap_names.mods.mod_items import SVEQuestItem from ..strings.ap_names.mods.mod_items import SVEQuestItem
from ..strings.crop_names import Fruit
from ..strings.generic_names import Generic from ..strings.generic_names import Generic
from ..strings.gift_names import Gift from ..strings.gift_names import Gift
from ..strings.quest_names import ModQuest
from ..strings.region_names import Region from ..strings.region_names import Region
from ..strings.season_names import Season from ..strings.season_names import Season
from ..strings.villager_names import NPC, ModNPC from ..strings.villager_names import NPC, ModNPC
if typing.TYPE_CHECKING:
from ..mods.logic.mod_logic import ModLogicMixin
else:
ModLogicMixin = object
possible_kids = ("Cute Baby", "Ugly Baby") possible_kids = ("Cute Baby", "Ugly Baby")
@@ -38,7 +44,7 @@ class RelationshipLogicMixin(BaseLogicMixin):
class RelationshipLogic(BaseLogic[Union[RelationshipLogicMixin, BuildingLogicMixin, SeasonLogicMixin, TimeLogicMixin, GiftLogicMixin, RegionLogicMixin, class RelationshipLogic(BaseLogic[Union[RelationshipLogicMixin, BuildingLogicMixin, SeasonLogicMixin, TimeLogicMixin, GiftLogicMixin, RegionLogicMixin,
ReceivedLogicMixin, HasLogicMixin]]): ReceivedLogicMixin, HasLogicMixin, ModLogicMixin]]):
def can_date(self, npc: str) -> StardewRule: def can_date(self, npc: str) -> StardewRule:
return self.logic.relationship.has_hearts(npc, 8) & self.logic.has(Gift.bouquet) return self.logic.relationship.has_hearts(npc, 8) & self.logic.has(Gift.bouquet)
@@ -141,7 +147,7 @@ ReceivedLogicMixin, HasLogicMixin]]):
rules.append(self.logic.region.can_reach(Region.volcano_floor_10)) rules.append(self.logic.region.can_reach(Region.volcano_floor_10))
elif npc == ModNPC.apples: elif npc == ModNPC.apples:
rules.append(self.logic.has(Fruit.starfruit)) rules.append(self.logic.mod.quest.has_completed_aurora_vineyard_bundle())
elif npc == ModNPC.scarlett: elif npc == ModNPC.scarlett:
scarlett_job = self.logic.received(SVEQuestItem.scarlett_job_offer) scarlett_job = self.logic.received(SVEQuestItem.scarlett_job_offer)

View File

@@ -12,6 +12,7 @@ from ...logic.season_logic import SeasonLogicMixin
from ...logic.time_logic import TimeLogicMixin from ...logic.time_logic import TimeLogicMixin
from ...stardew_rule import StardewRule from ...stardew_rule import StardewRule
from ...strings.animal_product_names import AnimalProduct from ...strings.animal_product_names import AnimalProduct
from ...strings.ap_names.mods.mod_items import SVEQuestItem
from ...strings.artisan_good_names import ArtisanGood from ...strings.artisan_good_names import ArtisanGood
from ...strings.crop_names import Fruit, SVEFruit, SVEVegetable, Vegetable from ...strings.crop_names import Fruit, SVEFruit, SVEVegetable, Vegetable
from ...strings.fertilizer_names import Fertilizer from ...strings.fertilizer_names import Fertilizer
@@ -83,7 +84,8 @@ TimeLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MonsterLogicMixin]]):
self.logic.region.can_reach(SVERegion.grandpas_shed), self.logic.region.can_reach(SVERegion.grandpas_shed),
ModQuest.MarlonsBoat: self.logic.has_all(*(Loot.void_essence, Loot.solar_essence, Loot.slime, Loot.bat_wing, Loot.bug_meat)) & ModQuest.MarlonsBoat: self.logic.has_all(*(Loot.void_essence, Loot.solar_essence, Loot.slime, Loot.bat_wing, Loot.bug_meat)) &
self.logic.relationship.can_meet(ModNPC.lance) & self.logic.region.can_reach(SVERegion.guild_summit), self.logic.relationship.can_meet(ModNPC.lance) & self.logic.region.can_reach(SVERegion.guild_summit),
ModQuest.AuroraVineyard: self.logic.has(Fruit.starfruit) & self.logic.region.can_reach(SVERegion.aurora_vineyard), ModQuest.AuroraVineyard: self.logic.region.can_reach(SVERegion.aurora_vineyard) & self.logic.received(SVEQuestItem.aurora_vineyard_tablet) &
self.logic.has(Fruit.starfruit) & self.logic.region.can_reach(Region.forest),
ModQuest.MonsterCrops: self.logic.has_all(*(SVEVegetable.monster_mushroom, SVEFruit.slime_berry, SVEFruit.monster_fruit, SVEVegetable.void_root)), ModQuest.MonsterCrops: self.logic.has_all(*(SVEVegetable.monster_mushroom, SVEFruit.slime_berry, SVEFruit.monster_fruit, SVEVegetable.void_root)),
ModQuest.VoidSoul: self.logic.has(ModLoot.void_soul) & self.logic.region.can_reach(Region.farm) & ModQuest.VoidSoul: self.logic.has(ModLoot.void_soul) & self.logic.region.can_reach(Region.farm) &
self.logic.season.has_any_not_winter() & self.logic.region.can_reach(SVERegion.badlands_entrance) & self.logic.season.has_any_not_winter() & self.logic.region.can_reach(SVERegion.badlands_entrance) &
@@ -91,6 +93,12 @@ TimeLogicMixin, SeasonLogicMixin, RelationshipLogicMixin, MonsterLogicMixin]]):
self.logic.monster.can_kill_any((Monster.shadow_brute, Monster.shadow_shaman, Monster.shadow_sniper)), self.logic.monster.can_kill_any((Monster.shadow_brute, Monster.shadow_shaman, Monster.shadow_sniper)),
} }
def has_completed_aurora_vineyard_bundle(self):
if self.options.quest_locations.has_story_quests():
return self.logic.received(SVEQuestItem.aurora_vineyard_reclamation)
return self.logic.quest.can_complete_quest(ModQuest.AuroraVineyard)
def _get_distant_lands_quest_rules(self): def _get_distant_lands_quest_rules(self):
if ModNames.distant_lands not in self.options.mods: if ModNames.distant_lands not in self.options.mods:
return {} return {}

View File

@@ -41,24 +41,24 @@ class SVELogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, QuestLogicMixi
return self.logic.or_(*(self.logic.received(rune) for rune in rune_list)) return self.logic.or_(*(self.logic.received(rune) for rune in rune_list))
def has_iridium_bomb(self): def has_iridium_bomb(self):
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.quest.can_complete_quest(ModQuest.RailroadBoulder) return self.logic.received(SVEQuestItem.iridium_bomb)
return self.logic.received(SVEQuestItem.iridium_bomb) return self.logic.quest.can_complete_quest(ModQuest.RailroadBoulder)
def has_marlon_boat(self): def has_marlon_boat(self):
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.quest.can_complete_quest(ModQuest.MarlonsBoat) return self.logic.received(SVEQuestItem.marlon_boat_paddle)
return self.logic.received(SVEQuestItem.marlon_boat_paddle) return self.logic.quest.can_complete_quest(ModQuest.MarlonsBoat)
def has_grandpa_shed_repaired(self): def has_grandpa_shed_repaired(self):
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.quest.can_complete_quest(ModQuest.GrandpasShed) return self.logic.received(SVEQuestItem.grandpa_shed)
return self.logic.received(SVEQuestItem.grandpa_shed) return self.logic.quest.can_complete_quest(ModQuest.GrandpasShed)
def has_bear_knowledge(self): def has_bear_knowledge(self):
if self.options.quest_locations < 0: if self.options.quest_locations.has_story_quests():
return self.logic.quest.can_complete_quest(Quest.strange_note) return self.logic.received(Wallet.bears_knowledge)
return self.logic.received(Wallet.bears_knowledge) return self.logic.quest.can_complete_quest(Quest.strange_note)
def can_buy_bear_recipe(self): def can_buy_bear_recipe(self):
access_rule = (self.logic.quest.can_complete_quest(Quest.strange_note) & self.logic.tool.has_tool(Tool.axe, ToolMaterial.basic) & access_rule = (self.logic.quest.can_complete_quest(Quest.strange_note) & self.logic.tool.has_tool(Tool.axe, ToolMaterial.basic) &

View File

@@ -384,6 +384,12 @@ class QuestLocations(NamedRange):
"maximum": 56, "maximum": 56,
} }
def has_story_quests(self) -> bool:
return self.value >= 0
def has_no_story_quests(self) -> bool:
return not self.has_story_quests()
class Fishsanity(Choice): class Fishsanity(Choice):
"""Locations for catching each fish the first time? """Locations for catching each fish the first time?

View File

@@ -149,7 +149,7 @@ def set_bundle_rules(bundle_rooms: List[BundleRoom], logic: StardewLogic, multiw
bundle_rules = logic.bundle.can_complete_bundle(bundle) bundle_rules = logic.bundle.can_complete_bundle(bundle)
if bundle_room.name == CCRoom.raccoon_requests: if bundle_room.name == CCRoom.raccoon_requests:
num = int(bundle.name[-1]) num = int(bundle.name[-1])
extra_raccoons = 1 if world_options.quest_locations >= 0 else 0 extra_raccoons = 1 if world_options.quest_locations.has_story_quests() else 0
extra_raccoons = extra_raccoons + num extra_raccoons = extra_raccoons + num
bundle_rules = logic.received(CommunityUpgrade.raccoon, extra_raccoons) & bundle_rules bundle_rules = logic.received(CommunityUpgrade.raccoon, extra_raccoons) & bundle_rules
if num > 1: if num > 1:
@@ -505,7 +505,7 @@ def set_cropsanity_rules(logic: StardewLogic, multiworld, player, world_content:
def set_story_quests_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions): def set_story_quests_rules(all_location_names: Set[str], logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
if world_options.quest_locations < 0: if world_options.quest_locations.has_no_story_quests():
return return
for quest in locations.locations_by_tag[LocationTags.STORY_QUEST]: for quest in locations.locations_by_tag[LocationTags.STORY_QUEST]:
if quest.name in all_location_names and (quest.mod_name is None or quest.mod_name in world_options.mods): if quest.name in all_location_names and (quest.mod_name is None or quest.mod_name in world_options.mods):
@@ -540,9 +540,9 @@ slay_monsters = "Slay Monsters"
def set_help_wanted_quests_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions): def set_help_wanted_quests_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions):
help_wanted_number = world_options.quest_locations.value if world_options.quest_locations.has_no_story_quests():
if help_wanted_number < 0:
return return
help_wanted_number = world_options.quest_locations.value
for i in range(0, help_wanted_number): for i in range(0, help_wanted_number):
set_number = i // 7 set_number = i // 7
month_rule = logic.time.has_lived_months(set_number) month_rule = logic.time.has_lived_months(set_number)
@@ -973,6 +973,7 @@ def set_sve_rules(logic: StardewLogic, multiworld: MultiWorld, player: int, worl
set_entrance_rule(multiworld, player, SVEEntrance.use_bear_shop, (logic.mod.sve.can_buy_bear_recipe())) set_entrance_rule(multiworld, player, SVEEntrance.use_bear_shop, (logic.mod.sve.can_buy_bear_recipe()))
set_entrance_rule(multiworld, player, SVEEntrance.railroad_to_grampleton_station, logic.received(SVEQuestItem.scarlett_job_offer)) set_entrance_rule(multiworld, player, SVEEntrance.railroad_to_grampleton_station, logic.received(SVEQuestItem.scarlett_job_offer))
set_entrance_rule(multiworld, player, SVEEntrance.museum_to_gunther_bedroom, logic.relationship.has_hearts(ModNPC.gunther, 2)) set_entrance_rule(multiworld, player, SVEEntrance.museum_to_gunther_bedroom, logic.relationship.has_hearts(ModNPC.gunther, 2))
set_entrance_rule(multiworld, player, SVEEntrance.to_aurora_basement, logic.mod.quest.has_completed_aurora_vineyard_bundle())
logic.mod.sve.initialize_rules() logic.mod.sve.initialize_rules()
for location in logic.registry.sve_location_rules: for location in logic.registry.sve_location_rules:
MultiWorldRules.set_rule(multiworld.get_location(location, player), MultiWorldRules.set_rule(multiworld.get_location(location, player),

View File

@@ -19,6 +19,12 @@ class SkillLevel:
class SVEQuestItem: class SVEQuestItem:
aurora_vineyard_tablet = "Aurora Vineyard Tablet" aurora_vineyard_tablet = "Aurora Vineyard Tablet"
"""Triggers the apparition of the bundle tablet in the Aurora Vineyard, so you can do the Aurora Vineyard quest.
This aim to break dependencies on completing the Community Center.
"""
aurora_vineyard_reclamation = "Aurora Vineyard Reclamation"
"""Triggers the unlock of the Aurora Vineyard basement, so you can meet Apples.
"""
iridium_bomb = "Iridium Bomb" iridium_bomb = "Iridium Bomb"
void_soul = "Void Spirit Peace Agreement" void_soul = "Void Spirit Peace Agreement"
kittyfish_spell = "Kittyfish Spell" kittyfish_spell = "Kittyfish Spell"
@@ -29,10 +35,10 @@ class SVEQuestItem:
fable_reef_portal = "Fable Reef Portal" fable_reef_portal = "Fable Reef Portal"
grandpa_shed = "Grandpa's Shed" grandpa_shed = "Grandpa's Shed"
sve_always_quest_items: List[str] = [kittyfish_spell, scarlett_job_offer, morgan_schooling] sve_always_quest_items: list[str] = [kittyfish_spell, scarlett_job_offer, morgan_schooling, aurora_vineyard_tablet, ]
sve_always_quest_items_ginger_island: List[str] = [fable_reef_portal] sve_always_quest_items_ginger_island: list[str] = [fable_reef_portal, ]
sve_quest_items: List[str] = [aurora_vineyard_tablet, iridium_bomb, void_soul, grandpa_shed] sve_quest_items: list[str] = [iridium_bomb, void_soul, grandpa_shed, aurora_vineyard_reclamation, ]
sve_quest_items_ginger_island: List[str] = [marlon_boat_paddle] sve_quest_items_ginger_island: list[str] = [marlon_boat_paddle, ]
class SVELocation: class SVELocation:
@@ -53,4 +59,3 @@ class SVERunes:
nexus_wizard = "Nexus: Wizard Runes" nexus_wizard = "Nexus: Wizard Runes"
nexus_items: List[str] = [nexus_farm, nexus_wizard, nexus_spring, nexus_aurora, nexus_guild, nexus_junimo, nexus_outpost] nexus_items: List[str] = [nexus_farm, nexus_wizard, nexus_spring, nexus_aurora, nexus_guild, nexus_junimo, nexus_outpost]

View File

@@ -1,10 +1,9 @@
import random import random
from BaseClasses import get_seed from BaseClasses import get_seed, ItemClassification
from .. import SVTestBase, SVTestCase, allsanity_mods_6_x_x, fill_dataclass_with_default from .. import SVTestBase, SVTestCase, allsanity_mods_6_x_x, fill_dataclass_with_default
from ..assertion import ModAssertMixin, WorldAssertMixin from ..assertion import ModAssertMixin, WorldAssertMixin
from ... import items, Group, ItemClassification, create_content from ... import options, items, Group, create_content
from ... import options
from ...mods.mod_data import ModNames from ...mods.mod_data import ModNames
from ...options import SkillProgression, Walnutsanity from ...options import SkillProgression, Walnutsanity
from ...options.options import all_mods from ...options.options import all_mods
@@ -188,3 +187,17 @@ class TestModEntranceRando(SVTestCase):
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()), self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
f"Connections are duplicated in randomization.") f"Connections are duplicated in randomization.")
class TestVanillaLogicAlternativeWhenQuestsAreNotRandomized(WorldAssertMixin, SVTestBase):
"""We often forget to add an alternative rule that works when quests are not randomized. When this happens, some
Location are not reachable because they depend on items that are only added to the pool when quests are randomized.
"""
options = allsanity_mods_6_x_x() | {
options.QuestLocations.internal_name: options.QuestLocations.special_range_names["none"],
options.Goal.internal_name: options.Goal.option_perfection,
}
def test_given_no_quest_all_mods_when_generate_then_can_reach_everything(self):
self.collect_everything()
self.assert_can_reach_everything(self.multiworld)

View File

@@ -0,0 +1,29 @@
from .. import SVTestBase
from ... import options
from ...mods.mod_data import ModNames
from ...strings.ap_names.mods.mod_items import SVEQuestItem
from ...strings.quest_names import ModQuest
from ...strings.region_names import SVERegion
class TestAuroraVineyard(SVTestBase):
options = {
options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
options.Mods.internal_name: frozenset({ModNames.sve})
}
def test_need_tablet_to_do_quest(self):
self.collect("Starfruit Seeds")
self.collect("Bus Repair")
self.collect("Shipping Bin")
self.collect("Summer")
location_name = ModQuest.AuroraVineyard
self.assert_cannot_reach_location(location_name, self.multiworld.state)
self.collect(SVEQuestItem.aurora_vineyard_tablet)
self.assert_can_reach_location(location_name, self.multiworld.state)
def test_need_reclamation_to_go_downstairs(self):
region_name = SVERegion.aurora_vineyard_basement
self.assert_cannot_reach_region(region_name, self.multiworld.state)
self.collect(SVEQuestItem.aurora_vineyard_reclamation, 1)
self.assert_can_reach_region(region_name, self.multiworld.state)