871 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			871 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			Python
		
	
	
	
| import csv
 | |
| import enum
 | |
| import logging
 | |
| from dataclasses import dataclass, field
 | |
| from functools import reduce
 | |
| from pathlib import Path
 | |
| from random import Random
 | |
| from typing import Dict, List, Protocol, Union, Set, Optional
 | |
| 
 | |
| from BaseClasses import Item, ItemClassification
 | |
| from . import data
 | |
| from .content.feature import friendsanity
 | |
| from .content.game_content import StardewContent
 | |
| from .data.game_item import ItemTag
 | |
| from .logic.logic_event import all_events
 | |
| from .mods.mod_data import ModNames
 | |
| from .options import StardewValleyOptions, TrapItems, FestivalLocations, ExcludeGingerIsland, SpecialOrderLocations, SeasonRandomization, Museumsanity, \
 | |
|     BuildingProgression, SkillProgression, ToolProgression, ElevatorProgression, BackpackProgression, ArcadeMachineLocations, Monstersanity, Goal, \
 | |
|     Chefsanity, Craftsanity, BundleRandomization, EntranceRandomization, Shipsanity, Walnutsanity, EnabledFillerBuffs
 | |
| from .strings.ap_names.ap_option_names import OptionName
 | |
| from .strings.ap_names.ap_weapon_names import APWeapon
 | |
| from .strings.ap_names.buff_names import Buff
 | |
| from .strings.ap_names.community_upgrade_names import CommunityUpgrade
 | |
| from .strings.ap_names.mods.mod_items import SVEQuestItem
 | |
| from .strings.currency_names import Currency
 | |
| from .strings.wallet_item_names import Wallet
 | |
| 
 | |
| ITEM_CODE_OFFSET = 717000
 | |
| 
 | |
| logger = logging.getLogger(__name__)
 | |
| world_folder = Path(__file__).parent
 | |
| 
 | |
| 
 | |
| class Group(enum.Enum):
 | |
|     RESOURCE_PACK = enum.auto()
 | |
|     FRIENDSHIP_PACK = enum.auto()
 | |
|     COMMUNITY_REWARD = enum.auto()
 | |
|     TRASH = enum.auto()
 | |
|     FOOTWEAR = enum.auto()
 | |
|     HATS = enum.auto()
 | |
|     RING = enum.auto()
 | |
|     WEAPON = enum.auto()
 | |
|     WEAPON_GENERIC = enum.auto()
 | |
|     WEAPON_SWORD = enum.auto()
 | |
|     WEAPON_CLUB = enum.auto()
 | |
|     WEAPON_DAGGER = enum.auto()
 | |
|     WEAPON_SLINGSHOT = enum.auto()
 | |
|     PROGRESSIVE_TOOLS = enum.auto()
 | |
|     SKILL_LEVEL_UP = enum.auto()
 | |
|     SKILL_MASTERY = enum.auto()
 | |
|     BUILDING = enum.auto()
 | |
|     WIZARD_BUILDING = enum.auto()
 | |
|     ARCADE_MACHINE_BUFFS = enum.auto()
 | |
|     BASE_RESOURCE = enum.auto()
 | |
|     WARP_TOTEM = enum.auto()
 | |
|     GEODE = enum.auto()
 | |
|     ORE = enum.auto()
 | |
|     FERTILIZER = enum.auto()
 | |
|     SEED = enum.auto()
 | |
|     CROPSANITY = enum.auto()
 | |
|     FISHING_RESOURCE = enum.auto()
 | |
|     SEASON = enum.auto()
 | |
|     TRAVELING_MERCHANT_DAY = enum.auto()
 | |
|     MUSEUM = enum.auto()
 | |
|     FRIENDSANITY = enum.auto()
 | |
|     FESTIVAL = enum.auto()
 | |
|     RARECROW = enum.auto()
 | |
|     TRAP = enum.auto()
 | |
|     BONUS = enum.auto()
 | |
|     MAXIMUM_ONE = enum.auto()
 | |
|     EXACTLY_TWO = enum.auto()
 | |
|     DEPRECATED = enum.auto()
 | |
|     RESOURCE_PACK_USEFUL = enum.auto()
 | |
|     SPECIAL_ORDER_BOARD = enum.auto()
 | |
|     SPECIAL_ORDER_QI = enum.auto()
 | |
|     BABY = enum.auto()
 | |
|     GINGER_ISLAND = enum.auto()
 | |
|     WALNUT_PURCHASE = enum.auto()
 | |
|     TV_CHANNEL = enum.auto()
 | |
|     QI_CRAFTING_RECIPE = enum.auto()
 | |
|     CHEFSANITY = enum.auto()
 | |
|     CHEFSANITY_STARTER = enum.auto()
 | |
|     CHEFSANITY_QOS = enum.auto()
 | |
|     CHEFSANITY_PURCHASE = enum.auto()
 | |
|     CHEFSANITY_FRIENDSHIP = enum.auto()
 | |
|     CHEFSANITY_SKILL = enum.auto()
 | |
|     CRAFTSANITY = enum.auto()
 | |
|     BOOK_POWER = enum.auto()
 | |
|     LOST_BOOK = enum.auto()
 | |
|     PLAYER_BUFF = enum.auto()
 | |
|     # Mods
 | |
|     MAGIC_SPELL = enum.auto()
 | |
|     MOD_WARP = enum.auto()
 | |
| 
 | |
| 
 | |
| @dataclass(frozen=True)
 | |
| class ItemData:
 | |
|     code_without_offset: Optional[int]
 | |
|     name: str
 | |
|     classification: ItemClassification
 | |
|     mod_name: Optional[str] = None
 | |
|     groups: Set[Group] = field(default_factory=frozenset)
 | |
| 
 | |
|     def __post_init__(self):
 | |
|         if not isinstance(self.groups, frozenset):
 | |
|             super().__setattr__("groups", frozenset(self.groups))
 | |
| 
 | |
|     @property
 | |
|     def code(self):
 | |
|         return ITEM_CODE_OFFSET + self.code_without_offset if self.code_without_offset is not None else None
 | |
| 
 | |
|     def has_any_group(self, *group: Group) -> bool:
 | |
|         groups = set(group)
 | |
|         return bool(groups.intersection(self.groups))
 | |
| 
 | |
| 
 | |
| class StardewItemFactory(Protocol):
 | |
|     def __call__(self, name: Union[str, ItemData], override_classification: ItemClassification = None) -> Item:
 | |
|         raise NotImplementedError
 | |
| 
 | |
| 
 | |
| class StardewItemDeleter(Protocol):
 | |
|     def __call__(self, item: Item):
 | |
|         raise NotImplementedError
 | |
| 
 | |
| 
 | |
| def load_item_csv():
 | |
|     from importlib.resources import files
 | |
| 
 | |
|     items = []
 | |
|     with files(data).joinpath("items.csv").open() as file:
 | |
|         item_reader = csv.DictReader(file)
 | |
|         for item in item_reader:
 | |
|             id = int(item["id"]) if item["id"] else None
 | |
|             classification = reduce((lambda a, b: a | b), {ItemClassification[str_classification] for str_classification in item["classification"].split(",")})
 | |
|             groups = {Group[group] for group in item["groups"].split(",") if group}
 | |
|             mod_name = str(item["mod_name"]) if item["mod_name"] else None
 | |
|             items.append(ItemData(id, item["name"], classification, mod_name, groups))
 | |
|     return items
 | |
| 
 | |
| 
 | |
| events = [
 | |
|     ItemData(None, e, ItemClassification.progression)
 | |
|     for e in sorted(all_events)
 | |
| ]
 | |
| 
 | |
| all_items: List[ItemData] = load_item_csv() + events
 | |
| item_table: Dict[str, ItemData] = {}
 | |
| items_by_group: Dict[Group, List[ItemData]] = {}
 | |
| 
 | |
| 
 | |
| def initialize_groups():
 | |
|     for item in all_items:
 | |
|         for group in item.groups:
 | |
|             item_group = items_by_group.get(group, list())
 | |
|             item_group.append(item)
 | |
|             items_by_group[group] = item_group
 | |
| 
 | |
| 
 | |
| def initialize_item_table():
 | |
|     item_table.update({item.name: item for item in all_items})
 | |
| 
 | |
| 
 | |
| initialize_item_table()
 | |
| initialize_groups()
 | |
| 
 | |
| 
 | |
| def get_too_many_items_error_message(locations_count: int, items_count: int) -> str:
 | |
|     return f"There should be at least as many locations [{locations_count}] as there are mandatory items [{items_count}]"
 | |
| 
 | |
| 
 | |
| def create_items(item_factory: StardewItemFactory, item_deleter: StardewItemDeleter, locations_count: int, items_to_exclude: List[Item],
 | |
|                  options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]:
 | |
|     items = []
 | |
|     unique_items = create_unique_items(item_factory, options, content, random)
 | |
| 
 | |
|     remove_items(item_deleter, items_to_exclude, unique_items)
 | |
| 
 | |
|     remove_items_if_no_room_for_them(item_deleter, unique_items, locations_count, random)
 | |
| 
 | |
|     items += unique_items
 | |
|     logger.debug(f"Created {len(unique_items)} unique items")
 | |
| 
 | |
|     unique_filler_items = create_unique_filler_items(item_factory, options, random, locations_count - len(items))
 | |
|     items += unique_filler_items
 | |
|     logger.debug(f"Created {len(unique_filler_items)} unique filler items")
 | |
| 
 | |
|     resource_pack_items = fill_with_resource_packs_and_traps(item_factory, options, random, items, locations_count)
 | |
|     items += resource_pack_items
 | |
|     logger.debug(f"Created {len(resource_pack_items)} resource packs")
 | |
| 
 | |
|     return items
 | |
| 
 | |
| 
 | |
| def remove_items(item_deleter: StardewItemDeleter, items_to_remove, items):
 | |
|     for item in items_to_remove:
 | |
|         if item in items:
 | |
|             items.remove(item)
 | |
|             item_deleter(item)
 | |
| 
 | |
| 
 | |
| def remove_items_if_no_room_for_them(item_deleter: StardewItemDeleter, unique_items: List[Item], locations_count: int, random: Random):
 | |
|     if len(unique_items) <= locations_count:
 | |
|         return
 | |
| 
 | |
|     number_of_items_to_remove = len(unique_items) - locations_count
 | |
|     removable_items = [item for item in unique_items if item.classification == ItemClassification.filler or item.classification == ItemClassification.trap]
 | |
|     if len(removable_items) < number_of_items_to_remove:
 | |
|         logger.debug(f"Player has more items than locations, trying to remove {number_of_items_to_remove} random non-progression items")
 | |
|         removable_items = [item for item in unique_items if not item.classification & ItemClassification.progression]
 | |
|     else:
 | |
|         logger.debug(f"Player has more items than locations, trying to remove {number_of_items_to_remove} random filler items")
 | |
|     assert len(removable_items) >= number_of_items_to_remove, get_too_many_items_error_message(locations_count, len(unique_items))
 | |
|     items_to_remove = random.sample(removable_items, number_of_items_to_remove)
 | |
|     remove_items(item_deleter, items_to_remove, unique_items)
 | |
| 
 | |
| 
 | |
| def create_unique_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, random: Random) -> List[Item]:
 | |
|     items = []
 | |
| 
 | |
|     items.extend(item_factory(item) for item in items_by_group[Group.COMMUNITY_REWARD])
 | |
|     items.append(item_factory(CommunityUpgrade.movie_theater))  # It is a community reward, but we need two of them
 | |
|     create_raccoons(item_factory, options, items)
 | |
|     items.append(item_factory(Wallet.metal_detector))  # Always offer at least one metal detector
 | |
| 
 | |
|     create_backpack_items(item_factory, options, items)
 | |
|     create_weapons(item_factory, options, items)
 | |
|     items.append(item_factory("Skull Key"))
 | |
|     create_elevators(item_factory, options, items)
 | |
|     create_tools(item_factory, options, items)
 | |
|     create_skills(item_factory, options, items)
 | |
|     create_wizard_buildings(item_factory, options, items)
 | |
|     create_carpenter_buildings(item_factory, options, items)
 | |
|     items.append(item_factory("Railroad Boulder Removed"))
 | |
|     items.append(item_factory(CommunityUpgrade.fruit_bats))
 | |
|     items.append(item_factory(CommunityUpgrade.mushroom_boxes))
 | |
|     items.append(item_factory("Beach Bridge"))
 | |
|     create_tv_channels(item_factory, options, items)
 | |
|     create_quest_rewards(item_factory, options, items)
 | |
|     create_stardrops(item_factory, options, content, items)
 | |
|     create_museum_items(item_factory, options, items)
 | |
|     create_arcade_machine_items(item_factory, options, items)
 | |
|     create_movement_buffs(item_factory, options, items)
 | |
|     create_traveling_merchant_items(item_factory, items)
 | |
|     items.append(item_factory("Return Scepter"))
 | |
|     create_seasons(item_factory, options, items)
 | |
|     create_seeds(item_factory, content, items)
 | |
|     create_friendsanity_items(item_factory, options, content, items, random)
 | |
|     create_festival_rewards(item_factory, options, items)
 | |
|     create_special_order_board_rewards(item_factory, options, items)
 | |
|     create_special_order_qi_rewards(item_factory, options, items)
 | |
|     create_walnuts(item_factory, options, items)
 | |
|     create_walnut_purchase_rewards(item_factory, options, items)
 | |
|     create_crafting_recipes(item_factory, options, items)
 | |
|     create_cooking_recipes(item_factory, options, items)
 | |
|     create_shipsanity_items(item_factory, options, items)
 | |
|     create_booksanity_items(item_factory, content, items)
 | |
|     create_goal_items(item_factory, options, items)
 | |
|     items.append(item_factory("Golden Egg"))
 | |
|     items.append(item_factory(CommunityUpgrade.mr_qi_plane_ride))
 | |
| 
 | |
|     create_sve_special_items(item_factory, options, items)
 | |
|     create_magic_mod_spells(item_factory, options, items)
 | |
|     create_deepwoods_pendants(item_factory, options, items)
 | |
|     create_archaeology_items(item_factory, options, items)
 | |
| 
 | |
|     return items
 | |
| 
 | |
| 
 | |
| def create_raccoons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     number_progressive_raccoons = 9
 | |
|     if options.quest_locations < 0:
 | |
|         number_progressive_raccoons = number_progressive_raccoons - 1
 | |
| 
 | |
|     items.extend(item_factory(item) for item in [CommunityUpgrade.raccoon] * number_progressive_raccoons)
 | |
| 
 | |
| 
 | |
| def create_backpack_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if (options.backpack_progression == BackpackProgression.option_progressive or
 | |
|             options.backpack_progression == BackpackProgression.option_early_progressive):
 | |
|         items.extend(item_factory(item) for item in ["Progressive Backpack"] * 2)
 | |
|         if ModNames.big_backpack in options.mods:
 | |
|             items.append(item_factory("Progressive Backpack"))
 | |
| 
 | |
| 
 | |
| def create_weapons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     weapons = weapons_count(options)
 | |
|     items.extend(item_factory(item) for item in [APWeapon.slingshot] * 2)
 | |
|     monstersanity = options.monstersanity
 | |
|     if monstersanity == Monstersanity.option_none:  # Without monstersanity, might not be enough checks to split the weapons
 | |
|         items.extend(item_factory(item) for item in [APWeapon.weapon] * weapons)
 | |
|         items.extend(item_factory(item) for item in [APWeapon.footwear] * 3)  # 1-2 | 3-4 | 6-7-8
 | |
|         return
 | |
| 
 | |
|     items.extend(item_factory(item) for item in [APWeapon.sword] * weapons)
 | |
|     items.extend(item_factory(item) for item in [APWeapon.club] * weapons)
 | |
|     items.extend(item_factory(item) for item in [APWeapon.dagger] * weapons)
 | |
|     items.extend(item_factory(item) for item in [APWeapon.footwear] * 4)  # 1-2 | 3-4 | 6-7-8 | 11-13
 | |
|     if monstersanity == Monstersanity.option_goals or monstersanity == Monstersanity.option_one_per_category or \
 | |
|             monstersanity == Monstersanity.option_short_goals or monstersanity == Monstersanity.option_very_short_goals:
 | |
|         return
 | |
|     if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
 | |
|         rings_items = [item for item in items_by_group[Group.RING] if item.classification is not ItemClassification.filler]
 | |
|     else:
 | |
|         rings_items = [item for item in items_by_group[Group.RING]]
 | |
|     items.extend(item_factory(item) for item in rings_items)
 | |
| 
 | |
| 
 | |
| def create_elevators(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.elevator_progression == ElevatorProgression.option_vanilla:
 | |
|         return
 | |
| 
 | |
|     items.extend([item_factory(item) for item in ["Progressive Mine Elevator"] * 24])
 | |
|     if ModNames.deepwoods in options.mods:
 | |
|         items.extend([item_factory(item) for item in ["Progressive Woods Obelisk Sigils"] * 10])
 | |
|     if ModNames.skull_cavern_elevator in options.mods:
 | |
|         items.extend([item_factory(item) for item in ["Progressive Skull Cavern Elevator"] * 8])
 | |
| 
 | |
| 
 | |
| def create_tools(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.tool_progression & ToolProgression.option_progressive:
 | |
|         for item_data in items_by_group[Group.PROGRESSIVE_TOOLS]:
 | |
|             name = item_data.name
 | |
|             if "Trash Can" in name:
 | |
|                 items.extend([item_factory(item) for item in [item_data] * 3])
 | |
|                 items.append(item_factory(item_data, ItemClassification.useful))
 | |
|             else:
 | |
|                 items.extend([item_factory(item) for item in [item_data] * 4])
 | |
|         if options.skill_progression == SkillProgression.option_progressive_with_masteries:
 | |
|             items.append(item_factory("Progressive Scythe"))
 | |
|             items.append(item_factory("Progressive Fishing Rod"))
 | |
|     items.append(item_factory("Progressive Scythe"))
 | |
| 
 | |
| 
 | |
| def create_skills(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.skill_progression == SkillProgression.option_vanilla:
 | |
|         return
 | |
| 
 | |
|     for item in items_by_group[Group.SKILL_LEVEL_UP]:
 | |
|         if item.mod_name not in options.mods and item.mod_name is not None:
 | |
|             continue
 | |
|         items.extend(item_factory(item) for item in [item.name] * 10)
 | |
| 
 | |
|     if options.skill_progression != SkillProgression.option_progressive_with_masteries:
 | |
|         return
 | |
| 
 | |
|     for item in items_by_group[Group.SKILL_MASTERY]:
 | |
|         if item.mod_name not in options.mods and item.mod_name is not None:
 | |
|             continue
 | |
|         items.append(item_factory(item))
 | |
| 
 | |
| 
 | |
| def create_wizard_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     useless_buildings_classification = ItemClassification.progression_skip_balancing if world_is_perfection(options) else ItemClassification.useful
 | |
|     items.append(item_factory("Earth Obelisk", useless_buildings_classification))
 | |
|     items.append(item_factory("Water Obelisk", useless_buildings_classification))
 | |
|     items.append(item_factory("Desert Obelisk"))
 | |
|     items.append(item_factory("Junimo Hut"))
 | |
|     items.append(item_factory("Gold Clock", useless_buildings_classification))
 | |
|     if options.exclude_ginger_island == ExcludeGingerIsland.option_false:
 | |
|         items.append(item_factory("Island Obelisk"))
 | |
|     if ModNames.deepwoods in options.mods:
 | |
|         items.append(item_factory("Woods Obelisk"))
 | |
| 
 | |
| 
 | |
| def create_carpenter_buildings(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     building_option = options.building_progression
 | |
|     if not building_option & BuildingProgression.option_progressive:
 | |
|         return
 | |
|     items.append(item_factory("Progressive Coop"))
 | |
|     items.append(item_factory("Progressive Coop"))
 | |
|     items.append(item_factory("Progressive Coop"))
 | |
|     items.append(item_factory("Progressive Barn"))
 | |
|     items.append(item_factory("Progressive Barn"))
 | |
|     items.append(item_factory("Progressive Barn"))
 | |
|     items.append(item_factory("Well"))
 | |
|     items.append(item_factory("Silo"))
 | |
|     items.append(item_factory("Mill"))
 | |
|     items.append(item_factory("Progressive Shed"))
 | |
|     items.append(item_factory("Progressive Shed", ItemClassification.useful))
 | |
|     items.append(item_factory("Fish Pond"))
 | |
|     items.append(item_factory("Stable"))
 | |
|     items.append(item_factory("Slime Hutch"))
 | |
|     items.append(item_factory("Shipping Bin"))
 | |
|     items.append(item_factory("Progressive House"))
 | |
|     items.append(item_factory("Progressive House"))
 | |
|     items.append(item_factory("Progressive House"))
 | |
|     if ModNames.tractor in options.mods:
 | |
|         items.append(item_factory("Tractor Garage"))
 | |
| 
 | |
| 
 | |
| def create_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     create_special_quest_rewards(item_factory, options, items)
 | |
|     create_help_wanted_quest_rewards(item_factory, options, items)
 | |
| 
 | |
|     create_quest_rewards_sve(item_factory, options, items)
 | |
| 
 | |
| 
 | |
| def create_special_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.quest_locations < 0:
 | |
|         return
 | |
|     # items.append(item_factory("Adventurer's Guild")) # Now unlocked always!
 | |
|     items.append(item_factory(Wallet.club_card))
 | |
|     items.append(item_factory(Wallet.magnifying_glass))
 | |
|     if ModNames.sve in options.mods:
 | |
|         items.append(item_factory(Wallet.bears_knowledge))
 | |
|     else:
 | |
|         items.append(item_factory(Wallet.bears_knowledge, ItemClassification.useful))  # Not necessary outside of SVE
 | |
|     items.append(item_factory(Wallet.iridium_snake_milk))
 | |
|     items.append(item_factory("Dark Talisman"))
 | |
|     if options.exclude_ginger_island == ExcludeGingerIsland.option_false:
 | |
|         items.append(item_factory("Fairy Dust Recipe"))
 | |
| 
 | |
| 
 | |
| def create_help_wanted_quest_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.quest_locations <= 0:
 | |
|         return
 | |
| 
 | |
|     number_help_wanted = options.quest_locations.value
 | |
|     quest_per_prize_ticket = 3
 | |
|     number_prize_tickets = number_help_wanted // quest_per_prize_ticket
 | |
|     items.extend(item_factory(item) for item in [Currency.prize_ticket] * number_prize_tickets)
 | |
| 
 | |
| 
 | |
| def create_stardrops(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item]):
 | |
|     stardrops_classification = get_stardrop_classification(options)
 | |
|     items.append(item_factory("Stardrop", stardrops_classification))  # The Mines level 100
 | |
|     items.append(item_factory("Stardrop", stardrops_classification))  # Old Master Cannoli
 | |
|     items.append(item_factory("Stardrop", stardrops_classification))  # Krobus Stardrop
 | |
|     if content.features.fishsanity.is_enabled:
 | |
|         items.append(item_factory("Stardrop", stardrops_classification))  # Master Angler Stardrop
 | |
|     if ModNames.deepwoods in options.mods:
 | |
|         items.append(item_factory("Stardrop", stardrops_classification))  # Petting the Unicorn
 | |
|     if content.features.friendsanity.is_enabled:
 | |
|         items.append(item_factory("Stardrop", stardrops_classification))  # Spouse Stardrop
 | |
| 
 | |
| 
 | |
| def create_museum_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     items.append(item_factory(Wallet.rusty_key))
 | |
|     items.append(item_factory(Wallet.dwarvish_translation_guide))
 | |
|     items.append(item_factory("Ancient Seeds Recipe"))
 | |
|     items.append(item_factory("Stardrop", get_stardrop_classification(options)))
 | |
|     if options.museumsanity == Museumsanity.option_none:
 | |
|         return
 | |
|     items.extend(item_factory(item) for item in ["Magic Rock Candy"] * 10)
 | |
|     items.extend(item_factory(item) for item in ["Ancient Seeds"] * 5)
 | |
|     items.append(item_factory(Wallet.metal_detector))
 | |
| 
 | |
| 
 | |
| def create_friendsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, content: StardewContent, items: List[Item], random: Random):
 | |
|     if not content.features.friendsanity.is_enabled:
 | |
|         return
 | |
| 
 | |
|     create_babies(item_factory, items, random)
 | |
| 
 | |
|     for villager in content.villagers.values():
 | |
|         item_name = friendsanity.to_item_name(villager.name)
 | |
| 
 | |
|         for _ in content.features.friendsanity.get_randomized_hearts(villager):
 | |
|             items.append(item_factory(item_name, ItemClassification.progression))
 | |
| 
 | |
|     need_pet = options.goal == Goal.option_grandpa_evaluation
 | |
|     pet_item_classification = ItemClassification.progression_skip_balancing if need_pet else ItemClassification.useful
 | |
| 
 | |
|     for _ in content.features.friendsanity.get_pet_randomized_hearts():
 | |
|         items.append(item_factory(friendsanity.pet_heart_item_name, pet_item_classification))
 | |
| 
 | |
| 
 | |
| def create_babies(item_factory: StardewItemFactory, items: List[Item], random: Random):
 | |
|     baby_items = [item for item in items_by_group[Group.BABY]]
 | |
|     for i in range(2):
 | |
|         chosen_baby = random.choice(baby_items)
 | |
|         items.append(item_factory(chosen_baby))
 | |
| 
 | |
| 
 | |
| def create_arcade_machine_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.arcade_machine_locations == ArcadeMachineLocations.option_full_shuffling:
 | |
|         items.append(item_factory("JotPK: Progressive Boots"))
 | |
|         items.append(item_factory("JotPK: Progressive Boots"))
 | |
|         items.append(item_factory("JotPK: Progressive Gun"))
 | |
|         items.append(item_factory("JotPK: Progressive Gun"))
 | |
|         items.append(item_factory("JotPK: Progressive Gun"))
 | |
|         items.append(item_factory("JotPK: Progressive Gun"))
 | |
|         items.append(item_factory("JotPK: Progressive Ammo"))
 | |
|         items.append(item_factory("JotPK: Progressive Ammo"))
 | |
|         items.append(item_factory("JotPK: Progressive Ammo"))
 | |
|         items.append(item_factory("JotPK: Extra Life"))
 | |
|         items.append(item_factory("JotPK: Extra Life"))
 | |
|         items.append(item_factory("JotPK: Increased Drop Rate"))
 | |
|         items.extend(item_factory(item) for item in ["Junimo Kart: Extra Life"] * 8)
 | |
| 
 | |
| 
 | |
| def create_movement_buffs(item_factory, options: StardewValleyOptions, items: List[Item]):
 | |
|     movement_buffs: int = options.movement_buff_number.value
 | |
|     items.extend(item_factory(item) for item in [Buff.movement] * movement_buffs)
 | |
| 
 | |
| 
 | |
| def create_traveling_merchant_items(item_factory: StardewItemFactory, items: List[Item]):
 | |
|     items.extend([*(item_factory(item) for item in items_by_group[Group.TRAVELING_MERCHANT_DAY]),
 | |
|                   *(item_factory(item) for item in ["Traveling Merchant Stock Size"] * 6)])
 | |
| 
 | |
| 
 | |
| def create_seasons(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.season_randomization == SeasonRandomization.option_disabled:
 | |
|         return
 | |
| 
 | |
|     if options.season_randomization == SeasonRandomization.option_progressive:
 | |
|         items.extend([item_factory(item) for item in ["Progressive Season"] * 3])
 | |
|         return
 | |
| 
 | |
|     items.extend([item_factory(item) for item in items_by_group[Group.SEASON]])
 | |
| 
 | |
| 
 | |
| def create_seeds(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
 | |
|     if not content.features.cropsanity.is_enabled:
 | |
|         return
 | |
| 
 | |
|     items.extend(item_factory(item_table[seed.name]) for seed in content.find_tagged_items(ItemTag.CROPSANITY_SEED))
 | |
| 
 | |
| 
 | |
| def create_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     items.append(item_factory("Deluxe Scarecrow Recipe"))
 | |
|     if options.festival_locations == FestivalLocations.option_disabled:
 | |
|         return
 | |
| 
 | |
|     festival_rewards = [item_factory(item) for item in items_by_group[Group.FESTIVAL] if item.classification != ItemClassification.filler]
 | |
|     items.extend([*festival_rewards, item_factory("Stardrop", get_stardrop_classification(options))])
 | |
| 
 | |
| 
 | |
| def create_walnuts(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     walnutsanity = options.walnutsanity
 | |
|     if options.exclude_ginger_island == ExcludeGingerIsland.option_true or walnutsanity == Walnutsanity.preset_none:
 | |
|         return
 | |
| 
 | |
|     # Give baseline walnuts just to be nice
 | |
|     num_single_walnuts = 0
 | |
|     num_triple_walnuts = 2
 | |
|     num_penta_walnuts = 1
 | |
|     # https://stardewvalleywiki.com/Golden_Walnut
 | |
|     # Totals should be accurate, but distribution is slightly offset to make room for baseline walnuts
 | |
|     if OptionName.walnutsanity_puzzles in walnutsanity:  # 61
 | |
|         num_single_walnuts += 6  # 6
 | |
|         num_triple_walnuts += 5  # 15
 | |
|         num_penta_walnuts += 8  # 40
 | |
|     if OptionName.walnutsanity_bushes in walnutsanity:  # 25
 | |
|         num_single_walnuts += 16  # 16
 | |
|         num_triple_walnuts += 3  # 9
 | |
|     if OptionName.walnutsanity_dig_spots in walnutsanity:  # 18
 | |
|         num_single_walnuts += 18  # 18
 | |
|     if OptionName.walnutsanity_repeatables in walnutsanity:  # 33
 | |
|         num_single_walnuts += 30  # 30
 | |
|         num_triple_walnuts += 1  # 3
 | |
| 
 | |
|     items.extend([item_factory(item) for item in ["Golden Walnut"] * num_single_walnuts])
 | |
|     items.extend([item_factory(item) for item in ["3 Golden Walnuts"] * num_triple_walnuts])
 | |
|     items.extend([item_factory(item) for item in ["5 Golden Walnuts"] * num_penta_walnuts])
 | |
| 
 | |
| 
 | |
| def create_walnut_purchase_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
 | |
|         return
 | |
| 
 | |
|     items.extend([item_factory("Boat Repair"),
 | |
|                   item_factory("Open Professor Snail Cave"),
 | |
|                   item_factory("Ostrich Incubator Recipe"),
 | |
|                   item_factory("Treehouse"),
 | |
|                   *[item_factory(item) for item in items_by_group[Group.WALNUT_PURCHASE]]])
 | |
| 
 | |
| 
 | |
| def create_special_order_board_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.special_order_locations & SpecialOrderLocations.option_board:
 | |
|         special_order_board_items = [item for item in items_by_group[Group.SPECIAL_ORDER_BOARD]]
 | |
|         items.extend([item_factory(item) for item in special_order_board_items])
 | |
| 
 | |
| 
 | |
| def special_order_board_item_classification(item: ItemData, need_all_recipes: bool) -> ItemClassification:
 | |
|     if item.classification is ItemClassification.useful:
 | |
|         return ItemClassification.useful
 | |
|     if item.name == "Special Order Board":
 | |
|         return ItemClassification.progression
 | |
|     if need_all_recipes and "Recipe" in item.name:
 | |
|         return ItemClassification.progression_skip_balancing
 | |
|     if item.name == "Monster Musk Recipe":
 | |
|         return ItemClassification.progression_skip_balancing
 | |
|     return ItemClassification.useful
 | |
| 
 | |
| 
 | |
| def create_special_order_qi_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if options.exclude_ginger_island == ExcludeGingerIsland.option_true:
 | |
|         return
 | |
|     qi_gem_rewards = []
 | |
|     if options.bundle_randomization >= BundleRandomization.option_remixed:
 | |
|         qi_gem_rewards.append("15 Qi Gems")
 | |
|         qi_gem_rewards.append("15 Qi Gems")
 | |
| 
 | |
|     if options.special_order_locations & SpecialOrderLocations.value_qi:
 | |
|         qi_gem_rewards.extend(["100 Qi Gems", "10 Qi Gems", "40 Qi Gems", "25 Qi Gems", "25 Qi Gems",
 | |
|                                "40 Qi Gems", "20 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems"])
 | |
| 
 | |
|     qi_gem_items = [item_factory(reward) for reward in qi_gem_rewards]
 | |
|     items.extend(qi_gem_items)
 | |
| 
 | |
| 
 | |
| def create_tv_channels(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     channels = [channel for channel in items_by_group[Group.TV_CHANNEL]]
 | |
|     if options.entrance_randomization == EntranceRandomization.option_disabled:
 | |
|         channels = [channel for channel in channels if channel.name != "The Gateway Gazette"]
 | |
|     items.extend([item_factory(item) for item in channels])
 | |
| 
 | |
| 
 | |
| def create_crafting_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     has_craftsanity = options.craftsanity == Craftsanity.option_all
 | |
|     crafting_recipes = []
 | |
|     crafting_recipes.extend([recipe for recipe in items_by_group[Group.QI_CRAFTING_RECIPE]])
 | |
|     if has_craftsanity:
 | |
|         crafting_recipes.extend([recipe for recipe in items_by_group[Group.CRAFTSANITY]])
 | |
|     crafting_recipes = remove_excluded_items(crafting_recipes, options)
 | |
|     items.extend([item_factory(item) for item in crafting_recipes])
 | |
| 
 | |
| 
 | |
| def create_cooking_recipes(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     chefsanity = options.chefsanity
 | |
|     if chefsanity == Chefsanity.option_none:
 | |
|         return
 | |
| 
 | |
|     chefsanity_recipes_by_name = {recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_STARTER]}  # Dictionary to not make duplicates
 | |
| 
 | |
|     if chefsanity & Chefsanity.option_queen_of_sauce:
 | |
|         chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_QOS]})
 | |
|     if chefsanity & Chefsanity.option_purchases:
 | |
|         chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_PURCHASE]})
 | |
|     if chefsanity & Chefsanity.option_friendship:
 | |
|         chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_FRIENDSHIP]})
 | |
|     if chefsanity & Chefsanity.option_skills:
 | |
|         chefsanity_recipes_by_name.update({recipe.name: recipe for recipe in items_by_group[Group.CHEFSANITY_SKILL]})
 | |
| 
 | |
|     filtered_chefsanity_recipes = remove_excluded_items(list(chefsanity_recipes_by_name.values()), options)
 | |
|     items.extend([item_factory(item) for item in filtered_chefsanity_recipes])
 | |
| 
 | |
| 
 | |
| def create_shipsanity_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     shipsanity = options.shipsanity
 | |
|     if shipsanity != Shipsanity.option_everything:
 | |
|         return
 | |
| 
 | |
|     items.append(item_factory(Wallet.metal_detector))
 | |
| 
 | |
| 
 | |
| def create_booksanity_items(item_factory: StardewItemFactory, content: StardewContent, items: List[Item]):
 | |
|     booksanity = content.features.booksanity
 | |
|     if not booksanity.is_enabled:
 | |
|         return
 | |
| 
 | |
|     items.extend(item_factory(item_table[booksanity.to_item_name(book.name)]) for book in content.find_tagged_items(ItemTag.BOOK_POWER))
 | |
|     progressive_lost_book = item_table[booksanity.progressive_lost_book]
 | |
|     items.extend(item_factory(progressive_lost_book) for _ in content.features.booksanity.get_randomized_lost_books())
 | |
| 
 | |
| 
 | |
| def create_goal_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     goal = options.goal
 | |
|     if goal != Goal.option_perfection and goal != Goal.option_complete_collection:
 | |
|         return
 | |
| 
 | |
|     items.append(item_factory(Wallet.metal_detector))
 | |
| 
 | |
| 
 | |
| def create_archaeology_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     mods = options.mods
 | |
|     if ModNames.archaeology not in mods:
 | |
|         return
 | |
| 
 | |
|     items.append(item_factory(Wallet.metal_detector))
 | |
| 
 | |
| 
 | |
| def create_filler_festival_rewards(item_factory: StardewItemFactory, options: StardewValleyOptions) -> List[Item]:
 | |
|     if options.festival_locations == FestivalLocations.option_disabled:
 | |
|         return []
 | |
| 
 | |
|     return [item_factory(item) for item in items_by_group[Group.FESTIVAL] if
 | |
|             item.classification == ItemClassification.filler]
 | |
| 
 | |
| 
 | |
| def create_magic_mod_spells(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if ModNames.magic not in options.mods:
 | |
|         return
 | |
|     items.extend([item_factory(item) for item in items_by_group[Group.MAGIC_SPELL]])
 | |
| 
 | |
| 
 | |
| def create_deepwoods_pendants(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if ModNames.deepwoods not in options.mods:
 | |
|         return
 | |
|     items.extend([item_factory(item) for item in ["Pendant of Elders", "Pendant of Community", "Pendant of Depths"]])
 | |
| 
 | |
| 
 | |
| def create_sve_special_items(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if ModNames.sve not in options.mods:
 | |
|         return
 | |
| 
 | |
|     items.extend([item_factory(item) for item in items_by_group[Group.MOD_WARP] if item.mod_name == ModNames.sve])
 | |
| 
 | |
| 
 | |
| def create_quest_rewards_sve(item_factory: StardewItemFactory, options: StardewValleyOptions, items: List[Item]):
 | |
|     if ModNames.sve not in options.mods:
 | |
|         return
 | |
| 
 | |
|     exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
 | |
|     items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items])
 | |
|     if not exclude_ginger_island:
 | |
|         items.extend([item_factory(item) for item in SVEQuestItem.sve_always_quest_items_ginger_island])
 | |
| 
 | |
|     if options.quest_locations < 0:
 | |
|         return
 | |
| 
 | |
|     items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items])
 | |
|     if exclude_ginger_island:
 | |
|         return
 | |
|     items.extend([item_factory(item) for item in SVEQuestItem.sve_quest_items_ginger_island])
 | |
| 
 | |
| 
 | |
| def create_unique_filler_items(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random,
 | |
|                                available_item_slots: int) -> List[Item]:
 | |
|     items = []
 | |
| 
 | |
|     items.extend(create_filler_festival_rewards(item_factory, options))
 | |
| 
 | |
|     if len(items) > available_item_slots:
 | |
|         items = random.sample(items, available_item_slots)
 | |
|     return items
 | |
| 
 | |
| 
 | |
| def weapons_count(options: StardewValleyOptions):
 | |
|     weapon_count = 5
 | |
|     if ModNames.sve in options.mods:
 | |
|         weapon_count += 1
 | |
|     return weapon_count
 | |
| 
 | |
| 
 | |
| def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, options: StardewValleyOptions, random: Random,
 | |
|                                        items_already_added: List[Item],
 | |
|                                        number_locations: int) -> List[Item]:
 | |
|     include_traps = options.trap_items != TrapItems.option_no_traps
 | |
|     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]
 | |
|     trap_items = [trap for trap in items_by_group[Group.TRAP]
 | |
|                   if trap.name not in items_already_added_names and
 | |
|                   (trap.mod_name is None or trap.mod_name in options.mods)]
 | |
|     player_buffs = get_allowed_player_buffs(options.enabled_filler_buffs)
 | |
| 
 | |
|     priority_filler_items = []
 | |
|     priority_filler_items.extend(useful_resource_packs)
 | |
|     priority_filler_items.extend(player_buffs)
 | |
| 
 | |
|     if include_traps:
 | |
|         priority_filler_items.extend(trap_items)
 | |
| 
 | |
|     exclude_ginger_island = options.exclude_ginger_island == ExcludeGingerIsland.option_true
 | |
|     all_filler_packs = remove_excluded_items(get_all_filler_items(include_traps, exclude_ginger_island), options)
 | |
|     all_filler_packs.extend(player_buffs)
 | |
|     priority_filler_items = remove_excluded_items(priority_filler_items, options)
 | |
| 
 | |
|     number_priority_items = len(priority_filler_items)
 | |
|     required_resource_pack = number_locations - len(items_already_added)
 | |
|     if required_resource_pack < number_priority_items:
 | |
|         chosen_priority_items = [item_factory(resource_pack) for resource_pack in
 | |
|                                  random.sample(priority_filler_items, required_resource_pack)]
 | |
|         return chosen_priority_items
 | |
| 
 | |
|     items = []
 | |
|     chosen_priority_items = [item_factory(resource_pack,
 | |
|                                           ItemClassification.trap if resource_pack.classification == ItemClassification.trap else ItemClassification.useful)
 | |
|                              for resource_pack in priority_filler_items]
 | |
|     items.extend(chosen_priority_items)
 | |
|     required_resource_pack -= number_priority_items
 | |
|     all_filler_packs = [filler_pack for filler_pack in all_filler_packs
 | |
|                         if Group.MAXIMUM_ONE not in filler_pack.groups or
 | |
|                         (filler_pack.name not in [priority_item.name for priority_item in
 | |
|                                                   priority_filler_items] and filler_pack.name not in items_already_added_names)]
 | |
| 
 | |
|     while required_resource_pack > 0:
 | |
|         resource_pack = random.choice(all_filler_packs)
 | |
|         exactly_2 = Group.EXACTLY_TWO in resource_pack.groups
 | |
|         while exactly_2 and required_resource_pack == 1:
 | |
|             resource_pack = random.choice(all_filler_packs)
 | |
|             exactly_2 = Group.EXACTLY_TWO in resource_pack.groups
 | |
|         classification = ItemClassification.useful if resource_pack.classification == ItemClassification.progression else resource_pack.classification
 | |
|         items.append(item_factory(resource_pack, classification))
 | |
|         required_resource_pack -= 1
 | |
|         if exactly_2:
 | |
|             items.append(item_factory(resource_pack, classification))
 | |
|             required_resource_pack -= 1
 | |
|         if exactly_2 or Group.MAXIMUM_ONE in resource_pack.groups:
 | |
|             all_filler_packs.remove(resource_pack)
 | |
| 
 | |
|     return items
 | |
| 
 | |
| 
 | |
| def filter_deprecated_items(items: List[ItemData]) -> List[ItemData]:
 | |
|     return [item for item in items if Group.DEPRECATED not in item.groups]
 | |
| 
 | |
| 
 | |
| def filter_ginger_island_items(exclude_island: bool, items: List[ItemData]) -> List[ItemData]:
 | |
|     return [item for item in items if not exclude_island or Group.GINGER_ISLAND not in item.groups]
 | |
| 
 | |
| 
 | |
| def filter_mod_items(mods: Set[str], items: List[ItemData]) -> List[ItemData]:
 | |
|     return [item for item in items if item.mod_name is None or item.mod_name in mods]
 | |
| 
 | |
| 
 | |
| def remove_excluded_items(items, options: StardewValleyOptions):
 | |
|     return remove_excluded_items_island_mods(items, options.exclude_ginger_island == ExcludeGingerIsland.option_true, options.mods.value)
 | |
| 
 | |
| 
 | |
| def remove_excluded_items_island_mods(items, exclude_ginger_island: bool, mods: Set[str]):
 | |
|     deprecated_filter = filter_deprecated_items(items)
 | |
|     ginger_island_filter = filter_ginger_island_items(exclude_ginger_island, deprecated_filter)
 | |
|     mod_filter = filter_mod_items(mods, ginger_island_filter)
 | |
|     return mod_filter
 | |
| 
 | |
| 
 | |
| 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) -> List[ItemData]:
 | |
|     all_filler_items = [pack for pack in items_by_group[Group.RESOURCE_PACK]]
 | |
|     all_filler_items.extend(items_by_group[Group.TRASH])
 | |
|     if include_traps:
 | |
|         all_filler_items.extend(items_by_group[Group.TRAP])
 | |
|     all_filler_items = remove_excluded_items_island_mods(all_filler_items, exclude_ginger_island, set())
 | |
|     return all_filler_items
 | |
| 
 | |
| 
 | |
| def get_allowed_player_buffs(buff_option: EnabledFillerBuffs) -> List[ItemData]:
 | |
|     allowed_buffs = []
 | |
|     if OptionName.buff_luck in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.luck])
 | |
|     if OptionName.buff_damage in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.damage])
 | |
|     if OptionName.buff_defense in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.defense])
 | |
|     if OptionName.buff_immunity in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.immunity])
 | |
|     if OptionName.buff_health in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.health])
 | |
|     if OptionName.buff_energy in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.energy])
 | |
|     if OptionName.buff_bite in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.bite_rate])
 | |
|     if OptionName.buff_fish_trap in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.fish_trap])
 | |
|     if OptionName.buff_fishing_bar in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.fishing_bar])
 | |
|     if OptionName.buff_quality in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.quality])
 | |
|     if OptionName.buff_glow in buff_option:
 | |
|         allowed_buffs.append(item_table[Buff.glow])
 | |
|     return allowed_buffs
 | |
| 
 | |
| 
 | |
| def get_stardrop_classification(options) -> ItemClassification:
 | |
|     return ItemClassification.progression_skip_balancing if world_is_perfection(options) or world_is_stardrops(options) else ItemClassification.useful
 | |
| 
 | |
| 
 | |
| def world_is_perfection(options) -> bool:
 | |
|     return options.goal == Goal.option_perfection
 | |
| 
 | |
| 
 | |
| def world_is_stardrops(options) -> bool:
 | |
|     return options.goal == Goal.option_mystery_of_the_stardrops
 |