Stardew Valley: Generate proper filler for item links (#2069)

Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
agilbert1412 2023-11-22 11:04:33 -05:00 committed by GitHub
parent cfd2e9c47f
commit 0f98cf525f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 150 additions and 14 deletions

View File

@ -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
@ -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()
@ -270,7 +271,33 @@ class StardewValleyWorld(World):
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]:

View File

@ -303,8 +303,7 @@ artisan_goods_items = [truffle_oil, cloth, goat_cheese, cheese, honey, beer, jui
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]

View File

@ -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]
@ -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)
@ -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

View File

@ -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)