diff --git a/worlds/stardew_valley/__init__.py b/worlds/stardew_valley/__init__.py index a7e19f01439f..66635add619a 100644 --- a/worlds/stardew_valley/__init__.py +++ b/worlds/stardew_valley/__init__.py @@ -1,34 +1,31 @@ import logging +import typing from random import Random -from typing import Dict, Any, Iterable, Optional, List, TextIO, cast +from typing import Dict, Any, Iterable, Optional, List, TextIO from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState from Options import PerGameCommonOptions from worlds.AutoWorld import World, WebWorld from worlds.LauncherComponents import launch_subprocess, components, Component, icon_paths, Type -from . import rules from .bundles.bundle_room import BundleRoom from .bundles.bundles import get_all_bundles -from .content import content_packs, StardewContent, unpack_content, create_content +from .content import StardewContent, create_content from .early_items import setup_early_items -from .items import item_table, create_items, ItemData, Group, items_by_group, get_all_filler_items, remove_limited_amount_packs +from .items import item_table, create_items, ItemData, Group, items_by_group, generate_filler_choice_pool from .locations import location_table, create_locations, LocationData, locations_by_tag -from .logic.bundle_logic import BundleLogic from .logic.logic import StardewLogic -from .logic.time_logic import MAX_MONTHS from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, EnabledFillerBuffs, NumberOfMovementBuffs, \ - BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization, FarmType, Walnutsanity + BuildingProgression, EntranceRandomization, FarmType from .options.forced_options import force_change_options_if_incompatible from .options.option_groups import sv_option_groups from .options.presets import sv_options_presets +from .options.worlds_group import apply_most_restrictive_options from .regions import create_regions from .rules import set_rules from .stardew_rule import True_, StardewRule, HasProgressionPercent, true_ from .strings.ap_names.event_names import Event -from .strings.entrance_names import Entrance as EntranceName from .strings.goal_names import Goal as GoalName -from .strings.metal_names import Ore -from .strings.region_names import Region as RegionName, LogicRegion +from .strings.region_names import LogicRegion logger = logging.getLogger(__name__) @@ -118,6 +115,16 @@ class StardewValleyWorld(World): total_progression_items: int + @classmethod + def create_group(cls, multiworld: MultiWorld, new_player_id: int, players: set[int]) -> World: + world_group = super().create_group(multiworld, new_player_id, players) + + group_options = typing.cast(StardewValleyOptions, world_group.options) + worlds_options = [typing.cast(StardewValleyOptions, multiworld.worlds[player].options) for player in players] + apply_most_restrictive_options(group_options, worlds_options) + + return world_group + def __init__(self, multiworld: MultiWorld, player: int): super().__init__(multiworld, player) self.filler_item_pool_names = [] @@ -335,32 +342,9 @@ def generate_basic(self): def get_filler_item_name(self) -> str: if not self.filler_item_pool_names: - self.generate_filler_item_pool_names() + self.filler_item_pool_names = generate_filler_choice_pool(self.options) return self.random.choice(self.filler_item_pool_names) - def generate_filler_item_pool_names(self): - include_traps, exclude_island = self.get_filler_item_rules() - available_filler = get_all_filler_items(include_traps, exclude_island) - available_filler = remove_limited_amount_packs(available_filler) - self.filler_item_pool_names = [item.name for item in available_filler] - - def get_filler_item_rules(self): - if self.player in self.multiworld.groups: - link_group = self.multiworld.groups[self.player] - include_traps = True - exclude_island = False - for player in link_group["players"]: - if self.multiworld.game[player] != self.game: - continue - player_options = cast(StardewValleyOptions, self.multiworld.worlds[player].options) - if player_options.trap_items == TrapItems.option_no_traps: - include_traps = False - if player_options.exclude_ginger_island == ExcludeGingerIsland.option_true: - exclude_island = True - return include_traps, exclude_island - else: - return self.options.trap_items != TrapItems.option_no_traps, self.options.exclude_ginger_island == ExcludeGingerIsland.option_true - def write_spoiler_header(self, spoiler_handle: TextIO) -> None: """Write to the spoiler header. If individual it's right at the end of that player's options, if as stage it's right under the common header before per-player options.""" diff --git a/worlds/stardew_valley/items.py b/worlds/stardew_valley/items.py index a92206d87b14..31fa24f150de 100644 --- a/worlds/stardew_valley/items.py +++ b/worlds/stardew_valley/items.py @@ -792,6 +792,16 @@ def remove_excluded_items_island_mods(items, exclude_ginger_island: bool, mods: return mod_filter +def generate_filler_choice_pool(options: StardewValleyOptions) -> list[str]: + include_traps = options.trap_items != TrapItems.option_no_traps + exclude_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true + + available_filler = get_all_filler_items(include_traps, exclude_island) + available_filler = remove_limited_amount_packs(available_filler) + + return [item.name for item in available_filler] + + def remove_limited_amount_packs(packs): return [pack for pack in packs if Group.MAXIMUM_ONE not in pack.groups and Group.EXACTLY_TWO not in pack.groups] diff --git a/worlds/stardew_valley/options/worlds_group.py b/worlds/stardew_valley/options/worlds_group.py new file mode 100644 index 000000000000..bbf508c61c04 --- /dev/null +++ b/worlds/stardew_valley/options/worlds_group.py @@ -0,0 +1,14 @@ +from typing import Iterable + +from .options import StardewValleyOptions + + +def apply_most_restrictive_options(group_option: StardewValleyOptions, world_options: Iterable[StardewValleyOptions]) -> None: + """Merge the options of the worlds member of the group that can impact fillers generation into the option class of the group. + """ + + # If at least one world disabled ginger island, disabling it for the whole group + group_option.exclude_ginger_island.value = max(o.exclude_ginger_island.value for o in world_options) + + # If at least one world disabled traps, disabling them for the whole group + group_option.trap_items.value = min(o.trap_items.value for o in world_options) diff --git a/worlds/stardew_valley/test/assertion/goal_assert.py b/worlds/stardew_valley/test/assertion/goal_assert.py index 2b2efbf2ec03..3ba40d5a84b8 100644 --- a/worlds/stardew_valley/test/assertion/goal_assert.py +++ b/worlds/stardew_valley/test/assertion/goal_assert.py @@ -2,7 +2,7 @@ from BaseClasses import MultiWorld from .option_assert import get_stardew_options -from ... import options, ExcludeGingerIsland +from ... import options def is_goal(multiworld: MultiWorld, goal: int) -> bool: @@ -36,7 +36,7 @@ def is_not_perfection(multiworld: MultiWorld) -> bool: class GoalAssertMixin(TestCase): def assert_ginger_island_is_included(self, multiworld: MultiWorld): - self.assertEqual(get_stardew_options(multiworld).exclude_ginger_island, ExcludeGingerIsland.option_false) + self.assertEqual(get_stardew_options(multiworld).exclude_ginger_island, options.ExcludeGingerIsland.option_false) def assert_walnut_hunter_world_is_valid(self, multiworld: MultiWorld): if is_not_walnut_hunter(multiworld): diff --git a/worlds/stardew_valley/test/assertion/option_assert.py b/worlds/stardew_valley/test/assertion/option_assert.py index a07831f73e3f..ed7842bf8d98 100644 --- a/worlds/stardew_valley/test/assertion/option_assert.py +++ b/worlds/stardew_valley/test/assertion/option_assert.py @@ -2,7 +2,7 @@ from BaseClasses import MultiWorld from .world_assert import get_all_item_names, get_all_location_names -from ... import StardewValleyWorld, options, item_table, Group, location_table, ExcludeGingerIsland +from ... import StardewValleyWorld, options, item_table, Group, location_table from ...locations import LocationTags from ...strings.ap_names.transport_names import Transportation @@ -49,7 +49,7 @@ def assert_cannot_reach_island(self, multiworld: MultiWorld): def assert_can_reach_island_if_should(self, multiworld: MultiWorld): stardew_options = get_stardew_options(multiworld) - include_island = stardew_options.exclude_ginger_island.value == ExcludeGingerIsland.option_false + include_island = stardew_options.exclude_ginger_island.value == options.ExcludeGingerIsland.option_false if include_island: self.assert_can_reach_island(multiworld) else: