| 
									
										
										
										
											2025-05-10 17:57:24 -04:00
										 |  |  | import csv | 
					
						
							|  |  |  | import enum | 
					
						
							|  |  |  | from dataclasses import dataclass, field | 
					
						
							|  |  |  | from functools import reduce | 
					
						
							|  |  |  | from pathlib import Path | 
					
						
							|  |  |  | from typing import Dict, List, Protocol, Union, Set, Optional | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | from BaseClasses import Item, ItemClassification | 
					
						
							|  |  |  | from .. import data | 
					
						
							|  |  |  | from ..logic.logic_event import all_events | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ITEM_CODE_OFFSET = 717000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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() | 
					
						
							| 
									
										
										
										
											2025-06-28 11:36:09 -04:00
										 |  |  |     DESERT_TRANSPORTATION = enum.auto() | 
					
						
							|  |  |  |     ISLAND_TRANSPORTATION = enum.auto() | 
					
						
							| 
									
										
										
										
											2025-05-10 17:57:24 -04:00
										 |  |  |     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() | 
					
						
							|  |  |  |     AT_LEAST_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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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() |