diff --git a/worlds/stardew_valley/__init__.py b/worlds/stardew_valley/__init__.py index e2d49e64..fe690308 100644 --- a/worlds/stardew_valley/__init__.py +++ b/worlds/stardew_valley/__init__.py @@ -1,4 +1,5 @@ import logging +import typing from random import Random from typing import Dict, Any, Iterable, Optional, List, TextIO, cast @@ -9,14 +10,15 @@ from .bundles.bundle_room import BundleRoom from .bundles.bundles import get_all_bundles 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.logic import StardewLogic from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, EnabledFillerBuffs, NumberOfMovementBuffs, \ - BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization, FarmType + 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 @@ -89,6 +91,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 = [] @@ -299,32 +311,9 @@ class StardewValleyWorld(World): 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 056a4f6e..1fbe012e 100644 --- a/worlds/stardew_valley/items.py +++ b/worlds/stardew_valley/items.py @@ -808,6 +808,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 00000000..bbf508c6 --- /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 2b2efbf2..3ba40d5a 100644 --- a/worlds/stardew_valley/test/assertion/goal_assert.py +++ b/worlds/stardew_valley/test/assertion/goal_assert.py @@ -2,7 +2,7 @@ from unittest import TestCase 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 a07831f7..ed7842bf 100644 --- a/worlds/stardew_valley/test/assertion/option_assert.py +++ b/worlds/stardew_valley/test/assertion/option_assert.py @@ -2,7 +2,7 @@ from unittest import TestCase 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 @@ class OptionAssertMixin(TestCase): 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: