255 lines
13 KiB
Python
255 lines
13 KiB
Python
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)
|