from random import Random from typing import List, Dict, Union from .data.bundle_data import * from .logic import StardewLogic from .options import BundleRandomization, BundlePrice vanilla_bundles = { "Pantry/0": "Spring Crops/O 465 20/24 1 0 188 1 0 190 1 0 192 1 0/0", "Pantry/1": "Summer Crops/O 621 1/256 1 0 260 1 0 258 1 0 254 1 0/3", "Pantry/2": "Fall Crops/BO 10 1/270 1 0 272 1 0 276 1 0 280 1 0/2", "Pantry/3": "Quality Crops/BO 15 1/24 5 2 254 5 2 276 5 2 270 5 2/6/3", "Pantry/4": "Animal/BO 16 1/186 1 0 182 1 0 174 1 0 438 1 0 440 1 0 442 1 0/4/5", # 639 1 0 640 1 0 641 1 0 642 1 0 643 1 0 "Pantry/5": "Artisan/BO 12 1/432 1 0 428 1 0 426 1 0 424 1 0 340 1 0 344 1 0 613 1 0 634 1 0 635 1 0 636 1 0 637 1 0 638 1 0/1/6", "Crafts Room/13": "Spring Foraging/O 495 30/16 1 0 18 1 0 20 1 0 22 1 0/0", "Crafts Room/14": "Summer Foraging/O 496 30/396 1 0 398 1 0 402 1 0/3", "Crafts Room/15": "Fall Foraging/O 497 30/404 1 0 406 1 0 408 1 0 410 1 0/2", "Crafts Room/16": "Winter Foraging/O 498 30/412 1 0 414 1 0 416 1 0 418 1 0/6", "Crafts Room/17": "Construction/BO 114 1/388 99 0 388 99 0 390 99 0 709 10 0/4", "Crafts Room/19": "Exotic Foraging/O 235 5/88 1 0 90 1 0 78 1 0 420 1 0 422 1 0 724 1 0 725 1 0 726 1 0 257 1 0/1/5", "Fish Tank/6": "River Fish/O 685 30/145 1 0 143 1 0 706 1 0 699 1 0/6", "Fish Tank/7": "Lake Fish/O 687 1/136 1 0 142 1 0 700 1 0 698 1 0/0", "Fish Tank/8": "Ocean Fish/O 690 5/131 1 0 130 1 0 150 1 0 701 1 0/5", "Fish Tank/9": "Night Fishing/R 516 1/140 1 0 132 1 0 148 1 0/1", "Fish Tank/10": "Specialty Fish/O 242 5/128 1 0 156 1 0 164 1 0 734 1 0/4", "Fish Tank/11": "Crab Pot/O 710 3/715 1 0 716 1 0 717 1 0 718 1 0 719 1 0 720 1 0 721 1 0 722 1 0 723 1 0 372 1 0/1/5", "Boiler Room/20": "Blacksmith's/BO 13 1/334 1 0 335 1 0 336 1 0/2", "Boiler Room/21": "Geologist's/O 749 5/80 1 0 86 1 0 84 1 0 82 1 0/1", "Boiler Room/22": "Adventurer's/R 518 1/766 99 0 767 10 0 768 1 0 769 1 0/1/2", "Vault/23": "2,500g/O 220 3/-1 2500 2500/4", "Vault/24": "5,000g/O 369 30/-1 5000 5000/2", "Vault/25": "10,000g/BO 9 1/-1 10000 10000/3", "Vault/26": "25,000g/BO 21 1/-1 25000 25000/1", "Bulletin Board/31": "Chef's/O 221 3/724 1 0 259 1 0 430 1 0 376 1 0 228 1 0 194 1 0/4", "Bulletin Board/32": "Field Research/BO 20 1/422 1 0 392 1 0 702 1 0 536 1 0/5", "Bulletin Board/33": "Enchanter's/O 336 5/725 1 0 348 1 0 446 1 0 637 1 0/1", "Bulletin Board/34": "Dye/BO 25 1/420 1 0 397 1 0 421 1 0 444 1 0 62 1 0 266 1 0/6", "Bulletin Board/35": "Fodder/BO 104 1/262 10 0 178 10 0 613 3 0/3", # "Abandoned Joja Mart/36": "The Missing//348 1 1 807 1 0 74 1 0 454 5 2 795 1 2 445 1 0/1/5" } class Bundle: room: str sprite: str original_name: str name: str rewards: List[str] requirements: List[BundleItem] color: str number_required: int def __init__(self, key: str, value: str): key_parts = key.split("/") self.room = key_parts[0] self.sprite = key_parts[1] value_parts = value.split("/") self.original_name = value_parts[0] self.name = value_parts[0] self.rewards = self.parse_stardew_objects(value_parts[1]) self.requirements = self.parse_stardew_bundle_items(value_parts[2]) self.color = value_parts[3] if len(value_parts) > 4: self.number_required = int(value_parts[4]) else: self.number_required = len(self.requirements) def __repr__(self): return f"{self.original_name} -> {repr(self.requirements)}" def get_name_with_bundle(self) -> str: return f"{self.original_name} Bundle" def to_pair(self) -> (str, str): key = f"{self.room}/{self.sprite}" str_rewards = "" for reward in self.rewards: str_rewards += f" {reward}" str_rewards = str_rewards.strip() str_requirements = "" for requirement in self.requirements: str_requirements += f" {requirement.item.item_id} {requirement.amount} {requirement.quality}" str_requirements = str_requirements.strip() value = f"{self.name}/{str_rewards}/{str_requirements}/{self.color}/{self.number_required}" return key, value def remove_rewards(self): self.rewards = [] def change_number_required(self, difference: int): self.number_required = min(len(self.requirements), max(1, self.number_required + difference)) if len(self.requirements) == 1 and self.requirements[0].item.item_id == -1: one_fifth = self.requirements[0].amount / 5 new_amount = int(self.requirements[0].amount + (difference * one_fifth)) self.requirements[0] = BundleItem.money_bundle(new_amount) thousand_amount = int(new_amount / 1000) dollar_amount = str(new_amount % 1000) while len(dollar_amount) < 3: dollar_amount = f"0{dollar_amount}" self.name = f"{thousand_amount},{dollar_amount}g" def randomize_requirements(self, random: Random, potential_requirements: Union[List[BundleItem], List[List[BundleItem]]]): if not potential_requirements: return number_to_generate = len(self.requirements) self.requirements.clear() if number_to_generate > len(potential_requirements): choices: Union[BundleItem, List[BundleItem]] = random.choices(potential_requirements, k=number_to_generate) else: choices: Union[BundleItem, List[BundleItem]] = random.sample(potential_requirements, number_to_generate) for choice in choices: if isinstance(choice, BundleItem): self.requirements.append(choice) else: self.requirements.append(random.choice(choice)) def assign_requirements(self, new_requirements: List[BundleItem]) -> List[BundleItem]: number_to_generate = len(self.requirements) self.requirements.clear() for requirement in new_requirements: self.requirements.append(requirement) if len(self.requirements) >= number_to_generate: return new_requirements[number_to_generate:] @staticmethod def parse_stardew_objects(string_objects: str) -> List[str]: objects = [] if len(string_objects) < 5: return objects rewards_parts = string_objects.split(" ") for index in range(0, len(rewards_parts), 3): objects.append(f"{rewards_parts[index]} {rewards_parts[index + 1]} {rewards_parts[index + 2]}") return objects @staticmethod def parse_stardew_bundle_items(string_objects: str) -> List[BundleItem]: bundle_items = [] parts = string_objects.split(" ") for index in range(0, len(parts), 3): item_id = int(parts[index]) bundle_item = BundleItem(all_bundle_items_by_id[item_id].item, int(parts[index + 1]), int(parts[index + 2])) bundle_items.append(bundle_item) return bundle_items # Shuffling the Vault doesn't really work with the stardew system in place # shuffle_vault_amongst_themselves(random, bundles) def get_all_bundles(random: Random, logic: StardewLogic, randomization: BundleRandomization, price: BundlePrice) -> Dict[str, Bundle]: bundles = {} for bundle_key in vanilla_bundles: bundle_value = vanilla_bundles[bundle_key] bundle = Bundle(bundle_key, bundle_value) bundles[bundle.get_name_with_bundle()] = bundle if randomization == BundleRandomization.option_thematic: shuffle_bundles_thematically(random, bundles) elif randomization == BundleRandomization.option_shuffled: shuffle_bundles_completely(random, logic, bundles) price_difference = 0 if price == BundlePrice.option_very_cheap: price_difference = -2 elif price == BundlePrice.option_cheap: price_difference = -1 elif price == BundlePrice.option_expensive: price_difference = 1 for bundle_key in bundles: bundles[bundle_key].remove_rewards() bundles[bundle_key].change_number_required(price_difference) return bundles def shuffle_bundles_completely(random: Random, logic: StardewLogic, bundles: Dict[str, Bundle]): total_required_item_number = sum(len(bundle.requirements) for bundle in bundles.values()) quality_crops_items_set = set(quality_crops_items) all_bundle_items_without_quality_and_money = [item for item in all_bundle_items_except_money if item not in quality_crops_items_set] + \ random.sample(quality_crops_items, 10) choices = random.sample(all_bundle_items_without_quality_and_money, total_required_item_number - 4) items_sorted = sorted(choices, key=lambda x: logic.item_rules[x.item.name].get_difficulty()) keys = sorted(bundles.keys()) random.shuffle(keys) for key in keys: if not bundles[key].original_name.endswith("00g"): items_sorted = bundles[key].assign_requirements(items_sorted) def shuffle_bundles_thematically(random: Random, bundles: Dict[str, Bundle]): shuffle_crafts_room_bundle_thematically(random, bundles) shuffle_pantry_bundle_thematically(random, bundles) shuffle_fish_tank_thematically(random, bundles) shuffle_boiler_room_thematically(random, bundles) shuffle_bulletin_board_thematically(random, bundles) def shuffle_crafts_room_bundle_thematically(random: Random, bundles: Dict[str, Bundle]): bundles["Spring Foraging Bundle"].randomize_requirements(random, spring_foraging_items) bundles["Summer Foraging Bundle"].randomize_requirements(random, summer_foraging_items) bundles["Fall Foraging Bundle"].randomize_requirements(random, fall_foraging_items) bundles["Winter Foraging Bundle"].randomize_requirements(random, winter_foraging_items) bundles["Exotic Foraging Bundle"].randomize_requirements(random, exotic_foraging_items) bundles["Construction Bundle"].randomize_requirements(random, construction_items) def shuffle_pantry_bundle_thematically(random: Random, bundles: Dict[str, Bundle]): bundles["Spring Crops Bundle"].randomize_requirements(random, spring_crop_items) bundles["Summer Crops Bundle"].randomize_requirements(random, summer_crops_items) bundles["Fall Crops Bundle"].randomize_requirements(random, fall_crops_items) bundles["Quality Crops Bundle"].randomize_requirements(random, quality_crops_items) bundles["Animal Bundle"].randomize_requirements(random, animal_product_items) bundles["Artisan Bundle"].randomize_requirements(random, artisan_goods_items) def shuffle_fish_tank_thematically(random: Random, bundles: Dict[str, Bundle]): bundles["River Fish Bundle"].randomize_requirements(random, river_fish_items) bundles["Lake Fish Bundle"].randomize_requirements(random, lake_fish_items) bundles["Ocean Fish Bundle"].randomize_requirements(random, ocean_fish_items) bundles["Night Fishing Bundle"].randomize_requirements(random, night_fish_items) bundles["Crab Pot Bundle"].randomize_requirements(random, crab_pot_items) bundles["Specialty Fish Bundle"].randomize_requirements(random, specialty_fish_items) def shuffle_boiler_room_thematically(random: Random, bundles: Dict[str, Bundle]): bundles["Blacksmith's Bundle"].randomize_requirements(random, blacksmith_items) bundles["Geologist's Bundle"].randomize_requirements(random, geologist_items) bundles["Adventurer's Bundle"].randomize_requirements(random, adventurer_items) def shuffle_bulletin_board_thematically(random: Random, bundles: Dict[str, Bundle]): bundles["Chef's Bundle"].randomize_requirements(random, chef_items) bundles["Dye Bundle"].randomize_requirements(random, dye_items) bundles["Field Research Bundle"].randomize_requirements(random, field_research_items) bundles["Fodder Bundle"].randomize_requirements(random, fodder_items) bundles["Enchanter's Bundle"].randomize_requirements(random, enchanter_items) def shuffle_vault_amongst_themselves(random: Random, bundles: Dict[str, Bundle]): bundles["2,500g Bundle"].randomize_requirements(random, vault_bundle_items) bundles["5,000g Bundle"].randomize_requirements(random, vault_bundle_items) bundles["10,000g Bundle"].randomize_requirements(random, vault_bundle_items) bundles["25,000g Bundle"].randomize_requirements(random, vault_bundle_items)