Skip to content

Commit

Permalink
Stardew Valley: Generate proper filler for item links (#2069)
Browse files Browse the repository at this point in the history
Co-authored-by: Zach Parks <[email protected]>
  • Loading branch information
agilbert1412 and ThePhar authored Nov 22, 2023
1 parent cfd2e9c commit 0f98cf5
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 14 deletions.
35 changes: 31 additions & 4 deletions worlds/stardew_valley/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import logging
from typing import Dict, Any, Iterable, Optional, Union, Set, List

from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState, ItemClassification, MultiWorld
from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState, ItemClassification, MultiWorld, Group as ItemLinkGroup
from Options import PerGameCommonOptions
from worlds.AutoWorld import World, WebWorld
from . import rules
from .bundles import get_all_bundles, Bundle
from .items import item_table, create_items, ItemData, Group, items_by_group
from .items import item_table, create_items, ItemData, Group, items_by_group, get_all_filler_items, remove_limited_amount_packs
from .locations import location_table, create_locations, LocationData
from .logic import StardewLogic, StardewRule, True_, MAX_MONTHS
from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, BundlePrice, NumberOfLuckBuffs, NumberOfMovementBuffs, \
BackpackProgression, BuildingProgression, ExcludeGingerIsland
BackpackProgression, BuildingProgression, ExcludeGingerIsland, TrapItems
from .presets import sv_options_presets
from .regions import create_regions
from .rules import set_rules
Expand Down Expand Up @@ -74,6 +74,7 @@ class StardewValleyWorld(World):
def __init__(self, world: MultiWorld, player: int):
super().__init__(world, player)
self.all_progression_items = set()
self.filler_item_pool_names = []

def generate_early(self):
self.force_change_options_if_incompatible()
Expand Down Expand Up @@ -270,7 +271,33 @@ def generate_basic(self):
pass

def get_filler_item_name(self) -> str:
return "Joja Cola"
if not self.filler_item_pool_names:
self.generate_filler_item_pool_names()
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: ItemLinkGroup = self.multiworld.groups[self.player]
include_traps = True
exclude_island = False
for player in link_group["players"]:
player_options = self.multiworld.worlds[player].options
if self.multiworld.game[player] != self.game:

continue
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 fill_slot_data(self) -> Dict[str, Any]:

Expand Down
3 changes: 1 addition & 2 deletions worlds/stardew_valley/data/bundle_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,8 +303,7 @@ def __lt__(self, other):

river_fish_items = [chub, catfish, rainbow_trout, lingcod, walleye, perch, pike, bream,
salmon, sunfish, tiger_trout, shad, smallmouth_bass, dorado]
lake_fish_items = [chub, rainbow_trout, lingcod, walleye, perch, carp, midnight_carp,
largemouth_bass, sturgeon, bullhead, midnight_carp]
lake_fish_items = [chub, rainbow_trout, lingcod, walleye, perch, carp, midnight_carp, largemouth_bass, sturgeon, bullhead]
ocean_fish_items = [tilapia, pufferfish, tuna, super_cucumber, flounder, anchovy, sardine, red_mullet,
herring, eel, octopus, red_snapper, squid, sea_cucumber, albacore, halibut]
night_fish_items = [walleye, bream, super_cucumber, eel, squid, midnight_carp]
Expand Down
26 changes: 18 additions & 8 deletions worlds/stardew_valley/items.py
Original file line number Diff line number Diff line change
Expand Up @@ -468,10 +468,6 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options
items_already_added: List[Item],
number_locations: int) -> List[Item]:
include_traps = options.trap_items != TrapItems.option_no_traps
all_filler_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK]]
all_filler_packs.extend(items_by_group[Group.TRASH])
if include_traps:
all_filler_packs.extend(items_by_group[Group.TRAP])
items_already_added_names = [item.name for item in items_already_added]
useful_resource_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK_USEFUL]
if pack.name not in items_already_added_names]
Expand All @@ -484,8 +480,9 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options
if include_traps:
priority_filler_items.extend(trap_items)

all_filler_packs = remove_excluded_packs(all_filler_packs, options)
priority_filler_items = remove_excluded_packs(priority_filler_items, options)
exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
all_filler_packs = get_all_filler_items(include_traps, exclude_ginger_island)
priority_filler_items = remove_excluded_packs(priority_filler_items, exclude_ginger_island)

number_priority_items = len(priority_filler_items)
required_resource_pack = number_locations - len(items_already_added)
Expand Down Expand Up @@ -519,8 +516,21 @@ def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options
return items


def remove_excluded_packs(packs, options: StardewValleyOptions):
def remove_excluded_packs(packs, exclude_ginger_island: bool):
included_packs = [pack for pack in packs if Group.DEPRECATED not in pack.groups]
if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
if exclude_ginger_island:
included_packs = [pack for pack in included_packs if Group.GINGER_ISLAND not in pack.groups]
return included_packs


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]


def get_all_filler_items(include_traps: bool, exclude_ginger_island: bool):
all_filler_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK]]
all_filler_packs.extend(items_by_group[Group.TRASH])
if include_traps:
all_filler_packs.extend(items_by_group[Group.TRAP])
all_filler_packs = remove_excluded_packs(all_filler_packs, exclude_ginger_island)
return all_filler_packs
100 changes: 100 additions & 0 deletions worlds/stardew_valley/test/TestItemLink.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
from . import SVTestBase
from .. import options, item_table, Group

max_iterations = 2000


class TestItemLinksEverythingIncluded(SVTestBase):
options = {options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
options.TrapItems.internal_name: options.TrapItems.option_medium}

def test_filler_of_all_types_generated(self):
max_number_filler = 115
filler_generated = []
at_least_one_trap = False
at_least_one_island = False
for i in range(0, max_iterations):
filler = self.multiworld.worlds[1].get_filler_item_name()
if filler in filler_generated:
continue
filler_generated.append(filler)
self.assertNotIn(Group.MAXIMUM_ONE, item_table[filler].groups)
self.assertNotIn(Group.EXACTLY_TWO, item_table[filler].groups)
if Group.TRAP in item_table[filler].groups:
at_least_one_trap = True
if Group.GINGER_ISLAND in item_table[filler].groups:
at_least_one_island = True
if len(filler_generated) >= max_number_filler:
break
self.assertTrue(at_least_one_trap)
self.assertTrue(at_least_one_island)
self.assertGreaterEqual(len(filler_generated), max_number_filler)


class TestItemLinksNoIsland(SVTestBase):
options = {options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
options.TrapItems.internal_name: options.TrapItems.option_medium}

def test_filler_has_no_island_but_has_traps(self):
max_number_filler = 109
filler_generated = []
at_least_one_trap = False
for i in range(0, max_iterations):
filler = self.multiworld.worlds[1].get_filler_item_name()
if filler in filler_generated:
continue
filler_generated.append(filler)
self.assertNotIn(Group.GINGER_ISLAND, item_table[filler].groups)
self.assertNotIn(Group.MAXIMUM_ONE, item_table[filler].groups)
self.assertNotIn(Group.EXACTLY_TWO, item_table[filler].groups)
if Group.TRAP in item_table[filler].groups:
at_least_one_trap = True
if len(filler_generated) >= max_number_filler:
break
self.assertTrue(at_least_one_trap)
self.assertGreaterEqual(len(filler_generated), max_number_filler)


class TestItemLinksNoTraps(SVTestBase):
options = {options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
options.TrapItems.internal_name: options.TrapItems.option_no_traps}

def test_filler_has_no_traps_but_has_island(self):
max_number_filler = 100
filler_generated = []
at_least_one_island = False
for i in range(0, max_iterations):
filler = self.multiworld.worlds[1].get_filler_item_name()
if filler in filler_generated:
continue
filler_generated.append(filler)
self.assertNotIn(Group.TRAP, item_table[filler].groups)
self.assertNotIn(Group.MAXIMUM_ONE, item_table[filler].groups)
self.assertNotIn(Group.EXACTLY_TWO, item_table[filler].groups)
if Group.GINGER_ISLAND in item_table[filler].groups:
at_least_one_island = True
if len(filler_generated) >= max_number_filler:
break
self.assertTrue(at_least_one_island)
self.assertGreaterEqual(len(filler_generated), max_number_filler)


class TestItemLinksNoTrapsAndIsland(SVTestBase):
options = {options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
options.TrapItems.internal_name: options.TrapItems.option_no_traps}

def test_filler_generated_without_island_or_traps(self):
max_number_filler = 94
filler_generated = []
for i in range(0, max_iterations):
filler = self.multiworld.worlds[1].get_filler_item_name()
if filler in filler_generated:
continue
filler_generated.append(filler)
self.assertNotIn(Group.GINGER_ISLAND, item_table[filler].groups)
self.assertNotIn(Group.TRAP, item_table[filler].groups)
self.assertNotIn(Group.MAXIMUM_ONE, item_table[filler].groups)
self.assertNotIn(Group.EXACTLY_TWO, item_table[filler].groups)
if len(filler_generated) >= max_number_filler:
break
self.assertGreaterEqual(len(filler_generated), max_number_filler)

0 comments on commit 0f98cf5

Please sign in to comment.