From aa149c50f0c27272ccb556e4cfcfc97adb7c6459 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:06:24 -0500 Subject: [PATCH 01/17] create tool progression feature and unwrap option --- worlds/stardew_valley/content/__init__.py | 33 ++++++++++- .../content/feature/__init__.py | 1 + .../content/feature/tool_progression.py | 58 +++++++++++++++++++ worlds/stardew_valley/content/game_content.py | 1 + 4 files changed, 92 insertions(+), 1 deletion(-) create mode 100644 worlds/stardew_valley/content/feature/tool_progression.py diff --git a/worlds/stardew_valley/content/__init__.py b/worlds/stardew_valley/content/__init__.py index 54b4d75d5e5c..c816bffed7da 100644 --- a/worlds/stardew_valley/content/__init__.py +++ b/worlds/stardew_valley/content/__init__.py @@ -1,5 +1,5 @@ from . import content_packs -from .feature import cropsanity, friendsanity, fishsanity, booksanity, skill_progression +from .feature import cropsanity, friendsanity, fishsanity, booksanity, skill_progression, tool_progression from .game_content import ContentPack, StardewContent, StardewFeatures from .unpacking import unpack_content from .. import options @@ -33,6 +33,7 @@ def choose_features(player_options: options.StardewValleyOptions) -> StardewFeat choose_fishsanity(player_options.fishsanity), choose_friendsanity(player_options.friendsanity, player_options.friendsanity_heart_size), choose_skill_progression(player_options.skill_progression), + choose_tool_progression(player_options.tool_progression), ) @@ -122,3 +123,33 @@ def choose_skill_progression(skill_progression_option: options.SkillProgression) raise ValueError(f"No skill progression feature mapped to {str(skill_progression_option.value)}") return skill_progression_feature + + +def choose_tool_progression(tool_option: options.ToolProgression) -> tool_progression.ToolProgressionFeature: + if tool_option == options.ToolProgression.option_vanilla: + return tool_progression.ToolProgressionVanilla() + + if tool_option == options.ToolProgression.option_vanilla_cheap: + return tool_progression.ToolProgressionVanilla( + price_multiplier=tool_progression.PriceMultipliers.CHEAP + ) + + if tool_option == options.ToolProgression.option_vanilla_very_cheap: + return tool_progression.ToolProgressionVanilla( + price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP + ) + + if tool_option == options.ToolProgression.option_progressive: + return tool_progression.ToolProgressionProgressive() + + if tool_option == options.ToolProgression.option_progressive_cheap: + return tool_progression.ToolProgressionProgressive( + price_multiplier=tool_progression.PriceMultipliers.CHEAP + ) + + if tool_option == options.ToolProgression.option_progressive_very_cheap: + return tool_progression.ToolProgressionProgressive( + price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP + ) + + raise ValueError(f"No tool progression feature mapped to {str(tool_option.value)}") diff --git a/worlds/stardew_valley/content/feature/__init__.py b/worlds/stardew_valley/content/feature/__init__.py index f3e5c6732e32..eb23f8105bd3 100644 --- a/worlds/stardew_valley/content/feature/__init__.py +++ b/worlds/stardew_valley/content/feature/__init__.py @@ -3,3 +3,4 @@ from . import fishsanity from . import friendsanity from . import skill_progression +from . import tool_progression diff --git a/worlds/stardew_valley/content/feature/tool_progression.py b/worlds/stardew_valley/content/feature/tool_progression.py new file mode 100644 index 000000000000..e6a9f278e124 --- /dev/null +++ b/worlds/stardew_valley/content/feature/tool_progression.py @@ -0,0 +1,58 @@ +from abc import ABC +from dataclasses import dataclass +from typing import ClassVar, Tuple + +from ...strings.tool_names import Tool + + +class PriceMultipliers: + CHEAP = 2 / 5 + VERY_CHEAP = 1 / 5 + + +progressive_house = "Progressive House" + +# This assumes that the farm house is always available, which might not be true forever... +progressive_house_by_upgrade_name = { + Tool.farm_house: 0, + Tool.kitchen: 1, + Tool.kids_room: 2, + Tool.cellar: 3 +} + + +def to_progressive_item(tool: str) -> Tuple[str, int]: + """Return the name of the progressive item and its quantity required to unlock the tool. + """ + if tool in [Tool.coop, Tool.barn, Tool.shed]: + return f"Progressive {tool}", 1 + elif tool.startswith("Big"): + return f"Progressive {tool[tool.index(' ') + 1:]}", 2 + elif tool.startswith("Deluxe"): + return f"Progressive {tool[tool.index(' ') + 1:]}", 3 + elif tool in progressive_house_by_upgrade_name: + return progressive_house, progressive_house_by_upgrade_name[tool] + + return tool, 1 + + +def to_location_name(tool: str) -> str: + return f"{tool} Blueprint" + + +@dataclass(frozen=True) +class ToolProgressionFeature(ABC): + is_progressive: ClassVar[bool] + price_multiplier: float = 1.0 + + # to_progressive_item = staticmethod(to_progressive_item) + + # to_location_name = staticmethod(to_location_name) + + +class ToolProgressionVanilla(ToolProgressionFeature): + is_progressive = False + + +class ToolProgressionProgressive(ToolProgressionFeature): + is_progressive = True diff --git a/worlds/stardew_valley/content/game_content.py b/worlds/stardew_valley/content/game_content.py index 7ff3217b04ed..838f04b14b48 100644 --- a/worlds/stardew_valley/content/game_content.py +++ b/worlds/stardew_valley/content/game_content.py @@ -54,6 +54,7 @@ class StardewFeatures: fishsanity: fishsanity.FishsanityFeature friendsanity: friendsanity.FriendsanityFeature skill_progression: skill_progression.SkillProgressionFeature + tool_progression: tool_progression.ToolProgressionFeature @dataclass(frozen=True) From a860c3d28266272f8518d8b0e220fe033ddae538 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:31:35 -0500 Subject: [PATCH 02/17] replace option usage with calling feature --- .../content/feature/tool_progression.py | 11 ----------- worlds/stardew_valley/content/game_content.py | 2 +- worlds/stardew_valley/early_items.py | 2 +- worlds/stardew_valley/items.py | 9 +++++---- worlds/stardew_valley/locations.py | 4 ++-- worlds/stardew_valley/logic/action_logic.py | 1 - worlds/stardew_valley/logic/mine_logic.py | 5 ++--- worlds/stardew_valley/logic/tool_logic.py | 5 ++--- worlds/stardew_valley/mods/logic/item_logic.py | 10 +++------- worlds/stardew_valley/rules.py | 10 +++++----- worlds/stardew_valley/test/TestGeneration.py | 6 +++--- worlds/stardew_valley/test/TestOptionFlags.py | 12 ++++++------ worlds/stardew_valley/test/content/__init__.py | 1 + 13 files changed, 31 insertions(+), 47 deletions(-) diff --git a/worlds/stardew_valley/content/feature/tool_progression.py b/worlds/stardew_valley/content/feature/tool_progression.py index e6a9f278e124..573195d52ad5 100644 --- a/worlds/stardew_valley/content/feature/tool_progression.py +++ b/worlds/stardew_valley/content/feature/tool_progression.py @@ -10,17 +10,6 @@ class PriceMultipliers: VERY_CHEAP = 1 / 5 -progressive_house = "Progressive House" - -# This assumes that the farm house is always available, which might not be true forever... -progressive_house_by_upgrade_name = { - Tool.farm_house: 0, - Tool.kitchen: 1, - Tool.kids_room: 2, - Tool.cellar: 3 -} - - def to_progressive_item(tool: str) -> Tuple[str, int]: """Return the name of the progressive item and its quantity required to unlock the tool. """ diff --git a/worlds/stardew_valley/content/game_content.py b/worlds/stardew_valley/content/game_content.py index 838f04b14b48..3aa3350f4714 100644 --- a/worlds/stardew_valley/content/game_content.py +++ b/worlds/stardew_valley/content/game_content.py @@ -3,7 +3,7 @@ from dataclasses import dataclass, field from typing import Dict, Iterable, Set, Any, Mapping, Type, Tuple, Union -from .feature import booksanity, cropsanity, fishsanity, friendsanity, skill_progression +from .feature import booksanity, cropsanity, fishsanity, friendsanity, skill_progression, tool_progression from ..data.fish_data import FishItem from ..data.game_item import GameItem, ItemSource, ItemTag from ..data.skill import Skill diff --git a/worlds/stardew_valley/early_items.py b/worlds/stardew_valley/early_items.py index 81e28956b3cf..5ad48912a28d 100644 --- a/worlds/stardew_valley/early_items.py +++ b/worlds/stardew_valley/early_items.py @@ -32,7 +32,7 @@ def setup_early_items(multiworld, options: stardew_options.StardewValleyOptions, if options.backpack_progression == stardew_options.BackpackProgression.option_early_progressive: early_forced.append("Progressive Backpack") - if options.tool_progression & stardew_options.ToolProgression.option_progressive: + if content.features.tool_progression.is_progressive: if content.features.fishsanity.is_enabled: early_candidates.append("Progressive Fishing Rod") early_forced.append("Progressive Pickaxe") diff --git a/worlds/stardew_valley/items.py b/worlds/stardew_valley/items.py index 6ac827f869cc..0ba89af76fcc 100644 --- a/worlds/stardew_valley/items.py +++ b/worlds/stardew_valley/items.py @@ -15,7 +15,7 @@ from .logic.logic_event import all_events from .mods.mod_data import ModNames from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Museumsanity, \ - BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \ + BuildingProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \ Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity, Walnutsanity, EnabledFillerBuffs from .strings.ap_names.ap_option_names import BuffOptionName, WalnutsanityOptionName from .strings.ap_names.ap_weapon_names import APWeapon @@ -226,7 +226,7 @@ def create_unique_items(item_factory: StardewItemFactory, options: StardewValley create_weapons(item_factory, options, items) items.append(item_factory("Skull Key")) create_elevators(item_factory, options, items) - create_tools(item_factory, options, content, items) + create_tools(item_factory, content, items) create_skills(item_factory, content, items) create_wizard_buildings(item_factory, options, items) create_carpenter_buildings(item_factory, options, items) @@ -316,8 +316,9 @@ def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOpt items.extend([item_factory(item) for item in ["Progressive Skull Cavern Elevator"] * 8]) -def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]): - if options.tool_progression & ToolProgression.option_progressive: +def create_tools(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]): + if content.features.tool_progression.is_progressive: + # TODO distribution of the tools should be moved in the tool progression feature for item_data in items_by_group[Group.PROGRESSIVE_TOOLS]: name = item_data.name if "Trash Can" in name: diff --git a/worlds/stardew_valley/locations.py b/worlds/stardew_valley/locations.py index b3a8db6f0341..b26401a5e24f 100644 --- a/worlds/stardew_valley/locations.py +++ b/worlds/stardew_valley/locations.py @@ -11,7 +11,7 @@ from .data.museum_data import all_museum_items from .mods.mod_data import ModNames from .options import ExcludeGingerIsland, ArcadeMachineLocations, SpecialOrderLocations, Museumsanity, \ - FestivalLocations, BuildingProgression, ToolProgression, ElevatorProgression, BackpackProgression, FarmType + FestivalLocations, BuildingProgression, ElevatorProgression, BackpackProgression, FarmType from .options import StardewValleyOptions, Craftsanity, Chefsanity, Cooksanity, Shipsanity, Monstersanity from .strings.goal_names import Goal from .strings.quest_names import ModQuest, Quest @@ -471,7 +471,7 @@ def create_locations(location_collector: StardewLocationCollector, extend_bundle_locations(randomized_locations, bundle_rooms) extend_backpack_locations(randomized_locations, options) - if options.tool_progression & ToolProgression.option_progressive: + if content.features.tool_progression.is_progressive: randomized_locations.extend(locations_by_tag[LocationTags.TOOL_UPGRADE]) extend_elevator_locations(randomized_locations, options) diff --git a/worlds/stardew_valley/logic/action_logic.py b/worlds/stardew_valley/logic/action_logic.py index dc5deda427f3..5b117de68cf2 100644 --- a/worlds/stardew_valley/logic/action_logic.py +++ b/worlds/stardew_valley/logic/action_logic.py @@ -6,7 +6,6 @@ from .received_logic import ReceivedLogicMixin from .region_logic import RegionLogicMixin from .tool_logic import ToolLogicMixin -from ..options import ToolProgression from ..stardew_rule import StardewRule, True_ from ..strings.generic_names import Generic from ..strings.geode_names import Geode diff --git a/worlds/stardew_valley/logic/mine_logic.py b/worlds/stardew_valley/logic/mine_logic.py index 350582ae0dbb..29a6706b49ca 100644 --- a/worlds/stardew_valley/logic/mine_logic.py +++ b/worlds/stardew_valley/logic/mine_logic.py @@ -10,7 +10,6 @@ from .skill_logic import SkillLogicMixin from .tool_logic import ToolLogicMixin from .. import options -from ..options import ToolProgression from ..stardew_rule import StardewRule, True_ from ..strings.performance_names import Performance from ..strings.region_names import Region @@ -59,7 +58,7 @@ def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule: weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier) rules.append(weapon_rule) - if self.options.tool_progression & ToolProgression.option_progressive: + if self.content.features.tool_progression.is_progressive: rules.append(self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier])) # No alternative for vanilla because we assume that you will grind the levels in the mines. @@ -88,7 +87,7 @@ def can_progress_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule weapon_rule = self.logic.combat.has_great_weapon rules.append(weapon_rule) - if self.options.tool_progression & ToolProgression.option_progressive: + if self.content.features.tool_progression.is_progressive: rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2)))) # No alternative for vanilla because we assume that you will grind the levels in the mines. diff --git a/worlds/stardew_valley/logic/tool_logic.py b/worlds/stardew_valley/logic/tool_logic.py index ba593c085ae4..e3d2d8884ee1 100644 --- a/worlds/stardew_valley/logic/tool_logic.py +++ b/worlds/stardew_valley/logic/tool_logic.py @@ -8,7 +8,6 @@ from .region_logic import RegionLogicMixin from .season_logic import SeasonLogicMixin from ..mods.logic.magic_logic import MagicLogicMixin -from ..options import ToolProgression from ..stardew_rule import StardewRule, True_, False_ from ..strings.ap_names.skill_level_names import ModSkillLevel from ..strings.region_names import Region @@ -57,7 +56,7 @@ def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule if material == ToolMaterial.basic or tool == Tool.scythe: return True_() - if self.options.tool_progression & ToolProgression.option_progressive: + if self.content.features.tool_progression.is_progressive: return self.logic.received(f"Progressive {tool}", tool_materials[material]) can_upgrade_rule = self.logic.has(f"{material} Bar") & self.logic.money.can_spend_at(Region.blacksmith, tool_upgrade_prices[material]) @@ -76,7 +75,7 @@ def can_use_tool_at(self, tool: str, material: str, region: str) -> StardewRule: def has_fishing_rod(self, level: int) -> StardewRule: assert 1 <= level <= 4, "Fishing rod 0 isn't real, it can't hurt you. Training is 1, Bamboo is 2, Fiberglass is 3 and Iridium is 4." - if self.options.tool_progression & ToolProgression.option_progressive: + if self.content.features.tool_progression.is_progressive: return self.logic.received(f"Progressive {Tool.fishing_rod}", level) if level <= 2: diff --git a/worlds/stardew_valley/mods/logic/item_logic.py b/worlds/stardew_valley/mods/logic/item_logic.py index ef5eab0134d1..fd87a4a0aceb 100644 --- a/worlds/stardew_valley/mods/logic/item_logic.py +++ b/worlds/stardew_valley/mods/logic/item_logic.py @@ -1,8 +1,6 @@ from typing import Dict, Union from ..mod_data import ModNames -from ... import options -from ...data.craftable_data import all_crafting_recipes_by_name from ...logic.base_logic import BaseLogicMixin, BaseLogic from ...logic.combat_logic import CombatLogicMixin from ...logic.cooking_logic import CookingLogicMixin @@ -20,11 +18,9 @@ from ...logic.skill_logic import SkillLogicMixin from ...logic.time_logic import TimeLogicMixin from ...logic.tool_logic import ToolLogicMixin -from ...options import Cropsanity -from ...stardew_rule import StardewRule, True_ +from ...stardew_rule import StardewRule from ...strings.artisan_good_names import ModArtisanGood -from ...strings.craftable_names import ModCraftable, ModMachine -from ...strings.fish_names import ModTrash +from ...strings.craftable_names import ModCraftable from ...strings.ingredient_names import Ingredient from ...strings.material_names import Material from ...strings.metal_names import all_fossils, all_artifacts, Ore, ModFossil @@ -83,7 +79,7 @@ def get_modified_item_rules_for_deep_woods(self, items: Dict[str, StardewRule]): # Gingerbread House } - if self.options.tool_progression & options.ToolProgression.option_progressive: + if self.content.features.tool_progression.is_progressive: options_to_update.update({ Ore.iridium: items[Ore.iridium] | self.logic.tool.can_use_tool_at(Tool.axe, ToolMaterial.iridium, DeepWoodsRegion.floor_50), # Iridium Tree }) diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py index 54afc31eb892..ca3e6ee29fee 100644 --- a/worlds/stardew_valley/rules.py +++ b/worlds/stardew_valley/rules.py @@ -19,9 +19,9 @@ from .logic.time_logic import MAX_MONTHS from .logic.tool_logic import tool_upgrade_prices from .mods.mod_data import ModNames -from .options import StardewValleyOptions, Walnutsanity -from .options import ToolProgression, BuildingProgression, ExcludeGingerIsland, SpecialOrderLocations, Museumsanity, BackpackProgression, Shipsanity, \ +from .options import BuildingProgression, ExcludeGingerIsland, SpecialOrderLocations, Museumsanity, BackpackProgression, Shipsanity, \ Monstersanity, Chefsanity, Craftsanity, ArcadeMachineLocations, Cooksanity +from .options import StardewValleyOptions, Walnutsanity from .stardew_rule import And, StardewRule, true_ from .stardew_rule.indirect_connection import look_for_indirect_connection from .stardew_rule.rule_explain import explain @@ -69,7 +69,7 @@ def set_rules(world): set_entrance_rules(logic, multiworld, player, world_options) set_ginger_island_rules(logic, multiworld, player, world_options) - set_tool_rules(logic, multiworld, player, world_options) + set_tool_rules(logic, multiworld, player, world_content) set_skills_rules(logic, multiworld, player, world_content) set_bundle_rules(bundle_rooms, logic, multiworld, player, world_options) set_building_rules(logic, multiworld, player, world_options) @@ -111,8 +111,8 @@ def set_isolated_locations_rules(logic: StardewLogic, multiworld, player): logic.season.has(Season.spring)) -def set_tool_rules(logic: StardewLogic, multiworld, player, world_options: StardewValleyOptions): - if not world_options.tool_progression & ToolProgression.option_progressive: +def set_tool_rules(logic: StardewLogic, multiworld, player, content: StardewContent): + if not content.features.tool_progression.is_progressive: return MultiWorldRules.add_rule(multiworld.get_location("Purchase Fiberglass Rod", player), diff --git a/worlds/stardew_valley/test/TestGeneration.py b/worlds/stardew_valley/test/TestGeneration.py index 56f338fe8e11..38882136ce91 100644 --- a/worlds/stardew_valley/test/TestGeneration.py +++ b/worlds/stardew_valley/test/TestGeneration.py @@ -5,8 +5,8 @@ from .. import items, location_table, options from ..items import Group from ..locations import LocationTags -from ..options import Friendsanity, SpecialOrderLocations, Shipsanity, Chefsanity, SeasonRandomization, Craftsanity, ExcludeGingerIsland, ToolProgression, \ - SkillProgression, Booksanity, Walnutsanity +from ..options import Friendsanity, SpecialOrderLocations, Shipsanity, Chefsanity, SeasonRandomization, Craftsanity, ExcludeGingerIsland, SkillProgression, \ + Booksanity, Walnutsanity from ..strings.region_names import Region @@ -320,7 +320,7 @@ def generate_items_for_extra_mine_levels(self, weapon_name: str) -> List[Item]: class TestSkullCavernLogic(SVTestBase): options = { options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla, - ToolProgression.internal_name: ToolProgression.option_progressive, + options.ToolProgression.internal_name: options.ToolProgression.option_progressive, options.SkillProgression.internal_name: options.SkillProgression.option_progressive, } diff --git a/worlds/stardew_valley/test/TestOptionFlags.py b/worlds/stardew_valley/test/TestOptionFlags.py index 05e52b40c4bd..88f2257cabee 100644 --- a/worlds/stardew_valley/test/TestOptionFlags.py +++ b/worlds/stardew_valley/test/TestOptionFlags.py @@ -9,7 +9,7 @@ class TestBitFlagsVanilla(SVTestBase): def test_options_are_not_detected_as_progressive(self): world_options = self.world.options - tool_progressive = world_options.tool_progression & ToolProgression.option_progressive + tool_progressive = self.world.content.features.tool_progression.is_progressive building_progressive = world_options.building_progression & BuildingProgression.option_progressive self.assertFalse(tool_progressive) self.assertFalse(building_progressive) @@ -26,7 +26,7 @@ class TestBitFlagsVanillaCheap(SVTestBase): def test_options_are_not_detected_as_progressive(self): world_options = self.world.options - tool_progressive = world_options.tool_progression & ToolProgression.option_progressive + tool_progressive = self.world.content.features.tool_progression.is_progressive building_progressive = world_options.building_progression & BuildingProgression.option_progressive self.assertFalse(tool_progressive) self.assertFalse(building_progressive) @@ -43,7 +43,7 @@ class TestBitFlagsVanillaVeryCheap(SVTestBase): def test_options_are_not_detected_as_progressive(self): world_options = self.world.options - tool_progressive = world_options.tool_progression & ToolProgression.option_progressive + tool_progressive = self.world.content.features.tool_progression.is_progressive building_progressive = world_options.building_progression & BuildingProgression.option_progressive self.assertFalse(tool_progressive) self.assertFalse(building_progressive) @@ -60,7 +60,7 @@ class TestBitFlagsProgressive(SVTestBase): def test_options_are_detected_as_progressive(self): world_options = self.world.options - tool_progressive = world_options.tool_progression & ToolProgression.option_progressive + tool_progressive = self.world.content.features.tool_progression.is_progressive building_progressive = world_options.building_progression & BuildingProgression.option_progressive self.assertTrue(tool_progressive) self.assertTrue(building_progressive) @@ -77,7 +77,7 @@ class TestBitFlagsProgressiveCheap(SVTestBase): def test_options_are_detected_as_progressive(self): world_options = self.world.options - tool_progressive = world_options.tool_progression & ToolProgression.option_progressive + tool_progressive = self.world.content.features.tool_progression.is_progressive building_progressive = world_options.building_progression & BuildingProgression.option_progressive self.assertTrue(tool_progressive) self.assertTrue(building_progressive) @@ -94,7 +94,7 @@ class TestBitFlagsProgressiveVeryCheap(SVTestBase): def test_options_are_detected_as_progressive(self): world_options = self.world.options - tool_progressive = world_options.tool_progression & ToolProgression.option_progressive + tool_progressive = self.world.content.features.tool_progression.is_progressive building_progressive = world_options.building_progression & BuildingProgression.option_progressive self.assertTrue(tool_progressive) self.assertTrue(building_progressive) diff --git a/worlds/stardew_valley/test/content/__init__.py b/worlds/stardew_valley/test/content/__init__.py index c666a3aae14d..0832c2e3c36d 100644 --- a/worlds/stardew_valley/test/content/__init__.py +++ b/worlds/stardew_valley/test/content/__init__.py @@ -9,6 +9,7 @@ feature.fishsanity.FishsanityNone(), feature.friendsanity.FriendsanityNone(), feature.skill_progression.SkillProgressionVanilla(), + feature.tool_progression.ToolProgressionVanilla() ) From 4dd372527bb6972607b8f9dca2bf84c406c85e60 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 11:48:08 -0500 Subject: [PATCH 03/17] add comment explaining why some logic is a weird place --- worlds/stardew_valley/logic/mine_logic.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/worlds/stardew_valley/logic/mine_logic.py b/worlds/stardew_valley/logic/mine_logic.py index 29a6706b49ca..be97dbc6e7df 100644 --- a/worlds/stardew_valley/logic/mine_logic.py +++ b/worlds/stardew_valley/logic/mine_logic.py @@ -58,6 +58,7 @@ def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule: weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier) rules.append(weapon_rule) + # No alternative for vanilla because we assume you will get ores to upgrade your tools in the mines. if self.content.features.tool_progression.is_progressive: rules.append(self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier])) @@ -87,7 +88,9 @@ def can_progress_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule weapon_rule = self.logic.combat.has_great_weapon rules.append(weapon_rule) + # No alternative for vanilla because we assume you will get ores to upgrade your tools in the mines. if self.content.features.tool_progression.is_progressive: + # TODO move that in tool logic somehow rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2)))) # No alternative for vanilla because we assume that you will grind the levels in the mines. From 44a936fb72d513f9131202c7c06a3b1aefed02e1 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:20:18 -0500 Subject: [PATCH 04/17] replace item creation logic with feature --- worlds/stardew_valley/content/__init__.py | 13 +++- .../content/feature/tool_progression.py | 62 ++++++++++++++----- worlds/stardew_valley/items.py | 33 ++++------ 3 files changed, 66 insertions(+), 42 deletions(-) diff --git a/worlds/stardew_valley/content/__init__.py b/worlds/stardew_valley/content/__init__.py index c816bffed7da..988915dd769f 100644 --- a/worlds/stardew_valley/content/__init__.py +++ b/worlds/stardew_valley/content/__init__.py @@ -33,7 +33,7 @@ def choose_features(player_options: options.StardewValleyOptions) -> StardewFeat choose_fishsanity(player_options.fishsanity), choose_friendsanity(player_options.friendsanity, player_options.friendsanity_heart_size), choose_skill_progression(player_options.skill_progression), - choose_tool_progression(player_options.tool_progression), + choose_tool_progression(player_options.tool_progression, player_options.skill_progression), ) @@ -125,7 +125,7 @@ def choose_skill_progression(skill_progression_option: options.SkillProgression) return skill_progression_feature -def choose_tool_progression(tool_option: options.ToolProgression) -> tool_progression.ToolProgressionFeature: +def choose_tool_progression(tool_option: options.ToolProgression, skill_option: options.SkillProgression) -> tool_progression.ToolProgressionFeature: if tool_option == options.ToolProgression.option_vanilla: return tool_progression.ToolProgressionVanilla() @@ -139,16 +139,23 @@ def choose_tool_progression(tool_option: options.ToolProgression) -> tool_progre price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP ) + tools_distribution = tool_progression.get_tools_distribution( + progressive_tools_enabled=True, + skill_masteries_enabled=skill_option == options.SkillProgression.option_progressive_with_masteries, + ) + if tool_option == options.ToolProgression.option_progressive: - return tool_progression.ToolProgressionProgressive() + return tool_progression.ToolProgressionProgressive(tools_distribution) if tool_option == options.ToolProgression.option_progressive_cheap: return tool_progression.ToolProgressionProgressive( + tools_distribution, price_multiplier=tool_progression.PriceMultipliers.CHEAP ) if tool_option == options.ToolProgression.option_progressive_very_cheap: return tool_progression.ToolProgressionProgressive( + tools_distribution, price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP ) diff --git a/worlds/stardew_valley/content/feature/tool_progression.py b/worlds/stardew_valley/content/feature/tool_progression.py index 573195d52ad5..1dd8b4c323a3 100644 --- a/worlds/stardew_valley/content/feature/tool_progression.py +++ b/worlds/stardew_valley/content/feature/tool_progression.py @@ -1,6 +1,10 @@ from abc import ABC -from dataclasses import dataclass -from typing import ClassVar, Tuple +from collections import Counter +from collections.abc import Mapping +from dataclasses import dataclass, field +from functools import cache +from types import MappingProxyType +from typing import ClassVar from ...strings.tool_names import Tool @@ -10,37 +14,61 @@ class PriceMultipliers: VERY_CHEAP = 1 / 5 -def to_progressive_item(tool: str) -> Tuple[str, int]: - """Return the name of the progressive item and its quantity required to unlock the tool. - """ - if tool in [Tool.coop, Tool.barn, Tool.shed]: - return f"Progressive {tool}", 1 - elif tool.startswith("Big"): - return f"Progressive {tool[tool.index(' ') + 1:]}", 2 - elif tool.startswith("Deluxe"): - return f"Progressive {tool[tool.index(' ') + 1:]}", 3 - elif tool in progressive_house_by_upgrade_name: - return progressive_house, progressive_house_by_upgrade_name[tool] +def to_progressive_item(tool: str) -> str: + """Return the name of the progressive item.""" + return f"Progressive {tool}" - return tool, 1 +# The golden scythe is always randomized +VANILLA_TOOL_DISTRIBUTION = MappingProxyType({ + Tool.scythe: 1, +}) -def to_location_name(tool: str) -> str: - return f"{tool} Blueprint" +# Masteries add another tier to the scythe and the fishing rod +PROGRESSIVE_TOOL_DISTRIBUTION = MappingProxyType({ + Tool.axe: 4, + Tool.hoe: 4, + Tool.pickaxe: 4, + Tool.pan: 4, + Tool.trash_can: 4, + Tool.watering_can: 4, + Tool.fishing_rod: 4, +}) + +SKILL_MASTERIES_TOOL_DISTRIBUTION = MappingProxyType({ + Tool.scythe: 1, + Tool.fishing_rod: 1, +}) + + +@cache +def get_tools_distribution(progressive_tools_enabled: bool, skill_masteries_enabled: bool) -> Mapping[str, int]: + distribution = Counter(VANILLA_TOOL_DISTRIBUTION) + + if progressive_tools_enabled: + distribution += PROGRESSIVE_TOOL_DISTRIBUTION + + if skill_masteries_enabled: + distribution += SKILL_MASTERIES_TOOL_DISTRIBUTION + + return MappingProxyType(distribution) @dataclass(frozen=True) class ToolProgressionFeature(ABC): is_progressive: ClassVar[bool] + tool_distribution: Mapping[str, int] price_multiplier: float = 1.0 - # to_progressive_item = staticmethod(to_progressive_item) + to_progressive_item = staticmethod(to_progressive_item) # to_location_name = staticmethod(to_location_name) +@dataclass(frozen=True) class ToolProgressionVanilla(ToolProgressionFeature): is_progressive = False + tool_distribution: Mapping[str, int] = field(default=VANILLA_TOOL_DISTRIBUTION) class ToolProgressionProgressive(ToolProgressionFeature): diff --git a/worlds/stardew_valley/items.py b/worlds/stardew_valley/items.py index 0ba89af76fcc..056a4f6e397d 100644 --- a/worlds/stardew_valley/items.py +++ b/worlds/stardew_valley/items.py @@ -23,6 +23,7 @@ from .strings.ap_names.community_upgrade_names import CommunityUpgrade from .strings.ap_names.mods.mod_items import SVEQuestItem from .strings.currency_names import Currency +from .strings.tool_names import Tool from .strings.wallet_item_names import Wallet ITEM_CODE_OFFSET = 717000 @@ -119,11 +120,6 @@ def __call__(self, name: Union[str, ItemData], override_classification: ItemClas raise NotImplementedError -class StardewItemDeleter(Protocol): - def __call__(self, item: Item): - raise NotImplementedError - - def load_item_csv(): from importlib.resources import files @@ -317,23 +313,16 @@ def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOpt def create_tools(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]): - if content.features.tool_progression.is_progressive: - # TODO distribution of the tools should be moved in the tool progression feature - for item_data in items_by_group[Group.PROGRESSIVE_TOOLS]: - name = item_data.name - if "Trash Can" in name: - items.extend([item_factory(item) for item in [item_data] * 3]) - items.append(item_factory(item_data, ItemClassification.useful)) - else: - items.extend([item_factory(item) for item in [item_data] * 4]) - - if content.features.skill_progression.are_masteries_shuffled: - # Masteries add another tier to the scythe and the fishing rod - items.append(item_factory("Progressive Scythe")) - items.append(item_factory("Progressive Fishing Rod")) - - # The golden scythe is always randomized - items.append(item_factory("Progressive Scythe")) + tool_progression = content.features.tool_progression + for tool, count in tool_progression.tool_distribution.items(): + item = item_table[tool_progression.to_progressive_item(tool)] + + # Trash can is only used in tool upgrade logic, so the last trash can is not progression because it basically does not unlock anything. + if tool == Tool.trash_can: + count -= 1 + items.append(item_factory(item, ItemClassification.useful)) + + items.extend([item_factory(item) for _ in range(count)]) def create_skills(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]): From c45a92e89dd77b2475bae6d45bc2c40983d5704b Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:38:55 -0500 Subject: [PATCH 05/17] self review and add unit tests --- worlds/stardew_valley/content/__init__.py | 8 +-- .../content/feature/tool_progression.py | 4 +- worlds/stardew_valley/rules.py | 3 +- .../content/feature/TestToolProgression.py | 53 +++++++++++++++++++ 4 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 worlds/stardew_valley/test/content/feature/TestToolProgression.py diff --git a/worlds/stardew_valley/content/__init__.py b/worlds/stardew_valley/content/__init__.py index 988915dd769f..f27242ae3965 100644 --- a/worlds/stardew_valley/content/__init__.py +++ b/worlds/stardew_valley/content/__init__.py @@ -131,12 +131,12 @@ def choose_tool_progression(tool_option: options.ToolProgression, skill_option: if tool_option == options.ToolProgression.option_vanilla_cheap: return tool_progression.ToolProgressionVanilla( - price_multiplier=tool_progression.PriceMultipliers.CHEAP + price_multiplier=tool_progression.PriceMultipliers.CHEAP, ) if tool_option == options.ToolProgression.option_vanilla_very_cheap: return tool_progression.ToolProgressionVanilla( - price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP + price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP, ) tools_distribution = tool_progression.get_tools_distribution( @@ -150,13 +150,13 @@ def choose_tool_progression(tool_option: options.ToolProgression, skill_option: if tool_option == options.ToolProgression.option_progressive_cheap: return tool_progression.ToolProgressionProgressive( tools_distribution, - price_multiplier=tool_progression.PriceMultipliers.CHEAP + price_multiplier=tool_progression.PriceMultipliers.CHEAP, ) if tool_option == options.ToolProgression.option_progressive_very_cheap: return tool_progression.ToolProgressionProgressive( tools_distribution, - price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP + price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP, ) raise ValueError(f"No tool progression feature mapped to {str(tool_option.value)}") diff --git a/worlds/stardew_valley/content/feature/tool_progression.py b/worlds/stardew_valley/content/feature/tool_progression.py index 1dd8b4c323a3..84d1cdd48c29 100644 --- a/worlds/stardew_valley/content/feature/tool_progression.py +++ b/worlds/stardew_valley/content/feature/tool_progression.py @@ -24,7 +24,6 @@ def to_progressive_item(tool: str) -> str: Tool.scythe: 1, }) -# Masteries add another tier to the scythe and the fishing rod PROGRESSIVE_TOOL_DISTRIBUTION = MappingProxyType({ Tool.axe: 4, Tool.hoe: 4, @@ -35,6 +34,7 @@ def to_progressive_item(tool: str) -> str: Tool.fishing_rod: 4, }) +# Masteries add another tier to the scythe and the fishing rod SKILL_MASTERIES_TOOL_DISTRIBUTION = MappingProxyType({ Tool.scythe: 1, Tool.fishing_rod: 1, @@ -62,8 +62,6 @@ class ToolProgressionFeature(ABC): to_progressive_item = staticmethod(to_progressive_item) - # to_location_name = staticmethod(to_location_name) - @dataclass(frozen=True) class ToolProgressionVanilla(ToolProgressionFeature): diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py index ca3e6ee29fee..18ff11d54dcb 100644 --- a/worlds/stardew_valley/rules.py +++ b/worlds/stardew_valley/rules.py @@ -20,8 +20,7 @@ from .logic.tool_logic import tool_upgrade_prices from .mods.mod_data import ModNames from .options import BuildingProgression, ExcludeGingerIsland, SpecialOrderLocations, Museumsanity, BackpackProgression, Shipsanity, \ - Monstersanity, Chefsanity, Craftsanity, ArcadeMachineLocations, Cooksanity -from .options import StardewValleyOptions, Walnutsanity + Monstersanity, Chefsanity, Craftsanity, ArcadeMachineLocations, Cooksanity, StardewValleyOptions, Walnutsanity from .stardew_rule import And, StardewRule, true_ from .stardew_rule.indirect_connection import look_for_indirect_connection from .stardew_rule.rule_explain import explain diff --git a/worlds/stardew_valley/test/content/feature/TestToolProgression.py b/worlds/stardew_valley/test/content/feature/TestToolProgression.py new file mode 100644 index 000000000000..fbca39b725a4 --- /dev/null +++ b/worlds/stardew_valley/test/content/feature/TestToolProgression.py @@ -0,0 +1,53 @@ +import unittest + +from ....content import choose_tool_progression +from ....options import ToolProgression, SkillProgression +from ....strings.tool_names import Tool + + +class TestToolDistribution(unittest.TestCase): + + def test_given_vanilla_tool_progression_when_create_tool_progression_feature_then_only_one_scythe_is_randomized(self): + tool_progression = ToolProgression(ToolProgression.option_vanilla) + skill_progression = SkillProgression.from_text("random") + + feature = choose_tool_progression(tool_progression, skill_progression) + + self.assertEqual(feature.tool_distribution, { + Tool.scythe: 1, + }) + + def test_given_progressive_tool_when_create_tool_progression_feature_then_all_tool_upgrades_are_randomized(self): + tool_progression = ToolProgression(ToolProgression.option_progressive) + skill_progression = SkillProgression(SkillProgression.option_vanilla) + + feature = choose_tool_progression(tool_progression, skill_progression) + + self.assertEqual(feature.tool_distribution, { + Tool.scythe: 1, + Tool.pickaxe: 4, + Tool.axe: 4, + Tool.hoe: 4, + Tool.watering_can: 4, + Tool.trash_can: 4, + Tool.pan: 4, + Tool.fishing_rod: 4, + }) + + def test_given_progressive_tool_and_skill_masteries_randomized_when_create_tool_progression_feature_then_additional_scythe_and_fishing_rod_are_randomized( + self): + tool_progression = ToolProgression(ToolProgression.option_progressive) + skill_progression = SkillProgression(SkillProgression.option_progressive_with_masteries) + + feature = choose_tool_progression(tool_progression, skill_progression) + + self.assertEqual(feature.tool_distribution, { + Tool.scythe: 2, + Tool.pickaxe: 4, + Tool.axe: 4, + Tool.hoe: 4, + Tool.watering_can: 4, + Tool.trash_can: 4, + Tool.pan: 4, + Tool.fishing_rod: 5, + }) From 14be2c2003ddfc7a308b3438e8bf06c83705b209 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:41:27 -0500 Subject: [PATCH 06/17] rename test cuz I named them too long --- .../test/content/feature/TestToolProgression.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/worlds/stardew_valley/test/content/feature/TestToolProgression.py b/worlds/stardew_valley/test/content/feature/TestToolProgression.py index fbca39b725a4..618c78dd7a92 100644 --- a/worlds/stardew_valley/test/content/feature/TestToolProgression.py +++ b/worlds/stardew_valley/test/content/feature/TestToolProgression.py @@ -7,7 +7,7 @@ class TestToolDistribution(unittest.TestCase): - def test_given_vanilla_tool_progression_when_create_tool_progression_feature_then_only_one_scythe_is_randomized(self): + def test_given_vanilla_tool_progression_when_create_feature_then_only_one_scythe_is_randomized(self): tool_progression = ToolProgression(ToolProgression.option_vanilla) skill_progression = SkillProgression.from_text("random") @@ -17,9 +17,9 @@ def test_given_vanilla_tool_progression_when_create_tool_progression_feature_the Tool.scythe: 1, }) - def test_given_progressive_tool_when_create_tool_progression_feature_then_all_tool_upgrades_are_randomized(self): + def test_given_progressive_tool_when_create_feature_then_all_tool_upgrades_are_randomized(self): tool_progression = ToolProgression(ToolProgression.option_progressive) - skill_progression = SkillProgression(SkillProgression.option_vanilla) + skill_progression = SkillProgression(SkillProgression.option_progressive) feature = choose_tool_progression(tool_progression, skill_progression) @@ -34,8 +34,7 @@ def test_given_progressive_tool_when_create_tool_progression_feature_then_all_to Tool.fishing_rod: 4, }) - def test_given_progressive_tool_and_skill_masteries_randomized_when_create_tool_progression_feature_then_additional_scythe_and_fishing_rod_are_randomized( - self): + def test_given_progressive_tool_and_skill_masteries_when_create_feature_then_additional_scythe_and_fishing_rod_are_randomized(self): tool_progression = ToolProgression(ToolProgression.option_progressive) skill_progression = SkillProgression(SkillProgression.option_progressive_with_masteries) From effaa2c9ee11244cbd880df323c462aa9cddea3a Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:54:47 -0500 Subject: [PATCH 07/17] add a test for the trash can useful stuff cuz I thought there was a bug but turns out it works --- worlds/stardew_valley/test/TestOptions.py | 74 ++++++++--------------- 1 file changed, 26 insertions(+), 48 deletions(-) diff --git a/worlds/stardew_valley/test/TestOptions.py b/worlds/stardew_valley/test/TestOptions.py index 2cd83f013ae5..06bbfd457ac8 100644 --- a/worlds/stardew_valley/test/TestOptions.py +++ b/worlds/stardew_valley/test/TestOptions.py @@ -1,17 +1,17 @@ import itertools +from BaseClasses import ItemClassification from Options import NamedRange -from . import SVTestCase, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, solo_multiworld +from . import SVTestCase, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, solo_multiworld, SVTestBase from .assertion import WorldAssertMixin from .long.option_names import all_option_choices from .. import items_by_group, Group, StardewValleyWorld from ..locations import locations_by_tag, LocationTags, location_table -from ..options import ExcludeGingerIsland, ToolProgression, Goal, SeasonRandomization, TrapItems, SpecialOrderLocations, ArcadeMachineLocations, \ - SkillProgression +from ..options import ExcludeGingerIsland, ToolProgression, Goal, SeasonRandomization, TrapItems, SpecialOrderLocations, ArcadeMachineLocations from ..strings.goal_names import Goal as GoalName from ..strings.season_names import Season from ..strings.special_order_names import SpecialOrder -from ..strings.tool_names import ToolMaterial, Tool +from ..strings.tool_names import ToolMaterial, Tool, APTool SEASONS = {Season.spring, Season.summer, Season.fall, Season.winter} TOOLS = {"Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can", "Fishing Rod"} @@ -77,52 +77,30 @@ def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_p self.assertEqual(items.count(Season.progressive), 3) -class TestToolProgression(SVTestCase): - def test_given_vanilla_when_generate_then_no_tool_in_pool(self): - world_options = {ToolProgression.internal_name: ToolProgression.option_vanilla} - with solo_multiworld(world_options) as (multi_world, _): - items = {item.name for item in multi_world.get_items()} - for tool in TOOLS: - self.assertNotIn(tool, items) - - def test_given_progressive_when_generate_then_each_tool_is_in_pool_4_times(self): - world_options = {ToolProgression.internal_name: ToolProgression.option_progressive, - SkillProgression.internal_name: SkillProgression.option_progressive} - with solo_multiworld(world_options) as (multi_world, _): - items = [item.name for item in multi_world.get_items()] - for tool in TOOLS: - count = items.count("Progressive " + tool) - self.assertEqual(count, 4, f"Progressive {tool} was there {count} times") - scythe_count = items.count("Progressive Scythe") - self.assertEqual(scythe_count, 1, f"Progressive Scythe was there {scythe_count} times") - self.assertEqual(items.count("Golden Scythe"), 0, f"Golden Scythe is deprecated") - - def test_given_progressive_with_masteries_when_generate_then_fishing_rod_is_in_the_pool_5_times(self): - world_options = {ToolProgression.internal_name: ToolProgression.option_progressive, - SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries} - with solo_multiworld(world_options) as (multi_world, _): - items = [item.name for item in multi_world.get_items()] - for tool in TOOLS: - count = items.count("Progressive " + tool) - expected_count = 5 if tool == "Fishing Rod" else 4 - self.assertEqual(count, expected_count, f"Progressive {tool} was there {count} times") - scythe_count = items.count("Progressive Scythe") - self.assertEqual(scythe_count, 2, f"Progressive Scythe was there {scythe_count} times") - self.assertEqual(items.count("Golden Scythe"), 0, f"Golden Scythe is deprecated") +class TestToolProgression(SVTestBase): + options = { + ToolProgression.internal_name: ToolProgression.option_progressive, + } def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self): - world_options = {ToolProgression.internal_name: ToolProgression.option_progressive} - with solo_multiworld(world_options) as (multi_world, _): - locations = {locations.name for locations in multi_world.get_locations(1)} - for material, tool in itertools.product(ToolMaterial.tiers.values(), - [Tool.hoe, Tool.pickaxe, Tool.axe, Tool.watering_can, Tool.trash_can]): - if material == ToolMaterial.basic: - continue - self.assertIn(f"{material} {tool} Upgrade", locations) - self.assertIn("Purchase Training Rod", locations) - self.assertIn("Bamboo Pole Cutscene", locations) - self.assertIn("Purchase Fiberglass Rod", locations) - self.assertIn("Purchase Iridium Rod", locations) + locations = set(self.get_real_location_names()) + for material, tool in itertools.product(ToolMaterial.tiers.values(), + [Tool.hoe, Tool.pickaxe, Tool.axe, Tool.watering_can, Tool.trash_can]): + if material == ToolMaterial.basic: + continue + self.assertIn(f"{material} {tool} Upgrade", locations) + self.assertIn("Purchase Training Rod", locations) + self.assertIn("Bamboo Pole Cutscene", locations) + self.assertIn("Purchase Fiberglass Rod", locations) + self.assertIn("Purchase Iridium Rod", locations) + + def test_given_progressive_when_generate_then_only_3_trash_can_are_progressive(self): + trash_cans = self.get_items_by_name(APTool.trash_can) + progressive_count = sum([1 for item in trash_cans if item.classification == ItemClassification.progression]) + useful_count = sum([1 for item in trash_cans if item.classification == ItemClassification.useful]) + + self.assertEqual(progressive_count, 3) + self.assertEqual(useful_count, 1) class TestGenerateAllOptionsWithExcludeGingerIsland(WorldAssertMixin, SVTestCase): From 691e706e58c46de2b655c2a7b16a95ceeb13230f Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 14:58:38 -0500 Subject: [PATCH 08/17] self review again --- worlds/stardew_valley/logic/mine_logic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/worlds/stardew_valley/logic/mine_logic.py b/worlds/stardew_valley/logic/mine_logic.py index be97dbc6e7df..26a15c8e15d8 100644 --- a/worlds/stardew_valley/logic/mine_logic.py +++ b/worlds/stardew_valley/logic/mine_logic.py @@ -90,7 +90,6 @@ def can_progress_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule # No alternative for vanilla because we assume you will get ores to upgrade your tools in the mines. if self.content.features.tool_progression.is_progressive: - # TODO move that in tool logic somehow rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2)))) # No alternative for vanilla because we assume that you will grind the levels in the mines. From 2d8e053972a77c04f18cd21ce1cc1322dff29efc Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:14:23 -0500 Subject: [PATCH 09/17] remove price_multiplier, turns out it's unused during generation --- worlds/stardew_valley/content/__init__.py | 30 ++++--------------- .../content/feature/tool_progression.py | 6 ---- 2 files changed, 6 insertions(+), 30 deletions(-) diff --git a/worlds/stardew_valley/content/__init__.py b/worlds/stardew_valley/content/__init__.py index f27242ae3965..8c57d71db047 100644 --- a/worlds/stardew_valley/content/__init__.py +++ b/worlds/stardew_valley/content/__init__.py @@ -126,37 +126,19 @@ def choose_skill_progression(skill_progression_option: options.SkillProgression) def choose_tool_progression(tool_option: options.ToolProgression, skill_option: options.SkillProgression) -> tool_progression.ToolProgressionFeature: - if tool_option == options.ToolProgression.option_vanilla: + if (tool_option == options.ToolProgression.option_vanilla + or tool_option == options.ToolProgression.option_vanilla_cheap + or tool_option == options.ToolProgression.option_vanilla_very_cheap): return tool_progression.ToolProgressionVanilla() - if tool_option == options.ToolProgression.option_vanilla_cheap: - return tool_progression.ToolProgressionVanilla( - price_multiplier=tool_progression.PriceMultipliers.CHEAP, - ) - - if tool_option == options.ToolProgression.option_vanilla_very_cheap: - return tool_progression.ToolProgressionVanilla( - price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP, - ) - tools_distribution = tool_progression.get_tools_distribution( progressive_tools_enabled=True, skill_masteries_enabled=skill_option == options.SkillProgression.option_progressive_with_masteries, ) - if tool_option == options.ToolProgression.option_progressive: + if (tool_option == options.ToolProgression.option_progressive + or tool_option == options.ToolProgression.option_progressive_cheap + or tool_option == options.ToolProgression.option_progressive_very_cheap): return tool_progression.ToolProgressionProgressive(tools_distribution) - if tool_option == options.ToolProgression.option_progressive_cheap: - return tool_progression.ToolProgressionProgressive( - tools_distribution, - price_multiplier=tool_progression.PriceMultipliers.CHEAP, - ) - - if tool_option == options.ToolProgression.option_progressive_very_cheap: - return tool_progression.ToolProgressionProgressive( - tools_distribution, - price_multiplier=tool_progression.PriceMultipliers.VERY_CHEAP, - ) - raise ValueError(f"No tool progression feature mapped to {str(tool_option.value)}") diff --git a/worlds/stardew_valley/content/feature/tool_progression.py b/worlds/stardew_valley/content/feature/tool_progression.py index 84d1cdd48c29..7fc11e8eb362 100644 --- a/worlds/stardew_valley/content/feature/tool_progression.py +++ b/worlds/stardew_valley/content/feature/tool_progression.py @@ -9,11 +9,6 @@ from ...strings.tool_names import Tool -class PriceMultipliers: - CHEAP = 2 / 5 - VERY_CHEAP = 1 / 5 - - def to_progressive_item(tool: str) -> str: """Return the name of the progressive item.""" return f"Progressive {tool}" @@ -58,7 +53,6 @@ def get_tools_distribution(progressive_tools_enabled: bool, skill_masteries_enab class ToolProgressionFeature(ABC): is_progressive: ClassVar[bool] tool_distribution: Mapping[str, int] - price_multiplier: float = 1.0 to_progressive_item = staticmethod(to_progressive_item) From d147517abe3dfc6fd5d6fa8dbe4b73bb7d59abe0 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Sat, 14 Dec 2024 15:26:54 -0500 Subject: [PATCH 10/17] damn it 3.11 why are you like this --- worlds/stardew_valley/content/feature/tool_progression.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/worlds/stardew_valley/content/feature/tool_progression.py b/worlds/stardew_valley/content/feature/tool_progression.py index 7fc11e8eb362..d5fe5cef99de 100644 --- a/worlds/stardew_valley/content/feature/tool_progression.py +++ b/worlds/stardew_valley/content/feature/tool_progression.py @@ -60,7 +60,8 @@ class ToolProgressionFeature(ABC): @dataclass(frozen=True) class ToolProgressionVanilla(ToolProgressionFeature): is_progressive = False - tool_distribution: Mapping[str, int] = field(default=VANILLA_TOOL_DISTRIBUTION) + # FIXME change the default_factory to a simple default when python 3.11 is no longer supported + tool_distribution: Mapping[str, int] = field(default_factory=lambda: VANILLA_TOOL_DISTRIBUTION) class ToolProgressionProgressive(ToolProgressionFeature): From 77ecb06fd285756debd01664c104471a41e949b4 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Fri, 20 Dec 2024 20:12:06 -0500 Subject: [PATCH 11/17] use blacksmith region when checking vanilla tools --- worlds/stardew_valley/logic/tool_logic.py | 7 ++++++- worlds/stardew_valley/rules.py | 14 +++++++------- worlds/stardew_valley/strings/entrance_names.py | 13 +++++++++---- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/worlds/stardew_valley/logic/tool_logic.py b/worlds/stardew_valley/logic/tool_logic.py index e3d2d8884ee1..bf6d49e0e4f7 100644 --- a/worlds/stardew_valley/logic/tool_logic.py +++ b/worlds/stardew_valley/logic/tool_logic.py @@ -10,6 +10,7 @@ from ..mods.logic.magic_logic import MagicLogicMixin from ..stardew_rule import StardewRule, True_, False_ from ..strings.ap_names.skill_level_names import ModSkillLevel +from ..strings.entrance_names import LogicEntrance from ..strings.region_names import Region from ..strings.spells import MagicSpell from ..strings.tool_names import ToolMaterial, Tool @@ -59,7 +60,7 @@ def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule if self.content.features.tool_progression.is_progressive: return self.logic.received(f"Progressive {tool}", tool_materials[material]) - can_upgrade_rule = self.logic.has(f"{material} Bar") & self.logic.money.can_spend_at(Region.blacksmith, tool_upgrade_prices[material]) + can_upgrade_rule = self.logic.tool._can_purchase_upgrade(material) if tool == Tool.pan: has_base_pan = self.logic.received("Glittering Boulder Removed") & self.logic.region.can_reach(Region.mountain) if material == ToolMaterial.copper: @@ -68,6 +69,10 @@ def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule return can_upgrade_rule + @cache_self1 + def _can_purchase_upgrade(self, material: str) -> StardewRule: + return self.logic.region.can_reach(LogicEntrance.blacksmith_upgrade(material)) + def can_use_tool_at(self, tool: str, material: str, region: str) -> StardewRule: return self.has_tool(tool, material) & self.logic.region.can_reach(region) diff --git a/worlds/stardew_valley/rules.py b/worlds/stardew_valley/rules.py index 18ff11d54dcb..01acc7b82225 100644 --- a/worlds/stardew_valley/rules.py +++ b/worlds/stardew_valley/rules.py @@ -280,13 +280,6 @@ def set_skull_cavern_floor_entrance_rules(logic, multiworld, player): set_entrance_rule(multiworld, player, dig_to_skull_floor(floor), rule) -def set_blacksmith_entrance_rules(logic, multiworld, player): - set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_copper, MetalBar.copper, ToolMaterial.copper) - set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_iron, MetalBar.iron, ToolMaterial.iron) - set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_gold, MetalBar.gold, ToolMaterial.gold) - set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_iridium, MetalBar.iridium, ToolMaterial.iridium) - - def set_skill_entrance_rules(logic, multiworld, player, world_options: StardewValleyOptions): set_entrance_rule(multiworld, player, LogicEntrance.grow_spring_crops, logic.farming.has_farming_tools & logic.season.has_spring) set_entrance_rule(multiworld, player, LogicEntrance.grow_summer_crops, logic.farming.has_farming_tools & logic.season.has_summer) @@ -305,6 +298,13 @@ def set_skill_entrance_rules(logic, multiworld, player, world_options: StardewVa set_entrance_rule(multiworld, player, LogicEntrance.fishing, logic.skill.can_get_fishing_xp) +def set_blacksmith_entrance_rules(logic, multiworld, player): + set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_copper, MetalBar.copper, ToolMaterial.copper) + set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_iron, MetalBar.iron, ToolMaterial.iron) + set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_gold, MetalBar.gold, ToolMaterial.gold) + set_blacksmith_upgrade_rule(logic, multiworld, player, LogicEntrance.blacksmith_iridium, MetalBar.iridium, ToolMaterial.iridium) + + def set_blacksmith_upgrade_rule(logic, multiworld, player, entrance_name: str, item_name: str, tool_material: str): upgrade_rule = logic.has(item_name) & logic.money.can_spend(tool_upgrade_prices[tool_material]) set_entrance_rule(multiworld, player, entrance_name, upgrade_rule) diff --git a/worlds/stardew_valley/strings/entrance_names.py b/worlds/stardew_valley/strings/entrance_names.py index b1c84004eb7a..bad46c42947d 100644 --- a/worlds/stardew_valley/strings/entrance_names.py +++ b/worlds/stardew_valley/strings/entrance_names.py @@ -194,10 +194,15 @@ class LogicEntrance: island_cooking = "Island Cooking" shipping = "Use Shipping Bin" watch_queen_of_sauce = "Watch Queen of Sauce" - blacksmith_copper = "Upgrade Copper Tools" - blacksmith_iron = "Upgrade Iron Tools" - blacksmith_gold = "Upgrade Gold Tools" - blacksmith_iridium = "Upgrade Iridium Tools" + + @staticmethod + def blacksmith_upgrade(material: str) -> str: + return f"Upgrade {material} Tools" + + blacksmith_copper = blacksmith_upgrade("Copper") + blacksmith_iron = blacksmith_upgrade("Iron") + blacksmith_gold = blacksmith_upgrade("Gold") + blacksmith_iridium = blacksmith_upgrade("Iridium") grow_spring_crops = "Grow Spring Crops" grow_summer_crops = "Grow Summer Crops" From 275afac2bf7755340e5d9b27c22fc99b312ed23c Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Fri, 20 Dec 2024 20:52:57 -0500 Subject: [PATCH 12/17] fix rule --- worlds/stardew_valley/logic/tool_logic.py | 5 ++- worlds/stardew_valley/strings/region_names.py | 13 ++++--- worlds/stardew_valley/test/__init__.py | 36 +++++++++++++++++++ .../test/performance/TestPerformance.py | 30 +++++++++++++++- 4 files changed, 76 insertions(+), 8 deletions(-) diff --git a/worlds/stardew_valley/logic/tool_logic.py b/worlds/stardew_valley/logic/tool_logic.py index bf6d49e0e4f7..9304d71bdb0c 100644 --- a/worlds/stardew_valley/logic/tool_logic.py +++ b/worlds/stardew_valley/logic/tool_logic.py @@ -10,8 +10,7 @@ from ..mods.logic.magic_logic import MagicLogicMixin from ..stardew_rule import StardewRule, True_, False_ from ..strings.ap_names.skill_level_names import ModSkillLevel -from ..strings.entrance_names import LogicEntrance -from ..strings.region_names import Region +from ..strings.region_names import Region, LogicRegion from ..strings.spells import MagicSpell from ..strings.tool_names import ToolMaterial, Tool @@ -71,7 +70,7 @@ def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule @cache_self1 def _can_purchase_upgrade(self, material: str) -> StardewRule: - return self.logic.region.can_reach(LogicEntrance.blacksmith_upgrade(material)) + return self.logic.region.can_reach(LogicRegion.blacksmith_upgrade(material)) def can_use_tool_at(self, tool: str, material: str, region: str) -> StardewRule: return self.has_tool(tool, material) & self.logic.region.can_reach(region) diff --git a/worlds/stardew_valley/strings/region_names.py b/worlds/stardew_valley/strings/region_names.py index 2bbc6228ab19..567f13158185 100644 --- a/worlds/stardew_valley/strings/region_names.py +++ b/worlds/stardew_valley/strings/region_names.py @@ -159,10 +159,15 @@ class LogicRegion: kitchen = "Kitchen" shipping = "Shipping" queen_of_sauce = "The Queen of Sauce" - blacksmith_copper = "Blacksmith Copper Upgrades" - blacksmith_iron = "Blacksmith Iron Upgrades" - blacksmith_gold = "Blacksmith Gold Upgrades" - blacksmith_iridium = "Blacksmith Iridium Upgrades" + + @staticmethod + def blacksmith_upgrade(material: str) -> str: + return f"Blacksmith {material} Upgrades" + + blacksmith_copper = blacksmith_upgrade("Copper") + blacksmith_iron = blacksmith_upgrade("Iron") + blacksmith_gold = blacksmith_upgrade("Gold") + blacksmith_iridium = blacksmith_upgrade("Iridium") spring_farming = "Spring Farming" summer_farming = "Summer Farming" diff --git a/worlds/stardew_valley/test/__init__.py b/worlds/stardew_valley/test/__init__.py index de0ed97882e3..7f550f796008 100644 --- a/worlds/stardew_valley/test/__init__.py +++ b/worlds/stardew_valley/test/__init__.py @@ -98,6 +98,42 @@ def allsanity_mods_6_x_x(): return allsanity +def default_6_x_x_with_vanilla_tools(): + return { + options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.default, + options.BackpackProgression.internal_name: options.BackpackProgression.default, + options.Booksanity.internal_name: options.Booksanity.default, + options.BuildingProgression.internal_name: options.BuildingProgression.default, + options.BundlePrice.internal_name: options.BundlePrice.default, + options.BundleRandomization.internal_name: options.BundleRandomization.default, + options.Chefsanity.internal_name: options.Chefsanity.default, + options.Cooksanity.internal_name: options.Cooksanity.default, + options.Craftsanity.internal_name: options.Craftsanity.default, + options.Cropsanity.internal_name: options.Cropsanity.default, + options.ElevatorProgression.internal_name: options.ElevatorProgression.default, + options.EntranceRandomization.internal_name: options.EntranceRandomization.default, + options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.default, + options.FestivalLocations.internal_name: options.FestivalLocations.default, + options.Fishsanity.internal_name: options.Fishsanity.default, + options.Friendsanity.internal_name: options.Friendsanity.default, + options.FriendsanityHeartSize.internal_name: options.FriendsanityHeartSize.default, + options.Goal.internal_name: options.Goal.default, + options.Mods.internal_name: options.Mods.default, + options.Monstersanity.internal_name: options.Monstersanity.default, + options.Museumsanity.internal_name: options.Museumsanity.default, + options.NumberOfMovementBuffs.internal_name: options.NumberOfMovementBuffs.default, + options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.default, + options.QuestLocations.internal_name: options.QuestLocations.default, + options.SeasonRandomization.internal_name: options.SeasonRandomization.default, + options.Shipsanity.internal_name: options.Shipsanity.default, + options.SkillProgression.internal_name: options.SkillProgression.default, + options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.default, + options.ToolProgression.internal_name: options.ToolProgression.option_vanilla, + options.TrapItems.internal_name: options.TrapItems.default, + options.Walnutsanity.internal_name: options.Walnutsanity.default + } + + def get_minsanity_options(): return { options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled, diff --git a/worlds/stardew_valley/test/performance/TestPerformance.py b/worlds/stardew_valley/test/performance/TestPerformance.py index b5ad0cae66c6..805001ebd276 100644 --- a/worlds/stardew_valley/test/performance/TestPerformance.py +++ b/worlds/stardew_valley/test/performance/TestPerformance.py @@ -8,7 +8,8 @@ from BaseClasses import get_seed from Fill import distribute_items_restrictive, balance_multiworld_progression from worlds import AutoWorld -from .. import SVTestCase, minimal_locations_maximal_items, setup_multiworld, default_6_x_x, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x +from .. import SVTestCase, minimal_locations_maximal_items, setup_multiworld, default_6_x_x, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, \ + default_6_x_x_with_vanilla_tools assert default_6_x_x assert allsanity_no_mods_6_x_x @@ -160,6 +161,33 @@ def test_10_player(self): self.performance_test_multiworld(multiworld_options) +class TestDefaultOptionsVanillaTools(SVPerformanceTestCase): + acceptable_time_per_player = 2 + options = default_6_x_x_with_vanilla_tools() + results = [] + + def test_solo(self): + number_players = 1 + multiworld_options = [self.options] * number_players + self.performance_test_multiworld(multiworld_options) + + def test_duo(self): + number_players = 2 + multiworld_options = [self.options] * number_players + self.performance_test_multiworld(multiworld_options) + + def test_5_player(self): + number_players = 5 + multiworld_options = [self.options] * number_players + self.performance_test_multiworld(multiworld_options) + + @unittest.skip + def test_10_player(self): + number_players = 10 + multiworld_options = [self.options] * number_players + self.performance_test_multiworld(multiworld_options) + + class TestMinLocationMaxItems(SVPerformanceTestCase): acceptable_time_per_player = 0.3 options = minimal_locations_maximal_items() From f53bac0a48d89941d024567e9dce685e369857ec Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Fri, 20 Dec 2024 22:18:53 -0500 Subject: [PATCH 13/17] move can mine using in tool logic --- worlds/stardew_valley/logic/mine_logic.py | 14 +++++++------- worlds/stardew_valley/logic/tool_logic.py | 14 ++++++++++++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/worlds/stardew_valley/logic/mine_logic.py b/worlds/stardew_valley/logic/mine_logic.py index 26a15c8e15d8..e332241c1016 100644 --- a/worlds/stardew_valley/logic/mine_logic.py +++ b/worlds/stardew_valley/logic/mine_logic.py @@ -14,7 +14,7 @@ from ..strings.performance_names import Performance from ..strings.region_names import Region from ..strings.skill_names import Skill -from ..strings.tool_names import Tool, ToolMaterial +from ..strings.tool_names import ToolMaterial class MineLogicMixin(BaseLogicMixin): @@ -55,12 +55,12 @@ def get_weapon_rule_for_floor_tier(self, tier: int): def can_progress_in_the_mines_from_floor(self, floor: int) -> StardewRule: tier = floor // 40 rules = [] + weapon_rule = self.logic.mine.get_weapon_rule_for_floor_tier(tier) rules.append(weapon_rule) - # No alternative for vanilla because we assume you will get ores to upgrade your tools in the mines. - if self.content.features.tool_progression.is_progressive: - rules.append(self.logic.tool.has_tool(Tool.pickaxe, ToolMaterial.tiers[tier])) + tool_rule = self.logic.tool.can_mine_using(ToolMaterial.tiers[tier]) + rules.append(tool_rule) # No alternative for vanilla because we assume that you will grind the levels in the mines. if self.content.features.skill_progression.is_progressive: @@ -85,12 +85,12 @@ def has_mine_elevator_to_floor(self, floor: int) -> StardewRule: def can_progress_in_the_skull_cavern_from_floor(self, floor: int) -> StardewRule: tier = floor // 50 rules = [] + weapon_rule = self.logic.combat.has_great_weapon rules.append(weapon_rule) - # No alternative for vanilla because we assume you will get ores to upgrade your tools in the mines. - if self.content.features.tool_progression.is_progressive: - rules.append(self.logic.received("Progressive Pickaxe", min(4, max(0, tier + 2)))) + tool_rule = self.logic.tool.can_mine_using(ToolMaterial.tiers[min(4, max(0, tier + 2))]) + rules.append(tool_rule) # No alternative for vanilla because we assume that you will grind the levels in the mines. if self.content.features.skill_progression.is_progressive: diff --git a/worlds/stardew_valley/logic/tool_logic.py b/worlds/stardew_valley/logic/tool_logic.py index 9304d71bdb0c..8292325af7d8 100644 --- a/worlds/stardew_valley/logic/tool_logic.py +++ b/worlds/stardew_valley/logic/tool_logic.py @@ -12,7 +12,7 @@ from ..strings.ap_names.skill_level_names import ModSkillLevel from ..strings.region_names import Region, LogicRegion from ..strings.spells import MagicSpell -from ..strings.tool_names import ToolMaterial, Tool +from ..strings.tool_names import ToolMaterial, Tool, APTool fishing_rod_prices = { 3: 1800, @@ -68,6 +68,16 @@ def has_tool(self, tool: str, material: str = ToolMaterial.basic) -> StardewRule return can_upgrade_rule + @cache_self1 + def can_mine_using(self, material: str) -> StardewRule: + if material == ToolMaterial.basic: + return self.logic.true_ + + if self.content.features.tool_progression.is_progressive: + return self.logic.received(APTool.pickaxe, tool_materials[material]) + else: + return self.logic.tool._can_purchase_upgrade(material) + @cache_self1 def _can_purchase_upgrade(self, material: str) -> StardewRule: return self.logic.region.can_reach(LogicRegion.blacksmith_upgrade(material)) @@ -80,7 +90,7 @@ def has_fishing_rod(self, level: int) -> StardewRule: assert 1 <= level <= 4, "Fishing rod 0 isn't real, it can't hurt you. Training is 1, Bamboo is 2, Fiberglass is 3 and Iridium is 4." if self.content.features.tool_progression.is_progressive: - return self.logic.received(f"Progressive {Tool.fishing_rod}", level) + return self.logic.received(APTool.fishing_rod, level) if level <= 2: # We assume you always have access to the Bamboo pole, because mod side there is a builtin way to get it back. From 4ed691cdbdd616ebd5bd759a52813959df97ffe8 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Wed, 8 Jan 2025 22:06:29 -0500 Subject: [PATCH 14/17] remove changes to performance test --- worlds/stardew_valley/content/__init__.py | 8 ++--- worlds/stardew_valley/options/options.py | 8 +++++ worlds/stardew_valley/test/__init__.py | 36 ------------------- .../test/performance/TestPerformance.py | 30 +--------------- 4 files changed, 11 insertions(+), 71 deletions(-) diff --git a/worlds/stardew_valley/content/__init__.py b/worlds/stardew_valley/content/__init__.py index 8c57d71db047..530850643749 100644 --- a/worlds/stardew_valley/content/__init__.py +++ b/worlds/stardew_valley/content/__init__.py @@ -126,9 +126,7 @@ def choose_skill_progression(skill_progression_option: options.SkillProgression) def choose_tool_progression(tool_option: options.ToolProgression, skill_option: options.SkillProgression) -> tool_progression.ToolProgressionFeature: - if (tool_option == options.ToolProgression.option_vanilla - or tool_option == options.ToolProgression.option_vanilla_cheap - or tool_option == options.ToolProgression.option_vanilla_very_cheap): + if tool_option.is_vanilla: return tool_progression.ToolProgressionVanilla() tools_distribution = tool_progression.get_tools_distribution( @@ -136,9 +134,7 @@ def choose_tool_progression(tool_option: options.ToolProgression, skill_option: skill_masteries_enabled=skill_option == options.SkillProgression.option_progressive_with_masteries, ) - if (tool_option == options.ToolProgression.option_progressive - or tool_option == options.ToolProgression.option_progressive_cheap - or tool_option == options.ToolProgression.option_progressive_very_cheap): + if tool_option.is_progressive: return tool_progression.ToolProgressionProgressive(tools_distribution) raise ValueError(f"No tool progression feature mapped to {str(tool_option.value)}") diff --git a/worlds/stardew_valley/options/options.py b/worlds/stardew_valley/options/options.py index 5d3b25b4da13..c01f048b44c2 100644 --- a/worlds/stardew_valley/options/options.py +++ b/worlds/stardew_valley/options/options.py @@ -246,6 +246,14 @@ class ToolProgression(Choice): option_progressive_cheap = 0b011 # 3 option_progressive_very_cheap = 0b101 # 5 + @property + def is_vanilla(self): + return not self.is_progressive + + @property + def is_progressive(self): + return bool(self.value & 0b001) + class ElevatorProgression(Choice): """Shuffle the elevator? diff --git a/worlds/stardew_valley/test/__init__.py b/worlds/stardew_valley/test/__init__.py index 7f550f796008..de0ed97882e3 100644 --- a/worlds/stardew_valley/test/__init__.py +++ b/worlds/stardew_valley/test/__init__.py @@ -98,42 +98,6 @@ def allsanity_mods_6_x_x(): return allsanity -def default_6_x_x_with_vanilla_tools(): - return { - options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.default, - options.BackpackProgression.internal_name: options.BackpackProgression.default, - options.Booksanity.internal_name: options.Booksanity.default, - options.BuildingProgression.internal_name: options.BuildingProgression.default, - options.BundlePrice.internal_name: options.BundlePrice.default, - options.BundleRandomization.internal_name: options.BundleRandomization.default, - options.Chefsanity.internal_name: options.Chefsanity.default, - options.Cooksanity.internal_name: options.Cooksanity.default, - options.Craftsanity.internal_name: options.Craftsanity.default, - options.Cropsanity.internal_name: options.Cropsanity.default, - options.ElevatorProgression.internal_name: options.ElevatorProgression.default, - options.EntranceRandomization.internal_name: options.EntranceRandomization.default, - options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.default, - options.FestivalLocations.internal_name: options.FestivalLocations.default, - options.Fishsanity.internal_name: options.Fishsanity.default, - options.Friendsanity.internal_name: options.Friendsanity.default, - options.FriendsanityHeartSize.internal_name: options.FriendsanityHeartSize.default, - options.Goal.internal_name: options.Goal.default, - options.Mods.internal_name: options.Mods.default, - options.Monstersanity.internal_name: options.Monstersanity.default, - options.Museumsanity.internal_name: options.Museumsanity.default, - options.NumberOfMovementBuffs.internal_name: options.NumberOfMovementBuffs.default, - options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.default, - options.QuestLocations.internal_name: options.QuestLocations.default, - options.SeasonRandomization.internal_name: options.SeasonRandomization.default, - options.Shipsanity.internal_name: options.Shipsanity.default, - options.SkillProgression.internal_name: options.SkillProgression.default, - options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.default, - options.ToolProgression.internal_name: options.ToolProgression.option_vanilla, - options.TrapItems.internal_name: options.TrapItems.default, - options.Walnutsanity.internal_name: options.Walnutsanity.default - } - - def get_minsanity_options(): return { options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled, diff --git a/worlds/stardew_valley/test/performance/TestPerformance.py b/worlds/stardew_valley/test/performance/TestPerformance.py index 805001ebd276..b5ad0cae66c6 100644 --- a/worlds/stardew_valley/test/performance/TestPerformance.py +++ b/worlds/stardew_valley/test/performance/TestPerformance.py @@ -8,8 +8,7 @@ from BaseClasses import get_seed from Fill import distribute_items_restrictive, balance_multiworld_progression from worlds import AutoWorld -from .. import SVTestCase, minimal_locations_maximal_items, setup_multiworld, default_6_x_x, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, \ - default_6_x_x_with_vanilla_tools +from .. import SVTestCase, minimal_locations_maximal_items, setup_multiworld, default_6_x_x, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x assert default_6_x_x assert allsanity_no_mods_6_x_x @@ -161,33 +160,6 @@ def test_10_player(self): self.performance_test_multiworld(multiworld_options) -class TestDefaultOptionsVanillaTools(SVPerformanceTestCase): - acceptable_time_per_player = 2 - options = default_6_x_x_with_vanilla_tools() - results = [] - - def test_solo(self): - number_players = 1 - multiworld_options = [self.options] * number_players - self.performance_test_multiworld(multiworld_options) - - def test_duo(self): - number_players = 2 - multiworld_options = [self.options] * number_players - self.performance_test_multiworld(multiworld_options) - - def test_5_player(self): - number_players = 5 - multiworld_options = [self.options] * number_players - self.performance_test_multiworld(multiworld_options) - - @unittest.skip - def test_10_player(self): - number_players = 10 - multiworld_options = [self.options] * number_players - self.performance_test_multiworld(multiworld_options) - - class TestMinLocationMaxItems(SVPerformanceTestCase): acceptable_time_per_player = 0.3 options = minimal_locations_maximal_items() From 5d3172988161d4cf770152401c6eda91df60327f Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Wed, 8 Jan 2025 23:57:05 -0500 Subject: [PATCH 15/17] properly set the option I guess --- worlds/stardew_valley/test/TestWalnutsanity.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/worlds/stardew_valley/test/TestWalnutsanity.py b/worlds/stardew_valley/test/TestWalnutsanity.py index c1e8c2c8f095..cdc24d1199f6 100644 --- a/worlds/stardew_valley/test/TestWalnutsanity.py +++ b/worlds/stardew_valley/test/TestWalnutsanity.py @@ -1,5 +1,5 @@ from . import SVTestBase -from ..options import ExcludeGingerIsland, Walnutsanity +from ..options import ExcludeGingerIsland, Walnutsanity, ToolProgression from ..strings.ap_names.ap_option_names import WalnutsanityOptionName @@ -7,6 +7,7 @@ class TestWalnutsanityNone(SVTestBase): options = { ExcludeGingerIsland: ExcludeGingerIsland.option_false, Walnutsanity: Walnutsanity.preset_none, + ToolProgression: ToolProgression.option_progressive, } def test_no_walnut_locations(self): From f7b49aad2b870662587df9c56d5db57ac480e1fa Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Wed, 8 Jan 2025 23:58:46 -0500 Subject: [PATCH 16/17] properly set options 2 --- worlds/stardew_valley/test/TestWalnutsanity.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/worlds/stardew_valley/test/TestWalnutsanity.py b/worlds/stardew_valley/test/TestWalnutsanity.py index cdc24d1199f6..70eb5aa1877a 100644 --- a/worlds/stardew_valley/test/TestWalnutsanity.py +++ b/worlds/stardew_valley/test/TestWalnutsanity.py @@ -1,5 +1,5 @@ from . import SVTestBase -from ..options import ExcludeGingerIsland, Walnutsanity, ToolProgression +from ..options import ExcludeGingerIsland, Walnutsanity, ToolProgression, SkillProgression from ..strings.ap_names.ap_option_names import WalnutsanityOptionName @@ -7,6 +7,7 @@ class TestWalnutsanityNone(SVTestBase): options = { ExcludeGingerIsland: ExcludeGingerIsland.option_false, Walnutsanity: Walnutsanity.preset_none, + SkillProgression: ToolProgression.option_progressive, ToolProgression: ToolProgression.option_progressive, } @@ -26,7 +27,7 @@ def test_logic_received_walnuts(self): self.collect("Island Obelisk") self.collect("Island West Turtle") self.collect("Progressive House") - items = self.collect("5 Golden Walnuts", 10) + self.collect("5 Golden Walnuts", 10) self.assertFalse(self.multiworld.state.can_reach_location("Parrot Express", self.player)) self.collect("Island North Turtle") @@ -51,6 +52,8 @@ class TestWalnutsanityPuzzles(SVTestBase): options = { ExcludeGingerIsland: ExcludeGingerIsland.option_false, Walnutsanity: frozenset({WalnutsanityOptionName.puzzles}), + SkillProgression: ToolProgression.option_progressive, + ToolProgression: ToolProgression.option_progressive, } def test_only_puzzle_walnut_locations(self): @@ -204,7 +207,7 @@ def test_logic_received_walnuts(self): self.assertTrue(self.multiworld.state.can_reach_location("Parrot Express", self.player)) self.remove(items) self.assertFalse(self.multiworld.state.can_reach_location("Parrot Express", self.player)) - items = self.collect("5 Golden Walnuts", 4) - items = self.collect("3 Golden Walnuts", 6) - items = self.collect("Golden Walnut", 2) + self.collect("5 Golden Walnuts", 4) + self.collect("3 Golden Walnuts", 6) + self.collect("Golden Walnut", 2) self.assertTrue(self.multiworld.state.can_reach_location("Parrot Express", self.player)) From 47566bb45f108947148be4acab80bd82dff4c743 Mon Sep 17 00:00:00 2001 From: Jouramie <16137441+Jouramie@users.noreply.github.com> Date: Wed, 22 Jan 2025 23:10:02 -0500 Subject: [PATCH 17/17] that's what happen when you code too late --- worlds/stardew_valley/options/options.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/worlds/stardew_valley/options/options.py b/worlds/stardew_valley/options/options.py index 33f1e25bef68..d9847773a775 100644 --- a/worlds/stardew_valley/options/options.py +++ b/worlds/stardew_valley/options/options.py @@ -249,10 +249,10 @@ class ToolProgression(Choice): @property def is_vanilla(self): return not self.is_progressive - + @property def is_progressive(self): - return bool(self.value & 0b001) + return bool(self.value & self.option_progressive) class ElevatorProgression(Choice):