Stardew Valley: Generate proper filler for item links (#2069)
Co-authored-by: Zach Parks <zach@alliware.com>
This commit is contained in:
parent
cfd2e9c47f
commit
0f98cf525f
|
@ -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]:
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
Loading…
Reference in New Issue