Major Game Update: Stardew Valley v3.x.x - The BK Update (#1686)
This is a major update for Stardew Valley, for version 3.x.x. Changes include a large number of new features, including Seasons Randomizer, SeedShuffle, Museumsanity, Friendsanity, Complete Collection Goal, Full House Goal, friendship multiplier Co-authored-by: Jouramie <jouramie@hotmail.com>
This commit is contained in:
parent
0c1e3097c3
commit
5eadbc9840
|
@ -1,15 +1,16 @@
|
|||
from typing import Dict, Any, Iterable, Optional, Union
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from . import rules, logic, options
|
||||
from .bundles import get_all_bundles, Bundle
|
||||
from .items import item_table, create_items, ItemData, Group
|
||||
from .items import item_table, create_items, ItemData, Group, items_by_group
|
||||
from .locations import location_table, create_locations, LocationData
|
||||
from .logic import StardewLogic, StardewRule, _True, _And
|
||||
from .logic import StardewLogic, StardewRule, True_
|
||||
from .options import stardew_valley_options, StardewOptions, fetch_options
|
||||
from .regions import create_regions
|
||||
from .rules import set_rules
|
||||
from ..generic.Rules import set_rule
|
||||
|
||||
client_version = 0
|
||||
|
||||
|
@ -52,8 +53,8 @@ class StardewValleyWorld(World):
|
|||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = {name: data.code for name, data in location_table.items()}
|
||||
|
||||
data_version = 1
|
||||
required_client_version = (0, 3, 9)
|
||||
data_version = 2
|
||||
required_client_version = (0, 4, 0)
|
||||
|
||||
options: StardewOptions
|
||||
logic: StardewLogic
|
||||
|
@ -88,38 +89,66 @@ class StardewValleyWorld(World):
|
|||
create_locations(add_location, self.options, self.multiworld.random)
|
||||
|
||||
def create_items(self):
|
||||
locations_count = len([location
|
||||
for location in self.multiworld.get_locations(self.player)
|
||||
if not location.event])
|
||||
self.precollect_starting_season()
|
||||
items_to_exclude = [excluded_items
|
||||
for excluded_items in self.multiworld.precollected_items[self.player]
|
||||
if not item_table[excluded_items.name].has_any_group(Group.RESOURCE_PACK,
|
||||
Group.FRIENDSHIP_PACK)]
|
||||
created_items = create_items(self.create_item, locations_count + len(items_to_exclude), self.options,
|
||||
|
||||
if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled:
|
||||
items_to_exclude = [item for item in items_to_exclude
|
||||
if item_table[item.name] not in items_by_group[Group.SEASON]]
|
||||
|
||||
locations_count = len([location
|
||||
for location in self.multiworld.get_locations(self.player)
|
||||
if not location.event])
|
||||
|
||||
created_items = create_items(self.create_item, locations_count, items_to_exclude, self.options,
|
||||
self.multiworld.random)
|
||||
|
||||
self.multiworld.itempool += created_items
|
||||
|
||||
for item in items_to_exclude:
|
||||
self.multiworld.itempool.remove(item)
|
||||
|
||||
self.setup_season_events()
|
||||
self.setup_early_items()
|
||||
self.setup_month_events()
|
||||
self.setup_victory()
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.multiworld, self.player, self.options, self.logic, self.modified_bundles)
|
||||
def precollect_starting_season(self) -> Optional[StardewItem]:
|
||||
if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_progressive:
|
||||
return
|
||||
|
||||
def create_item(self, item: Union[str, ItemData]) -> StardewItem:
|
||||
if isinstance(item, str):
|
||||
item = item_table[item]
|
||||
season_pool = items_by_group[Group.SEASON]
|
||||
|
||||
return StardewItem(item.name, item.classification, item.code, self.player)
|
||||
if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled:
|
||||
for season in season_pool:
|
||||
self.multiworld.push_precollected(self.create_item(season))
|
||||
return
|
||||
|
||||
def setup_season_events(self):
|
||||
self.multiworld.push_precollected(self.create_item("Spring"))
|
||||
self.create_event_location(location_table["Summer"], self.logic.received("Spring"), "Summer")
|
||||
self.create_event_location(location_table["Fall"], self.logic.received("Summer"), "Fall")
|
||||
self.create_event_location(location_table["Winter"], self.logic.received("Fall"), "Winter")
|
||||
self.create_event_location(location_table["Year Two"], self.logic.received("Winter"), "Year Two")
|
||||
if [item for item in self.multiworld.precollected_items[self.player]
|
||||
if item.name in {season.name for season in items_by_group[Group.SEASON]}]:
|
||||
return
|
||||
|
||||
if self.options[options.SeasonRandomization] == options.SeasonRandomization.option_randomized_not_winter:
|
||||
season_pool = [season for season in season_pool if season.name != "Winter"]
|
||||
|
||||
starting_season = self.create_item(self.multiworld.random.choice(season_pool))
|
||||
self.multiworld.push_precollected(starting_season)
|
||||
|
||||
def setup_early_items(self):
|
||||
if (self.options[options.BuildingProgression] ==
|
||||
options.BuildingProgression.option_progressive_early_shipping_bin):
|
||||
self.multiworld.early_items[self.player]["Shipping Bin"] = 1
|
||||
|
||||
if self.options[options.BackpackProgression] == options.BackpackProgression.option_early_progressive:
|
||||
self.multiworld.early_items[self.player]["Progressive Backpack"] = 1
|
||||
|
||||
def setup_month_events(self):
|
||||
for i in range(0, 8):
|
||||
month_end = LocationData(None, "Stardew Valley", f"Month End {i + 1}")
|
||||
if i == 0:
|
||||
self.create_event_location(month_end, True_(), "Month End")
|
||||
continue
|
||||
|
||||
self.create_event_location(month_end, self.logic.received("Month End", i).simplify(), "Month End")
|
||||
|
||||
def setup_victory(self):
|
||||
if self.options[options.Goal] == options.Goal.option_community_center:
|
||||
|
@ -142,16 +171,70 @@ class StardewValleyWorld(World):
|
|||
self.create_event_location(location_table["Catch Every Fish"],
|
||||
self.logic.can_catch_every_fish().simplify(),
|
||||
"Victory")
|
||||
elif self.options[options.Goal] == options.Goal.option_complete_collection:
|
||||
self.create_event_location(location_table["Complete the Museum Collection"],
|
||||
self.logic.can_complete_museum().simplify(),
|
||||
"Victory")
|
||||
elif self.options[options.Goal] == options.Goal.option_full_house:
|
||||
self.create_event_location(location_table["Full House"],
|
||||
self.logic.can_have_two_children().simplify(),
|
||||
"Victory")
|
||||
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
|
||||
|
||||
def create_event_location(self, location_data: LocationData, rule: StardewRule, item: str):
|
||||
def create_item(self, item: Union[str, ItemData]) -> StardewItem:
|
||||
if isinstance(item, str):
|
||||
item = item_table[item]
|
||||
|
||||
return StardewItem(item.name, item.classification, item.code, self.player)
|
||||
|
||||
def create_event_location(self, location_data: LocationData, rule: StardewRule, item: Optional[str] = None):
|
||||
if item is None:
|
||||
item = location_data.name
|
||||
|
||||
region = self.multiworld.get_region(location_data.region, self.player)
|
||||
location = StardewLocation(self.player, location_data.name, None, region)
|
||||
location.access_rule = rule
|
||||
region.locations.append(location)
|
||||
location.place_locked_item(self.create_item(item))
|
||||
|
||||
def set_rules(self):
|
||||
set_rules(self.multiworld, self.player, self.options, self.logic, self.modified_bundles)
|
||||
self.force_first_month_once_all_early_items_are_found()
|
||||
|
||||
def force_first_month_once_all_early_items_are_found(self):
|
||||
"""
|
||||
The Fill algorithm sweeps all event when calculating the early location. This causes an issue where
|
||||
location only locked behind event are considered early, which they are not really...
|
||||
|
||||
This patches the issue, by adding a dependency to the first month end on all early items, so all the locations
|
||||
that depends on it will not be considered early. This requires at least one early item to be progression, or
|
||||
it just won't work...
|
||||
"""
|
||||
|
||||
early_items = []
|
||||
for player, item_count in self.multiworld.early_items.items():
|
||||
for item, count in item_count.items():
|
||||
if self.multiworld.worlds[player].create_item(item).advancement:
|
||||
early_items.append((player, item, count))
|
||||
|
||||
for item, count in self.multiworld.local_early_items[self.player].items():
|
||||
if self.create_item(item).advancement:
|
||||
early_items.append((self.player, item, count))
|
||||
|
||||
def first_month_require_all_early_items(state: CollectionState) -> bool:
|
||||
for player, item, count in early_items:
|
||||
if not state.has(item, player, count):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
first_month_end = self.multiworld.get_location("Month End 1", self.player)
|
||||
set_rule(first_month_end, first_month_require_all_early_items)
|
||||
|
||||
def generate_basic(self):
|
||||
pass
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return "Joja Cola"
|
||||
|
||||
|
@ -162,28 +245,16 @@ class StardewValleyWorld(World):
|
|||
key, value = self.modified_bundles[bundle_key].to_pair()
|
||||
modified_bundles[key] = value
|
||||
|
||||
return {
|
||||
"starting_money": self.options[options.StartingMoney],
|
||||
"entrance_randomization": self.options[options.EntranceRandomization],
|
||||
"backpack_progression": self.options[options.BackpackProgression],
|
||||
"tool_progression": self.options[options.ToolProgression],
|
||||
"elevator_progression": self.options[options.TheMinesElevatorsProgression],
|
||||
"skill_progression": self.options[options.SkillProgression],
|
||||
"building_progression": self.options[options.BuildingProgression],
|
||||
"arcade_machine_progression": self.options[options.ArcadeMachineLocations],
|
||||
"help_wanted_locations": self.options[options.HelpWantedLocations],
|
||||
"fishsanity": self.options[options.Fishsanity],
|
||||
"death_link": self.options["death_link"],
|
||||
"goal": self.options[options.Goal],
|
||||
excluded_options = [options.ResourcePackMultiplier, options.BundleRandomization, options.BundlePrice,
|
||||
options.NumberOfPlayerBuffs]
|
||||
slot_data = dict(self.options.options)
|
||||
for option in excluded_options:
|
||||
slot_data.pop(option.internal_name)
|
||||
slot_data.update({
|
||||
"seed": self.multiworld.per_slot_randoms[self.player].randrange(1000000000), # Seed should be max 9 digits
|
||||
"multiple_day_sleep_enabled": self.options[options.MultipleDaySleepEnabled],
|
||||
"multiple_day_sleep_cost": self.options[options.MultipleDaySleepCost],
|
||||
"experience_multiplier": self.options[options.ExperienceMultiplier],
|
||||
"debris_multiplier": self.options[options.DebrisMultiplier],
|
||||
"quick_start": self.options[options.QuickStart],
|
||||
"gifting": self.options[options.Gifting],
|
||||
"gift_tax": self.options[options.GiftTax],
|
||||
"modified_bundles": modified_bundles,
|
||||
"randomized_entrances": self.randomized_entrances,
|
||||
"client_version": "2.2.2",
|
||||
}
|
||||
"modified_bundles": modified_bundles,
|
||||
"client_version": "3.0.0",
|
||||
})
|
||||
|
||||
return slot_data
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from random import Random
|
||||
from typing import List, Dict, Union
|
||||
|
||||
from .bundle_data import *
|
||||
from .data.bundle_data import *
|
||||
from .logic import StardewLogic
|
||||
from .options import BundleRandomization, BundlePrice
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
from .crops_data import CropItem, SeedItem, all_crops, all_purchasable_seeds
|
||||
from .fish_data import FishItem, all_fish
|
|
@ -1,14 +1,9 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
from . import fish_data
|
||||
from .common_data import quality_dict
|
||||
from .game_item import GameItem
|
||||
|
||||
quality_dict = {
|
||||
0: "",
|
||||
1: "Silver",
|
||||
2: "Gold",
|
||||
3: "Iridium"
|
||||
}
|
||||
from .museum_data import Mineral
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
@ -218,16 +213,16 @@ iridium_bar = BundleItem.item_bundle("Iridium Bar", 337, 1, 0)
|
|||
refined_quartz = BundleItem.item_bundle("Refined Quartz", 338, 2, 0)
|
||||
coal = BundleItem.item_bundle("Coal", 382, 5, 0)
|
||||
|
||||
quartz = BundleItem.item_bundle("Quartz", 80, 1, 0)
|
||||
fire_quartz = BundleItem.item_bundle("Fire Quartz", 82, 1, 0)
|
||||
frozen_tear = BundleItem.item_bundle("Frozen Tear", 84, 1, 0)
|
||||
earth_crystal = BundleItem.item_bundle("Earth Crystal", 86, 1, 0)
|
||||
emerald = BundleItem.item_bundle("Emerald", 60, 1, 0)
|
||||
aquamarine = BundleItem.item_bundle("Aquamarine", 62, 1, 0)
|
||||
ruby = BundleItem.item_bundle("Ruby", 64, 1, 0)
|
||||
amethyst = BundleItem.item_bundle("Amethyst", 66, 1, 0)
|
||||
topaz = BundleItem.item_bundle("Topaz", 68, 1, 0)
|
||||
jade = BundleItem.item_bundle("Jade", 70, 1, 0)
|
||||
quartz = BundleItem(Mineral.quartz, 1, 0)
|
||||
fire_quartz = BundleItem(Mineral.fire_quartz, 1, 0)
|
||||
frozen_tear = BundleItem(Mineral.frozen_tear, 1, 0)
|
||||
earth_crystal = BundleItem(Mineral.earth_crystal, 1, 0)
|
||||
emerald = BundleItem(Mineral.emerald, 1, 0)
|
||||
aquamarine = BundleItem(Mineral.aquamarine, 1, 0)
|
||||
ruby = BundleItem(Mineral.ruby, 1, 0)
|
||||
amethyst = BundleItem(Mineral.amethyst, 1, 0)
|
||||
topaz = BundleItem(Mineral.topaz, 1, 0)
|
||||
jade = BundleItem(Mineral.jade, 1, 0)
|
||||
|
||||
slime = BundleItem.item_bundle("Slime", 766, 99, 0)
|
||||
bug_meat = BundleItem.item_bundle("Bug Meat", 684, 10, 0)
|
||||
|
@ -325,13 +320,12 @@ elvish_jewelry = BundleItem.item_bundle("Elvish Jewelry", 104, 1, 0)
|
|||
ancient_drum = BundleItem.item_bundle("Ancient Drum", 123, 1, 0)
|
||||
dried_starfish = BundleItem.item_bundle("Dried Starfish", 116, 1, 0)
|
||||
|
||||
# TODO Dye Bundle
|
||||
dye_red_items = [cranberries, dwarf_scroll_1, hot_pepper, radish, rhubarb, spaghetti, strawberry, tomato, tulip]
|
||||
dye_red_items = [cranberries, hot_pepper, radish, rhubarb, spaghetti, strawberry, tomato, tulip]
|
||||
dye_orange_items = [poppy, pumpkin, apricot, orange, spice_berry, winter_root]
|
||||
dye_yellow_items = [dried_starfish, dwarf_scroll_4, elvish_jewelry, corn, parsnip, summer_spangle, sunflower]
|
||||
dye_green_items = [dwarf_scroll_2, fiddlehead_fern, kale, artichoke, bok_choy, green_bean]
|
||||
dye_blue_items = [blueberry, dwarf_scroll_3, blue_jazz, blackberry, crystal_fruit]
|
||||
dye_purple_items = [ancient_drum, beet, crocus, eggplant, red_cabbage, sweet_pea]
|
||||
dye_yellow_items = [corn, parsnip, summer_spangle, sunflower]
|
||||
dye_green_items = [fiddlehead_fern, kale, artichoke, bok_choy, green_bean]
|
||||
dye_blue_items = [blueberry, blue_jazz, blackberry, crystal_fruit]
|
||||
dye_purple_items = [beet, crocus, eggplant, red_cabbage, sweet_pea]
|
||||
dye_items = [dye_red_items, dye_orange_items, dye_yellow_items, dye_green_items, dye_blue_items, dye_purple_items]
|
||||
field_research_items = [purple_mushroom, nautilus_shell, chub, geode, frozen_geode, magma_geode, omni_geode,
|
||||
rainbow_shell, amethyst, bream, carp]
|
|
@ -0,0 +1,9 @@
|
|||
fishing_chest = "Fishing Chest"
|
||||
secret_note = "Secret Note"
|
||||
|
||||
quality_dict = {
|
||||
0: "",
|
||||
1: "Silver",
|
||||
2: "Gold",
|
||||
3: "Iridium"
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
crop,farm_growth_seasons,seed,seed_seasons,seed_regions
|
||||
Amaranth,Fall,Amaranth Seeds,Fall,"Pierre's General Store,JojaMart"
|
||||
Artichoke,Fall,Artichoke Seeds,Fall,"Pierre's General Store,JojaMart"
|
||||
Beet,Fall,Beet Seeds,Fall,The Desert
|
||||
Blue Jazz,Spring,Jazz Seeds,Spring,"Pierre's General Store,JojaMart"
|
||||
Blueberry,Summer,Blueberry Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Bok Choy,Fall,Bok Choy Seeds,Fall,"Pierre's General Store,JojaMart"
|
||||
Cactus Fruit,,Cactus Seeds,,The Desert
|
||||
Cauliflower,Spring,Cauliflower Seeds,Spring,"Pierre's General Store,JojaMart"
|
||||
Corn,"Summer,Fall",Corn Seeds,"Summer,Fall","Pierre's General Store,JojaMart"
|
||||
Cranberries,Fall,Cranberry Seeds,Fall,"Pierre's General Store,JojaMart"
|
||||
Eggplant,Fall,Eggplant Seeds,Fall,"Pierre's General Store,JojaMart"
|
||||
Fairy Rose,Fall,Fairy Seeds,Fall,"Pierre's General Store,JojaMart"
|
||||
Garlic,Spring,Garlic Seeds,Spring,"Pierre's General Store,JojaMart"
|
||||
Grape,Fall,Grape Starter,Fall,"Pierre's General Store,JojaMart"
|
||||
Green Bean,Spring,Bean Starter,Spring,"Pierre's General Store,JojaMart"
|
||||
Hops,Summer,Hops Starter,Summer,"Pierre's General Store,JojaMart"
|
||||
Hot Pepper,Summer,Pepper Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Kale,Spring,Kale Seeds,Spring,"Pierre's General Store,JojaMart"
|
||||
Melon,Summer,Melon Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Parsnip,Spring,Parsnip Seeds,Spring,"Pierre's General Store,JojaMart"
|
||||
Poppy,Summer,Poppy Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Potato,Spring,Potato Seeds,Spring,"Pierre's General Store,JojaMart"
|
||||
Pumpkin,Fall,Pumpkin Seeds,Fall,"Pierre's General Store,JojaMart"
|
||||
Radish,Summer,Radish Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Red Cabbage,Summer,Red Cabbage Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Rhubarb,Spring,Rhubarb Seeds,Spring,The Desert
|
||||
Starfruit,Summer,Starfruit Seeds,Summer,The Desert
|
||||
Strawberry,Spring,Strawberry Seeds,Spring,"Pierre's General Store,JojaMart"
|
||||
Summer Spangle,Summer,Spangle Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Sunflower,"Summer,Fall",Sunflower Seeds,"Summer,Fall","Pierre's General Store,JojaMart"
|
||||
Sweet Gem Berry,Fall,Rare Seed,"Spring,Summer",Traveling Cart
|
||||
Tomato,Summer,Tomato Seeds,Summer,"Pierre's General Store,JojaMart"
|
||||
Tulip,Spring,Tulip Bulb,Spring,"Pierre's General Store,JojaMart"
|
||||
Unmilled Rice,Spring,Rice Shoot,Spring,"Pierre's General Store,JojaMart"
|
||||
Wheat,"Summer,Fall",Wheat Seeds,"Summer,Fall","Pierre's General Store,JojaMart"
|
||||
Yam,Fall,Yam Seeds,Fall,"Pierre's General Store,JojaMart"
|
|
|
@ -0,0 +1,45 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SeedItem:
|
||||
name: str
|
||||
seasons: List[str]
|
||||
regions: List[str]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CropItem:
|
||||
name: str
|
||||
farm_growth_seasons: List[str]
|
||||
seed: SeedItem
|
||||
|
||||
|
||||
def load_crop_csv():
|
||||
import csv
|
||||
try:
|
||||
from importlib.resources import files
|
||||
except ImportError:
|
||||
from importlib_resources import files # noqa
|
||||
|
||||
with files(__package__).joinpath("crops.csv").open() as file:
|
||||
reader = csv.DictReader(file)
|
||||
crops = []
|
||||
seeds = []
|
||||
|
||||
for item in reader:
|
||||
seeds.append(SeedItem(item["seed"],
|
||||
[season for season in item["seed_seasons"].split(",")]
|
||||
if item["seed_seasons"] else [],
|
||||
[region for region in item["seed_regions"].split(",")]
|
||||
if item["seed_regions"] else []))
|
||||
crops.append(CropItem(item["crop"],
|
||||
[season for season in item["farm_growth_seasons"].split(",")]
|
||||
if item["farm_growth_seasons"] else [],
|
||||
seeds[-1]))
|
||||
return crops, seeds
|
||||
|
||||
|
||||
# TODO Those two should probably be split to we can include rest of seeds
|
||||
all_crops, all_purchasable_seeds = load_crop_csv()
|
|
@ -0,0 +1,2 @@
|
|||
class Entrance:
|
||||
to_stardew_valley = "To Stardew Valley"
|
|
@ -0,0 +1,124 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import List, Tuple, Union
|
||||
|
||||
from . import season_data as season
|
||||
from .game_item import GameItem
|
||||
from .region_data import SVRegion
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FishItem(GameItem):
|
||||
locations: Tuple[str]
|
||||
seasons: Tuple[str]
|
||||
difficulty: int
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.name} [{self.item_id}] (Locations: {self.locations} |" \
|
||||
f" Seasons: {self.seasons} |" \
|
||||
f" Difficulty: {self.difficulty}) "
|
||||
|
||||
|
||||
fresh_water = (SVRegion.farm, SVRegion.forest, SVRegion.town, SVRegion.mountain)
|
||||
ocean = (SVRegion.beach,)
|
||||
town_river = (SVRegion.town,)
|
||||
mountain_lake = (SVRegion.mountain,)
|
||||
forest_pond = (SVRegion.forest,)
|
||||
forest_river = (SVRegion.forest,)
|
||||
secret_woods = (SVRegion.secret_woods,)
|
||||
mines_floor_20 = (SVRegion.mines_floor_20,)
|
||||
mines_floor_60 = (SVRegion.mines_floor_60,)
|
||||
mines_floor_100 = (SVRegion.mines_floor_100,)
|
||||
sewers = (SVRegion.sewers,)
|
||||
desert = (SVRegion.desert,)
|
||||
mutant_bug_lair = (SVRegion.mutant_bug_lair,)
|
||||
witch_swamp = (SVRegion.witch_swamp,)
|
||||
night_market = (SVRegion.beach,)
|
||||
ginger_island_ocean = (SVRegion.ginger_island,)
|
||||
ginger_island_river = (SVRegion.ginger_island,)
|
||||
pirate_cove = (SVRegion.pirate_cove,)
|
||||
|
||||
all_fish: List[FishItem] = []
|
||||
|
||||
|
||||
def create_fish(name: str, item_id: int, locations: Tuple[str, ...], seasons: Union[str, Tuple[str, ...]],
|
||||
difficulty: int) -> FishItem:
|
||||
if isinstance(seasons, str):
|
||||
seasons = (seasons,)
|
||||
|
||||
fish_item = FishItem(name, item_id, locations, seasons, difficulty)
|
||||
all_fish.append(fish_item)
|
||||
return fish_item
|
||||
|
||||
|
||||
albacore = create_fish("Albacore", 705, ocean, (season.fall, season.winter), 60)
|
||||
anchovy = create_fish("Anchovy", 129, ocean, (season.spring, season.fall), 30)
|
||||
blue_discus = create_fish("Blue Discus", 838, ginger_island_river, season.all_seasons, 60)
|
||||
bream = create_fish("Bream", 132, town_river + forest_river, season.all_seasons, 35)
|
||||
bullhead = create_fish("Bullhead", 700, mountain_lake, season.all_seasons, 46)
|
||||
carp = create_fish("Carp", 142, mountain_lake + secret_woods + sewers + mutant_bug_lair, season.not_winter, 15)
|
||||
catfish = create_fish("Catfish", 143, town_river + forest_river + secret_woods, (season.spring, season.fall), 75)
|
||||
chub = create_fish("Chub", 702, forest_river + mountain_lake, season.all_seasons, 35)
|
||||
dorado = create_fish("Dorado", 704, forest_river, season.summer, 78)
|
||||
eel = create_fish("Eel", 148, ocean, (season.spring, season.fall), 70)
|
||||
flounder = create_fish("Flounder", 267, ocean, (season.spring, season.summer), 50)
|
||||
ghostfish = create_fish("Ghostfish", 156, mines_floor_20 + mines_floor_60, season.all_seasons, 50)
|
||||
halibut = create_fish("Halibut", 708, ocean, season.not_fall, 50)
|
||||
herring = create_fish("Herring", 147, ocean, (season.spring, season.winter), 25)
|
||||
ice_pip = create_fish("Ice Pip", 161, mines_floor_60, season.all_seasons, 85)
|
||||
largemouth_bass = create_fish("Largemouth Bass", 136, mountain_lake, season.all_seasons, 50)
|
||||
lava_eel = create_fish("Lava Eel", 162, mines_floor_100, season.all_seasons, 90)
|
||||
lingcod = create_fish("Lingcod", 707, town_river + forest_river + mountain_lake, season.winter, 85)
|
||||
lionfish = create_fish("Lionfish", 837, ginger_island_ocean, season.all_seasons, 50)
|
||||
midnight_carp = create_fish("Midnight Carp", 269, mountain_lake + forest_pond + ginger_island_river,
|
||||
(season.fall, season.winter), 55)
|
||||
octopus = create_fish("Octopus", 149, ocean, season.summer, 95)
|
||||
perch = create_fish("Perch", 141, town_river + forest_river + forest_pond + mountain_lake, season.winter, 35)
|
||||
pike = create_fish("Pike", 144, town_river + forest_river + forest_pond, (season.summer, season.winter), 60)
|
||||
pufferfish = create_fish("Pufferfish", 128, ocean + ginger_island_ocean, season.summer, 80)
|
||||
rainbow_trout = create_fish("Rainbow Trout", 138, town_river + forest_river + mountain_lake, season.summer, 45)
|
||||
red_mullet = create_fish("Red Mullet", 146, ocean, (season.summer, season.winter), 55)
|
||||
red_snapper = create_fish("Red Snapper", 150, ocean, (season.summer, season.fall), 40)
|
||||
salmon = create_fish("Salmon", 139, town_river + forest_river, season.fall, 50)
|
||||
sandfish = create_fish("Sandfish", 164, desert, season.all_seasons, 65)
|
||||
sardine = create_fish("Sardine", 131, ocean, (season.spring, season.fall, season.winter), 30)
|
||||
scorpion_carp = create_fish("Scorpion Carp", 165, desert, season.all_seasons, 90)
|
||||
sea_cucumber = create_fish("Sea Cucumber", 154, ocean, (season.fall, season.winter), 40)
|
||||
shad = create_fish("Shad", 706, town_river + forest_river, season.not_winter, 45)
|
||||
slimejack = create_fish("Slimejack", 796, mutant_bug_lair, season.all_seasons, 55)
|
||||
smallmouth_bass = create_fish("Smallmouth Bass", 137, town_river + forest_river, (season.spring, season.fall), 28)
|
||||
squid = create_fish("Squid", 151, ocean, season.winter, 75)
|
||||
stingray = create_fish("Stingray", 836, pirate_cove, season.all_seasons, 80)
|
||||
stonefish = create_fish("Stonefish", 158, mines_floor_20, season.all_seasons, 65)
|
||||
sturgeon = create_fish("Sturgeon", 698, mountain_lake, (season.summer, season.winter), 78)
|
||||
sunfish = create_fish("Sunfish", 145, town_river + forest_river, (season.spring, season.summer), 30)
|
||||
super_cucumber = create_fish("Super Cucumber", 155, ocean + ginger_island_ocean, (season.summer, season.fall), 80)
|
||||
tiger_trout = create_fish("Tiger Trout", 699, town_river + forest_river, (season.fall, season.winter), 60)
|
||||
tilapia = create_fish("Tilapia", 701, ocean + ginger_island_ocean, (season.summer, season.fall), 50)
|
||||
tuna = create_fish("Tuna", 130, ocean + ginger_island_ocean, (season.summer, season.winter), 70)
|
||||
void_salmon = create_fish("Void Salmon", 795, witch_swamp, season.all_seasons, 80)
|
||||
walleye = create_fish("Walleye", 140, town_river + forest_river + forest_pond + mountain_lake, season.fall, 45)
|
||||
woodskip = create_fish("Woodskip", 734, secret_woods, season.all_seasons, 50)
|
||||
|
||||
blob_fish = create_fish("Blobfish", 800, night_market, season.winter, 75)
|
||||
midnight_squid = create_fish("Midnight Squid", 798, night_market, season.winter, 55)
|
||||
spook_fish = create_fish("Spook Fish", 799, night_market, season.winter, 60)
|
||||
|
||||
angler = create_fish("Angler", 160, town_river, season.fall, 85)
|
||||
crimsonfish = create_fish("Crimsonfish", 159, ocean, season.summer, 95)
|
||||
glacierfish = create_fish("Glacierfish", 775, forest_river, season.winter, 100)
|
||||
legend = create_fish("Legend", 163, mountain_lake, season.spring, 110)
|
||||
mutant_carp = create_fish("Mutant Carp", 682, sewers, season.all_seasons, 80)
|
||||
|
||||
clam = create_fish("Clam", 372, ocean, season.all_seasons, -1)
|
||||
cockle = create_fish("Cockle", 718, ocean, season.all_seasons, -1)
|
||||
crab = create_fish("Crab", 717, ocean, season.all_seasons, -1)
|
||||
crayfish = create_fish("Crayfish", 716, fresh_water, season.all_seasons, -1)
|
||||
lobster = create_fish("Lobster", 715, ocean, season.all_seasons, -1)
|
||||
mussel = create_fish("Mussel", 719, ocean, season.all_seasons, -1)
|
||||
oyster = create_fish("Oyster", 723, ocean, season.all_seasons, -1)
|
||||
periwinkle = create_fish("Periwinkle", 722, fresh_water, season.all_seasons, -1)
|
||||
shrimp = create_fish("Shrimp", 720, ocean, season.all_seasons, -1)
|
||||
snail = create_fish("Snail", 721, fresh_water, season.all_seasons, -1)
|
||||
|
||||
legendary_fish = [crimsonfish, angler, legend, glacierfish, mutant_carp]
|
||||
special_fish = [*legendary_fish, blob_fish, lava_eel, octopus, scorpion_carp, ice_pip, super_cucumber, dorado]
|
|
@ -0,0 +1,13 @@
|
|||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GameItem:
|
||||
name: str
|
||||
item_id: int
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.name} [{self.item_id}]"
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.name < other.name
|
|
@ -1,7 +1,7 @@
|
|||
id,name,classification,groups
|
||||
0,Joja Cola,filler,TRASH
|
||||
15,Rusty Key,progression,
|
||||
16,Dwarvish Translation Guide,progression,
|
||||
15,Rusty Key,progression,MUSEUM
|
||||
16,Dwarvish Translation Guide,progression,MUSEUM
|
||||
17,Bridge Repair,progression,COMMUNITY_REWARD
|
||||
18,Greenhouse,progression,COMMUNITY_REWARD
|
||||
19,Glittering Boulder Removed,progression,COMMUNITY_REWARD
|
||||
|
@ -98,20 +98,99 @@ id,name,classification,groups
|
|||
110,Luck Bonus,useful,
|
||||
111,Lava Katana,progression,"MINES_FLOOR_110,WEAPON"
|
||||
112,Progressive House,progression,
|
||||
113,Traveling Merchant: Sunday,progression,
|
||||
114,Traveling Merchant: Monday,progression,
|
||||
115,Traveling Merchant: Tuesday,progression,
|
||||
116,Traveling Merchant: Wednesday,progression,
|
||||
117,Traveling Merchant: Thursday,progression,
|
||||
118,Traveling Merchant: Friday,progression,
|
||||
119,Traveling Merchant: Saturday,progression,
|
||||
113,Traveling Merchant: Sunday,progression,TRAVELING_MERCHANT_DAY
|
||||
114,Traveling Merchant: Monday,progression,TRAVELING_MERCHANT_DAY
|
||||
115,Traveling Merchant: Tuesday,progression,TRAVELING_MERCHANT_DAY
|
||||
116,Traveling Merchant: Wednesday,progression,TRAVELING_MERCHANT_DAY
|
||||
117,Traveling Merchant: Thursday,progression,TRAVELING_MERCHANT_DAY
|
||||
118,Traveling Merchant: Friday,progression,TRAVELING_MERCHANT_DAY
|
||||
119,Traveling Merchant: Saturday,progression,TRAVELING_MERCHANT_DAY
|
||||
120,Traveling Merchant Stock Size,progression,
|
||||
121,Traveling Merchant Discount,progression,
|
||||
122,Return Scepter,useful,
|
||||
5000,Resource Pack: 500 Money,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5001,Resource Pack: 1000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5002,Resource Pack: 1500 Money,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5003,Resource Pack: 2000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
123,Progressive Season,progression,
|
||||
124,Spring,progression,SEASON
|
||||
125,Summer,progression,SEASON
|
||||
126,Fall,progression,SEASON
|
||||
127,Winter,progression,SEASON
|
||||
128,Amaranth Seeds,progression,SEED_SHUFFLE
|
||||
129,Artichoke Seeds,progression,SEED_SHUFFLE
|
||||
130,Beet Seeds,progression,SEED_SHUFFLE
|
||||
131,Jazz Seeds,progression,SEED_SHUFFLE
|
||||
132,Blueberry Seeds,progression,SEED_SHUFFLE
|
||||
133,Bok Choy Seeds,progression,SEED_SHUFFLE
|
||||
134,Cauliflower Seeds,progression,SEED_SHUFFLE
|
||||
135,Corn Seeds,progression,SEED_SHUFFLE
|
||||
136,Cranberry Seeds,progression,SEED_SHUFFLE
|
||||
137,Eggplant Seeds,progression,SEED_SHUFFLE
|
||||
138,Fairy Seeds,progression,SEED_SHUFFLE
|
||||
139,Garlic Seeds,progression,SEED_SHUFFLE
|
||||
140,Grape Starter,progression,SEED_SHUFFLE
|
||||
141,Bean Starter,progression,SEED_SHUFFLE
|
||||
142,Hops Starter,progression,SEED_SHUFFLE
|
||||
143,Pepper Seeds,progression,SEED_SHUFFLE
|
||||
144,Kale Seeds,progression,SEED_SHUFFLE
|
||||
145,Melon Seeds,progression,SEED_SHUFFLE
|
||||
146,Parsnip Seeds,progression,SEED_SHUFFLE
|
||||
147,Poppy Seeds,progression,SEED_SHUFFLE
|
||||
148,Potato Seeds,progression,SEED_SHUFFLE
|
||||
149,Pumpkin Seeds,progression,SEED_SHUFFLE
|
||||
150,Radish Seeds,progression,SEED_SHUFFLE
|
||||
151,Red Cabbage Seeds,progression,SEED_SHUFFLE
|
||||
152,Rhubarb Seeds,progression,SEED_SHUFFLE
|
||||
153,Starfruit Seeds,progression,SEED_SHUFFLE
|
||||
154,Strawberry Seeds,progression,SEED_SHUFFLE
|
||||
155,Spangle Seeds,progression,SEED_SHUFFLE
|
||||
156,Sunflower Seeds,progression,SEED_SHUFFLE
|
||||
157,Tomato Seeds,progression,SEED_SHUFFLE
|
||||
158,Tulip Bulb,progression,SEED_SHUFFLE
|
||||
159,Rice Shoot,progression,SEED_SHUFFLE
|
||||
160,Wheat Seeds,progression,SEED_SHUFFLE
|
||||
161,Yam Seeds,progression,SEED_SHUFFLE
|
||||
162,Cactus Seeds,progression,SEED_SHUFFLE
|
||||
163,Magic Rock Candy,useful,MUSEUM
|
||||
164,Ancient Seeds Recipe,progression,MUSEUM
|
||||
165,Ancient Seeds,useful,MUSEUM
|
||||
166,Traveling Merchant Metal Detector,progression,MUSEUM
|
||||
167,Alex: 1 <3,progression,FRIENDSANITY
|
||||
168,Elliott: 1 <3,progression,FRIENDSANITY
|
||||
169,Harvey: 1 <3,progression,FRIENDSANITY
|
||||
170,Sam: 1 <3,progression,FRIENDSANITY
|
||||
171,Sebastian: 1 <3,progression,FRIENDSANITY
|
||||
172,Shane: 1 <3,progression,FRIENDSANITY
|
||||
173,Abigail: 1 <3,progression,FRIENDSANITY
|
||||
174,Emily: 1 <3,progression,FRIENDSANITY
|
||||
175,Haley: 1 <3,progression,FRIENDSANITY
|
||||
176,Leah: 1 <3,progression,FRIENDSANITY
|
||||
177,Maru: 1 <3,progression,FRIENDSANITY
|
||||
178,Penny: 1 <3,progression,FRIENDSANITY
|
||||
179,Caroline: 1 <3,progression,FRIENDSANITY
|
||||
180,Clint: 1 <3,progression,FRIENDSANITY
|
||||
181,Demetrius: 1 <3,progression,FRIENDSANITY
|
||||
182,Dwarf: 1 <3,progression,FRIENDSANITY
|
||||
183,Evelyn: 1 <3,progression,FRIENDSANITY
|
||||
184,George: 1 <3,progression,FRIENDSANITY
|
||||
185,Gus: 1 <3,progression,FRIENDSANITY
|
||||
186,Jas: 1 <3,progression,FRIENDSANITY
|
||||
187,Jodi: 1 <3,progression,FRIENDSANITY
|
||||
188,Kent: 1 <3,progression,FRIENDSANITY
|
||||
189,Krobus: 1 <3,progression,FRIENDSANITY
|
||||
190,Leo: 1 <3,progression,FRIENDSANITY
|
||||
191,Lewis: 1 <3,progression,FRIENDSANITY
|
||||
192,Linus: 1 <3,progression,FRIENDSANITY
|
||||
193,Marnie: 1 <3,progression,FRIENDSANITY
|
||||
194,Pam: 1 <3,progression,FRIENDSANITY
|
||||
195,Pierre: 1 <3,progression,FRIENDSANITY
|
||||
196,Robin: 1 <3,progression,FRIENDSANITY
|
||||
197,Sandy: 1 <3,progression,FRIENDSANITY
|
||||
198,Vincent: 1 <3,progression,FRIENDSANITY
|
||||
199,Willy: 1 <3,progression,FRIENDSANITY
|
||||
200,Wizard: 1 <3,progression,FRIENDSANITY
|
||||
201,Pet: 1 <3,progression,FRIENDSANITY
|
||||
5000,Resource Pack: 500 Money,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5001,Resource Pack: 1000 Money,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5002,Resource Pack: 1500 Money,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5003,Resource Pack: 2000 Money,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5004,Resource Pack: 25 Stone,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5005,Resource Pack: 50 Stone,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5006,Resource Pack: 75 Stone,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
|
@ -120,10 +199,10 @@ id,name,classification,groups
|
|||
5009,Resource Pack: 50 Wood,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5010,Resource Pack: 75 Wood,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5011,Resource Pack: 100 Wood,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5012,Resource Pack: 5 Hardwood,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5013,Resource Pack: 10 Hardwood,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5014,Resource Pack: 15 Hardwood,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5015,Resource Pack: 20 Hardwood,useful,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5012,Resource Pack: 5 Hardwood,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5013,Resource Pack: 10 Hardwood,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5014,Resource Pack: 15 Hardwood,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5015,Resource Pack: 20 Hardwood,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5016,Resource Pack: 15 Fiber,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5017,Resource Pack: 30 Fiber,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
5018,Resource Pack: 45 Fiber,filler,"BASE_RESOURCE,RESOURCE_PACK"
|
||||
|
@ -178,10 +257,10 @@ id,name,classification,groups
|
|||
5067,Resource Pack: 6 Magma Geode,filler,"GEODE,RESOURCE_PACK"
|
||||
5068,Resource Pack: 9 Magma Geode,filler,"GEODE,RESOURCE_PACK"
|
||||
5069,Resource Pack: 12 Magma Geode,filler,"GEODE,RESOURCE_PACK"
|
||||
5070,Resource Pack: 2 Omni Geode,useful,"GEODE,RESOURCE_PACK"
|
||||
5071,Resource Pack: 4 Omni Geode,useful,"GEODE,RESOURCE_PACK"
|
||||
5072,Resource Pack: 6 Omni Geode,useful,"GEODE,RESOURCE_PACK"
|
||||
5073,Resource Pack: 8 Omni Geode,useful,"GEODE,RESOURCE_PACK"
|
||||
5070,Resource Pack: 2 Omni Geode,filler,"GEODE,RESOURCE_PACK"
|
||||
5071,Resource Pack: 4 Omni Geode,filler,"GEODE,RESOURCE_PACK"
|
||||
5072,Resource Pack: 6 Omni Geode,filler,"GEODE,RESOURCE_PACK"
|
||||
5073,Resource Pack: 8 Omni Geode,filler,"GEODE,RESOURCE_PACK"
|
||||
5074,Resource Pack: 25 Copper Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5075,Resource Pack: 50 Copper Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5076,Resource Pack: 75 Copper Ore,filler,"ORE,RESOURCE_PACK"
|
||||
|
@ -192,14 +271,14 @@ id,name,classification,groups
|
|||
5081,Resource Pack: 50 Iron Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5082,Resource Pack: 75 Iron Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5083,Resource Pack: 100 Iron Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5084,Resource Pack: 12 Gold Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5085,Resource Pack: 25 Gold Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5086,Resource Pack: 38 Gold Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5087,Resource Pack: 50 Gold Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5088,Resource Pack: 5 Iridium Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5089,Resource Pack: 10 Iridium Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5090,Resource Pack: 15 Iridium Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5091,Resource Pack: 20 Iridium Ore,useful,"ORE,RESOURCE_PACK"
|
||||
5084,Resource Pack: 12 Gold Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5085,Resource Pack: 25 Gold Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5086,Resource Pack: 38 Gold Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5087,Resource Pack: 50 Gold Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5088,Resource Pack: 5 Iridium Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5089,Resource Pack: 10 Iridium Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5090,Resource Pack: 15 Iridium Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5091,Resource Pack: 20 Iridium Ore,filler,"ORE,RESOURCE_PACK"
|
||||
5092,Resource Pack: 5 Quartz,filler,"ORE,RESOURCE_PACK"
|
||||
5093,Resource Pack: 10 Quartz,filler,"ORE,RESOURCE_PACK"
|
||||
5094,Resource Pack: 15 Quartz,filler,"ORE,RESOURCE_PACK"
|
||||
|
@ -240,24 +319,24 @@ id,name,classification,groups
|
|||
5129,Resource Pack: 28 Deluxe Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5130,Resource Pack: 36 Deluxe Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5131,Resource Pack: 40 Deluxe Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5132,Resource Pack: 2 Deluxe Fertilizer,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5133,Resource Pack: 6 Deluxe Fertilizer,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5134,Resource Pack: 10 Deluxe Fertilizer,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5135,Resource Pack: 14 Deluxe Fertilizer,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5136,Resource Pack: 18 Deluxe Fertilizer,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5137,Resource Pack: 20 Deluxe Fertilizer,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5138,Resource Pack: 2 Deluxe Retaining Soil,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5139,Resource Pack: 6 Deluxe Retaining Soil,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5140,Resource Pack: 10 Deluxe Retaining Soil,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5141,Resource Pack: 14 Deluxe Retaining Soil,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5142,Resource Pack: 18 Deluxe Retaining Soil,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5143,Resource Pack: 20 Deluxe Retaining Soil,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5144,Resource Pack: 2 Hyper Speed-Gro,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5145,Resource Pack: 6 Hyper Speed-Gro,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5146,Resource Pack: 10 Hyper Speed-Gro,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5147,Resource Pack: 14 Hyper Speed-Gro,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5148,Resource Pack: 18 Hyper Speed-Gro,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5149,Resource Pack: 20 Hyper Speed-Gro,useful,"FERTILIZER,RESOURCE_PACK"
|
||||
5132,Resource Pack: 2 Deluxe Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5133,Resource Pack: 6 Deluxe Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5134,Resource Pack: 10 Deluxe Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5135,Resource Pack: 14 Deluxe Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5136,Resource Pack: 18 Deluxe Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5137,Resource Pack: 20 Deluxe Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5138,Resource Pack: 2 Deluxe Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5139,Resource Pack: 6 Deluxe Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5140,Resource Pack: 10 Deluxe Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5141,Resource Pack: 14 Deluxe Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5142,Resource Pack: 18 Deluxe Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5143,Resource Pack: 20 Deluxe Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5144,Resource Pack: 2 Hyper Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5145,Resource Pack: 6 Hyper Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5146,Resource Pack: 10 Hyper Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5147,Resource Pack: 14 Hyper Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5148,Resource Pack: 18 Hyper Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5149,Resource Pack: 20 Hyper Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5150,Resource Pack: 2 Tree Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5151,Resource Pack: 6 Tree Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
5152,Resource Pack: 10 Tree Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
|
||||
|
@ -306,7 +385,7 @@ id,name,classification,groups
|
|||
5195,Resource Pack: 4 Crab Pot,filler,"FISHING_RESOURCE,RESOURCE_PACK"
|
||||
5196,Resource Pack: 5 Crab Pot,filler,"FISHING_RESOURCE,RESOURCE_PACK"
|
||||
5197,Resource Pack: 6 Crab Pot,filler,"FISHING_RESOURCE,RESOURCE_PACK"
|
||||
5198,Friendship Bonus (1 <3),useful,FRIENDSHIP_PACK
|
||||
5199,Friendship Bonus (2 <3),useful,FRIENDSHIP_PACK
|
||||
5200,Friendship Bonus (3 <3),useful,FRIENDSHIP_PACK
|
||||
5201,Friendship Bonus (4 <3),useful,FRIENDSHIP_PACK
|
||||
5198,Friendship Bonus (1 <3),filler,FRIENDSHIP_PACK
|
||||
5199,Friendship Bonus (2 <3),filler,FRIENDSHIP_PACK
|
||||
5200,Friendship Bonus (3 <3),filler,FRIENDSHIP_PACK
|
||||
5201,Friendship Bonus (4 <3),filler,FRIENDSHIP_PACK
|
||||
|
|
|
|
@ -377,3 +377,534 @@ id,region,name,tags
|
|||
1063,Beach,Fishsanity: Mussel,FISHSANITY
|
||||
1064,Beach,Fishsanity: Shrimp,FISHSANITY
|
||||
1065,Beach,Fishsanity: Oyster,FISHSANITY
|
||||
1100,Stardew Valley,Museumsanity: 5 Donations,MUSEUM_MILESTONES
|
||||
1101,Stardew Valley,Museumsanity: 10 Donations,MUSEUM_MILESTONES
|
||||
1102,Stardew Valley,Museumsanity: 15 Donations,MUSEUM_MILESTONES
|
||||
1103,Stardew Valley,Museumsanity: 20 Donations,MUSEUM_MILESTONES
|
||||
1104,Stardew Valley,Museumsanity: 25 Donations,MUSEUM_MILESTONES
|
||||
1105,Stardew Valley,Museumsanity: 30 Donations,MUSEUM_MILESTONES
|
||||
1106,Stardew Valley,Museumsanity: 35 Donations,MUSEUM_MILESTONES
|
||||
1107,Stardew Valley,Museumsanity: 40 Donations,MUSEUM_MILESTONES
|
||||
1108,Stardew Valley,Museumsanity: 50 Donations,MUSEUM_MILESTONES
|
||||
1109,Stardew Valley,Museumsanity: 60 Donations,MUSEUM_MILESTONES
|
||||
1110,Stardew Valley,Museumsanity: 70 Donations,MUSEUM_MILESTONES
|
||||
1111,Stardew Valley,Museumsanity: 80 Donations,MUSEUM_MILESTONES
|
||||
1112,Stardew Valley,Museumsanity: 90 Donations,MUSEUM_MILESTONES
|
||||
1113,Stardew Valley,Museumsanity: 95 Donations,MUSEUM_MILESTONES
|
||||
1114,Stardew Valley,Museumsanity: 11 Minerals,MUSEUM_MILESTONES
|
||||
1115,Stardew Valley,Museumsanity: 21 Minerals,MUSEUM_MILESTONES
|
||||
1116,Stardew Valley,Museumsanity: 31 Minerals,MUSEUM_MILESTONES
|
||||
1117,Stardew Valley,Museumsanity: 41 Minerals,MUSEUM_MILESTONES
|
||||
1118,Stardew Valley,Museumsanity: 50 Minerals,MUSEUM_MILESTONES
|
||||
1119,Stardew Valley,Museumsanity: 3 Artifacts,MUSEUM_MILESTONES
|
||||
1120,Stardew Valley,Museumsanity: 6 Artifacts,MUSEUM_MILESTONES
|
||||
1121,Stardew Valley,Museumsanity: 9 Artifacts,MUSEUM_MILESTONES
|
||||
1122,Stardew Valley,Museumsanity: 11 Artifacts,MUSEUM_MILESTONES
|
||||
1123,Stardew Valley,Museumsanity: 15 Artifacts,MUSEUM_MILESTONES
|
||||
1124,Stardew Valley,Museumsanity: 20 Artifacts,MUSEUM_MILESTONES
|
||||
1125,Stardew Valley,Museumsanity: Dwarf Scrolls,MUSEUM_MILESTONES
|
||||
1126,Stardew Valley,Museumsanity: Skeleton Front,MUSEUM_MILESTONES
|
||||
1127,Stardew Valley,Museumsanity: Skeleton Middle,MUSEUM_MILESTONES
|
||||
1128,Stardew Valley,Museumsanity: Skeleton Back,MUSEUM_MILESTONES
|
||||
1201,The Mines - Floor 20,Museumsanity: Dwarf Scroll I,MUSEUM_DONATIONS
|
||||
1202,The Mines - Floor 20,Museumsanity: Dwarf Scroll II,MUSEUM_DONATIONS
|
||||
1203,The Mines - Floor 60,Museumsanity: Dwarf Scroll III,MUSEUM_DONATIONS
|
||||
1204,The Mines - Floor 100,Museumsanity: Dwarf Scroll IV,MUSEUM_DONATIONS
|
||||
1205,Town,Museumsanity: Chipped Amphora,MUSEUM_DONATIONS
|
||||
1206,Forest,Museumsanity: Arrowhead,MUSEUM_DONATIONS
|
||||
1207,Forest,Museumsanity: Ancient Doll,MUSEUM_DONATIONS
|
||||
1208,Forest,Museumsanity: Elvish Jewelry,MUSEUM_DONATIONS
|
||||
1209,Forest,Museumsanity: Chewing Stick,MUSEUM_DONATIONS
|
||||
1210,Forest,Museumsanity: Ornamental Fan,MUSEUM_DONATIONS
|
||||
1211,Mountain,Museumsanity: Dinosaur Egg,MUSEUM_DONATIONS
|
||||
1212,Stardew Valley,Museumsanity: Rare Disc,MUSEUM_DONATIONS
|
||||
1213,Forest,Museumsanity: Ancient Sword,MUSEUM_DONATIONS
|
||||
1214,Town,Museumsanity: Rusty Spoon,MUSEUM_DONATIONS
|
||||
1215,Farm,Museumsanity: Rusty Spur,MUSEUM_DONATIONS
|
||||
1216,Mountain,Museumsanity: Rusty Cog,MUSEUM_DONATIONS
|
||||
1217,Farm,Museumsanity: Chicken Statue,MUSEUM_DONATIONS
|
||||
1218,Forest,Museumsanity: Ancient Seed,"MUSEUM_DONATIONS,MUSEUM_MILESTONES"
|
||||
1219,Forest,Museumsanity: Prehistoric Tool,MUSEUM_DONATIONS
|
||||
1220,Beach,Museumsanity: Dried Starfish,MUSEUM_DONATIONS
|
||||
1221,Beach,Museumsanity: Anchor,MUSEUM_DONATIONS
|
||||
1222,Beach,Museumsanity: Glass Shards,MUSEUM_DONATIONS
|
||||
1223,Forest,Museumsanity: Bone Flute,MUSEUM_DONATIONS
|
||||
1224,Forest,Museumsanity: Prehistoric Handaxe,MUSEUM_DONATIONS
|
||||
1225,The Mines - Floor 20,Museumsanity: Dwarvish Helm,MUSEUM_DONATIONS
|
||||
1226,The Mines - Floor 60,Museumsanity: Dwarf Gadget,MUSEUM_DONATIONS
|
||||
1227,Forest,Museumsanity: Ancient Drum,MUSEUM_DONATIONS
|
||||
1228,The Desert,Museumsanity: Golden Mask,MUSEUM_DONATIONS
|
||||
1229,The Desert,Museumsanity: Golden Relic,MUSEUM_DONATIONS
|
||||
1230,Town,Museumsanity: Strange Doll (Green),MUSEUM_DONATIONS
|
||||
1231,The Desert,Museumsanity: Strange Doll,MUSEUM_DONATIONS
|
||||
1232,Forest,Museumsanity: Prehistoric Scapula,MUSEUM_DONATIONS
|
||||
1233,Forest,Museumsanity: Prehistoric Tibia,MUSEUM_DONATIONS
|
||||
1234,Ginger Island,Museumsanity: Prehistoric Skull,MUSEUM_DONATIONS
|
||||
1235,Ginger Island,Museumsanity: Skeletal Hand,MUSEUM_DONATIONS
|
||||
1236,Ginger Island,Museumsanity: Prehistoric Rib,MUSEUM_DONATIONS
|
||||
1237,Ginger Island,Museumsanity: Prehistoric Vertebra,MUSEUM_DONATIONS
|
||||
1238,Ginger Island,Museumsanity: Skeletal Tail,MUSEUM_DONATIONS
|
||||
1239,Ginger Island,Museumsanity: Nautilus Fossil,MUSEUM_DONATIONS
|
||||
1240,Forest,Museumsanity: Amphibian Fossil,MUSEUM_DONATIONS
|
||||
1241,Forest,Museumsanity: Palm Fossil,MUSEUM_DONATIONS
|
||||
1242,Forest,Museumsanity: Trilobite,MUSEUM_DONATIONS
|
||||
1243,The Mines - Floor 20,Museumsanity: Quartz,MUSEUM_DONATIONS
|
||||
1244,The Mines - Floor 100,Museumsanity: Fire Quartz,MUSEUM_DONATIONS
|
||||
1245,The Mines - Floor 60,Museumsanity: Frozen Tear,MUSEUM_DONATIONS
|
||||
1246,The Mines - Floor 20,Museumsanity: Earth Crystal,MUSEUM_DONATIONS
|
||||
1247,The Mines - Floor 100,Museumsanity: Emerald,MUSEUM_DONATIONS
|
||||
1248,The Mines - Floor 60,Museumsanity: Aquamarine,MUSEUM_DONATIONS
|
||||
1249,The Mines - Floor 100,Museumsanity: Ruby,MUSEUM_DONATIONS
|
||||
1250,The Mines - Floor 20,Museumsanity: Amethyst,MUSEUM_DONATIONS
|
||||
1251,The Mines - Floor 20,Museumsanity: Topaz,MUSEUM_DONATIONS
|
||||
1252,The Mines - Floor 60,Museumsanity: Jade,MUSEUM_DONATIONS
|
||||
1253,The Mines - Floor 60,Museumsanity: Diamond,MUSEUM_DONATIONS
|
||||
1254,Skull Cavern Floor 100,Museumsanity: Prismatic Shard,MUSEUM_DONATIONS
|
||||
1255,Town,Museumsanity: Alamite,MUSEUM_DONATIONS
|
||||
1256,Town,Museumsanity: Bixite,MUSEUM_DONATIONS
|
||||
1257,Town,Museumsanity: Baryte,MUSEUM_DONATIONS
|
||||
1258,Town,Museumsanity: Aerinite,MUSEUM_DONATIONS
|
||||
1259,Town,Museumsanity: Calcite,MUSEUM_DONATIONS
|
||||
1260,Town,Museumsanity: Dolomite,MUSEUM_DONATIONS
|
||||
1261,Town,Museumsanity: Esperite,MUSEUM_DONATIONS
|
||||
1262,Town,Museumsanity: Fluorapatite,MUSEUM_DONATIONS
|
||||
1263,Town,Museumsanity: Geminite,MUSEUM_DONATIONS
|
||||
1264,Town,Museumsanity: Helvite,MUSEUM_DONATIONS
|
||||
1265,Town,Museumsanity: Jamborite,MUSEUM_DONATIONS
|
||||
1266,Town,Museumsanity: Jagoite,MUSEUM_DONATIONS
|
||||
1267,Town,Museumsanity: Kyanite,MUSEUM_DONATIONS
|
||||
1268,Town,Museumsanity: Lunarite,MUSEUM_DONATIONS
|
||||
1269,Town,Museumsanity: Malachite,MUSEUM_DONATIONS
|
||||
1270,Town,Museumsanity: Neptunite,MUSEUM_DONATIONS
|
||||
1271,Town,Museumsanity: Lemon Stone,MUSEUM_DONATIONS
|
||||
1272,Town,Museumsanity: Nekoite,MUSEUM_DONATIONS
|
||||
1273,Town,Museumsanity: Orpiment,MUSEUM_DONATIONS
|
||||
1274,Town,Museumsanity: Petrified Slime,MUSEUM_DONATIONS
|
||||
1275,Town,Museumsanity: Thunder Egg,MUSEUM_DONATIONS
|
||||
1276,Town,Museumsanity: Pyrite,MUSEUM_DONATIONS
|
||||
1277,Town,Museumsanity: Ocean Stone,MUSEUM_DONATIONS
|
||||
1278,Town,Museumsanity: Ghost Crystal,MUSEUM_DONATIONS
|
||||
1279,Town,Museumsanity: Tigerseye,MUSEUM_DONATIONS
|
||||
1280,Town,Museumsanity: Jasper,MUSEUM_DONATIONS
|
||||
1281,Town,Museumsanity: Opal,MUSEUM_DONATIONS
|
||||
1282,Town,Museumsanity: Fire Opal,MUSEUM_DONATIONS
|
||||
1283,Town,Museumsanity: Celestine,MUSEUM_DONATIONS
|
||||
1284,Town,Museumsanity: Marble,MUSEUM_DONATIONS
|
||||
1285,Town,Museumsanity: Sandstone,MUSEUM_DONATIONS
|
||||
1286,Town,Museumsanity: Granite,MUSEUM_DONATIONS
|
||||
1287,Town,Museumsanity: Basalt,MUSEUM_DONATIONS
|
||||
1288,Town,Museumsanity: Limestone,MUSEUM_DONATIONS
|
||||
1289,Town,Museumsanity: Soapstone,MUSEUM_DONATIONS
|
||||
1290,Town,Museumsanity: Hematite,MUSEUM_DONATIONS
|
||||
1291,Town,Museumsanity: Mudstone,MUSEUM_DONATIONS
|
||||
1292,Town,Museumsanity: Obsidian,MUSEUM_DONATIONS
|
||||
1293,Town,Museumsanity: Slate,MUSEUM_DONATIONS
|
||||
1294,Town,Museumsanity: Fairy Stone,MUSEUM_DONATIONS
|
||||
1295,Town,Museumsanity: Star Shards,MUSEUM_DONATIONS
|
||||
1301,Town,Friendsanity: Alex 1 <3,FRIENDSANITY
|
||||
1302,Town,Friendsanity: Alex 2 <3,FRIENDSANITY
|
||||
1303,Town,Friendsanity: Alex 3 <3,FRIENDSANITY
|
||||
1304,Town,Friendsanity: Alex 4 <3,FRIENDSANITY
|
||||
1305,Town,Friendsanity: Alex 5 <3,FRIENDSANITY
|
||||
1306,Town,Friendsanity: Alex 6 <3,FRIENDSANITY
|
||||
1307,Town,Friendsanity: Alex 7 <3,FRIENDSANITY
|
||||
1308,Town,Friendsanity: Alex 8 <3,FRIENDSANITY
|
||||
1309,Town,Friendsanity: Alex 9 <3,FRIENDSANITY
|
||||
1310,Town,Friendsanity: Alex 10 <3,FRIENDSANITY
|
||||
1311,Town,Friendsanity: Alex 11 <3,FRIENDSANITY
|
||||
1312,Town,Friendsanity: Alex 12 <3,FRIENDSANITY
|
||||
1313,Town,Friendsanity: Alex 13 <3,FRIENDSANITY
|
||||
1314,Town,Friendsanity: Alex 14 <3,FRIENDSANITY
|
||||
1315,Beach,Friendsanity: Elliott 1 <3,FRIENDSANITY
|
||||
1316,Beach,Friendsanity: Elliott 2 <3,FRIENDSANITY
|
||||
1317,Beach,Friendsanity: Elliott 3 <3,FRIENDSANITY
|
||||
1318,Beach,Friendsanity: Elliott 4 <3,FRIENDSANITY
|
||||
1319,Beach,Friendsanity: Elliott 5 <3,FRIENDSANITY
|
||||
1320,Beach,Friendsanity: Elliott 6 <3,FRIENDSANITY
|
||||
1321,Beach,Friendsanity: Elliott 7 <3,FRIENDSANITY
|
||||
1322,Beach,Friendsanity: Elliott 8 <3,FRIENDSANITY
|
||||
1323,Beach,Friendsanity: Elliott 9 <3,FRIENDSANITY
|
||||
1324,Beach,Friendsanity: Elliott 10 <3,FRIENDSANITY
|
||||
1325,Beach,Friendsanity: Elliott 11 <3,FRIENDSANITY
|
||||
1326,Beach,Friendsanity: Elliott 12 <3,FRIENDSANITY
|
||||
1327,Beach,Friendsanity: Elliott 13 <3,FRIENDSANITY
|
||||
1328,Beach,Friendsanity: Elliott 14 <3,FRIENDSANITY
|
||||
1329,Town,Friendsanity: Harvey 1 <3,FRIENDSANITY
|
||||
1330,Town,Friendsanity: Harvey 2 <3,FRIENDSANITY
|
||||
1331,Town,Friendsanity: Harvey 3 <3,FRIENDSANITY
|
||||
1332,Town,Friendsanity: Harvey 4 <3,FRIENDSANITY
|
||||
1333,Town,Friendsanity: Harvey 5 <3,FRIENDSANITY
|
||||
1334,Town,Friendsanity: Harvey 6 <3,FRIENDSANITY
|
||||
1335,Town,Friendsanity: Harvey 7 <3,FRIENDSANITY
|
||||
1336,Town,Friendsanity: Harvey 8 <3,FRIENDSANITY
|
||||
1337,Town,Friendsanity: Harvey 9 <3,FRIENDSANITY
|
||||
1338,Town,Friendsanity: Harvey 10 <3,FRIENDSANITY
|
||||
1339,Town,Friendsanity: Harvey 11 <3,FRIENDSANITY
|
||||
1340,Town,Friendsanity: Harvey 12 <3,FRIENDSANITY
|
||||
1341,Town,Friendsanity: Harvey 13 <3,FRIENDSANITY
|
||||
1342,Town,Friendsanity: Harvey 14 <3,FRIENDSANITY
|
||||
1343,Town,Friendsanity: Sam 1 <3,FRIENDSANITY
|
||||
1344,Town,Friendsanity: Sam 2 <3,FRIENDSANITY
|
||||
1345,Town,Friendsanity: Sam 3 <3,FRIENDSANITY
|
||||
1346,Town,Friendsanity: Sam 4 <3,FRIENDSANITY
|
||||
1347,Town,Friendsanity: Sam 5 <3,FRIENDSANITY
|
||||
1348,Town,Friendsanity: Sam 6 <3,FRIENDSANITY
|
||||
1349,Town,Friendsanity: Sam 7 <3,FRIENDSANITY
|
||||
1350,Town,Friendsanity: Sam 8 <3,FRIENDSANITY
|
||||
1351,Town,Friendsanity: Sam 9 <3,FRIENDSANITY
|
||||
1352,Town,Friendsanity: Sam 10 <3,FRIENDSANITY
|
||||
1353,Town,Friendsanity: Sam 11 <3,FRIENDSANITY
|
||||
1354,Town,Friendsanity: Sam 12 <3,FRIENDSANITY
|
||||
1355,Town,Friendsanity: Sam 13 <3,FRIENDSANITY
|
||||
1356,Town,Friendsanity: Sam 14 <3,FRIENDSANITY
|
||||
1357,Carpenter Shop,Friendsanity: Sebastian 1 <3,FRIENDSANITY
|
||||
1358,Carpenter Shop,Friendsanity: Sebastian 2 <3,FRIENDSANITY
|
||||
1359,Carpenter Shop,Friendsanity: Sebastian 3 <3,FRIENDSANITY
|
||||
1360,Carpenter Shop,Friendsanity: Sebastian 4 <3,FRIENDSANITY
|
||||
1361,Carpenter Shop,Friendsanity: Sebastian 5 <3,FRIENDSANITY
|
||||
1362,Carpenter Shop,Friendsanity: Sebastian 6 <3,FRIENDSANITY
|
||||
1363,Carpenter Shop,Friendsanity: Sebastian 7 <3,FRIENDSANITY
|
||||
1364,Carpenter Shop,Friendsanity: Sebastian 8 <3,FRIENDSANITY
|
||||
1365,Carpenter Shop,Friendsanity: Sebastian 9 <3,FRIENDSANITY
|
||||
1366,Carpenter Shop,Friendsanity: Sebastian 10 <3,FRIENDSANITY
|
||||
1367,Carpenter Shop,Friendsanity: Sebastian 11 <3,FRIENDSANITY
|
||||
1368,Carpenter Shop,Friendsanity: Sebastian 12 <3,FRIENDSANITY
|
||||
1369,Carpenter Shop,Friendsanity: Sebastian 13 <3,FRIENDSANITY
|
||||
1370,Carpenter Shop,Friendsanity: Sebastian 14 <3,FRIENDSANITY
|
||||
1371,Marnie's Ranch,Friendsanity: Shane 1 <3,FRIENDSANITY
|
||||
1372,Marnie's Ranch,Friendsanity: Shane 2 <3,FRIENDSANITY
|
||||
1373,Marnie's Ranch,Friendsanity: Shane 3 <3,FRIENDSANITY
|
||||
1374,Marnie's Ranch,Friendsanity: Shane 4 <3,FRIENDSANITY
|
||||
1375,Marnie's Ranch,Friendsanity: Shane 5 <3,FRIENDSANITY
|
||||
1376,Marnie's Ranch,Friendsanity: Shane 6 <3,FRIENDSANITY
|
||||
1377,Marnie's Ranch,Friendsanity: Shane 7 <3,FRIENDSANITY
|
||||
1378,Marnie's Ranch,Friendsanity: Shane 8 <3,FRIENDSANITY
|
||||
1379,Marnie's Ranch,Friendsanity: Shane 9 <3,FRIENDSANITY
|
||||
1380,Marnie's Ranch,Friendsanity: Shane 10 <3,FRIENDSANITY
|
||||
1381,Marnie's Ranch,Friendsanity: Shane 11 <3,FRIENDSANITY
|
||||
1382,Marnie's Ranch,Friendsanity: Shane 12 <3,FRIENDSANITY
|
||||
1383,Marnie's Ranch,Friendsanity: Shane 13 <3,FRIENDSANITY
|
||||
1384,Marnie's Ranch,Friendsanity: Shane 14 <3,FRIENDSANITY
|
||||
1385,Town,Friendsanity: Abigail 1 <3,FRIENDSANITY
|
||||
1386,Town,Friendsanity: Abigail 2 <3,FRIENDSANITY
|
||||
1387,Town,Friendsanity: Abigail 3 <3,FRIENDSANITY
|
||||
1388,Town,Friendsanity: Abigail 4 <3,FRIENDSANITY
|
||||
1389,Town,Friendsanity: Abigail 5 <3,FRIENDSANITY
|
||||
1390,Town,Friendsanity: Abigail 6 <3,FRIENDSANITY
|
||||
1391,Town,Friendsanity: Abigail 7 <3,FRIENDSANITY
|
||||
1392,Town,Friendsanity: Abigail 8 <3,FRIENDSANITY
|
||||
1393,Town,Friendsanity: Abigail 9 <3,FRIENDSANITY
|
||||
1394,Town,Friendsanity: Abigail 10 <3,FRIENDSANITY
|
||||
1395,Town,Friendsanity: Abigail 11 <3,FRIENDSANITY
|
||||
1396,Town,Friendsanity: Abigail 12 <3,FRIENDSANITY
|
||||
1397,Town,Friendsanity: Abigail 13 <3,FRIENDSANITY
|
||||
1398,Town,Friendsanity: Abigail 14 <3,FRIENDSANITY
|
||||
1399,Town,Friendsanity: Emily 1 <3,FRIENDSANITY
|
||||
1400,Town,Friendsanity: Emily 2 <3,FRIENDSANITY
|
||||
1401,Town,Friendsanity: Emily 3 <3,FRIENDSANITY
|
||||
1402,Town,Friendsanity: Emily 4 <3,FRIENDSANITY
|
||||
1403,Town,Friendsanity: Emily 5 <3,FRIENDSANITY
|
||||
1404,Town,Friendsanity: Emily 6 <3,FRIENDSANITY
|
||||
1405,Town,Friendsanity: Emily 7 <3,FRIENDSANITY
|
||||
1406,Town,Friendsanity: Emily 8 <3,FRIENDSANITY
|
||||
1407,Town,Friendsanity: Emily 9 <3,FRIENDSANITY
|
||||
1408,Town,Friendsanity: Emily 10 <3,FRIENDSANITY
|
||||
1409,Town,Friendsanity: Emily 11 <3,FRIENDSANITY
|
||||
1410,Town,Friendsanity: Emily 12 <3,FRIENDSANITY
|
||||
1411,Town,Friendsanity: Emily 13 <3,FRIENDSANITY
|
||||
1412,Town,Friendsanity: Emily 14 <3,FRIENDSANITY
|
||||
1413,Town,Friendsanity: Haley 1 <3,FRIENDSANITY
|
||||
1414,Town,Friendsanity: Haley 2 <3,FRIENDSANITY
|
||||
1415,Town,Friendsanity: Haley 3 <3,FRIENDSANITY
|
||||
1416,Town,Friendsanity: Haley 4 <3,FRIENDSANITY
|
||||
1417,Town,Friendsanity: Haley 5 <3,FRIENDSANITY
|
||||
1418,Town,Friendsanity: Haley 6 <3,FRIENDSANITY
|
||||
1419,Town,Friendsanity: Haley 7 <3,FRIENDSANITY
|
||||
1420,Town,Friendsanity: Haley 8 <3,FRIENDSANITY
|
||||
1421,Town,Friendsanity: Haley 9 <3,FRIENDSANITY
|
||||
1422,Town,Friendsanity: Haley 10 <3,FRIENDSANITY
|
||||
1423,Town,Friendsanity: Haley 11 <3,FRIENDSANITY
|
||||
1424,Town,Friendsanity: Haley 12 <3,FRIENDSANITY
|
||||
1425,Town,Friendsanity: Haley 13 <3,FRIENDSANITY
|
||||
1426,Town,Friendsanity: Haley 14 <3,FRIENDSANITY
|
||||
1427,Forest,Friendsanity: Leah 1 <3,FRIENDSANITY
|
||||
1428,Forest,Friendsanity: Leah 2 <3,FRIENDSANITY
|
||||
1429,Forest,Friendsanity: Leah 3 <3,FRIENDSANITY
|
||||
1430,Forest,Friendsanity: Leah 4 <3,FRIENDSANITY
|
||||
1431,Forest,Friendsanity: Leah 5 <3,FRIENDSANITY
|
||||
1432,Forest,Friendsanity: Leah 6 <3,FRIENDSANITY
|
||||
1433,Forest,Friendsanity: Leah 7 <3,FRIENDSANITY
|
||||
1434,Forest,Friendsanity: Leah 8 <3,FRIENDSANITY
|
||||
1435,Forest,Friendsanity: Leah 9 <3,FRIENDSANITY
|
||||
1436,Forest,Friendsanity: Leah 10 <3,FRIENDSANITY
|
||||
1437,Forest,Friendsanity: Leah 11 <3,FRIENDSANITY
|
||||
1438,Forest,Friendsanity: Leah 12 <3,FRIENDSANITY
|
||||
1439,Forest,Friendsanity: Leah 13 <3,FRIENDSANITY
|
||||
1440,Forest,Friendsanity: Leah 14 <3,FRIENDSANITY
|
||||
1441,Carpenter Shop,Friendsanity: Maru 1 <3,FRIENDSANITY
|
||||
1442,Carpenter Shop,Friendsanity: Maru 2 <3,FRIENDSANITY
|
||||
1443,Carpenter Shop,Friendsanity: Maru 3 <3,FRIENDSANITY
|
||||
1444,Carpenter Shop,Friendsanity: Maru 4 <3,FRIENDSANITY
|
||||
1445,Carpenter Shop,Friendsanity: Maru 5 <3,FRIENDSANITY
|
||||
1446,Carpenter Shop,Friendsanity: Maru 6 <3,FRIENDSANITY
|
||||
1447,Carpenter Shop,Friendsanity: Maru 7 <3,FRIENDSANITY
|
||||
1448,Carpenter Shop,Friendsanity: Maru 8 <3,FRIENDSANITY
|
||||
1449,Carpenter Shop,Friendsanity: Maru 9 <3,FRIENDSANITY
|
||||
1450,Carpenter Shop,Friendsanity: Maru 10 <3,FRIENDSANITY
|
||||
1451,Carpenter Shop,Friendsanity: Maru 11 <3,FRIENDSANITY
|
||||
1452,Carpenter Shop,Friendsanity: Maru 12 <3,FRIENDSANITY
|
||||
1453,Carpenter Shop,Friendsanity: Maru 13 <3,FRIENDSANITY
|
||||
1454,Carpenter Shop,Friendsanity: Maru 14 <3,FRIENDSANITY
|
||||
1455,Town,Friendsanity: Penny 1 <3,FRIENDSANITY
|
||||
1456,Town,Friendsanity: Penny 2 <3,FRIENDSANITY
|
||||
1457,Town,Friendsanity: Penny 3 <3,FRIENDSANITY
|
||||
1458,Town,Friendsanity: Penny 4 <3,FRIENDSANITY
|
||||
1459,Town,Friendsanity: Penny 5 <3,FRIENDSANITY
|
||||
1460,Town,Friendsanity: Penny 6 <3,FRIENDSANITY
|
||||
1461,Town,Friendsanity: Penny 7 <3,FRIENDSANITY
|
||||
1462,Town,Friendsanity: Penny 8 <3,FRIENDSANITY
|
||||
1463,Town,Friendsanity: Penny 9 <3,FRIENDSANITY
|
||||
1464,Town,Friendsanity: Penny 10 <3,FRIENDSANITY
|
||||
1465,Town,Friendsanity: Penny 11 <3,FRIENDSANITY
|
||||
1466,Town,Friendsanity: Penny 12 <3,FRIENDSANITY
|
||||
1467,Town,Friendsanity: Penny 13 <3,FRIENDSANITY
|
||||
1468,Town,Friendsanity: Penny 14 <3,FRIENDSANITY
|
||||
1469,Town,Friendsanity: Caroline 1 <3,FRIENDSANITY
|
||||
1470,Town,Friendsanity: Caroline 2 <3,FRIENDSANITY
|
||||
1471,Town,Friendsanity: Caroline 3 <3,FRIENDSANITY
|
||||
1472,Town,Friendsanity: Caroline 4 <3,FRIENDSANITY
|
||||
1473,Town,Friendsanity: Caroline 5 <3,FRIENDSANITY
|
||||
1474,Town,Friendsanity: Caroline 6 <3,FRIENDSANITY
|
||||
1475,Town,Friendsanity: Caroline 7 <3,FRIENDSANITY
|
||||
1476,Town,Friendsanity: Caroline 8 <3,FRIENDSANITY
|
||||
1477,Town,Friendsanity: Caroline 9 <3,FRIENDSANITY
|
||||
1478,Town,Friendsanity: Caroline 10 <3,FRIENDSANITY
|
||||
1480,Town,Friendsanity: Clint 1 <3,FRIENDSANITY
|
||||
1481,Town,Friendsanity: Clint 2 <3,FRIENDSANITY
|
||||
1482,Town,Friendsanity: Clint 3 <3,FRIENDSANITY
|
||||
1483,Town,Friendsanity: Clint 4 <3,FRIENDSANITY
|
||||
1484,Town,Friendsanity: Clint 5 <3,FRIENDSANITY
|
||||
1485,Town,Friendsanity: Clint 6 <3,FRIENDSANITY
|
||||
1486,Town,Friendsanity: Clint 7 <3,FRIENDSANITY
|
||||
1487,Town,Friendsanity: Clint 8 <3,FRIENDSANITY
|
||||
1488,Town,Friendsanity: Clint 9 <3,FRIENDSANITY
|
||||
1489,Town,Friendsanity: Clint 10 <3,FRIENDSANITY
|
||||
1491,Carpenter Shop,Friendsanity: Demetrius 1 <3,FRIENDSANITY
|
||||
1492,Carpenter Shop,Friendsanity: Demetrius 2 <3,FRIENDSANITY
|
||||
1493,Carpenter Shop,Friendsanity: Demetrius 3 <3,FRIENDSANITY
|
||||
1494,Carpenter Shop,Friendsanity: Demetrius 4 <3,FRIENDSANITY
|
||||
1495,Carpenter Shop,Friendsanity: Demetrius 5 <3,FRIENDSANITY
|
||||
1496,Carpenter Shop,Friendsanity: Demetrius 6 <3,FRIENDSANITY
|
||||
1497,Carpenter Shop,Friendsanity: Demetrius 7 <3,FRIENDSANITY
|
||||
1498,Carpenter Shop,Friendsanity: Demetrius 8 <3,FRIENDSANITY
|
||||
1499,Carpenter Shop,Friendsanity: Demetrius 9 <3,FRIENDSANITY
|
||||
1500,Carpenter Shop,Friendsanity: Demetrius 10 <3,FRIENDSANITY
|
||||
1502,The Mines,Friendsanity: Dwarf 1 <3,FRIENDSANITY
|
||||
1503,The Mines,Friendsanity: Dwarf 2 <3,FRIENDSANITY
|
||||
1504,The Mines,Friendsanity: Dwarf 3 <3,FRIENDSANITY
|
||||
1505,The Mines,Friendsanity: Dwarf 4 <3,FRIENDSANITY
|
||||
1506,The Mines,Friendsanity: Dwarf 5 <3,FRIENDSANITY
|
||||
1507,The Mines,Friendsanity: Dwarf 6 <3,FRIENDSANITY
|
||||
1508,The Mines,Friendsanity: Dwarf 7 <3,FRIENDSANITY
|
||||
1509,The Mines,Friendsanity: Dwarf 8 <3,FRIENDSANITY
|
||||
1510,The Mines,Friendsanity: Dwarf 9 <3,FRIENDSANITY
|
||||
1511,The Mines,Friendsanity: Dwarf 10 <3,FRIENDSANITY
|
||||
1513,Town,Friendsanity: Evelyn 1 <3,FRIENDSANITY
|
||||
1514,Town,Friendsanity: Evelyn 2 <3,FRIENDSANITY
|
||||
1515,Town,Friendsanity: Evelyn 3 <3,FRIENDSANITY
|
||||
1516,Town,Friendsanity: Evelyn 4 <3,FRIENDSANITY
|
||||
1517,Town,Friendsanity: Evelyn 5 <3,FRIENDSANITY
|
||||
1518,Town,Friendsanity: Evelyn 6 <3,FRIENDSANITY
|
||||
1519,Town,Friendsanity: Evelyn 7 <3,FRIENDSANITY
|
||||
1520,Town,Friendsanity: Evelyn 8 <3,FRIENDSANITY
|
||||
1521,Town,Friendsanity: Evelyn 9 <3,FRIENDSANITY
|
||||
1522,Town,Friendsanity: Evelyn 10 <3,FRIENDSANITY
|
||||
1524,Town,Friendsanity: George 1 <3,FRIENDSANITY
|
||||
1525,Town,Friendsanity: George 2 <3,FRIENDSANITY
|
||||
1526,Town,Friendsanity: George 3 <3,FRIENDSANITY
|
||||
1527,Town,Friendsanity: George 4 <3,FRIENDSANITY
|
||||
1528,Town,Friendsanity: George 5 <3,FRIENDSANITY
|
||||
1529,Town,Friendsanity: George 6 <3,FRIENDSANITY
|
||||
1530,Town,Friendsanity: George 7 <3,FRIENDSANITY
|
||||
1531,Town,Friendsanity: George 8 <3,FRIENDSANITY
|
||||
1532,Town,Friendsanity: George 9 <3,FRIENDSANITY
|
||||
1533,Town,Friendsanity: George 10 <3,FRIENDSANITY
|
||||
1535,Town,Friendsanity: Gus 1 <3,FRIENDSANITY
|
||||
1536,Town,Friendsanity: Gus 2 <3,FRIENDSANITY
|
||||
1537,Town,Friendsanity: Gus 3 <3,FRIENDSANITY
|
||||
1538,Town,Friendsanity: Gus 4 <3,FRIENDSANITY
|
||||
1539,Town,Friendsanity: Gus 5 <3,FRIENDSANITY
|
||||
1540,Town,Friendsanity: Gus 6 <3,FRIENDSANITY
|
||||
1541,Town,Friendsanity: Gus 7 <3,FRIENDSANITY
|
||||
1542,Town,Friendsanity: Gus 8 <3,FRIENDSANITY
|
||||
1543,Town,Friendsanity: Gus 9 <3,FRIENDSANITY
|
||||
1544,Town,Friendsanity: Gus 10 <3,FRIENDSANITY
|
||||
1546,Marnie's Ranch,Friendsanity: Jas 1 <3,FRIENDSANITY
|
||||
1547,Marnie's Ranch,Friendsanity: Jas 2 <3,FRIENDSANITY
|
||||
1548,Marnie's Ranch,Friendsanity: Jas 3 <3,FRIENDSANITY
|
||||
1549,Marnie's Ranch,Friendsanity: Jas 4 <3,FRIENDSANITY
|
||||
1550,Marnie's Ranch,Friendsanity: Jas 5 <3,FRIENDSANITY
|
||||
1551,Marnie's Ranch,Friendsanity: Jas 6 <3,FRIENDSANITY
|
||||
1552,Marnie's Ranch,Friendsanity: Jas 7 <3,FRIENDSANITY
|
||||
1553,Marnie's Ranch,Friendsanity: Jas 8 <3,FRIENDSANITY
|
||||
1554,Marnie's Ranch,Friendsanity: Jas 9 <3,FRIENDSANITY
|
||||
1555,Marnie's Ranch,Friendsanity: Jas 10 <3,FRIENDSANITY
|
||||
1557,Town,Friendsanity: Jodi 1 <3,FRIENDSANITY
|
||||
1558,Town,Friendsanity: Jodi 2 <3,FRIENDSANITY
|
||||
1559,Town,Friendsanity: Jodi 3 <3,FRIENDSANITY
|
||||
1560,Town,Friendsanity: Jodi 4 <3,FRIENDSANITY
|
||||
1561,Town,Friendsanity: Jodi 5 <3,FRIENDSANITY
|
||||
1562,Town,Friendsanity: Jodi 6 <3,FRIENDSANITY
|
||||
1563,Town,Friendsanity: Jodi 7 <3,FRIENDSANITY
|
||||
1564,Town,Friendsanity: Jodi 8 <3,FRIENDSANITY
|
||||
1565,Town,Friendsanity: Jodi 9 <3,FRIENDSANITY
|
||||
1566,Town,Friendsanity: Jodi 10 <3,FRIENDSANITY
|
||||
1568,Town,Friendsanity: Kent 1 <3,FRIENDSANITY
|
||||
1569,Town,Friendsanity: Kent 2 <3,FRIENDSANITY
|
||||
1570,Town,Friendsanity: Kent 3 <3,FRIENDSANITY
|
||||
1571,Town,Friendsanity: Kent 4 <3,FRIENDSANITY
|
||||
1572,Town,Friendsanity: Kent 5 <3,FRIENDSANITY
|
||||
1573,Town,Friendsanity: Kent 6 <3,FRIENDSANITY
|
||||
1574,Town,Friendsanity: Kent 7 <3,FRIENDSANITY
|
||||
1575,Town,Friendsanity: Kent 8 <3,FRIENDSANITY
|
||||
1576,Town,Friendsanity: Kent 9 <3,FRIENDSANITY
|
||||
1577,Town,Friendsanity: Kent 10 <3,FRIENDSANITY
|
||||
1579,Sewers,Friendsanity: Krobus 1 <3,FRIENDSANITY
|
||||
1580,Sewers,Friendsanity: Krobus 2 <3,FRIENDSANITY
|
||||
1581,Sewers,Friendsanity: Krobus 3 <3,FRIENDSANITY
|
||||
1582,Sewers,Friendsanity: Krobus 4 <3,FRIENDSANITY
|
||||
1583,Sewers,Friendsanity: Krobus 5 <3,FRIENDSANITY
|
||||
1584,Sewers,Friendsanity: Krobus 6 <3,FRIENDSANITY
|
||||
1585,Sewers,Friendsanity: Krobus 7 <3,FRIENDSANITY
|
||||
1586,Sewers,Friendsanity: Krobus 8 <3,FRIENDSANITY
|
||||
1587,Sewers,Friendsanity: Krobus 9 <3,FRIENDSANITY
|
||||
1588,Sewers,Friendsanity: Krobus 10 <3,FRIENDSANITY
|
||||
1590,Ginger Island,Friendsanity: Leo 1 <3,FRIENDSANITY
|
||||
1591,Ginger Island,Friendsanity: Leo 2 <3,FRIENDSANITY
|
||||
1592,Ginger Island,Friendsanity: Leo 3 <3,FRIENDSANITY
|
||||
1593,Ginger Island,Friendsanity: Leo 4 <3,FRIENDSANITY
|
||||
1594,Ginger Island,Friendsanity: Leo 5 <3,FRIENDSANITY
|
||||
1595,Ginger Island,Friendsanity: Leo 6 <3,FRIENDSANITY
|
||||
1596,Ginger Island,Friendsanity: Leo 7 <3,FRIENDSANITY
|
||||
1597,Ginger Island,Friendsanity: Leo 8 <3,FRIENDSANITY
|
||||
1598,Ginger Island,Friendsanity: Leo 9 <3,FRIENDSANITY
|
||||
1599,Ginger Island,Friendsanity: Leo 10 <3,FRIENDSANITY
|
||||
1601,Town,Friendsanity: Lewis 1 <3,FRIENDSANITY
|
||||
1602,Town,Friendsanity: Lewis 2 <3,FRIENDSANITY
|
||||
1603,Town,Friendsanity: Lewis 3 <3,FRIENDSANITY
|
||||
1604,Town,Friendsanity: Lewis 4 <3,FRIENDSANITY
|
||||
1605,Town,Friendsanity: Lewis 5 <3,FRIENDSANITY
|
||||
1606,Town,Friendsanity: Lewis 6 <3,FRIENDSANITY
|
||||
1607,Town,Friendsanity: Lewis 7 <3,FRIENDSANITY
|
||||
1608,Town,Friendsanity: Lewis 8 <3,FRIENDSANITY
|
||||
1609,Town,Friendsanity: Lewis 9 <3,FRIENDSANITY
|
||||
1610,Town,Friendsanity: Lewis 10 <3,FRIENDSANITY
|
||||
1612,Mountain,Friendsanity: Linus 1 <3,FRIENDSANITY
|
||||
1613,Mountain,Friendsanity: Linus 2 <3,FRIENDSANITY
|
||||
1614,Mountain,Friendsanity: Linus 3 <3,FRIENDSANITY
|
||||
1615,Mountain,Friendsanity: Linus 4 <3,FRIENDSANITY
|
||||
1616,Mountain,Friendsanity: Linus 5 <3,FRIENDSANITY
|
||||
1617,Mountain,Friendsanity: Linus 6 <3,FRIENDSANITY
|
||||
1618,Mountain,Friendsanity: Linus 7 <3,FRIENDSANITY
|
||||
1619,Mountain,Friendsanity: Linus 8 <3,FRIENDSANITY
|
||||
1620,Mountain,Friendsanity: Linus 9 <3,FRIENDSANITY
|
||||
1621,Mountain,Friendsanity: Linus 10 <3,FRIENDSANITY
|
||||
1623,Marnie's Ranch,Friendsanity: Marnie 1 <3,FRIENDSANITY
|
||||
1624,Marnie's Ranch,Friendsanity: Marnie 2 <3,FRIENDSANITY
|
||||
1625,Marnie's Ranch,Friendsanity: Marnie 3 <3,FRIENDSANITY
|
||||
1626,Marnie's Ranch,Friendsanity: Marnie 4 <3,FRIENDSANITY
|
||||
1627,Marnie's Ranch,Friendsanity: Marnie 5 <3,FRIENDSANITY
|
||||
1628,Marnie's Ranch,Friendsanity: Marnie 6 <3,FRIENDSANITY
|
||||
1629,Marnie's Ranch,Friendsanity: Marnie 7 <3,FRIENDSANITY
|
||||
1630,Marnie's Ranch,Friendsanity: Marnie 8 <3,FRIENDSANITY
|
||||
1631,Marnie's Ranch,Friendsanity: Marnie 9 <3,FRIENDSANITY
|
||||
1632,Marnie's Ranch,Friendsanity: Marnie 10 <3,FRIENDSANITY
|
||||
1634,Town,Friendsanity: Pam 1 <3,FRIENDSANITY
|
||||
1635,Town,Friendsanity: Pam 2 <3,FRIENDSANITY
|
||||
1636,Town,Friendsanity: Pam 3 <3,FRIENDSANITY
|
||||
1637,Town,Friendsanity: Pam 4 <3,FRIENDSANITY
|
||||
1638,Town,Friendsanity: Pam 5 <3,FRIENDSANITY
|
||||
1639,Town,Friendsanity: Pam 6 <3,FRIENDSANITY
|
||||
1640,Town,Friendsanity: Pam 7 <3,FRIENDSANITY
|
||||
1641,Town,Friendsanity: Pam 8 <3,FRIENDSANITY
|
||||
1642,Town,Friendsanity: Pam 9 <3,FRIENDSANITY
|
||||
1643,Town,Friendsanity: Pam 10 <3,FRIENDSANITY
|
||||
1645,Town,Friendsanity: Pierre 1 <3,FRIENDSANITY
|
||||
1646,Town,Friendsanity: Pierre 2 <3,FRIENDSANITY
|
||||
1647,Town,Friendsanity: Pierre 3 <3,FRIENDSANITY
|
||||
1648,Town,Friendsanity: Pierre 4 <3,FRIENDSANITY
|
||||
1649,Town,Friendsanity: Pierre 5 <3,FRIENDSANITY
|
||||
1650,Town,Friendsanity: Pierre 6 <3,FRIENDSANITY
|
||||
1651,Town,Friendsanity: Pierre 7 <3,FRIENDSANITY
|
||||
1652,Town,Friendsanity: Pierre 8 <3,FRIENDSANITY
|
||||
1653,Town,Friendsanity: Pierre 9 <3,FRIENDSANITY
|
||||
1654,Town,Friendsanity: Pierre 10 <3,FRIENDSANITY
|
||||
1656,Carpenter Shop,Friendsanity: Robin 1 <3,FRIENDSANITY
|
||||
1657,Carpenter Shop,Friendsanity: Robin 2 <3,FRIENDSANITY
|
||||
1658,Carpenter Shop,Friendsanity: Robin 3 <3,FRIENDSANITY
|
||||
1659,Carpenter Shop,Friendsanity: Robin 4 <3,FRIENDSANITY
|
||||
1660,Carpenter Shop,Friendsanity: Robin 5 <3,FRIENDSANITY
|
||||
1661,Carpenter Shop,Friendsanity: Robin 6 <3,FRIENDSANITY
|
||||
1662,Carpenter Shop,Friendsanity: Robin 7 <3,FRIENDSANITY
|
||||
1663,Carpenter Shop,Friendsanity: Robin 8 <3,FRIENDSANITY
|
||||
1664,Carpenter Shop,Friendsanity: Robin 9 <3,FRIENDSANITY
|
||||
1665,Carpenter Shop,Friendsanity: Robin 10 <3,FRIENDSANITY
|
||||
1667,The Desert,Friendsanity: Sandy 1 <3,FRIENDSANITY
|
||||
1668,The Desert,Friendsanity: Sandy 2 <3,FRIENDSANITY
|
||||
1669,The Desert,Friendsanity: Sandy 3 <3,FRIENDSANITY
|
||||
1670,The Desert,Friendsanity: Sandy 4 <3,FRIENDSANITY
|
||||
1671,The Desert,Friendsanity: Sandy 5 <3,FRIENDSANITY
|
||||
1672,The Desert,Friendsanity: Sandy 6 <3,FRIENDSANITY
|
||||
1673,The Desert,Friendsanity: Sandy 7 <3,FRIENDSANITY
|
||||
1674,The Desert,Friendsanity: Sandy 8 <3,FRIENDSANITY
|
||||
1675,The Desert,Friendsanity: Sandy 9 <3,FRIENDSANITY
|
||||
1676,The Desert,Friendsanity: Sandy 10 <3,FRIENDSANITY
|
||||
1678,Town,Friendsanity: Vincent 1 <3,FRIENDSANITY
|
||||
1679,Town,Friendsanity: Vincent 2 <3,FRIENDSANITY
|
||||
1680,Town,Friendsanity: Vincent 3 <3,FRIENDSANITY
|
||||
1681,Town,Friendsanity: Vincent 4 <3,FRIENDSANITY
|
||||
1682,Town,Friendsanity: Vincent 5 <3,FRIENDSANITY
|
||||
1683,Town,Friendsanity: Vincent 6 <3,FRIENDSANITY
|
||||
1684,Town,Friendsanity: Vincent 7 <3,FRIENDSANITY
|
||||
1685,Town,Friendsanity: Vincent 8 <3,FRIENDSANITY
|
||||
1686,Town,Friendsanity: Vincent 9 <3,FRIENDSANITY
|
||||
1687,Town,Friendsanity: Vincent 10 <3,FRIENDSANITY
|
||||
1689,Beach,Friendsanity: Willy 1 <3,FRIENDSANITY
|
||||
1690,Beach,Friendsanity: Willy 2 <3,FRIENDSANITY
|
||||
1691,Beach,Friendsanity: Willy 3 <3,FRIENDSANITY
|
||||
1692,Beach,Friendsanity: Willy 4 <3,FRIENDSANITY
|
||||
1693,Beach,Friendsanity: Willy 5 <3,FRIENDSANITY
|
||||
1694,Beach,Friendsanity: Willy 6 <3,FRIENDSANITY
|
||||
1695,Beach,Friendsanity: Willy 7 <3,FRIENDSANITY
|
||||
1696,Beach,Friendsanity: Willy 8 <3,FRIENDSANITY
|
||||
1697,Beach,Friendsanity: Willy 9 <3,FRIENDSANITY
|
||||
1698,Beach,Friendsanity: Willy 10 <3,FRIENDSANITY
|
||||
1700,Forest,Friendsanity: Wizard 1 <3,FRIENDSANITY
|
||||
1701,Forest,Friendsanity: Wizard 2 <3,FRIENDSANITY
|
||||
1702,Forest,Friendsanity: Wizard 3 <3,FRIENDSANITY
|
||||
1703,Forest,Friendsanity: Wizard 4 <3,FRIENDSANITY
|
||||
1704,Forest,Friendsanity: Wizard 5 <3,FRIENDSANITY
|
||||
1705,Forest,Friendsanity: Wizard 6 <3,FRIENDSANITY
|
||||
1706,Forest,Friendsanity: Wizard 7 <3,FRIENDSANITY
|
||||
1707,Forest,Friendsanity: Wizard 8 <3,FRIENDSANITY
|
||||
1708,Forest,Friendsanity: Wizard 9 <3,FRIENDSANITY
|
||||
1709,Forest,Friendsanity: Wizard 10 <3,FRIENDSANITY
|
||||
1710,Farm,Friendsanity: Pet 1 <3,FRIENDSANITY
|
||||
1711,Farm,Friendsanity: Pet 2 <3,FRIENDSANITY
|
||||
1712,Farm,Friendsanity: Pet 3 <3,FRIENDSANITY
|
||||
1713,Farm,Friendsanity: Pet 4 <3,FRIENDSANITY
|
||||
1714,Farm,Friendsanity: Pet 5 <3,FRIENDSANITY
|
||||
1715,Farm,Friendsanity: Friend 1 <3,FRIENDSANITY
|
||||
1716,Farm,Friendsanity: Friend 2 <3,FRIENDSANITY
|
||||
1717,Farm,Friendsanity: Friend 3 <3,FRIENDSANITY
|
||||
1718,Farm,Friendsanity: Friend 4 <3,FRIENDSANITY
|
||||
1719,Farm,Friendsanity: Friend 5 <3,FRIENDSANITY
|
||||
1720,Farm,Friendsanity: Friend 6 <3,FRIENDSANITY
|
||||
1721,Farm,Friendsanity: Friend 7 <3,FRIENDSANITY
|
||||
1722,Farm,Friendsanity: Friend 8 <3,FRIENDSANITY
|
||||
1723,Farm,Friendsanity: Suitor 9 <3,FRIENDSANITY
|
||||
1724,Farm,Friendsanity: Suitor 10 <3,FRIENDSANITY
|
||||
1725,Farm,Friendsanity: Spouse 11 <3,FRIENDSANITY
|
||||
1726,Farm,Friendsanity: Spouse 12 <3,FRIENDSANITY
|
||||
1727,Farm,Friendsanity: Spouse 13 <3,FRIENDSANITY
|
||||
1728,Farm,Friendsanity: Spouse 14 <3,FRIENDSANITY
|
||||
|
|
|
|
@ -0,0 +1,8 @@
|
|||
class Monster:
|
||||
duggy = "Duggy"
|
||||
blue_slime = "Blue Slime"
|
||||
pepper_rex = "Pepper Rex"
|
||||
stone_golem = "Stone Golem"
|
||||
|
||||
|
||||
frozen_monsters = (Monster.blue_slime,)
|
|
@ -0,0 +1,301 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import List, Tuple, Union, Optional
|
||||
|
||||
from . import common_data as common
|
||||
from .game_item import GameItem
|
||||
from .monster_data import Monster
|
||||
from .region_data import SVRegion
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class MuseumItem(GameItem):
|
||||
locations: Tuple[str, ...]
|
||||
geodes: Tuple[str, ...]
|
||||
monsters: Tuple[str, ...]
|
||||
difficulty: float
|
||||
|
||||
@staticmethod
|
||||
def of(name: str,
|
||||
item_id: int,
|
||||
difficulty: float,
|
||||
locations: Union[str, Tuple[str, ...]],
|
||||
geodes: Union[str, Tuple[str, ...]],
|
||||
monsters: Union[str, Tuple[str, ...]]) -> MuseumItem:
|
||||
if isinstance(locations, str):
|
||||
locations = (locations,)
|
||||
|
||||
if isinstance(geodes, str):
|
||||
geodes = (geodes,)
|
||||
|
||||
if isinstance(monsters, str):
|
||||
monsters = (monsters,)
|
||||
|
||||
return MuseumItem(name, item_id, locations, geodes, monsters, difficulty)
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.name} [{self.item_id}] (Locations: {self.locations} |" \
|
||||
f" Geodes: {self.geodes} |" \
|
||||
f" Monsters: {self.monsters}) "
|
||||
|
||||
|
||||
class Geode:
|
||||
geode = "Geode"
|
||||
frozen_geode = "Frozen Geode"
|
||||
magma_geode = "Magma Geode"
|
||||
omni_geode = "Omni Geode"
|
||||
artifact_trove = "Artifact Trove"
|
||||
|
||||
|
||||
unlikely = ()
|
||||
|
||||
all_artifact_items: List[MuseumItem] = []
|
||||
all_mineral_items: List[MuseumItem] = []
|
||||
|
||||
all_museum_items: List[MuseumItem] = []
|
||||
|
||||
|
||||
def create_artifact(name: str,
|
||||
item_id: int,
|
||||
difficulty: float,
|
||||
locations: Union[str, Tuple[str, ...]] = (),
|
||||
geodes: Union[str, Tuple[str, ...]] = (),
|
||||
monsters: Union[str, Tuple[str, ...]] = ()) -> MuseumItem:
|
||||
artifact_item = MuseumItem.of(name, item_id, difficulty, locations, geodes, monsters)
|
||||
all_artifact_items.append(artifact_item)
|
||||
all_museum_items.append(artifact_item)
|
||||
return artifact_item
|
||||
|
||||
|
||||
def create_mineral(name: str,
|
||||
item_id: int,
|
||||
locations: Union[str, Tuple[str, ...]],
|
||||
geodes: Union[str, Tuple[str, ...]] = (),
|
||||
monsters: Union[str, Tuple[str, ...]] = (),
|
||||
difficulty: Optional[float] = None) -> MuseumItem:
|
||||
if difficulty is None:
|
||||
difficulty = 0
|
||||
if "Geode" in geodes:
|
||||
difficulty += 1.0 / 32.0 * 100
|
||||
if "Frozen Geode" in geodes:
|
||||
difficulty += 1.0 / 30.0 * 100
|
||||
if "Magma Geode" in geodes:
|
||||
difficulty += 1.0 / 26.0 * 100
|
||||
if "Omni Geode" in geodes:
|
||||
difficulty += 31.0 / 2750.0 * 100
|
||||
|
||||
mineral_item = MuseumItem.of(name, item_id, difficulty, locations, geodes, monsters)
|
||||
all_mineral_items.append(mineral_item)
|
||||
all_museum_items.append(mineral_item)
|
||||
return mineral_item
|
||||
|
||||
|
||||
class Artifact:
|
||||
dwarf_scroll_i = create_artifact("Dwarf Scroll I", 96, 5.6, SVRegion.mines_floor_20,
|
||||
monsters=unlikely)
|
||||
dwarf_scroll_ii = create_artifact("Dwarf Scroll II", 97, 3, SVRegion.mines_floor_20,
|
||||
monsters=unlikely)
|
||||
dwarf_scroll_iii = create_artifact("Dwarf Scroll III", 98, 7.5, SVRegion.mines_floor_60,
|
||||
monsters=Monster.blue_slime)
|
||||
dwarf_scroll_iv = create_artifact("Dwarf Scroll IV", 99, 4, SVRegion.mines_floor_100)
|
||||
chipped_amphora = create_artifact("Chipped Amphora", 100, 6.7, SVRegion.town,
|
||||
geodes=Geode.artifact_trove)
|
||||
arrowhead = create_artifact("Arrowhead", 101, 8.5, (SVRegion.mountain, SVRegion.forest, SVRegion.bus_stop),
|
||||
geodes=Geode.artifact_trove)
|
||||
ancient_doll = create_artifact("Ancient Doll", 103, 13.1, (SVRegion.mountain, SVRegion.forest, SVRegion.bus_stop),
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
elvish_jewelry = create_artifact("Elvish Jewelry", 104, 5.3, SVRegion.forest,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
chewing_stick = create_artifact("Chewing Stick", 105, 10.3, (SVRegion.mountain, SVRegion.forest, SVRegion.town),
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
ornamental_fan = create_artifact("Ornamental Fan", 106, 7.4, (SVRegion.beach, SVRegion.forest, SVRegion.town),
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
dinosaur_egg = create_artifact("Dinosaur Egg", 107, 11.4, (SVRegion.mountain, SVRegion.skull_cavern),
|
||||
geodes=common.fishing_chest,
|
||||
monsters=Monster.pepper_rex)
|
||||
rare_disc = create_artifact("Rare Disc", 108, 5.6, SVRegion.stardew_valley,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest),
|
||||
monsters=unlikely)
|
||||
ancient_sword = create_artifact("Ancient Sword", 109, 5.8, (SVRegion.forest, SVRegion.mountain),
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
rusty_spoon = create_artifact("Rusty Spoon", 110, 9.6, SVRegion.town,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
rusty_spur = create_artifact("Rusty Spur", 111, 15.6, SVRegion.farm,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
rusty_cog = create_artifact("Rusty Cog", 112, 9.6, SVRegion.mountain,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
chicken_statue = create_artifact("Chicken Statue", 113, 13.5, SVRegion.farm,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
ancient_seed = create_artifact("Ancient Seed", 114, 8.4, (SVRegion.forest, SVRegion.mountain),
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest),
|
||||
monsters=unlikely)
|
||||
prehistoric_tool = create_artifact("Prehistoric Tool", 115, 11.1, (SVRegion.mountain, SVRegion.forest, SVRegion.bus_stop),
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
dried_starfish = create_artifact("Dried Starfish", 116, 12.5, SVRegion.beach,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
anchor = create_artifact("Anchor", 117, 8.5, SVRegion.beach, geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
glass_shards = create_artifact("Glass Shards", 118, 11.5, SVRegion.beach,
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
bone_flute = create_artifact("Bone Flute", 119, 6.3, (SVRegion.mountain, SVRegion.forest, SVRegion.town),
|
||||
geodes=(Geode.artifact_trove, common.fishing_chest))
|
||||
prehistoric_handaxe = create_artifact("Prehistoric Handaxe", 120, 13.7,
|
||||
(SVRegion.mountain, SVRegion.forest, SVRegion.bus_stop),
|
||||
geodes=Geode.artifact_trove)
|
||||
dwarvish_helm = create_artifact("Dwarvish Helm", 121, 8.7, SVRegion.mines_floor_20,
|
||||
geodes=(Geode.geode, Geode.omni_geode, Geode.artifact_trove))
|
||||
dwarf_gadget = create_artifact("Dwarf Gadget", 122, 9.7, SVRegion.mines_floor_60,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode, Geode.artifact_trove))
|
||||
ancient_drum = create_artifact("Ancient Drum", 123, 9.5, (SVRegion.bus_stop, SVRegion.forest, SVRegion.town),
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode, Geode.artifact_trove))
|
||||
golden_mask = create_artifact("Golden Mask", 124, 6.7, SVRegion.desert,
|
||||
geodes=Geode.artifact_trove)
|
||||
golden_relic = create_artifact("Golden Relic", 125, 9.7, SVRegion.desert,
|
||||
geodes=Geode.artifact_trove)
|
||||
strange_doll_green = create_artifact("Strange Doll (Green)", 126, 10, SVRegion.town,
|
||||
geodes=common.secret_note)
|
||||
strange_doll = create_artifact("Strange Doll", 127, 10, SVRegion.desert,
|
||||
geodes=common.secret_note)
|
||||
prehistoric_scapula = create_artifact("Prehistoric Scapula", 579, 6.2,
|
||||
(SVRegion.dig_site, SVRegion.forest, SVRegion.town))
|
||||
prehistoric_tibia = create_artifact("Prehistoric Tibia", 580, 16.6,
|
||||
(SVRegion.dig_site, SVRegion.forest, SVRegion.railroad))
|
||||
prehistoric_skull = create_artifact("Prehistoric Skull", 581, 3.9, (SVRegion.dig_site, SVRegion.mountain))
|
||||
skeletal_hand = create_artifact("Skeletal Hand", 582, 7.9, (SVRegion.dig_site, SVRegion.backwoods, SVRegion.beach))
|
||||
prehistoric_rib = create_artifact("Prehistoric Rib", 583, 15, (SVRegion.dig_site, SVRegion.farm, SVRegion.town),
|
||||
monsters=Monster.pepper_rex)
|
||||
prehistoric_vertebra = create_artifact("Prehistoric Vertebra", 584, 12.7, (SVRegion.dig_site, SVRegion.bus_stop),
|
||||
monsters=Monster.pepper_rex)
|
||||
skeletal_tail = create_artifact("Skeletal Tail", 585, 5.1, (SVRegion.dig_site, SVRegion.mines_floor_20),
|
||||
geodes=common.fishing_chest)
|
||||
nautilus_fossil = create_artifact("Nautilus Fossil", 586, 6.9, (SVRegion.dig_site, SVRegion.beach),
|
||||
geodes=common.fishing_chest)
|
||||
amphibian_fossil = create_artifact("Amphibian Fossil", 587, 6.3, (SVRegion.dig_site, SVRegion.forest, SVRegion.mountain),
|
||||
geodes=common.fishing_chest)
|
||||
palm_fossil = create_artifact("Palm Fossil", 588, 10.2,
|
||||
(SVRegion.dig_site, SVRegion.desert, SVRegion.forest, SVRegion.beach))
|
||||
trilobite = create_artifact("Trilobite", 589, 7.4, (SVRegion.dig_site, SVRegion.desert, SVRegion.forest, SVRegion.beach))
|
||||
|
||||
|
||||
class Mineral:
|
||||
quartz = create_mineral("Quartz", 80, SVRegion.mines_floor_20,
|
||||
monsters=Monster.stone_golem)
|
||||
fire_quartz = create_mineral("Fire Quartz", 82, SVRegion.mines_floor_100,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode, common.fishing_chest),
|
||||
difficulty=1.0 / 12.0)
|
||||
frozen_tear = create_mineral("Frozen Tear", 84, SVRegion.mines_floor_60,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode, common.fishing_chest),
|
||||
monsters=unlikely,
|
||||
difficulty=1.0 / 12.0)
|
||||
earth_crystal = create_mineral("Earth Crystal", 86, SVRegion.mines_floor_20,
|
||||
geodes=(Geode.geode, Geode.omni_geode, common.fishing_chest),
|
||||
monsters=Monster.duggy,
|
||||
difficulty=1.0 / 12.0)
|
||||
emerald = create_mineral("Emerald", 60, SVRegion.mines_floor_100,
|
||||
geodes=common.fishing_chest)
|
||||
aquamarine = create_mineral("Aquamarine", 62, SVRegion.mines_floor_60,
|
||||
geodes=common.fishing_chest)
|
||||
ruby = create_mineral("Ruby", 64, SVRegion.mines_floor_100,
|
||||
geodes=common.fishing_chest)
|
||||
amethyst = create_mineral("Amethyst", 66, SVRegion.mines_floor_20,
|
||||
geodes=common.fishing_chest)
|
||||
topaz = create_mineral("Topaz", 68, SVRegion.mines_floor_20,
|
||||
geodes=common.fishing_chest)
|
||||
jade = create_mineral("Jade", 70, SVRegion.mines_floor_60,
|
||||
geodes=common.fishing_chest)
|
||||
diamond = create_mineral("Diamond", 72, SVRegion.mines_floor_60,
|
||||
geodes=common.fishing_chest)
|
||||
prismatic_shard = create_mineral("Prismatic Shard", 74, SVRegion.perfect_skull_cavern,
|
||||
geodes=unlikely,
|
||||
monsters=unlikely)
|
||||
alamite = create_mineral("Alamite", 538, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
bixite = create_mineral("Bixite", 539, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode),
|
||||
monsters=unlikely)
|
||||
baryte = create_mineral("Baryte", 540, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
aerinite = create_mineral("Aerinite", 541, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
calcite = create_mineral("Calcite", 542, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
dolomite = create_mineral("Dolomite", 543, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
esperite = create_mineral("Esperite", 544, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
fluorapatite = create_mineral("Fluorapatite", 545, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
geminite = create_mineral("Geminite", 546, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
helvite = create_mineral("Helvite", 547, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
jamborite = create_mineral("Jamborite", 548, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
jagoite = create_mineral("Jagoite", 549, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
kyanite = create_mineral("Kyanite", 550, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
lunarite = create_mineral("Lunarite", 551, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
malachite = create_mineral("Malachite", 552, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
neptunite = create_mineral("Neptunite", 553, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
lemon_stone = create_mineral("Lemon Stone", 554, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
nekoite = create_mineral("Nekoite", 555, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
orpiment = create_mineral("Orpiment", 556, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
petrified_slime = create_mineral("Petrified Slime", 557, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
thunder_egg = create_mineral("Thunder Egg", 558, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
pyrite = create_mineral("Pyrite", 559, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
ocean_stone = create_mineral("Ocean Stone", 560, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
ghost_crystal = create_mineral("Ghost Crystal", 561, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
tigerseye = create_mineral("Tigerseye", 562, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
jasper = create_mineral("Jasper", 563, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
opal = create_mineral("Opal", 564, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
fire_opal = create_mineral("Fire Opal", 565, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
celestine = create_mineral("Celestine", 566, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
marble = create_mineral("Marble", 567, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
sandstone = create_mineral("Sandstone", 568, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
granite = create_mineral("Granite", 569, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
basalt = create_mineral("Basalt", 570, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
limestone = create_mineral("Limestone", 571, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
soapstone = create_mineral("Soapstone", 572, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
hematite = create_mineral("Hematite", 573, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
mudstone = create_mineral("Mudstone", 574, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
obsidian = create_mineral("Obsidian", 575, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
slate = create_mineral("Slate", 576, SVRegion.town,
|
||||
geodes=(Geode.geode, Geode.omni_geode))
|
||||
fairy_stone = create_mineral("Fairy Stone", 577, SVRegion.town,
|
||||
geodes=(Geode.frozen_geode, Geode.omni_geode))
|
||||
star_shards = create_mineral("Star Shards", 578, SVRegion.town,
|
||||
geodes=(Geode.magma_geode, Geode.omni_geode))
|
||||
|
||||
|
||||
dwarf_scrolls = (Artifact.dwarf_scroll_i, Artifact.dwarf_scroll_ii, Artifact.dwarf_scroll_iii, Artifact.dwarf_scroll_iv)
|
||||
skeleton_front = (Artifact.prehistoric_skull, Artifact.skeletal_hand, Artifact.prehistoric_scapula)
|
||||
skeleton_middle = (Artifact.prehistoric_rib, Artifact.prehistoric_vertebra)
|
||||
skeleton_back = (Artifact.prehistoric_tibia, Artifact.skeletal_tail)
|
||||
|
||||
all_museum_items_by_name = {item.name: item for item in all_museum_items}
|
|
@ -0,0 +1,99 @@
|
|||
class SVRegion:
|
||||
menu = "Menu"
|
||||
stardew_valley = "Stardew Valley"
|
||||
farm_house = "Farmhouse"
|
||||
cellar = "Cellar"
|
||||
farm = "Farm"
|
||||
town = "Town"
|
||||
beach = "Beach"
|
||||
mountain = "Mountain"
|
||||
forest = "Forest"
|
||||
bus_stop = "Bus Stop"
|
||||
backwoods = "Backwoods"
|
||||
railroad = "Railroad"
|
||||
secret_woods = "Secret Woods"
|
||||
community_center = "Community Center"
|
||||
pantry = "Pantry"
|
||||
crafts_room = "Crafts Room"
|
||||
fish_tank = "Fish Tank"
|
||||
boiler_room = "Boiler Room"
|
||||
vault = "Vault"
|
||||
bulletin_board = "Bulletin Board"
|
||||
desert = "The Desert"
|
||||
mines = "The Mines"
|
||||
skull_cavern_entrance = "Skull Cavern Entrance"
|
||||
skull_cavern = "Skull Cavern"
|
||||
sewers = "Sewers"
|
||||
mutant_bug_lair = "Mutant Bug Lair"
|
||||
witch_swamp = "Witch's Swamp"
|
||||
ginger_island = "Ginger Island"
|
||||
pirate_cove = ginger_island
|
||||
dig_site = ginger_island
|
||||
perfect_skull_cavern = "Skull Cavern Floor 100"
|
||||
hospital = "Hospital"
|
||||
carpenter = "Carpenter Shop"
|
||||
alex_house = "Alex's House"
|
||||
elliott_house = "Elliott's House"
|
||||
ranch = "Marnie's Ranch"
|
||||
traveling_cart = "Traveling Cart"
|
||||
farm_cave = "Farmcave"
|
||||
greenhouse = "Greenhouse"
|
||||
tunnel = "Tunnel"
|
||||
tunnel_entrance = "Tunnel Entrance"
|
||||
leah_house = "Leah's Cottage"
|
||||
wizard_tower = "Wizard Tower"
|
||||
wizard_basement = "Wizard Basement"
|
||||
tent = "Tent"
|
||||
sebastian_room = "Sebastian's Room"
|
||||
adventurer_guild = "Adventurer's Guild"
|
||||
quarry = "Quarry"
|
||||
quarry_mine_entrance = "Quarry Mine Entrance"
|
||||
quarry_mine = "Quarry Mine"
|
||||
witch_warp_cave = "Witch Warp Cave"
|
||||
harvey_room = "Harvey's Room"
|
||||
pierre_store = "Pierre's General Store"
|
||||
sunroom = "Sunroom"
|
||||
saloon = "Saloon"
|
||||
blacksmith = "Clint's Blacksmith"
|
||||
trailer = "Trailer"
|
||||
museum = "Museum"
|
||||
mayor_house = "Mayor's Manor"
|
||||
haley_house = "Haley's House"
|
||||
sam_house = "Sam's House"
|
||||
jojamart = "JojaMart"
|
||||
fish_shop = "Willy's Fish Shop"
|
||||
tide_pools = "Tide Pools"
|
||||
bathhouse_entrance = "Bathhouse Entrance"
|
||||
locker_room = "Locker Room"
|
||||
public_bath = "Public Bath"
|
||||
jotpk_world_1 = "JotPK World 1"
|
||||
jotpk_world_2 = "JotPK World 2"
|
||||
jotpk_world_3 = "JotPK World 3"
|
||||
junimo_kart_1 = "Junimo Kart 1"
|
||||
junimo_kart_2 = "Junimo Kart 2"
|
||||
junimo_kart_3 = "Junimo Kart 3"
|
||||
mines_floor_5 = "The Mines - Floor 5"
|
||||
mines_floor_10 = "The Mines - Floor 10"
|
||||
mines_floor_15 = "The Mines - Floor 15"
|
||||
mines_floor_20 = "The Mines - Floor 20"
|
||||
mines_floor_25 = "The Mines - Floor 25"
|
||||
mines_floor_30 = "The Mines - Floor 30"
|
||||
mines_floor_35 = "The Mines - Floor 35"
|
||||
mines_floor_40 = "The Mines - Floor 40"
|
||||
mines_floor_45 = "The Mines - Floor 45"
|
||||
mines_floor_50 = "The Mines - Floor 50"
|
||||
mines_floor_55 = "The Mines - Floor 55"
|
||||
mines_floor_60 = "The Mines - Floor 60"
|
||||
mines_floor_65 = "The Mines - Floor 65"
|
||||
mines_floor_70 = "The Mines - Floor 70"
|
||||
mines_floor_75 = "The Mines - Floor 75"
|
||||
mines_floor_80 = "The Mines - Floor 80"
|
||||
mines_floor_85 = "The Mines - Floor 85"
|
||||
mines_floor_90 = "The Mines - Floor 90"
|
||||
mines_floor_95 = "The Mines - Floor 95"
|
||||
mines_floor_100 = "The Mines - Floor 100"
|
||||
mines_floor_105 = "The Mines - Floor 105"
|
||||
mines_floor_110 = "The Mines - Floor 110"
|
||||
mines_floor_115 = "The Mines - Floor 115"
|
||||
mines_floor_120 = "The Mines - Floor 120"
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
spring = "Spring"
|
||||
summer = "Summer"
|
||||
fall = "Fall"
|
||||
winter = "Winter"
|
||||
|
||||
not_spring = (summer, fall, winter)
|
||||
not_summer = (spring, fall, winter)
|
||||
not_fall = (spring, summer, winter)
|
||||
not_winter = (spring, summer, fall)
|
||||
all_seasons = (spring, summer, fall, winter)
|
|
@ -0,0 +1,250 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Set, List, FrozenSet, Tuple
|
||||
from .region_data import SVRegion
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Villager:
|
||||
name: str
|
||||
bachelor: bool
|
||||
locations: Tuple[str]
|
||||
birthday: str
|
||||
gifts: Tuple[str]
|
||||
available: bool
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.name} [Bachelor: {self.bachelor}] [Available from start: {self.available}]" \
|
||||
f"(Locations: {self.locations} |" \
|
||||
f" Birthday: {self.birthday} |" \
|
||||
f" Gifts: {self.gifts}) "
|
||||
|
||||
|
||||
town = (SVRegion.town,)
|
||||
beach = (SVRegion.beach,)
|
||||
forest = (SVRegion.forest,)
|
||||
mountain = (SVRegion.mountain,)
|
||||
hospital = (SVRegion.hospital,)
|
||||
carpenter = (SVRegion.carpenter,)
|
||||
alex_house = (SVRegion.alex_house,)
|
||||
elliott_house = (SVRegion.elliott_house,)
|
||||
ranch = (SVRegion.ranch,)
|
||||
mines = (SVRegion.mines,)
|
||||
desert = (SVRegion.desert,)
|
||||
oasis = (SVRegion.desert,)
|
||||
sewers = (SVRegion.sewers,)
|
||||
island = (SVRegion.ginger_island,)
|
||||
|
||||
golden_pumpkin = ("Golden Pumpkin",)
|
||||
# magic_rock_candy = ("Magic Rock Candy",)
|
||||
pearl = ("Pearl",)
|
||||
prismatic_shard = ("Prismatic Shard",)
|
||||
rabbit_foot = ("Rabbit's Foot",)
|
||||
universal_loves = golden_pumpkin + pearl + prismatic_shard + rabbit_foot # , *magic_rock_candy}
|
||||
universal_loves_no_prismatic_shard = golden_pumpkin + pearl + rabbit_foot # , *magic_rock_candy}
|
||||
universal_loves_no_rabbit_foot = golden_pumpkin + pearl + prismatic_shard # , *magic_rock_candy}
|
||||
complete_breakfast = ("Complete Breakfast",)
|
||||
salmon_dinner = ("Salmon Dinner",)
|
||||
crab_cakes = ("Crab Cakes",)
|
||||
duck_feather = ("Duck Feather",)
|
||||
lobster = ("Lobster",)
|
||||
pomegranate = ("Pomegranate",)
|
||||
squid_ink = ("Squid Ink",)
|
||||
# tom_kha_soup = ("Tom Kha Soup",)
|
||||
elliott_loves = duck_feather + lobster + pomegranate + squid_ink + crab_cakes # | tom_kha_soup
|
||||
coffee = ("Coffee",)
|
||||
pickles = ("Pickles",)
|
||||
# super_meal = ("Super Meal",)
|
||||
truffle_oil = ("Truffle Oil",)
|
||||
wine = ("Wine",)
|
||||
harvey_loves = coffee + pickles + truffle_oil + wine # | super_meal
|
||||
cactus_fruit = ("Cactus Fruit",)
|
||||
maple_bar = ("Maple Bar",)
|
||||
pizza = ("Pizza",)
|
||||
tigerseye = ("Tigerseye",)
|
||||
sam_loves = cactus_fruit + maple_bar + pizza + tigerseye
|
||||
frozen_tear = ("Frozen Tear",)
|
||||
obsidian = ("Obsidian",)
|
||||
# pumpkin_soup = ("Pumpkin Soup",)
|
||||
# sashimi = ("Sashimi",)
|
||||
void_egg = ("Void Egg",)
|
||||
sebastian_loves = frozen_tear + obsidian + void_egg # | pumpkin_soup + sashimi
|
||||
beer = ("Beer",)
|
||||
hot_pepper = ("Hot Pepper",)
|
||||
# pepper_poppers = ("Pepper Poppers",)
|
||||
shane_loves = beer + hot_pepper + pizza # | pepper_poppers
|
||||
amethyst = ("Amethyst",)
|
||||
# banana_pudding = ("Banana Pudding",)
|
||||
blackberry_cobbler = ("Blackberry Cobbler",)
|
||||
chocolate_cake = ("Chocolate Cake",)
|
||||
pufferfish = ("Pufferfish",)
|
||||
pumpkin = ("Pumpkin",)
|
||||
# spicy_eel = ("Spicy Eel",)
|
||||
abigail_loves = amethyst + blackberry_cobbler + chocolate_cake + pufferfish + pumpkin # | banana_pudding + spicy_eel
|
||||
aquamarine = ("Aquamarine",)
|
||||
cloth = ("Cloth",)
|
||||
emerald = ("Emerald",)
|
||||
jade = ("Jade",)
|
||||
ruby = ("Ruby",)
|
||||
survival_burger = ("Survival Burger",)
|
||||
topaz = ("Topaz",)
|
||||
wool = ("Wool",)
|
||||
emily_loves = amethyst + aquamarine + cloth + emerald + jade + ruby + survival_burger + topaz + wool
|
||||
coconut = ("Coconut",)
|
||||
fruit_salad = ("Fruit Salad",)
|
||||
pink_cake = ("Pink Cake",)
|
||||
sunflower = ("Sunflower",)
|
||||
haley_loves = coconut + fruit_salad + pink_cake + sunflower
|
||||
goat_cheese = ("Goat Cheese",)
|
||||
poppyseed_muffin = ("Poppyseed Muffin",)
|
||||
salad = ("Salad",)
|
||||
stir_fry = ("Stir Fry",)
|
||||
truffle = ("Truffle",)
|
||||
# vegetable_medley = ("Vegetable Medley",)
|
||||
leah_loves = goat_cheese + poppyseed_muffin + salad + stir_fry + truffle + wine # | vegetable_medley
|
||||
battery_pack = ("Battery Pack",)
|
||||
cauliflower = ("Cauliflower",)
|
||||
cheese_cauliflower = ("Cheese Cauliflower",)
|
||||
diamond = ("Diamond",)
|
||||
gold_bar = ("Gold Bar",)
|
||||
iridium_bar = ("Iridium Bar",)
|
||||
miners_treat = ("Miner's Treat",)
|
||||
pepper_poppers = ("Pepper Poppers",)
|
||||
radioactive_bar = ("Radioactive Bar",)
|
||||
rhubarb_pie = ("Rhubarb Pie",)
|
||||
strawberry = ("Strawberry",)
|
||||
maru_loves = battery_pack + cauliflower + diamond + gold_bar + iridium_bar + miners_treat + radioactive_bar + strawberry # | cheese_cauliflower + pepper_poppers + rhubarb_pie
|
||||
melon = ("Melon",)
|
||||
poppy = ("Poppy",)
|
||||
# red_plate = ("Red Plate",)
|
||||
roots_platter = ("Roots Platter",)
|
||||
sandfish = ("Sandfish",)
|
||||
penny_loves = diamond + emerald + melon + poppy + poppyseed_muffin + roots_platter + sandfish # | tom_kha_soup + red_plate
|
||||
# fish_taco = ("Fish Taco",)
|
||||
green_tea = ("Green Tea",)
|
||||
summer_spangle = ("Summer Spangle",)
|
||||
tropical_curry = ("Tropical Curry",)
|
||||
caroline_loves = summer_spangle + tropical_curry # | fish_taco + green_tea
|
||||
artichoke_dip = ("Artichoke Dip",)
|
||||
fiddlehead_risotto = ("Fiddlehead Risotto",)
|
||||
omni_geode = ("Omni Geode",)
|
||||
clint_loves = amethyst + aquamarine + artichoke_dip + emerald + fiddlehead_risotto + gold_bar + iridium_bar + jade + \
|
||||
omni_geode + ruby + topaz
|
||||
# bean_hotpot = ("Bean Hotpot",)
|
||||
ice_cream = ("Ice Cream",)
|
||||
# rice_pudding = ("Rice Pudding",)
|
||||
demetrius_loves = ice_cream + strawberry # | bean_hotpot + rice_pudding
|
||||
lemon_stone = ("Lemon Stone",)
|
||||
dwarf_loves = amethyst + aquamarine + emerald + jade + lemon_stone + omni_geode + ruby + topaz
|
||||
beet = ("Beet",)
|
||||
fairy_rose = ("Fairy Rose",)
|
||||
# stuffing = ("Stuffing",)
|
||||
tulip = ("Tulip",)
|
||||
evelyn_loves = beet + chocolate_cake + diamond + fairy_rose + tulip # | stuffing
|
||||
# fried_mushroom = ("Fried Mushroom",)
|
||||
leek = ("Leek",)
|
||||
george_loves = leek # | fried_mushroom
|
||||
# escargot = ("Escargot",)
|
||||
orange = ("Orange",)
|
||||
gus_loves = diamond + orange + tropical_curry # | escargot + fish_taco
|
||||
plum_pudding = ("Plum Pudding",)
|
||||
jas_loves = fairy_rose + pink_cake + plum_pudding
|
||||
# crispy_bass = ("Crispy Bass",)
|
||||
# eggplant_parmesan = ("Eggplant Parmesan",)
|
||||
# fried_eel = ("Fried Eel",)
|
||||
pancakes = ("Pancakes",)
|
||||
jodi_loves = chocolate_cake + diamond + pancakes + rhubarb_pie # | vegetable_medley + crispy_bass + eggplant_parmesan + fried_eel
|
||||
roasted_hazelnuts = ("Roasted Hazelnuts",)
|
||||
kent_loves = fiddlehead_risotto + roasted_hazelnuts
|
||||
void_mayonnaise = ("Void Mayonnaise",)
|
||||
wild_horseradish = ("Wild Horseradish",)
|
||||
krobus_loves = diamond + iridium_bar + pumpkin + void_egg + void_mayonnaise + wild_horseradish
|
||||
mango = ("Mango",)
|
||||
ostrich_egg = ("Ostrich Egg",)
|
||||
# poi = ("Poi",)
|
||||
leo_loves = duck_feather + mango + ostrich_egg # | poi
|
||||
# autumns_bounty = ("Autumn's Bounty",)
|
||||
glazed_yams = ("Glazed Yams",)
|
||||
lewis_loves = glazed_yams + green_tea + hot_pepper # | autumns_bounty + vegetable_medley
|
||||
# blueberry_tart = ("Blueberry Tart",)
|
||||
dish_o_the_sea = ("Dish O' The Sea",)
|
||||
yam = ("Yam",)
|
||||
linus_loves = cactus_fruit + coconut + dish_o_the_sea + yam # | blueberry_tart
|
||||
farmers_lunch = ("Farmer's Lunch",)
|
||||
pumpkin_pie = ("Pumpkin Pie",)
|
||||
marnie_loves = diamond + farmers_lunch + pink_cake + pumpkin_pie
|
||||
mead = ("Mead",)
|
||||
pale_ale = ("Pale Ale",)
|
||||
parsnip = ("Parsnip",)
|
||||
# parsnip_soup = ("Parsnip Soup",)
|
||||
pina_colada = ("Piña Colada",)
|
||||
pam_loves = beer + cactus_fruit + glazed_yams + mead + pale_ale + parsnip + pina_colada # | parsnip_soup
|
||||
# fried_calamari = ("Fried Calamari",)
|
||||
pierre_loves = () # fried_calamari
|
||||
peach = ("Peach",)
|
||||
spaghetti = ("Spaghetti",)
|
||||
robin_loves = goat_cheese + peach + spaghetti
|
||||
crocus = ("Crocus",)
|
||||
daffodil = ("Daffodil",)
|
||||
# mango_stocky_rice = ("Mango Sticky Rice",)
|
||||
sweet_pea = ("Sweet Pea",)
|
||||
sandy_loves = crocus + daffodil + sweet_pea # | mango_stocky_rice
|
||||
cranberry_candy = ("Cranberry Candy",)
|
||||
ginger_ale = ("Ginger Ale",)
|
||||
grape = ("Grape",)
|
||||
snail = ("Snail",)
|
||||
vincent_loves = cranberry_candy + ginger_ale + grape + pink_cake + snail
|
||||
catfish = ("Catfish",)
|
||||
octopus = ("Octopus",)
|
||||
willy_loves = catfish + diamond + iridium_bar + mead + octopus + pumpkin
|
||||
purple_mushroom = ("Purple Mushroom",)
|
||||
solar_essence = ("Solar Essence",)
|
||||
super_cucumber = ("Super Cucumber",)
|
||||
void_essence = ("Void Essence",)
|
||||
wizard_loves = purple_mushroom + solar_essence + super_cucumber + void_essence
|
||||
|
||||
all_villagers: List[Villager] = []
|
||||
|
||||
|
||||
def villager(name: str, bachelor: bool, locations: Tuple[str, ...], birthday: str, gifts: Tuple[str, ...],
|
||||
available: bool) -> Villager:
|
||||
npc = Villager(name, bachelor, locations, birthday, gifts, available)
|
||||
all_villagers.append(npc)
|
||||
return npc
|
||||
|
||||
|
||||
josh = villager("Alex", True, town + alex_house, "Summer", universal_loves + complete_breakfast + salmon_dinner, True)
|
||||
elliott = villager("Elliott", True, town + beach + elliott_house, "Fall", universal_loves + elliott_loves, True)
|
||||
harvey = villager("Harvey", True, town + hospital, "Winter", universal_loves + harvey_loves, True)
|
||||
sam = villager("Sam", True, town, "Summer", universal_loves + sam_loves, True)
|
||||
sebastian = villager("Sebastian", True, carpenter, "Winter", universal_loves + sebastian_loves, True)
|
||||
shane = villager("Shane", True, ranch, "Spring", universal_loves + shane_loves, True)
|
||||
best_girl = villager("Abigail", True, town, "Fall", universal_loves + abigail_loves, True)
|
||||
emily = villager("Emily", True, town, "Spring", universal_loves + emily_loves, True)
|
||||
hoe = villager("Haley", True, town, "Spring", universal_loves_no_prismatic_shard + haley_loves, True)
|
||||
leah = villager("Leah", True, forest, "Winter", universal_loves + leah_loves, True)
|
||||
nerd = villager("Maru", True, carpenter, "Summer", universal_loves + maru_loves, True)
|
||||
penny = villager("Penny", True, town, "Fall", universal_loves_no_rabbit_foot + penny_loves, True)
|
||||
caroline = villager("Caroline", False, town, "Winter", universal_loves + caroline_loves, True)
|
||||
clint = villager("Clint", False, town, "Winter", universal_loves + clint_loves, True)
|
||||
demetrius = villager("Demetrius", False, carpenter, "Summer", universal_loves + demetrius_loves, True)
|
||||
dwarf = villager("Dwarf", False, mines, "Summer", universal_loves + dwarf_loves, False)
|
||||
gilf = villager("Evelyn", False, town, "Winter", universal_loves + evelyn_loves, True)
|
||||
boomer = villager("George", False, town, "Fall", universal_loves + george_loves, True)
|
||||
gus = villager("Gus", False, town, "Summer", universal_loves + gus_loves, True)
|
||||
jas = villager("Jas", False, ranch, "Summer", universal_loves + jas_loves, True)
|
||||
jodi = villager("Jodi", False, town, "Fall", universal_loves + jodi_loves, True)
|
||||
kent = villager("Kent", False, town, "Spring", universal_loves + kent_loves, False)
|
||||
krobus = villager("Krobus", False, sewers, "Winter", universal_loves + krobus_loves, False)
|
||||
leo = villager("Leo", False, island, "Summer", universal_loves + leo_loves, False)
|
||||
lewis = villager("Lewis", False, town, "Spring", universal_loves + lewis_loves, True)
|
||||
linus = villager("Linus", False, mountain, "Winter", universal_loves + linus_loves, True)
|
||||
marnie = villager("Marnie", False, ranch, "Fall", universal_loves + marnie_loves, True)
|
||||
pam = villager("Pam", False, town, "Spring", universal_loves + pam_loves, True)
|
||||
pierre = villager("Pierre", False, town, "Spring", universal_loves + pierre_loves, True)
|
||||
milf = villager("Robin", False, carpenter, "Fall", universal_loves + robin_loves, True)
|
||||
sandy = villager("Sandy", False, oasis, "Fall", universal_loves + sandy_loves, False)
|
||||
vincent = villager("Vincent", False, town, "Spring", universal_loves + vincent_loves, True)
|
||||
willy = villager("Willy", False, beach, "Summer", universal_loves + willy_loves, True)
|
||||
wizard = villager("Wizard", False, forest, "Winter", universal_loves + wizard_loves, True)
|
||||
|
||||
all_villagers_by_name = {item.name: item for item in all_villagers}
|
|
@ -19,6 +19,8 @@ The player can choose from a number of goals, using their YAML settings.
|
|||
- Reach the bottom of the Pelican Town Mineshaft
|
||||
- Complete the "Cryptic Note" quest, by meeting Mr Qi on floor 100 of the Skull Cavern
|
||||
- Get the achievement "Master Angler", which requires catching every fish in the game
|
||||
- Get the achievement "A Complete Collection", which requires donating all the artifacts and minerals to the museum
|
||||
- Get the achievement "Full House", which requires getting married and having two kids.
|
||||
|
||||
## What are location check in Stardew Valley?
|
||||
|
||||
|
@ -38,12 +40,26 @@ There also are a number of location checks that are optional, and individual pla
|
|||
- Arcade Machines
|
||||
- Help Wanted quests
|
||||
- Fishsanity: Catching individual fish
|
||||
- Museumsanity: Donating individual items to the museum, or reaching the museum milestones for donations
|
||||
- Friendsanity: Reaching specific friendship levels with NPCs
|
||||
|
||||
## Which items can be in another player's world?
|
||||
|
||||
Every normal reward from the above locations can be in another player's world.
|
||||
For the locations which do not include a normal reward, Resource Packs are instead added to the pool. These can contain ores, seeds, fertilizers, warp totems, etc.
|
||||
There are a few extra items, which are added to the pool but do not have a matching location. These include
|
||||
|
||||
A player can enable some settings that will add some items to the pool that are relevant to progression
|
||||
- Seasons Randomizer:
|
||||
- All 4 seasons will be items, and one of them will be selected randomly and be added to the player's start inventory
|
||||
- At the end of each month, the player can choose the next season, instead of following the vanilla season order. On Seasons Randomizer, they can only choose from the seasons they have received.
|
||||
- Seed Shuffle:
|
||||
- Every single seed in the game starts off locked and cannot be purchased from any merchant. Their unlocks are received as multiworld items.
|
||||
- The way merchants sell seeds is considerably changed. Pierre sells fewer seeds at a high price, while Joja sells unlimited seeds but in huge discount packs, not individually.
|
||||
- Museumsanity:
|
||||
- The items that are normally obtained from museum donation milestones are added to the item pool. Some items, like the magic rock candy, are duplicated for convenience.
|
||||
- The Traveling Merchant now sells artifacts and minerals, with a bias towards undonated ones, to mitigate randomness. She will sell these items as the player receives "Traveling Merchant Metal Detector" items.
|
||||
|
||||
There are a few extra vanilla items, which are added to the pool for convenience, but do not have a matching location. These include
|
||||
- Wizard Buildings
|
||||
- Return Scepter
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
- Stardew Valley on PC (Recommended: [Steam version](https://store.steampowered.com/app/413150/Stardew_Valley/))
|
||||
- SMAPI ([Mod loader for Stardew Valley](https://smapi.io/))
|
||||
- [StardewArchipelago Mod Release 2.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
|
||||
- It is important to use a mod release of version 2.x.x to play seeds that have been generated here. Later releases can only be used with later releases of the world generator, that are not hosted on archipelago.gg yet.
|
||||
- It is important to use a mod release of version 3.x.x to play seeds that have been generated here. Later releases can only be used with later releases of the world generator, that are not hosted on archipelago.gg yet.
|
||||
|
||||
## Optional Software
|
||||
- Archipelago from the [Archipelago Releases Page](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
|
@ -56,7 +56,14 @@ The password is optional.
|
|||
|
||||
Your game will connect automatically to Archipelago, and reconnect automatically when loading the save, later.
|
||||
|
||||
You will never need to enter this information again for this character.
|
||||
You will never need to enter this information again for this character, unless your room changes its ip or port.
|
||||
If the room's ip or port **does** change, you can follow these instructions to modify the connection information of your save file
|
||||
- Launch modded Stardew Valley
|
||||
- While **on the main menu** of the game, enter the follow command **in the SMAPI console**:
|
||||
- `connect_override ip:port slot password`
|
||||
- Example: `connect_override archipelago.gg:38281 StardewPlayer`
|
||||
- Load your save game. The new connection information will be used, instead of the saved one
|
||||
- Play a full day, sleep, and save the game. This connection information will overwrite the previous one and become permanent.
|
||||
|
||||
### Interacting with the MultiWorld from in-game
|
||||
|
||||
|
|
|
@ -1,127 +0,0 @@
|
|||
from typing import List, Tuple
|
||||
|
||||
from .game_item import FishItem
|
||||
|
||||
spring = ("Spring",)
|
||||
summer = ("Summer",)
|
||||
fall = ("Fall",)
|
||||
winter = ("Winter",)
|
||||
spring_summer = (*spring, *summer)
|
||||
spring_fall = (*spring, *fall)
|
||||
spring_winter = (*spring, *winter)
|
||||
summer_fall = (*summer, *fall)
|
||||
summer_winter = (*summer, *winter)
|
||||
fall_winter = (*fall, *winter)
|
||||
spring_summer_fall = (*spring, *summer, *fall)
|
||||
spring_summer_winter = (*spring, *summer, *winter)
|
||||
spring_fall_winter = (*spring, *fall, *winter)
|
||||
all_seasons = (*spring, *summer, *fall, *winter)
|
||||
|
||||
town = ("Town",)
|
||||
beach = ("Beach",)
|
||||
mountain = ("Mountain",)
|
||||
forest = ("Forest",)
|
||||
secret_woods = ("Secret Woods",)
|
||||
desert = ("The Desert",)
|
||||
mines_20 = ("The Mines - Floor 20",)
|
||||
mines_60 = ("The Mines - Floor 60",)
|
||||
mines_100 = ("The Mines - Floor 100",)
|
||||
sewers = ("Sewers",)
|
||||
mutant_bug_lair = ("Mutant Bug Lair",)
|
||||
witch_swamp = ("Witch's Swamp",)
|
||||
ginger_island = ("Ginger Island",)
|
||||
ginger_island_ocean = ginger_island
|
||||
ginger_island_river = ginger_island
|
||||
pirate_cove = ginger_island
|
||||
night_market = beach
|
||||
lakes = (*mountain, *secret_woods, *sewers)
|
||||
ocean = beach
|
||||
rivers = (*town, *forest)
|
||||
rivers_secret_woods = (*rivers, *secret_woods)
|
||||
forest_mountain = (*forest, *mountain)
|
||||
rivers_mountain_lake = (*town, *forest, *mountain)
|
||||
mines_20_60 = (*mines_20, *mines_60)
|
||||
|
||||
all_fish_items: List[FishItem] = []
|
||||
|
||||
|
||||
def fish(name: str, item_id: int, locations: Tuple[str, ...], seasons: Tuple[str, ...], difficulty: int) -> FishItem:
|
||||
fish_item = FishItem(name, item_id, locations, seasons, difficulty)
|
||||
all_fish_items.append(fish_item)
|
||||
return fish_item
|
||||
|
||||
|
||||
carp = fish("Carp", 142, lakes, all_seasons, 15)
|
||||
herring = fish("Herring", 147, ocean, spring_winter, 25)
|
||||
smallmouth_bass = fish("Smallmouth Bass", 137, rivers, spring_fall, 28)
|
||||
anchovy = fish("Anchovy", 129, ocean, spring_fall, 30)
|
||||
sardine = fish("Sardine", 131, ocean, spring_fall_winter, 30)
|
||||
sunfish = fish("Sunfish", 145, rivers, spring_summer, 30)
|
||||
perch = fish("Perch", 141, rivers_mountain_lake, winter, 35)
|
||||
chub = fish("Chub", 702, forest_mountain, all_seasons, 35)
|
||||
bream = fish("Bream", 132, rivers, all_seasons, 35)
|
||||
red_snapper = fish("Red Snapper", 150, ocean, summer_fall, 40)
|
||||
sea_cucumber = fish("Sea Cucumber", 154, ocean, fall_winter, 40)
|
||||
rainbow_trout = fish("Rainbow Trout", 138, rivers_mountain_lake, summer, 45)
|
||||
walleye = fish("Walleye", 140, rivers_mountain_lake, fall, 45)
|
||||
shad = fish("Shad", 706, rivers, spring_summer_fall, 45)
|
||||
bullhead = fish("Bullhead", 700, mountain, all_seasons, 46)
|
||||
largemouth_bass = fish("Largemouth Bass", 136, mountain, all_seasons, 50)
|
||||
salmon = fish("Salmon", 139, rivers, fall, 50)
|
||||
ghostfish = fish("Ghostfish", 156, mines_20_60, all_seasons, 50)
|
||||
tilapia = fish("Tilapia", 701, ocean, summer_fall, 50)
|
||||
woodskip = fish("Woodskip", 734, secret_woods, all_seasons, 50)
|
||||
flounder = fish("Flounder", 267, ocean, spring_summer, 50)
|
||||
halibut = fish("Halibut", 708, ocean, spring_summer_winter, 50)
|
||||
lionfish = fish("Lionfish", 837, ginger_island_ocean, all_seasons, 50)
|
||||
slimejack = fish("Slimejack", 796, mutant_bug_lair, all_seasons, 55)
|
||||
midnight_carp = fish("Midnight Carp", 269, forest_mountain, fall_winter, 55)
|
||||
red_mullet = fish("Red Mullet", 146, ocean, summer_winter, 55)
|
||||
pike = fish("Pike", 144, rivers, summer_winter, 60)
|
||||
tiger_trout = fish("Tiger Trout", 699, rivers, fall_winter, 60)
|
||||
blue_discus = fish("Blue Discus", 838, ginger_island_river, all_seasons, 60)
|
||||
albacore = fish("Albacore", 705, ocean, fall_winter, 60)
|
||||
sandfish = fish("Sandfish", 164, desert, all_seasons, 65)
|
||||
stonefish = fish("Stonefish", 158, mines_20, all_seasons, 65)
|
||||
tuna = fish("Tuna", 130, ocean, summer_winter, 70)
|
||||
eel = fish("Eel", 148, ocean, spring_fall, 70)
|
||||
catfish = fish("Catfish", 143, rivers_secret_woods, spring_fall, 75)
|
||||
squid = fish("Squid", 151, ocean, winter, 75)
|
||||
sturgeon = fish("Sturgeon", 698, mountain, summer_winter, 78)
|
||||
dorado = fish("Dorado", 704, forest, summer, 78)
|
||||
pufferfish = fish("Pufferfish", 128, ocean, summer, 80)
|
||||
void_salmon = fish("Void Salmon", 795, witch_swamp, all_seasons, 80)
|
||||
super_cucumber = fish("Super Cucumber", 155, ocean, summer_fall, 80)
|
||||
stingray = fish("Stingray", 836, pirate_cove, all_seasons, 80)
|
||||
ice_pip = fish("Ice Pip", 161, mines_60, all_seasons, 85)
|
||||
lingcod = fish("Lingcod", 707, rivers_mountain_lake, winter, 85)
|
||||
scorpion_carp = fish("Scorpion Carp", 165, desert, all_seasons, 90)
|
||||
lava_eel = fish("Lava Eel", 162, mines_100, all_seasons, 90)
|
||||
octopus = fish("Octopus", 149, ocean, summer, 95)
|
||||
|
||||
midnight_squid = fish("Midnight Squid", 798, night_market, winter, 55)
|
||||
spook_fish = fish("Spook Fish", 799, night_market, winter, 60)
|
||||
blob_fish = fish("Blobfish", 800, night_market, winter, 75)
|
||||
|
||||
crimsonfish = fish("Crimsonfish", 159, ocean, summer, 95)
|
||||
angler = fish("Angler", 160, town, fall, 85)
|
||||
legend = fish("Legend", 163, mountain, spring, 110)
|
||||
glacierfish = fish("Glacierfish", 775, forest, winter, 100)
|
||||
mutant_carp = fish("Mutant Carp", 682, sewers, all_seasons, 80)
|
||||
|
||||
crayfish = fish("Crayfish", 716, rivers, all_seasons, -1)
|
||||
snail = fish("Snail", 721, rivers, all_seasons, -1)
|
||||
periwinkle = fish("Periwinkle", 722, rivers, all_seasons, -1)
|
||||
lobster = fish("Lobster", 715, ocean, all_seasons, -1)
|
||||
clam = fish("Clam", 372, ocean, all_seasons, -1)
|
||||
crab = fish("Crab", 717, ocean, all_seasons, -1)
|
||||
cockle = fish("Cockle", 718, ocean, all_seasons, -1)
|
||||
mussel = fish("Mussel", 719, ocean, all_seasons, -1)
|
||||
shrimp = fish("Shrimp", 720, ocean, all_seasons, -1)
|
||||
oyster = fish("Oyster", 723, ocean, all_seasons, -1)
|
||||
|
||||
legendary_fish = [crimsonfish, angler, legend, glacierfish, mutant_carp]
|
||||
special_fish = [*legendary_fish, blob_fish, lava_eel, octopus, scorpion_carp, ice_pip, super_cucumber, dorado]
|
||||
|
||||
all_fish_items_by_name = {fish.name: fish for fish in all_fish_items}
|
||||
all_fish_items_by_id = {fish.item_id: fish for fish in all_fish_items}
|
|
@ -1,26 +0,0 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Tuple
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GameItem:
|
||||
name: str
|
||||
item_id: int
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.name} [{self.item_id}]"
|
||||
|
||||
def __lt__(self, other):
|
||||
return self.name < other.name
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FishItem(GameItem):
|
||||
locations: Tuple[str]
|
||||
seasons: Tuple[str]
|
||||
difficulty: int
|
||||
|
||||
def __repr__(self):
|
||||
return f"{self.name} [{self.item_id}] (Locations: {self.locations} |" \
|
||||
f" Seasons: {self.seasons} |" \
|
||||
f" Difficulty: {self.difficulty}) "
|
|
@ -14,6 +14,8 @@ from typing import Dict, List, Protocol, Union, Set, Optional, FrozenSet
|
|||
|
||||
from BaseClasses import Item, ItemClassification
|
||||
from . import options, data
|
||||
from .data.villagers_data import all_villagers
|
||||
from .options import StardewOptions
|
||||
|
||||
ITEM_CODE_OFFSET = 717000
|
||||
|
||||
|
@ -47,7 +49,12 @@ class Group(enum.Enum):
|
|||
ORE = enum.auto()
|
||||
FERTILIZER = enum.auto()
|
||||
SEED = enum.auto()
|
||||
SEED_SHUFFLE = enum.auto()
|
||||
FISHING_RESOURCE = enum.auto()
|
||||
SEASON = enum.auto()
|
||||
TRAVELING_MERCHANT_DAY = enum.auto()
|
||||
MUSEUM = enum.auto()
|
||||
FRIENDSANITY = enum.auto()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
@ -132,7 +139,7 @@ def load_item_csv():
|
|||
try:
|
||||
from importlib.resources import files
|
||||
except ImportError:
|
||||
from importlib_resources import files
|
||||
from importlib_resources import files # noqa
|
||||
|
||||
items = []
|
||||
with files(data).joinpath("items.csv").open() as file:
|
||||
|
@ -149,7 +156,7 @@ def load_resource_pack_csv() -> List[ResourcePackData]:
|
|||
try:
|
||||
from importlib.resources import files
|
||||
except ImportError:
|
||||
from importlib_resources import files
|
||||
from importlib_resources import files # noqa
|
||||
|
||||
resource_packs = []
|
||||
with files(data).joinpath("resource_packs.csv").open() as file:
|
||||
|
@ -166,11 +173,7 @@ def load_resource_pack_csv() -> List[ResourcePackData]:
|
|||
|
||||
events = [
|
||||
ItemData(None, "Victory", ItemClassification.progression),
|
||||
ItemData(None, "Spring", ItemClassification.progression),
|
||||
ItemData(None, "Summer", ItemClassification.progression),
|
||||
ItemData(None, "Fall", ItemClassification.progression),
|
||||
ItemData(None, "Winter", ItemClassification.progression),
|
||||
ItemData(None, "Year Two", ItemClassification.progression),
|
||||
ItemData(None, "Month End", ItemClassification.progression),
|
||||
]
|
||||
|
||||
all_items: List[ItemData] = load_item_csv() + events
|
||||
|
@ -197,10 +200,14 @@ initialize_item_table()
|
|||
initialize_groups()
|
||||
|
||||
|
||||
def create_items(item_factory: StardewItemFactory, locations_count: int, world_options: options.StardewOptions,
|
||||
random: Random) \
|
||||
-> List[Item]:
|
||||
def create_items(item_factory: StardewItemFactory, locations_count: int, items_to_exclude: List[Item], world_options: StardewOptions,
|
||||
random: Random) -> List[Item]:
|
||||
items = create_unique_items(item_factory, world_options, random)
|
||||
|
||||
for item in items_to_exclude:
|
||||
if item in items:
|
||||
items.remove(item)
|
||||
|
||||
assert len(items) <= locations_count, \
|
||||
"There should be at least as many locations as there are mandatory items"
|
||||
logger.debug(f"Created {len(items)} unique items")
|
||||
|
@ -212,7 +219,37 @@ def create_items(item_factory: StardewItemFactory, locations_count: int, world_o
|
|||
return items
|
||||
|
||||
|
||||
def create_backpack_items(item_factory: StardewItemFactory, world_options: options.StardewOptions, items: List[Item]):
|
||||
def create_unique_items(item_factory: StardewItemFactory, world_options: StardewOptions, random: Random) -> List[Item]:
|
||||
items = []
|
||||
|
||||
items.extend(item_factory(item) for item in items_by_group[Group.COMMUNITY_REWARD])
|
||||
|
||||
create_backpack_items(item_factory, world_options, items)
|
||||
create_mine_rewards(item_factory, items, random)
|
||||
create_mine_elevators(item_factory, world_options, items)
|
||||
create_tools(item_factory, world_options, items)
|
||||
create_skills(item_factory, world_options, items)
|
||||
create_wizard_buildings(item_factory, items)
|
||||
create_carpenter_buildings(item_factory, world_options, items)
|
||||
items.append(item_factory("Beach Bridge"))
|
||||
create_special_quest_rewards(item_factory, items)
|
||||
create_stardrops(item_factory, items)
|
||||
create_museum_items(item_factory, world_options, items)
|
||||
create_arcade_machine_items(item_factory, world_options, items)
|
||||
items.append(item_factory(random.choice(items_by_group[Group.GALAXY_WEAPONS])))
|
||||
items.append(
|
||||
item_factory(friendship_pack.create_name_from_multiplier(world_options[options.ResourcePackMultiplier])))
|
||||
create_player_buffs(item_factory, world_options, items)
|
||||
items.extend(create_traveling_merchant_items(item_factory))
|
||||
items.append(item_factory("Return Scepter"))
|
||||
items.extend(create_seasons(item_factory, world_options))
|
||||
items.extend(create_seeds(item_factory, world_options))
|
||||
create_friendsanity_items(item_factory, world_options, items)
|
||||
|
||||
return items
|
||||
|
||||
|
||||
def create_backpack_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
|
||||
if (world_options[options.BackpackProgression] == options.BackpackProgression.option_progressive or
|
||||
world_options[options.BackpackProgression] == options.BackpackProgression.option_early_progressive):
|
||||
items.extend(item_factory(item) for item in ["Progressive Backpack"] * 2)
|
||||
|
@ -232,7 +269,7 @@ def create_mine_rewards(item_factory: StardewItemFactory, items: List[Item], ran
|
|||
items.append(item_factory("Skull Key"))
|
||||
|
||||
|
||||
def create_mine_elevators(item_factory: StardewItemFactory, world_options: options.StardewOptions, items: List[Item]):
|
||||
def create_mine_elevators(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
|
||||
if (world_options[options.TheMinesElevatorsProgression] ==
|
||||
options.TheMinesElevatorsProgression.option_progressive or
|
||||
world_options[options.TheMinesElevatorsProgression] ==
|
||||
|
@ -240,13 +277,13 @@ def create_mine_elevators(item_factory: StardewItemFactory, world_options: optio
|
|||
items.extend([item_factory(item) for item in ["Progressive Mine Elevator"] * 24])
|
||||
|
||||
|
||||
def create_tools(item_factory: StardewItemFactory, world_options: options.StardewOptions, items: List[Item]):
|
||||
def create_tools(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
|
||||
if world_options[options.ToolProgression] == options.ToolProgression.option_progressive:
|
||||
items.extend(item_factory(item) for item in items_by_group[Group.PROGRESSIVE_TOOLS] * 4)
|
||||
items.append(item_factory("Golden Scythe"))
|
||||
|
||||
|
||||
def create_skills(item_factory: StardewItemFactory, world_options: options.StardewOptions, items: List[Item]):
|
||||
def create_skills(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
|
||||
if world_options[options.SkillProgression] == options.SkillProgression.option_progressive:
|
||||
items.extend([item_factory(item) for item in items_by_group[Group.SKILL_LEVEL_UP] * 10])
|
||||
|
||||
|
@ -260,7 +297,7 @@ def create_wizard_buildings(item_factory: StardewItemFactory, items: List[Item])
|
|||
items.append(item_factory("Gold Clock"))
|
||||
|
||||
|
||||
def create_carpenter_buildings(item_factory: StardewItemFactory, world_options: options.StardewOptions,
|
||||
def create_carpenter_buildings(item_factory: StardewItemFactory, world_options: StardewOptions,
|
||||
items: List[Item]):
|
||||
if world_options[options.BuildingProgression] in {options.BuildingProgression.option_progressive,
|
||||
options.BuildingProgression.option_progressive_early_shipping_bin}:
|
||||
|
@ -297,7 +334,41 @@ def create_stardrops(item_factory: StardewItemFactory, items: List[Item]):
|
|||
items.append(item_factory("Stardrop")) # Old Master Cannoli
|
||||
|
||||
|
||||
def create_arcade_machine_items(item_factory: StardewItemFactory, world_options: options.StardewOptions,
|
||||
def create_museum_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
|
||||
if world_options[options.Museumsanity] == options.Museumsanity.option_none:
|
||||
return
|
||||
items.extend(item_factory(item) for item in ["Magic Rock Candy"] * 5)
|
||||
items.extend(item_factory(item) for item in ["Ancient Seeds"] * 5)
|
||||
items.extend(item_factory(item) for item in ["Traveling Merchant Metal Detector"] * 4)
|
||||
items.append(item_factory("Ancient Seeds Recipe"))
|
||||
items.append(item_factory("Stardrop"))
|
||||
items.append(item_factory("Rusty Key"))
|
||||
items.append(item_factory("Dwarvish Translation Guide"))
|
||||
|
||||
|
||||
def create_friendsanity_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
|
||||
if world_options[options.Friendsanity] == options.Friendsanity.option_none:
|
||||
return
|
||||
exclude_non_bachelors = world_options[options.Friendsanity] == options.Friendsanity.option_bachelors
|
||||
exclude_locked_villagers = world_options[options.Friendsanity] == options.Friendsanity.option_starting_npcs or \
|
||||
world_options[options.Friendsanity] == options.Friendsanity.option_bachelors
|
||||
exclude_post_marriage_hearts = world_options[options.Friendsanity] != options.Friendsanity.option_all_with_marriage
|
||||
for villager in all_villagers:
|
||||
if not villager.available and exclude_locked_villagers:
|
||||
continue
|
||||
if not villager.bachelor and exclude_non_bachelors:
|
||||
continue
|
||||
for heart in range(1, 15):
|
||||
if villager.bachelor and exclude_post_marriage_hearts and heart > 8:
|
||||
continue
|
||||
if villager.bachelor or heart < 11:
|
||||
items.append(item_factory(f"{villager.name}: 1 <3"))
|
||||
if not exclude_non_bachelors:
|
||||
for heart in range(1, 6):
|
||||
items.append(item_factory(f"Pet: 1 <3"))
|
||||
|
||||
|
||||
def create_arcade_machine_items(item_factory: StardewItemFactory, world_options: StardewOptions,
|
||||
items: List[Item]):
|
||||
if world_options[options.ArcadeMachineLocations] == options.ArcadeMachineLocations.option_full_shuffling:
|
||||
items.append(item_factory("JotPK: Progressive Boots"))
|
||||
|
@ -321,43 +392,29 @@ def create_player_buffs(item_factory: StardewItemFactory, world_options: options
|
|||
items.extend(item_factory(item) for item in ["Luck Bonus"] * number_of_buffs)
|
||||
|
||||
|
||||
def create_traveling_merchant_items(item_factory: StardewItemFactory, items: List[Item]):
|
||||
items.append(item_factory("Traveling Merchant: Sunday"))
|
||||
items.append(item_factory("Traveling Merchant: Monday"))
|
||||
items.append(item_factory("Traveling Merchant: Tuesday"))
|
||||
items.append(item_factory("Traveling Merchant: Wednesday"))
|
||||
items.append(item_factory("Traveling Merchant: Thursday"))
|
||||
items.append(item_factory("Traveling Merchant: Friday"))
|
||||
items.append(item_factory("Traveling Merchant: Saturday"))
|
||||
items.extend(item_factory(item) for item in ["Traveling Merchant Stock Size"] * 6)
|
||||
items.extend(item_factory(item) for item in ["Traveling Merchant Discount"] * 8)
|
||||
def create_traveling_merchant_items(item_factory: StardewItemFactory) -> List[Item]:
|
||||
return [
|
||||
*(item_factory(item) for item in items_by_group[Group.TRAVELING_MERCHANT_DAY]),
|
||||
*(item_factory(item) for item in ["Traveling Merchant Stock Size"] * 6),
|
||||
*(item_factory(item) for item in ["Traveling Merchant Discount"] * 8),
|
||||
]
|
||||
|
||||
|
||||
def create_unique_items(item_factory: StardewItemFactory, world_options: options.StardewOptions, random: Random) -> \
|
||||
List[Item]:
|
||||
items = []
|
||||
def create_seasons(item_factory: StardewItemFactory, world_options: StardewOptions) -> List[Item]:
|
||||
if world_options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled:
|
||||
return []
|
||||
|
||||
items.extend(item_factory(item) for item in items_by_group[Group.COMMUNITY_REWARD])
|
||||
if world_options[options.SeasonRandomization] == options.SeasonRandomization.option_progressive:
|
||||
return [item_factory(item) for item in ["Progressive Season"] * 3]
|
||||
|
||||
create_backpack_items(item_factory, world_options, items)
|
||||
create_mine_rewards(item_factory, items, random)
|
||||
create_mine_elevators(item_factory, world_options, items)
|
||||
create_tools(item_factory, world_options, items)
|
||||
create_skills(item_factory, world_options, items)
|
||||
create_wizard_buildings(item_factory, items)
|
||||
create_carpenter_buildings(item_factory, world_options, items)
|
||||
items.append(item_factory("Beach Bridge"))
|
||||
create_special_quest_rewards(item_factory, items)
|
||||
create_stardrops(item_factory, items)
|
||||
create_arcade_machine_items(item_factory, world_options, items)
|
||||
items.append(item_factory(random.choice(items_by_group[Group.GALAXY_WEAPONS])))
|
||||
items.append(
|
||||
item_factory(friendship_pack.create_name_from_multiplier(world_options[options.ResourcePackMultiplier])))
|
||||
create_player_buffs(item_factory, world_options, items)
|
||||
create_traveling_merchant_items(item_factory, items)
|
||||
items.append(item_factory("Return Scepter"))
|
||||
return [item_factory(item) for item in items_by_group[Group.SEASON]]
|
||||
|
||||
return items
|
||||
|
||||
def create_seeds(item_factory: StardewItemFactory, world_options: StardewOptions) -> List[Item]:
|
||||
if world_options[options.SeedShuffle] == options.SeedShuffle.option_disabled:
|
||||
return []
|
||||
|
||||
return [item_factory(item) for item in items_by_group[Group.SEED_SHUFFLE]]
|
||||
|
||||
|
||||
def fill_with_resource_packs(item_factory: StardewItemFactory, world_options: options.StardewOptions, random: Random,
|
||||
|
|
|
@ -5,7 +5,9 @@ from random import Random
|
|||
from typing import Optional, Dict, Protocol, List, FrozenSet
|
||||
|
||||
from . import options, data
|
||||
from .fish_data import legendary_fish, special_fish, all_fish_items
|
||||
from .data.fish_data import legendary_fish, special_fish, all_fish
|
||||
from .data.museum_data import all_museum_items
|
||||
from .data.villagers_data import all_villagers
|
||||
|
||||
LOCATION_CODE_OFFSET = 717000
|
||||
|
||||
|
@ -46,6 +48,9 @@ class LocationTags(enum.Enum):
|
|||
HELP_WANTED = enum.auto()
|
||||
TRAVELING_MERCHANT = enum.auto()
|
||||
FISHSANITY = enum.auto()
|
||||
MUSEUM_MILESTONES = enum.auto()
|
||||
MUSEUM_DONATIONS = enum.auto()
|
||||
FRIENDSANITY = enum.auto()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
|
@ -88,10 +93,8 @@ events_locations = [
|
|||
LocationData(None, "The Mines - Floor 120", "Reach the Bottom of The Mines"),
|
||||
LocationData(None, "Skull Cavern", "Complete Quest Cryptic Note"),
|
||||
LocationData(None, "Stardew Valley", "Catch Every Fish"),
|
||||
LocationData(None, "Stardew Valley", "Summer"),
|
||||
LocationData(None, "Stardew Valley", "Fall"),
|
||||
LocationData(None, "Stardew Valley", "Winter"),
|
||||
LocationData(None, "Stardew Valley", "Year Two"),
|
||||
LocationData(None, "Stardew Valley", "Complete the Museum Collection"),
|
||||
LocationData(None, "Stardew Valley", "Full House"),
|
||||
]
|
||||
|
||||
all_locations = load_location_csv() + events_locations
|
||||
|
@ -133,11 +136,46 @@ def extend_fishsanity_locations(randomized_locations: List[LocationData], fishsa
|
|||
randomized_locations.extend(location_table[f"{prefix}{legendary.name}"] for legendary in legendary_fish)
|
||||
elif fishsanity == options.Fishsanity.option_special:
|
||||
randomized_locations.extend(location_table[f"{prefix}{special.name}"] for special in special_fish)
|
||||
elif fishsanity == options.Fishsanity.option_random_selection:
|
||||
elif fishsanity == options.Fishsanity.option_randomized:
|
||||
randomized_locations.extend(location_table[f"{prefix}{fish.name}"]
|
||||
for fish in all_fish_items if random.random() < 0.4)
|
||||
for fish in all_fish if random.random() < 0.4)
|
||||
elif fishsanity == options.Fishsanity.option_all:
|
||||
randomized_locations.extend(location_table[f"{prefix}{fish.name}"] for fish in all_fish_items)
|
||||
randomized_locations.extend(location_table[f"{prefix}{fish.name}"] for fish in all_fish)
|
||||
|
||||
|
||||
def extend_museumsanity_locations(randomized_locations: List[LocationData], museumsanity: int, random: Random):
|
||||
prefix = "Museumsanity: "
|
||||
if museumsanity == options.Museumsanity.option_none:
|
||||
return
|
||||
elif museumsanity == options.Museumsanity.option_milestones:
|
||||
randomized_locations.extend(locations_by_tag[LocationTags.MUSEUM_MILESTONES])
|
||||
elif museumsanity == options.Museumsanity.option_randomized:
|
||||
randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"]
|
||||
for museum_item in all_museum_items if random.random() < 0.4)
|
||||
elif museumsanity == options.Museumsanity.option_all:
|
||||
randomized_locations.extend(location_table[f"{prefix}{museum_item.name}"] for museum_item in all_museum_items)
|
||||
|
||||
|
||||
def extend_friendsanity_locations(randomized_locations: List[LocationData], friendsanity: int):
|
||||
if friendsanity == options.Friendsanity.option_none:
|
||||
return
|
||||
exclude_non_bachelors = friendsanity == options.Friendsanity.option_bachelors
|
||||
exclude_locked_villagers = friendsanity == options.Friendsanity.option_starting_npcs or \
|
||||
friendsanity == options.Friendsanity.option_bachelors
|
||||
exclude_post_marriage_hearts = friendsanity != options.Friendsanity.option_all_with_marriage
|
||||
for villager in all_villagers:
|
||||
if not villager.available and exclude_locked_villagers:
|
||||
continue
|
||||
if not villager.bachelor and exclude_non_bachelors:
|
||||
continue
|
||||
for heart in range(1, 15):
|
||||
if villager.bachelor and exclude_post_marriage_hearts and heart > 8:
|
||||
continue
|
||||
if villager.bachelor or heart < 11:
|
||||
randomized_locations.append(location_table[f"Friendsanity: {villager.name} {heart} <3"])
|
||||
if not exclude_non_bachelors:
|
||||
for heart in range(1, 6):
|
||||
randomized_locations.append(location_table[f"Friendsanity: Pet {heart} <3"])
|
||||
|
||||
|
||||
def create_locations(location_collector: StardewLocationCollector,
|
||||
|
@ -170,6 +208,8 @@ def create_locations(location_collector: StardewLocationCollector,
|
|||
|
||||
extend_help_wanted_quests(randomized_locations, world_options[options.HelpWantedLocations])
|
||||
extend_fishsanity_locations(randomized_locations, world_options[options.Fishsanity], random)
|
||||
extend_museumsanity_locations(randomized_locations, world_options[options.Museumsanity], random)
|
||||
extend_friendsanity_locations(randomized_locations, world_options[options.Friendsanity])
|
||||
|
||||
for location_data in randomized_locations:
|
||||
location_collector(location_data.name, location_data.code, location_data.region)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,12 +1,12 @@
|
|||
from dataclasses import dataclass
|
||||
from typing import Dict, Union, Protocol, runtime_checkable
|
||||
from typing import Dict, Union, Protocol, runtime_checkable, ClassVar
|
||||
|
||||
from Options import Option, Range, DeathLink, SpecialRange, Toggle, Choice
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class StardewOption(Protocol):
|
||||
internal_name: str
|
||||
internal_name: ClassVar[str]
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -22,18 +22,27 @@ class StardewOptions:
|
|||
|
||||
class Goal(Choice):
|
||||
"""What's your goal with this play-through?
|
||||
With Community Center, the world will be completed once you complete the Community Center.
|
||||
With Grandpa's Evaluation, the world will be completed once 4 candles are lit around Grandpa's Shrine.
|
||||
With Bottom of the Mines, the world will be completed once you reach level 120 in the local mineshaft.
|
||||
With Cryptic Note, the world will be completed once you complete the quest "Cryptic Note" where Mr Qi asks you to reach floor 100 in the Skull Cavern
|
||||
With Master Angler, the world will be completed once you have caught every fish in the game. Pairs well with Fishsanity"""
|
||||
Community Center: The world will be completed once you complete the Community Center.
|
||||
Grandpa's Evaluation: The world will be completed once 4 candles are lit at Grandpa's Shrine.
|
||||
Bottom of the Mines: The world will be completed once you reach level 120 in the mineshaft.
|
||||
Cryptic Note: The world will be completed once you complete the quest "Cryptic Note" where Mr Qi asks you to
|
||||
reach floor 100 in the Skull Cavern.
|
||||
Master Angler: The world will be completed once you have caught every fish in the game. Pairs well with
|
||||
Fishsanity.
|
||||
Complete Collection: The world will be completed once you have completed the museum by donating every possible
|
||||
item. Pairs well with Museumsanity.
|
||||
Full House: The world will be completed once you get married and have two kids. Pairs well with Friendsanity.
|
||||
"""
|
||||
internal_name = "goal"
|
||||
display_name = "Goal"
|
||||
default = 0
|
||||
option_community_center = 0
|
||||
option_grandpa_evaluation = 1
|
||||
option_bottom_of_the_mines = 2
|
||||
option_cryptic_note = 3
|
||||
option_master_angler = 4
|
||||
option_complete_collection = 5
|
||||
option_full_house = 6
|
||||
|
||||
@classmethod
|
||||
def get_option_name(cls, value) -> str:
|
||||
|
@ -63,9 +72,8 @@ class StartingMoney(SpecialRange):
|
|||
|
||||
|
||||
class ResourcePackMultiplier(SpecialRange):
|
||||
"""How many items will be in the resource pack. A lower setting mean fewer resources in each pack.
|
||||
A higher setting means more resources in each pack. Easy (200) doubles the default quantity.
|
||||
This also include Friendship bonuses that replace the one from the Bulletin Board."""
|
||||
"""How many items will be in the resource packs. A lower setting mean fewer resources in each pack.
|
||||
A higher setting means more resources in each pack. Easy (200) doubles the default quantity"""
|
||||
internal_name = "resource_pack_multiplier"
|
||||
default = 100
|
||||
range_start = 0
|
||||
|
@ -83,9 +91,9 @@ class ResourcePackMultiplier(SpecialRange):
|
|||
|
||||
class BundleRandomization(Choice):
|
||||
"""What items are needed for the community center bundles?
|
||||
With Vanilla, you get the standard bundles from the game
|
||||
With Thematic, every bundle will require random items within their original category
|
||||
With Shuffled, every bundle will require random items without logic"""
|
||||
Vanilla: Standard bundles from the vanilla game
|
||||
Thematic: Every bundle will require random items compatible with their original theme
|
||||
Shuffled: Every bundle will require random items and follow no particular structure"""
|
||||
internal_name = "bundle_randomization"
|
||||
display_name = "Bundle Randomization"
|
||||
default = 1
|
||||
|
@ -96,10 +104,10 @@ class BundleRandomization(Choice):
|
|||
|
||||
class BundlePrice(Choice):
|
||||
"""How many items are needed for the community center bundles?
|
||||
With Very Cheap, every bundle will require two items fewer than usual
|
||||
With Cheap, every bundle will require 1 item fewer than usual
|
||||
With Normal, every bundle will require the vanilla number of items
|
||||
With Expensive, every bundle will require 1 extra item"""
|
||||
Very Cheap: Every bundle will require 2 items fewer than usual
|
||||
Cheap: Every bundle will require 1 item fewer than usual
|
||||
Normal: Every bundle will require the vanilla number of items
|
||||
Expensive: Every bundle will require 1 extra item when applicable"""
|
||||
internal_name = "bundle_price"
|
||||
display_name = "Bundle Price"
|
||||
default = 2
|
||||
|
@ -111,13 +119,16 @@ class BundlePrice(Choice):
|
|||
|
||||
class EntranceRandomization(Choice):
|
||||
"""Should area entrances be randomized?
|
||||
With Disabled, no entrance randomization is done
|
||||
With Pelican Town, only buildings in the main town area are randomized with each other
|
||||
With Non Progression, only buildings that are always available are randomized with each other
|
||||
Disabled: No entrance randomization is done
|
||||
Pelican Town: Only buildings in the main town area are randomized among each other
|
||||
Non Progression: Only buildings that are always available are randomized with each other
|
||||
"""
|
||||
# With Buildings, All buildings in the world are randomized with each other
|
||||
# With Everything, All buildings and areas are randomized with each other
|
||||
# With Chaos, same as everything, but the buildings are shuffled again every in-game day. You can't learn it!
|
||||
# Buildings: All buildings in the world are randomized with each other
|
||||
# Everything: All buildings and areas are randomized with each other
|
||||
# Chaos, same as everything: but the buildings are shuffled again every in-game day. You can't learn it!
|
||||
# Buildings One-way: Entrance pairs are disconnected, they aren't two-way!
|
||||
# Everything One-way: Entrance pairs are disconnected, and every entrance is in the shuffle
|
||||
# Chaos One-way: Entrance pairs are disconnected, and they change every day!
|
||||
|
||||
internal_name = "entrance_randomization"
|
||||
display_name = "Entrance Randomization"
|
||||
|
@ -128,14 +139,47 @@ class EntranceRandomization(Choice):
|
|||
# option_buildings = 3
|
||||
# option_everything = 4
|
||||
# option_chaos = 4
|
||||
# option_buildings_one_way = 5
|
||||
# option_everything_one_way = 6
|
||||
# option_chaos_one_way = 7
|
||||
|
||||
|
||||
class SeasonRandomization(Choice):
|
||||
"""Should seasons be randomized?
|
||||
All settings allow you to choose which season you want to play next (from those unlocked) at the end of a season.
|
||||
Disabled: You will start in Spring with all seasons unlocked.
|
||||
Randomized: The seasons will be unlocked randomly as Archipelago items.
|
||||
Randomized Not Winter: The seasons are randomized, but you're guaranteed not to start with winter.
|
||||
Progressive: You will start in Spring and unlock the seasons in their original order.
|
||||
"""
|
||||
internal_name = "season_randomization"
|
||||
display_name = "Season Randomization"
|
||||
default = 1
|
||||
option_disabled = 0
|
||||
option_randomized = 1
|
||||
option_randomized_not_winter = 2
|
||||
option_progressive = 3
|
||||
|
||||
|
||||
class SeedShuffle(Choice):
|
||||
"""Should seeds be randomized?
|
||||
Pierre now sells a random amount of seasonal seeds and Joja sells them without season requirements, but only in
|
||||
huge packs.
|
||||
Disabled: All the seeds will be unlocked from the start.
|
||||
Randomized: The seeds will be unlocked as Archipelago items
|
||||
"""
|
||||
internal_name = "seed_shuffle"
|
||||
display_name = "Seed Shuffle"
|
||||
default = 1
|
||||
option_disabled = 0
|
||||
option_shuffled = 1
|
||||
|
||||
|
||||
class BackpackProgression(Choice):
|
||||
"""How is the backpack progression handled?
|
||||
With Vanilla, you can buy them at Pierre's.
|
||||
With Progressive, you will randomly find Progressive Backpack to upgrade.
|
||||
With Early Progressive, you can expect you first Backpack before the second season, and the third before the forth
|
||||
season.
|
||||
Vanilla: You can buy them at Pierre's General Store.
|
||||
Progressive: You will randomly find Progressive Backpack upgrades.
|
||||
Early Progressive: You can expect your first Backpack in sphere 1.
|
||||
"""
|
||||
internal_name = "backpack_progression"
|
||||
display_name = "Backpack Progression"
|
||||
|
@ -147,9 +191,8 @@ class BackpackProgression(Choice):
|
|||
|
||||
class ToolProgression(Choice):
|
||||
"""How is the tool progression handled?
|
||||
With Vanilla, Clint will upgrade your tools with ore.
|
||||
With Progressive, you will randomly find Progressive Tool to upgrade.
|
||||
With World Checks, the tools of different quality will be found in the world."""
|
||||
Vanilla: Clint will upgrade your tools with ore.
|
||||
Progressive: You will randomly find Progressive Tool upgrades."""
|
||||
internal_name = "tool_progression"
|
||||
display_name = "Tool Progression"
|
||||
default = 1
|
||||
|
@ -159,11 +202,11 @@ class ToolProgression(Choice):
|
|||
|
||||
class TheMinesElevatorsProgression(Choice):
|
||||
"""How is The Mines' Elevator progression handled?
|
||||
With Vanilla, you will unlock a new elevator floor every 5 floor in the mine.
|
||||
With Progressive, you will randomly find Progressive Mine Elevator to go deeper. Location are sent for reaching
|
||||
Vanilla: You will unlock a new elevator floor every 5 floor in the mine.
|
||||
Progressive: You will randomly find Progressive Mine Elevator to go deeper. Location are sent for reaching
|
||||
every level multiple of 5.
|
||||
With Progressive from previous floor, you will randomly find Progressive Mine Elevator to go deeper. Location are
|
||||
sent for taking the ladder or stair to every level multiple of 5, taking the elevator does not count."""
|
||||
Progressive from previous floor: Locations are sent for taking the ladder or stairs to every 5
|
||||
levels, taking the elevator does not count."""
|
||||
internal_name = "elevator_progression"
|
||||
display_name = "Elevator Progression"
|
||||
default = 2
|
||||
|
@ -174,9 +217,9 @@ class TheMinesElevatorsProgression(Choice):
|
|||
|
||||
class SkillProgression(Choice):
|
||||
"""How is the skill progression handled?
|
||||
With Vanilla, you will level up and get the normal reward at each level.
|
||||
With Progressive, the xp will be counted internally, locations will be sent when you gain a virtual level. Your real
|
||||
levels will be scattered around the world."""
|
||||
Vanilla: You will level up and get the normal reward at each level.
|
||||
Progressive: The xp will be earned internally, locations will be sent when you earn a level. Your real
|
||||
levels will be scattered around the multiworld."""
|
||||
internal_name = "skill_progression"
|
||||
display_name = "Skill Progression"
|
||||
default = 1
|
||||
|
@ -186,11 +229,10 @@ class SkillProgression(Choice):
|
|||
|
||||
class BuildingProgression(Choice):
|
||||
"""How is the building progression handled?
|
||||
With Vanilla, you will buy each building and upgrade one at the time.
|
||||
With Progressive, you will receive the buildings and will be able to build the first one of each building for free,
|
||||
Vanilla: You will buy each building normally.
|
||||
Progressive: You will receive the buildings and will be able to build the first one of each type for free,
|
||||
once it is received. If you want more of the same building, it will cost the vanilla price.
|
||||
This option INCLUDES the shipping bin as a building you need to receive.
|
||||
With Progressive early shipping bin, you can expect to receive the shipping bin before the end of the first season.
|
||||
Progressive early shipping bin: You can expect your shipping bin in sphere 1.
|
||||
"""
|
||||
internal_name = "building_progression"
|
||||
display_name = "Building Progression"
|
||||
|
@ -202,11 +244,11 @@ class BuildingProgression(Choice):
|
|||
|
||||
class ArcadeMachineLocations(Choice):
|
||||
"""How are the Arcade Machines handled?
|
||||
With Vanilla, the arcade machines are not included in the Archipelago shuffling.
|
||||
With Victories, each Arcade Machine will contain one check on victory
|
||||
With Victories Easy, the arcade machines are both made considerably easier to be more accessible for the average
|
||||
Vanilla: The arcade machines are not included in the Archipelago shuffling.
|
||||
Victories: Each Arcade Machine will contain one check on victory
|
||||
Victories Easy: The arcade machines are both made considerably easier to be more accessible for the average
|
||||
player.
|
||||
With Full Shuffling, the arcade machines will contain multiple checks each, and different buffs that make the game
|
||||
Full Shuffling: The arcade machines will contain multiple checks each, and different buffs that make the game
|
||||
easier are in the item pool. Junimo Kart has one check at the end of each level.
|
||||
Journey of the Prairie King has one check after each boss, plus one check for each vendor equipment.
|
||||
"""
|
||||
|
@ -220,7 +262,7 @@ class ArcadeMachineLocations(Choice):
|
|||
|
||||
|
||||
class HelpWantedLocations(SpecialRange):
|
||||
"""How many "Help Wanted" quests need to be completed as ArchipelagoLocations
|
||||
"""How many "Help Wanted" quests need to be completed as Archipelago Locations
|
||||
Out of every 7 quests, 4 will be item deliveries, and then 1 of each for: Fishing, Gathering and Slaying Monsters.
|
||||
Choosing a multiple of 7 is recommended."""
|
||||
internal_name = "help_wanted_locations"
|
||||
|
@ -241,11 +283,11 @@ class HelpWantedLocations(SpecialRange):
|
|||
|
||||
class Fishsanity(Choice):
|
||||
"""Locations for catching fish?
|
||||
With None, there are no locations for catching fish
|
||||
With Legendaries, each of the 5 legendary fish are locations that contain items
|
||||
With Special, a curated selection of strong fish are locations that contain items
|
||||
With Random Selection, a random selection of fish are locations that contain items
|
||||
With All, every single fish in the game is a location that contains an item. Pairs well with the Master Angler Goal
|
||||
None: There are no locations for catching fish
|
||||
Legendaries: Each of the 5 legendary fish are checks
|
||||
Special: A curated selection of strong fish are checks
|
||||
Randomized: A random selection of fish are checks
|
||||
All: Every single fish in the game is a location that contains an item. Pairs well with the Master Angler Goal
|
||||
"""
|
||||
internal_name = "fishsanity"
|
||||
display_name = "Fishsanity"
|
||||
|
@ -253,10 +295,45 @@ class Fishsanity(Choice):
|
|||
option_none = 0
|
||||
option_legendaries = 1
|
||||
option_special = 2
|
||||
option_random_selection = 3
|
||||
option_randomized = 3
|
||||
option_all = 4
|
||||
|
||||
|
||||
class Museumsanity(Choice):
|
||||
"""Locations for museum donations?
|
||||
None: There are no locations for donating artifacts and minerals to the museum
|
||||
Milestones: The donation milestones from the vanilla game are checks
|
||||
Randomized: A random selection of minerals and artifacts are checks
|
||||
All: Every single donation will be a check
|
||||
"""
|
||||
internal_name = "museumsanity"
|
||||
display_name = "Museumsanity"
|
||||
default = 1
|
||||
option_none = 0
|
||||
option_milestones = 1
|
||||
option_randomized = 2
|
||||
option_all = 3
|
||||
|
||||
|
||||
class Friendsanity(Choice):
|
||||
"""Locations for friendships?
|
||||
None: There are no checks for befriending villagers
|
||||
Bachelors: Each heart of a bachelor is a check
|
||||
Starting NPCs: Each heart for npcs that are immediately available is a check
|
||||
All: Every heart with every NPC is a check, including Leo, Kent, Sandy, etc
|
||||
All With Marriage: Marriage candidates must also be dated, married, and befriended up to 14 hearts.
|
||||
"""
|
||||
internal_name = "friendsanity"
|
||||
display_name = "Friendsanity"
|
||||
default = 0
|
||||
option_none = 0
|
||||
# option_marry_one_person = 1
|
||||
option_bachelors = 2
|
||||
option_starting_npcs = 3
|
||||
option_all = 4
|
||||
option_all_with_marriage = 5
|
||||
|
||||
|
||||
class NumberOfPlayerBuffs(Range):
|
||||
"""Number of buffs to the player of each type that exist as items in the pool.
|
||||
Buffs include movement speed (+25% multiplier, stacks additively)
|
||||
|
@ -270,14 +347,14 @@ class NumberOfPlayerBuffs(Range):
|
|||
|
||||
|
||||
class MultipleDaySleepEnabled(Toggle):
|
||||
"""Should you be able to sleep automatically multiple day strait?"""
|
||||
"""Enable the ability to sleep automatically for multiple days straight?"""
|
||||
internal_name = "multiple_day_sleep_enabled"
|
||||
display_name = "Multiple Day Sleep Enabled"
|
||||
default = 1
|
||||
|
||||
|
||||
class MultipleDaySleepCost(SpecialRange):
|
||||
"""How must gold it cost to sleep through multiple days? You will have to pay that amount for each day slept."""
|
||||
"""How much gold it will cost to use MultiSleep. You will have to pay that amount for each day skipped."""
|
||||
internal_name = "multiple_day_sleep_cost"
|
||||
display_name = "Multiple Day Sleep Cost"
|
||||
range_start = 0
|
||||
|
@ -293,7 +370,7 @@ class MultipleDaySleepCost(SpecialRange):
|
|||
|
||||
|
||||
class ExperienceMultiplier(SpecialRange):
|
||||
"""How fast do you want to level up. A lower setting mean less experience.
|
||||
"""How fast you want to earn skill experience. A lower setting mean less experience.
|
||||
A higher setting means more experience."""
|
||||
internal_name = "experience_multiplier"
|
||||
display_name = "Experience Multiplier"
|
||||
|
@ -311,13 +388,33 @@ class ExperienceMultiplier(SpecialRange):
|
|||
}
|
||||
|
||||
|
||||
class FriendshipMultiplier(SpecialRange):
|
||||
"""How fast you want to earn friendship points with villagers.
|
||||
A lower setting mean less friendship per action.
|
||||
A higher setting means more friendship per action."""
|
||||
internal_name = "friendship_multiplier"
|
||||
display_name = "Friendship Multiplier"
|
||||
range_start = 25
|
||||
range_end = 400
|
||||
# step = 25
|
||||
default = 200
|
||||
|
||||
special_range_names = {
|
||||
"half": 50,
|
||||
"vanilla": 100,
|
||||
"double": 200,
|
||||
"triple": 300,
|
||||
"quadruple": 400,
|
||||
}
|
||||
|
||||
|
||||
class DebrisMultiplier(Choice):
|
||||
"""How much debris spawn on the player's farm?
|
||||
With Vanilla, debris spawns normally
|
||||
With Half, debris will spawn at half the normal rate
|
||||
With Quarter, debris will spawn at one quarter of the normal rate
|
||||
With None, No debris will spawn on the farm, ever
|
||||
With Start Clear, debris will spawn at the normal rate, but the farm will be completely clear when starting the game
|
||||
"""How much debris will spawn on the player's farm?
|
||||
Vanilla: debris spawns normally
|
||||
Half: debris will spawn at half the normal rate
|
||||
Quarter: debris will spawn at one quarter of the normal rate
|
||||
None: No debris will spawn on the farm, ever
|
||||
Start Clear: debris will spawn at the normal rate, but the farm will be completely clear when starting the game
|
||||
"""
|
||||
internal_name = "debris_multiplier"
|
||||
display_name = "Debris Multiplier"
|
||||
|
@ -364,33 +461,37 @@ class GiftTax(SpecialRange):
|
|||
}
|
||||
|
||||
|
||||
stardew_valley_options: Dict[str, type(Option)] = {
|
||||
option.internal_name: option
|
||||
for option in [
|
||||
StartingMoney,
|
||||
ResourcePackMultiplier,
|
||||
BundleRandomization,
|
||||
BundlePrice,
|
||||
EntranceRandomization,
|
||||
BackpackProgression,
|
||||
ToolProgression,
|
||||
SkillProgression,
|
||||
BuildingProgression,
|
||||
TheMinesElevatorsProgression,
|
||||
ArcadeMachineLocations,
|
||||
HelpWantedLocations,
|
||||
Fishsanity,
|
||||
NumberOfPlayerBuffs,
|
||||
Goal,
|
||||
MultipleDaySleepEnabled,
|
||||
MultipleDaySleepCost,
|
||||
ExperienceMultiplier,
|
||||
DebrisMultiplier,
|
||||
QuickStart,
|
||||
Gifting,
|
||||
GiftTax,
|
||||
]
|
||||
}
|
||||
stardew_valley_option_classes = [
|
||||
StartingMoney,
|
||||
ResourcePackMultiplier,
|
||||
BundleRandomization,
|
||||
BundlePrice,
|
||||
EntranceRandomization,
|
||||
SeasonRandomization,
|
||||
SeedShuffle,
|
||||
BackpackProgression,
|
||||
ToolProgression,
|
||||
SkillProgression,
|
||||
BuildingProgression,
|
||||
TheMinesElevatorsProgression,
|
||||
ArcadeMachineLocations,
|
||||
HelpWantedLocations,
|
||||
Fishsanity,
|
||||
Museumsanity,
|
||||
Friendsanity,
|
||||
NumberOfPlayerBuffs,
|
||||
Goal,
|
||||
MultipleDaySleepEnabled,
|
||||
MultipleDaySleepCost,
|
||||
ExperienceMultiplier,
|
||||
FriendshipMultiplier,
|
||||
DebrisMultiplier,
|
||||
QuickStart,
|
||||
Gifting,
|
||||
GiftTax,
|
||||
]
|
||||
stardew_valley_options: Dict[str, type(Option)] = {option.internal_name: option for option in
|
||||
stardew_valley_option_classes}
|
||||
default_options = {option.internal_name: option.default for option in stardew_valley_options.values()}
|
||||
stardew_valley_options["death_link"] = DeathLink
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ from typing import Iterable, Dict, Protocol, Optional, List, Tuple
|
|||
|
||||
from BaseClasses import Region, Entrance
|
||||
from . import options
|
||||
from .data.region_data import SVRegion
|
||||
from .options import StardewOptions
|
||||
|
||||
|
||||
|
@ -42,219 +43,237 @@ class ConnectionData:
|
|||
|
||||
|
||||
stardew_valley_regions = [
|
||||
RegionData("Menu", ["To Stardew Valley"]),
|
||||
RegionData("Stardew Valley", ["To Farmhouse"]),
|
||||
RegionData("Farmhouse", ["Outside to Farm", "Downstairs to Cellar"]),
|
||||
RegionData("Cellar"),
|
||||
RegionData("Farm", ["Farm to Backwoods", "Farm to Bus Stop", "Farm to Forest", "Farm to Farmcave", "Enter Greenhouse",
|
||||
"Use Desert Obelisk", "Use Island Obelisk"]),
|
||||
RegionData("Backwoods", ["Backwoods to Mountain"]),
|
||||
RegionData("Bus Stop", ["Bus Stop to Town", "Take Bus to Desert", "Bus Stop to Tunnel Entrance"]),
|
||||
RegionData("Forest", ["Forest to Town", "Enter Secret Woods", "Forest to Wizard Tower", "Forest to Marnie's Ranch",
|
||||
"Forest to Leah's Cottage", "Forest to Sewers"]),
|
||||
RegionData("Farmcave"),
|
||||
RegionData("Greenhouse"),
|
||||
RegionData("Mountain", ["Mountain to Railroad", "Mountain to Tent", "Mountain to Carpenter Shop", "Mountain to The Mines",
|
||||
"Enter Quarry", "Mountain to Adventurer's Guild", "Mountain to Town"]),
|
||||
RegionData("Tunnel Entrance", ["Enter Tunnel"]),
|
||||
RegionData("Tunnel"),
|
||||
RegionData("Town", ["Town to Community Center", "Town to Beach", "Town to Hospital",
|
||||
"Town to Pierre's General Store", "Town to Saloon", "Town to Alex's House", "Town to Trailer", "Town to Mayor's Manor",
|
||||
"Town to Sam's House", "Town to Haley's House", "Town to Sewers", "Town to Clint's Blacksmith", "Town to Museum",
|
||||
RegionData(SVRegion.menu, ["To Stardew Valley"]),
|
||||
RegionData(SVRegion.stardew_valley, ["To Farmhouse"]),
|
||||
RegionData(SVRegion.farm_house, ["Outside to Farm", "Downstairs to Cellar"]),
|
||||
RegionData(SVRegion.cellar),
|
||||
RegionData(SVRegion.farm,
|
||||
["Farm to Backwoods", "Farm to Bus Stop", "Farm to Forest", "Farm to Farmcave", "Enter Greenhouse",
|
||||
"Use Desert Obelisk", "Use Island Obelisk"]),
|
||||
RegionData(SVRegion.backwoods, ["Backwoods to Mountain"]),
|
||||
RegionData(SVRegion.bus_stop, ["Bus Stop to Town", "Take Bus to Desert", "Bus Stop to Tunnel Entrance"]),
|
||||
RegionData(SVRegion.forest, ["Forest to Town", "Enter Secret Woods", "Forest to Wizard Tower", "Forest to Marnie's Ranch",
|
||||
"Forest to Leah's Cottage", "Forest to Sewers", "Talk to Traveling Merchant"]),
|
||||
RegionData(SVRegion.traveling_cart),
|
||||
RegionData(SVRegion.farm_cave),
|
||||
RegionData(SVRegion.greenhouse),
|
||||
RegionData(SVRegion.mountain,
|
||||
["Mountain to Railroad", "Mountain to Tent", "Mountain to Carpenter Shop", "Mountain to The Mines",
|
||||
"Enter Quarry", "Mountain to Adventurer's Guild", "Mountain to Town"]),
|
||||
RegionData(SVRegion.tunnel_entrance, ["Enter Tunnel"]),
|
||||
RegionData(SVRegion.tunnel),
|
||||
RegionData(SVRegion.town, ["Town to Community Center", "Town to Beach", "Town to Hospital",
|
||||
"Town to Pierre's General Store", "Town to Saloon", "Town to Alex's House", "Town to Trailer",
|
||||
"Town to Mayor's Manor",
|
||||
"Town to Sam's House", "Town to Haley's House", "Town to Sewers", "Town to Clint's Blacksmith",
|
||||
"Town to Museum",
|
||||
"Town to JojaMart"]),
|
||||
RegionData("Beach", ["Beach to Willy's Fish Shop", "Enter Elliott's House", "Enter Tide Pools"]),
|
||||
RegionData("Railroad", ["Enter Bathhouse Entrance", "Enter Witch Warp Cave"]), # "Enter Perfection Cutscene Area"
|
||||
RegionData("Marnie's Ranch"),
|
||||
RegionData("Leah's Cottage"),
|
||||
RegionData("Sewers", ["Enter Mutant Bug Lair"]),
|
||||
RegionData("Mutant Bug Lair"),
|
||||
RegionData("Wizard Tower", ["Enter Wizard Basement"]),
|
||||
RegionData("Wizard Basement"),
|
||||
RegionData("Tent"),
|
||||
RegionData("Carpenter Shop", ["Enter Sebastian's Room"]),
|
||||
RegionData("Sebastian's Room"),
|
||||
RegionData("Adventurer's Guild"),
|
||||
RegionData("Community Center",
|
||||
["Access Crafts Room", "Access Pantry", "Access Fish Tank", "Access Boiler Room", "Access Bulletin Board",
|
||||
RegionData(SVRegion.beach, ["Beach to Willy's Fish Shop", "Enter Elliott's House", "Enter Tide Pools"]),
|
||||
RegionData(SVRegion.railroad, ["Enter Bathhouse Entrance", "Enter Witch Warp Cave"]), # "Enter Perfection Cutscene Area"
|
||||
RegionData(SVRegion.ranch),
|
||||
RegionData(SVRegion.leah_house),
|
||||
RegionData(SVRegion.sewers, ["Enter Mutant Bug Lair"]),
|
||||
RegionData(SVRegion.mutant_bug_lair),
|
||||
RegionData(SVRegion.wizard_tower, ["Enter Wizard Basement"]),
|
||||
RegionData(SVRegion.wizard_basement),
|
||||
RegionData(SVRegion.tent),
|
||||
RegionData(SVRegion.carpenter, ["Enter Sebastian's Room"]),
|
||||
RegionData(SVRegion.sebastian_room),
|
||||
RegionData(SVRegion.adventurer_guild),
|
||||
RegionData(SVRegion.community_center,
|
||||
["Access Crafts Room", "Access Pantry", "Access Fish Tank", "Access Boiler Room",
|
||||
"Access Bulletin Board",
|
||||
"Access Vault"]),
|
||||
RegionData("Crafts Room"),
|
||||
RegionData("Pantry"),
|
||||
RegionData("Fish Tank"),
|
||||
RegionData("Boiler Room"),
|
||||
RegionData("Bulletin Board"),
|
||||
RegionData("Vault"),
|
||||
RegionData("Hospital", ["Enter Harvey's Room"]),
|
||||
RegionData("Harvey's Room"),
|
||||
RegionData("Pierre's General Store", ["Enter Sunroom"]),
|
||||
RegionData("Sunroom"),
|
||||
RegionData("Saloon", ["Play Journey of the Prairie King", "Play Junimo Kart"]),
|
||||
RegionData("Alex's House"),
|
||||
RegionData("Trailer"),
|
||||
RegionData("Mayor's Manor"),
|
||||
RegionData("Sam's House"),
|
||||
RegionData("Haley's House"),
|
||||
RegionData("Clint's Blacksmith"),
|
||||
RegionData("Museum"),
|
||||
RegionData("JojaMart"),
|
||||
RegionData("Willy's Fish Shop"),
|
||||
RegionData("Elliott's House"),
|
||||
RegionData("Tide Pools"),
|
||||
RegionData("Bathhouse Entrance", ["Enter Locker Room"]),
|
||||
RegionData("Locker Room", ["Enter Public Bath"]),
|
||||
RegionData("Public Bath"),
|
||||
RegionData("Witch Warp Cave", ["Enter Witch's Swamp"]),
|
||||
RegionData("Witch's Swamp"),
|
||||
RegionData("Quarry", ["Enter Quarry Mine Entrance"]),
|
||||
RegionData("Quarry Mine Entrance", ["Enter Quarry Mine"]),
|
||||
RegionData("Quarry Mine"),
|
||||
RegionData("Secret Woods"),
|
||||
RegionData("The Desert", ["Enter Skull Cavern Entrance"]),
|
||||
RegionData("Skull Cavern Entrance", ["Enter Skull Cavern"]),
|
||||
RegionData("Skull Cavern"),
|
||||
RegionData("Ginger Island"),
|
||||
RegionData("JotPK World 1", ["Reach JotPK World 2"]),
|
||||
RegionData("JotPK World 2", ["Reach JotPK World 3"]),
|
||||
RegionData("JotPK World 3"),
|
||||
RegionData("Junimo Kart 1", ["Reach Junimo Kart 2"]),
|
||||
RegionData("Junimo Kart 2", ["Reach Junimo Kart 3"]),
|
||||
RegionData("Junimo Kart 3"),
|
||||
RegionData("The Mines", ["Dig to The Mines - Floor 5", "Dig to The Mines - Floor 10", "Dig to The Mines - Floor 15",
|
||||
"Dig to The Mines - Floor 20", "Dig to The Mines - Floor 25", "Dig to The Mines - Floor 30",
|
||||
"Dig to The Mines - Floor 35", "Dig to The Mines - Floor 40", "Dig to The Mines - Floor 45",
|
||||
"Dig to The Mines - Floor 50", "Dig to The Mines - Floor 55", "Dig to The Mines - Floor 60",
|
||||
"Dig to The Mines - Floor 65", "Dig to The Mines - Floor 70", "Dig to The Mines - Floor 75",
|
||||
"Dig to The Mines - Floor 80", "Dig to The Mines - Floor 85", "Dig to The Mines - Floor 90",
|
||||
"Dig to The Mines - Floor 95", "Dig to The Mines - Floor 100", "Dig to The Mines - Floor 105",
|
||||
"Dig to The Mines - Floor 110", "Dig to The Mines - Floor 115", "Dig to The Mines - Floor 120"]),
|
||||
RegionData("The Mines - Floor 5"),
|
||||
RegionData("The Mines - Floor 10"),
|
||||
RegionData("The Mines - Floor 15"),
|
||||
RegionData("The Mines - Floor 20"),
|
||||
RegionData("The Mines - Floor 25"),
|
||||
RegionData("The Mines - Floor 30"),
|
||||
RegionData("The Mines - Floor 35"),
|
||||
RegionData("The Mines - Floor 40"),
|
||||
RegionData("The Mines - Floor 45"),
|
||||
RegionData("The Mines - Floor 50"),
|
||||
RegionData("The Mines - Floor 55"),
|
||||
RegionData("The Mines - Floor 60"),
|
||||
RegionData("The Mines - Floor 65"),
|
||||
RegionData("The Mines - Floor 70"),
|
||||
RegionData("The Mines - Floor 75"),
|
||||
RegionData("The Mines - Floor 80"),
|
||||
RegionData("The Mines - Floor 85"),
|
||||
RegionData("The Mines - Floor 90"),
|
||||
RegionData("The Mines - Floor 95"),
|
||||
RegionData("The Mines - Floor 100"),
|
||||
RegionData("The Mines - Floor 105"),
|
||||
RegionData("The Mines - Floor 110"),
|
||||
RegionData("The Mines - Floor 115"),
|
||||
RegionData("The Mines - Floor 120"),
|
||||
RegionData(SVRegion.crafts_room),
|
||||
RegionData(SVRegion.pantry),
|
||||
RegionData(SVRegion.fish_tank),
|
||||
RegionData(SVRegion.boiler_room),
|
||||
RegionData(SVRegion.bulletin_board),
|
||||
RegionData(SVRegion.vault),
|
||||
RegionData(SVRegion.hospital, ["Enter Harvey's Room"]),
|
||||
RegionData(SVRegion.harvey_room),
|
||||
RegionData(SVRegion.pierre_store, ["Enter Sunroom"]),
|
||||
RegionData(SVRegion.sunroom),
|
||||
RegionData(SVRegion.saloon, ["Play Journey of the Prairie King", "Play Junimo Kart"]),
|
||||
RegionData(SVRegion.alex_house),
|
||||
RegionData(SVRegion.trailer),
|
||||
RegionData(SVRegion.mayor_house),
|
||||
RegionData(SVRegion.sam_house),
|
||||
RegionData(SVRegion.haley_house),
|
||||
RegionData(SVRegion.blacksmith),
|
||||
RegionData(SVRegion.museum),
|
||||
RegionData(SVRegion.jojamart),
|
||||
RegionData(SVRegion.fish_shop),
|
||||
RegionData(SVRegion.elliott_house),
|
||||
RegionData(SVRegion.tide_pools),
|
||||
RegionData(SVRegion.bathhouse_entrance, ["Enter Locker Room"]),
|
||||
RegionData(SVRegion.locker_room, ["Enter Public Bath"]),
|
||||
RegionData(SVRegion.public_bath),
|
||||
RegionData(SVRegion.witch_warp_cave, ["Enter Witch's Swamp"]),
|
||||
RegionData(SVRegion.witch_swamp),
|
||||
RegionData(SVRegion.quarry, ["Enter Quarry Mine Entrance"]),
|
||||
RegionData(SVRegion.quarry_mine_entrance, ["Enter Quarry Mine"]),
|
||||
RegionData(SVRegion.quarry_mine),
|
||||
RegionData(SVRegion.secret_woods),
|
||||
RegionData(SVRegion.desert, ["Enter Skull Cavern Entrance"]),
|
||||
RegionData(SVRegion.skull_cavern_entrance, ["Enter Skull Cavern"]),
|
||||
RegionData(SVRegion.skull_cavern, ["Mine to Skull Cavern Floor 100"]),
|
||||
RegionData(SVRegion.perfect_skull_cavern),
|
||||
RegionData(SVRegion.ginger_island),
|
||||
RegionData(SVRegion.jotpk_world_1, ["Reach JotPK World 2"]),
|
||||
RegionData(SVRegion.jotpk_world_2, ["Reach JotPK World 3"]),
|
||||
RegionData(SVRegion.jotpk_world_3),
|
||||
RegionData(SVRegion.junimo_kart_1, ["Reach Junimo Kart 2"]),
|
||||
RegionData(SVRegion.junimo_kart_2, ["Reach Junimo Kart 3"]),
|
||||
RegionData(SVRegion.junimo_kart_3),
|
||||
RegionData(SVRegion.mines, ["Dig to The Mines - Floor 5", "Dig to The Mines - Floor 10", "Dig to The Mines - Floor 15",
|
||||
"Dig to The Mines - Floor 20", "Dig to The Mines - Floor 25",
|
||||
"Dig to The Mines - Floor 30",
|
||||
"Dig to The Mines - Floor 35", "Dig to The Mines - Floor 40",
|
||||
"Dig to The Mines - Floor 45",
|
||||
"Dig to The Mines - Floor 50", "Dig to The Mines - Floor 55",
|
||||
"Dig to The Mines - Floor 60",
|
||||
"Dig to The Mines - Floor 65", "Dig to The Mines - Floor 70",
|
||||
"Dig to The Mines - Floor 75",
|
||||
"Dig to The Mines - Floor 80", "Dig to The Mines - Floor 85",
|
||||
"Dig to The Mines - Floor 90",
|
||||
"Dig to The Mines - Floor 95", "Dig to The Mines - Floor 100",
|
||||
"Dig to The Mines - Floor 105",
|
||||
"Dig to The Mines - Floor 110", "Dig to The Mines - Floor 115",
|
||||
"Dig to The Mines - Floor 120"]),
|
||||
RegionData(SVRegion.mines_floor_5),
|
||||
RegionData(SVRegion.mines_floor_10),
|
||||
RegionData(SVRegion.mines_floor_15),
|
||||
RegionData(SVRegion.mines_floor_20),
|
||||
RegionData(SVRegion.mines_floor_25),
|
||||
RegionData(SVRegion.mines_floor_30),
|
||||
RegionData(SVRegion.mines_floor_35),
|
||||
RegionData(SVRegion.mines_floor_40),
|
||||
RegionData(SVRegion.mines_floor_45),
|
||||
RegionData(SVRegion.mines_floor_50),
|
||||
RegionData(SVRegion.mines_floor_55),
|
||||
RegionData(SVRegion.mines_floor_60),
|
||||
RegionData(SVRegion.mines_floor_65),
|
||||
RegionData(SVRegion.mines_floor_70),
|
||||
RegionData(SVRegion.mines_floor_75),
|
||||
RegionData(SVRegion.mines_floor_80),
|
||||
RegionData(SVRegion.mines_floor_85),
|
||||
RegionData(SVRegion.mines_floor_90),
|
||||
RegionData(SVRegion.mines_floor_95),
|
||||
RegionData(SVRegion.mines_floor_100),
|
||||
RegionData(SVRegion.mines_floor_105),
|
||||
RegionData(SVRegion.mines_floor_110),
|
||||
RegionData(SVRegion.mines_floor_115),
|
||||
RegionData(SVRegion.mines_floor_120),
|
||||
]
|
||||
|
||||
# Exists and where they lead
|
||||
mandatory_connections = [
|
||||
ConnectionData("To Stardew Valley", "Stardew Valley"),
|
||||
ConnectionData("To Farmhouse", "Farmhouse"),
|
||||
ConnectionData("Outside to Farm", "Farm"),
|
||||
ConnectionData("Downstairs to Cellar", "Cellar"),
|
||||
ConnectionData("Farm to Backwoods", "Backwoods"),
|
||||
ConnectionData("Farm to Bus Stop", "Bus Stop"),
|
||||
ConnectionData("Farm to Forest", "Forest"),
|
||||
ConnectionData("Farm to Farmcave", "Farmcave", flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Greenhouse", "Greenhouse"),
|
||||
ConnectionData("Use Desert Obelisk", "The Desert"),
|
||||
ConnectionData("Use Island Obelisk", "Ginger Island"),
|
||||
ConnectionData("Backwoods to Mountain", "Mountain"),
|
||||
ConnectionData("Bus Stop to Town", "Town"),
|
||||
ConnectionData("Bus Stop to Tunnel Entrance", "Tunnel Entrance"),
|
||||
ConnectionData("Take Bus to Desert", "The Desert"),
|
||||
ConnectionData("Enter Tunnel", "Tunnel"),
|
||||
ConnectionData("Forest to Town", "Town"),
|
||||
ConnectionData("Forest to Wizard Tower", "Wizard Tower", flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Wizard Basement", "Wizard Basement"),
|
||||
ConnectionData("Forest to Marnie's Ranch", "Marnie's Ranch", flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Forest to Leah's Cottage", "Leah's Cottage"),
|
||||
ConnectionData("Enter Secret Woods", "Secret Woods"),
|
||||
ConnectionData("Forest to Sewers", "Sewers"),
|
||||
ConnectionData("Town to Sewers", "Sewers"),
|
||||
ConnectionData("Enter Mutant Bug Lair", "Mutant Bug Lair"),
|
||||
ConnectionData("Mountain to Railroad", "Railroad"),
|
||||
ConnectionData("Mountain to Tent", "Tent", flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Mountain to Carpenter Shop", "Carpenter Shop", flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Sebastian's Room", "Sebastian's Room"),
|
||||
ConnectionData("Mountain to Adventurer's Guild", "Adventurer's Guild"),
|
||||
ConnectionData("Enter Quarry", "Quarry"),
|
||||
ConnectionData("Enter Quarry Mine Entrance", "Quarry Mine Entrance"),
|
||||
ConnectionData("Enter Quarry Mine", "Quarry Mine"),
|
||||
ConnectionData("Mountain to Town", "Town"),
|
||||
ConnectionData("Town to Community Center", "Community Center", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Access Crafts Room", "Crafts Room"),
|
||||
ConnectionData("Access Pantry", "Pantry"),
|
||||
ConnectionData("Access Fish Tank", "Fish Tank"),
|
||||
ConnectionData("Access Boiler Room", "Boiler Room"),
|
||||
ConnectionData("Access Bulletin Board", "Bulletin Board"),
|
||||
ConnectionData("Access Vault", "Vault"),
|
||||
ConnectionData("Town to Hospital", "Hospital", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Enter Harvey's Room", "Harvey's Room"),
|
||||
ConnectionData("Town to Pierre's General Store", "Pierre's General Store", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Enter Sunroom", "Sunroom"),
|
||||
ConnectionData("Town to Clint's Blacksmith", "Clint's Blacksmith", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Saloon", "Saloon", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Play Journey of the Prairie King", "JotPK World 1"),
|
||||
ConnectionData("Reach JotPK World 2", "JotPK World 2"),
|
||||
ConnectionData("Reach JotPK World 3", "JotPK World 3"),
|
||||
ConnectionData("Play Junimo Kart", "Junimo Kart 1"),
|
||||
ConnectionData("Reach Junimo Kart 2", "Junimo Kart 2"),
|
||||
ConnectionData("Reach Junimo Kart 3", "Junimo Kart 3"),
|
||||
ConnectionData("Town to Sam's House", "Sam's House", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Haley's House", "Haley's House", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Mayor's Manor", "Mayor's Manor", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Alex's House", "Alex's House", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Trailer", "Trailer", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Museum", "Museum", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to JojaMart", "JojaMart", flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Beach", "Beach"),
|
||||
ConnectionData("Enter Elliott's House", "Elliott's House"),
|
||||
ConnectionData("Beach to Willy's Fish Shop", "Willy's Fish Shop", flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Tide Pools", "Tide Pools"),
|
||||
ConnectionData("Mountain to The Mines", "The Mines", flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Dig to The Mines - Floor 5", "The Mines - Floor 5"),
|
||||
ConnectionData("Dig to The Mines - Floor 10", "The Mines - Floor 10"),
|
||||
ConnectionData("Dig to The Mines - Floor 15", "The Mines - Floor 15"),
|
||||
ConnectionData("Dig to The Mines - Floor 20", "The Mines - Floor 20"),
|
||||
ConnectionData("Dig to The Mines - Floor 25", "The Mines - Floor 25"),
|
||||
ConnectionData("Dig to The Mines - Floor 30", "The Mines - Floor 30"),
|
||||
ConnectionData("Dig to The Mines - Floor 35", "The Mines - Floor 35"),
|
||||
ConnectionData("Dig to The Mines - Floor 40", "The Mines - Floor 40"),
|
||||
ConnectionData("Dig to The Mines - Floor 45", "The Mines - Floor 45"),
|
||||
ConnectionData("Dig to The Mines - Floor 50", "The Mines - Floor 50"),
|
||||
ConnectionData("Dig to The Mines - Floor 55", "The Mines - Floor 55"),
|
||||
ConnectionData("Dig to The Mines - Floor 60", "The Mines - Floor 60"),
|
||||
ConnectionData("Dig to The Mines - Floor 65", "The Mines - Floor 65"),
|
||||
ConnectionData("Dig to The Mines - Floor 70", "The Mines - Floor 70"),
|
||||
ConnectionData("Dig to The Mines - Floor 75", "The Mines - Floor 75"),
|
||||
ConnectionData("Dig to The Mines - Floor 80", "The Mines - Floor 80"),
|
||||
ConnectionData("Dig to The Mines - Floor 85", "The Mines - Floor 85"),
|
||||
ConnectionData("Dig to The Mines - Floor 90", "The Mines - Floor 90"),
|
||||
ConnectionData("Dig to The Mines - Floor 95", "The Mines - Floor 95"),
|
||||
ConnectionData("Dig to The Mines - Floor 100", "The Mines - Floor 100"),
|
||||
ConnectionData("Dig to The Mines - Floor 105", "The Mines - Floor 105"),
|
||||
ConnectionData("Dig to The Mines - Floor 110", "The Mines - Floor 110"),
|
||||
ConnectionData("Dig to The Mines - Floor 115", "The Mines - Floor 115"),
|
||||
ConnectionData("Dig to The Mines - Floor 120", "The Mines - Floor 120"),
|
||||
ConnectionData("Enter Skull Cavern Entrance", "Skull Cavern Entrance"),
|
||||
ConnectionData("Enter Skull Cavern", "Skull Cavern"),
|
||||
ConnectionData("Enter Witch Warp Cave", "Witch Warp Cave"),
|
||||
ConnectionData("Enter Witch's Swamp", "Witch's Swamp"),
|
||||
ConnectionData("Enter Bathhouse Entrance", "Bathhouse Entrance"),
|
||||
ConnectionData("Enter Locker Room", "Locker Room"),
|
||||
ConnectionData("Enter Public Bath", "Public Bath"),
|
||||
ConnectionData("To Stardew Valley", SVRegion.stardew_valley),
|
||||
ConnectionData("To Farmhouse", SVRegion.farm_house),
|
||||
ConnectionData("Outside to Farm", SVRegion.farm),
|
||||
ConnectionData("Downstairs to Cellar", SVRegion.cellar),
|
||||
ConnectionData("Farm to Backwoods", SVRegion.backwoods),
|
||||
ConnectionData("Farm to Bus Stop", SVRegion.bus_stop),
|
||||
ConnectionData("Farm to Forest", SVRegion.forest),
|
||||
ConnectionData("Farm to Farmcave", SVRegion.farm_cave, flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Greenhouse", SVRegion.greenhouse),
|
||||
ConnectionData("Use Desert Obelisk", SVRegion.desert),
|
||||
ConnectionData("Use Island Obelisk", SVRegion.ginger_island),
|
||||
ConnectionData("Backwoods to Mountain", SVRegion.mountain),
|
||||
ConnectionData("Bus Stop to Town", SVRegion.town),
|
||||
ConnectionData("Bus Stop to Tunnel Entrance", SVRegion.tunnel_entrance),
|
||||
ConnectionData("Take Bus to Desert", SVRegion.desert),
|
||||
ConnectionData("Enter Tunnel", SVRegion.tunnel),
|
||||
ConnectionData("Forest to Town", SVRegion.town),
|
||||
ConnectionData("Forest to Wizard Tower", SVRegion.wizard_tower, flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Wizard Basement", SVRegion.wizard_basement),
|
||||
ConnectionData("Forest to Marnie's Ranch", SVRegion.ranch, flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Forest to Leah's Cottage", SVRegion.leah_house),
|
||||
ConnectionData("Enter Secret Woods", SVRegion.secret_woods),
|
||||
ConnectionData("Forest to Sewers", SVRegion.sewers),
|
||||
ConnectionData("Talk to Traveling Merchant", SVRegion.traveling_cart),
|
||||
ConnectionData("Town to Sewers", SVRegion.sewers),
|
||||
ConnectionData("Enter Mutant Bug Lair", SVRegion.mutant_bug_lair),
|
||||
ConnectionData("Mountain to Railroad", SVRegion.railroad),
|
||||
ConnectionData("Mountain to Tent", SVRegion.tent, flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Mountain to Carpenter Shop", SVRegion.carpenter, flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Sebastian's Room", SVRegion.sebastian_room),
|
||||
ConnectionData("Mountain to Adventurer's Guild", SVRegion.adventurer_guild),
|
||||
ConnectionData("Enter Quarry", SVRegion.quarry),
|
||||
ConnectionData("Enter Quarry Mine Entrance", SVRegion.quarry_mine_entrance),
|
||||
ConnectionData("Enter Quarry Mine", SVRegion.quarry_mine),
|
||||
ConnectionData("Mountain to Town", SVRegion.town),
|
||||
ConnectionData("Town to Community Center", SVRegion.community_center, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Access Crafts Room", SVRegion.crafts_room),
|
||||
ConnectionData("Access Pantry", SVRegion.pantry),
|
||||
ConnectionData("Access Fish Tank", SVRegion.fish_tank),
|
||||
ConnectionData("Access Boiler Room", SVRegion.boiler_room),
|
||||
ConnectionData("Access Bulletin Board", SVRegion.bulletin_board),
|
||||
ConnectionData("Access Vault", SVRegion.vault),
|
||||
ConnectionData("Town to Hospital", SVRegion.hospital, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Enter Harvey's Room", SVRegion.harvey_room),
|
||||
ConnectionData("Town to Pierre's General Store", SVRegion.pierre_store, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Enter Sunroom", SVRegion.sunroom),
|
||||
ConnectionData("Town to Clint's Blacksmith", SVRegion.blacksmith, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Saloon", SVRegion.saloon, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Play Journey of the Prairie King", SVRegion.jotpk_world_1),
|
||||
ConnectionData("Reach JotPK World 2", SVRegion.jotpk_world_2),
|
||||
ConnectionData("Reach JotPK World 3", SVRegion.jotpk_world_3),
|
||||
ConnectionData("Play Junimo Kart", SVRegion.junimo_kart_1),
|
||||
ConnectionData("Reach Junimo Kart 2", SVRegion.junimo_kart_2),
|
||||
ConnectionData("Reach Junimo Kart 3", SVRegion.junimo_kart_3),
|
||||
ConnectionData("Town to Sam's House", SVRegion.sam_house, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Haley's House", SVRegion.haley_house, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Mayor's Manor", SVRegion.mayor_house, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Alex's House", SVRegion.alex_house, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Trailer", SVRegion.trailer, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Museum", SVRegion.museum, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to JojaMart", SVRegion.jojamart, flag=RandomizationFlag.PELICAN_TOWN),
|
||||
ConnectionData("Town to Beach", SVRegion.beach),
|
||||
ConnectionData("Enter Elliott's House", SVRegion.elliott_house),
|
||||
ConnectionData("Beach to Willy's Fish Shop", SVRegion.fish_shop, flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Enter Tide Pools", SVRegion.tide_pools),
|
||||
ConnectionData("Mountain to The Mines", SVRegion.mines, flag=RandomizationFlag.NON_PROGRESSION),
|
||||
ConnectionData("Dig to The Mines - Floor 5", SVRegion.mines_floor_5),
|
||||
ConnectionData("Dig to The Mines - Floor 10", SVRegion.mines_floor_10),
|
||||
ConnectionData("Dig to The Mines - Floor 15", SVRegion.mines_floor_15),
|
||||
ConnectionData("Dig to The Mines - Floor 20", SVRegion.mines_floor_20),
|
||||
ConnectionData("Dig to The Mines - Floor 25", SVRegion.mines_floor_25),
|
||||
ConnectionData("Dig to The Mines - Floor 30", SVRegion.mines_floor_30),
|
||||
ConnectionData("Dig to The Mines - Floor 35", SVRegion.mines_floor_35),
|
||||
ConnectionData("Dig to The Mines - Floor 40", SVRegion.mines_floor_40),
|
||||
ConnectionData("Dig to The Mines - Floor 45", SVRegion.mines_floor_45),
|
||||
ConnectionData("Dig to The Mines - Floor 50", SVRegion.mines_floor_50),
|
||||
ConnectionData("Dig to The Mines - Floor 55", SVRegion.mines_floor_55),
|
||||
ConnectionData("Dig to The Mines - Floor 60", SVRegion.mines_floor_60),
|
||||
ConnectionData("Dig to The Mines - Floor 65", SVRegion.mines_floor_65),
|
||||
ConnectionData("Dig to The Mines - Floor 70", SVRegion.mines_floor_70),
|
||||
ConnectionData("Dig to The Mines - Floor 75", SVRegion.mines_floor_75),
|
||||
ConnectionData("Dig to The Mines - Floor 80", SVRegion.mines_floor_80),
|
||||
ConnectionData("Dig to The Mines - Floor 85", SVRegion.mines_floor_85),
|
||||
ConnectionData("Dig to The Mines - Floor 90", SVRegion.mines_floor_90),
|
||||
ConnectionData("Dig to The Mines - Floor 95", SVRegion.mines_floor_95),
|
||||
ConnectionData("Dig to The Mines - Floor 100", SVRegion.mines_floor_100),
|
||||
ConnectionData("Dig to The Mines - Floor 105", SVRegion.mines_floor_105),
|
||||
ConnectionData("Dig to The Mines - Floor 110", SVRegion.mines_floor_110),
|
||||
ConnectionData("Dig to The Mines - Floor 115", SVRegion.mines_floor_115),
|
||||
ConnectionData("Dig to The Mines - Floor 120", SVRegion.mines_floor_120),
|
||||
ConnectionData("Enter Skull Cavern Entrance", SVRegion.skull_cavern_entrance),
|
||||
ConnectionData("Enter Skull Cavern", SVRegion.skull_cavern),
|
||||
ConnectionData("Mine to Skull Cavern Floor 100", SVRegion.perfect_skull_cavern),
|
||||
ConnectionData("Enter Witch Warp Cave", SVRegion.witch_warp_cave),
|
||||
ConnectionData("Enter Witch's Swamp", SVRegion.witch_swamp),
|
||||
ConnectionData("Enter Bathhouse Entrance", SVRegion.bathhouse_entrance),
|
||||
ConnectionData("Enter Locker Room", SVRegion.locker_room),
|
||||
ConnectionData("Enter Public Bath", SVRegion.public_bath),
|
||||
]
|
||||
|
||||
|
||||
def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewOptions) -> Tuple[Iterable[Region], Dict[str, str]]:
|
||||
regions: Dict[str: Region] = {region.name: region_factory(region.name, region.exits) for region in stardew_valley_regions}
|
||||
def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewOptions) -> Tuple[
|
||||
Iterable[Region], Dict[str, str]]:
|
||||
regions: Dict[str: Region] = {region.name: region_factory(region.name, region.exits) for region in
|
||||
stardew_valley_regions}
|
||||
entrances: Dict[str: Entrance] = {entrance.name: entrance
|
||||
for region in regions.values()
|
||||
for entrance in region.exits}
|
||||
|
@ -272,9 +291,11 @@ def create_regions(region_factory: RegionFactory, random: Random, world_options:
|
|||
def randomize_connections(random: Random, world_options: StardewOptions) -> Tuple[List[ConnectionData], Dict[str, str]]:
|
||||
connections_to_randomize = []
|
||||
if world_options[options.EntranceRandomization] == options.EntranceRandomization.option_pelican_town:
|
||||
connections_to_randomize = [connection for connection in mandatory_connections if RandomizationFlag.PELICAN_TOWN in connection.flag]
|
||||
connections_to_randomize = [connection for connection in mandatory_connections if
|
||||
RandomizationFlag.PELICAN_TOWN in connection.flag]
|
||||
elif world_options[options.EntranceRandomization] == options.EntranceRandomization.option_non_progression:
|
||||
connections_to_randomize = [connection for connection in mandatory_connections if RandomizationFlag.NON_PROGRESSION in connection.flag]
|
||||
connections_to_randomize = [connection for connection in mandatory_connections if
|
||||
RandomizationFlag.NON_PROGRESSION in connection.flag]
|
||||
random.shuffle(connections_to_randomize)
|
||||
|
||||
destination_pool = list(connections_to_randomize)
|
||||
|
|
|
@ -1,49 +1,51 @@
|
|||
import itertools
|
||||
from typing import Dict
|
||||
from typing import Dict, List
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from worlds.generic import Rules as MultiWorldRules
|
||||
from . import options, locations
|
||||
from .bundles import Bundle
|
||||
from .data.museum_data import all_museum_items, all_mineral_items, all_artifact_items, \
|
||||
dwarf_scrolls, skeleton_front, \
|
||||
skeleton_middle, skeleton_back, all_museum_items_by_name
|
||||
from .locations import LocationTags
|
||||
from .logic import StardewLogic, _And, season_per_skill_level, tool_prices, week_days
|
||||
|
||||
help_wanted_per_season = {
|
||||
1: "Spring",
|
||||
2: "Summer",
|
||||
3: "Fall",
|
||||
4: "Winter",
|
||||
5: "Year Two",
|
||||
6: "Year Two",
|
||||
7: "Year Two",
|
||||
8: "Year Two",
|
||||
9: "Year Two",
|
||||
10: "Year Two",
|
||||
}
|
||||
from .logic import StardewLogic, And, month_end_per_skill_level, tool_prices, week_days
|
||||
from .options import StardewOptions
|
||||
|
||||
|
||||
def set_rules(multi_world: MultiWorld, player: int, world_options: options.StardewOptions, logic: StardewLogic,
|
||||
def set_rules(multi_world: MultiWorld, player: int, world_options: StardewOptions, logic: StardewLogic,
|
||||
current_bundles: Dict[str, Bundle]):
|
||||
summer = multi_world.get_location("Summer", player)
|
||||
all_location_names = list(location.name for location in multi_world.get_locations(player))
|
||||
|
||||
for floor in range(5, 120 + 5, 5):
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance(f"Dig to The Mines - Floor {floor}", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance(f"Dig to The Mines - Floor {floor}", player),
|
||||
logic.can_mine_to_floor(floor).simplify())
|
||||
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance("Enter Quarry", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Enter Tide Pools", player),
|
||||
logic.received("Beach Bridge").simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Enter Quarry", player),
|
||||
logic.received("Bridge Repair").simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance("Enter Secret Woods", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Enter Secret Woods", player),
|
||||
logic.has_tool("Axe", "Iron").simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance("Take Bus to Desert", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Forest to Sewers", player),
|
||||
logic.has_rusty_key().simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Town to Sewers", player),
|
||||
logic.has_rusty_key().simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Take Bus to Desert", player),
|
||||
logic.received("Bus Repair").simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance("Enter Skull Cavern", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Enter Skull Cavern", player),
|
||||
logic.received("Skull Key").simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Mine to Skull Cavern Floor 100", player),
|
||||
logic.can_mine_perfectly_in_the_skull_cavern().simplify())
|
||||
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance("Use Desert Obelisk", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Use Desert Obelisk", player),
|
||||
logic.received("Desert Obelisk").simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance("Use Island Obelisk", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Use Island Obelisk", player),
|
||||
logic.received("Island Obelisk").simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Talk to Traveling Merchant", player),
|
||||
logic.has_traveling_merchant())
|
||||
MultiWorldRules.set_rule(multi_world.get_entrance("Enter Greenhouse", player),
|
||||
logic.received("Greenhouse"))
|
||||
|
||||
# Those checks do not exist if ToolProgression is vanilla
|
||||
if world_options[options.ToolProgression] != options.ToolProgression.option_vanilla:
|
||||
|
@ -68,43 +70,46 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: options.Stard
|
|||
if world_options[options.SkillProgression] != options.SkillProgression.option_vanilla:
|
||||
for i in range(1, 11):
|
||||
MultiWorldRules.set_rule(multi_world.get_location(f"Level {i} Farming", player),
|
||||
(logic.received(season_per_skill_level["Farming", i])).simplify())
|
||||
(logic.received("Month End", month_end_per_skill_level["Farming", i])).simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_location(f"Level {i} Fishing", player),
|
||||
(logic.can_get_fishing_xp() &
|
||||
logic.received(season_per_skill_level["Fishing", i])).simplify())
|
||||
logic.received("Month End", month_end_per_skill_level["Fishing", i])).simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location(f"Level {i} Foraging", player),
|
||||
logic.received(season_per_skill_level["Foraging", i]).simplify())
|
||||
logic.received("Month End", month_end_per_skill_level["Foraging", i]).simplify())
|
||||
if i >= 6:
|
||||
MultiWorldRules.add_rule(multi_world.get_location(f"Level {i} Foraging", player),
|
||||
logic.has_tool("Axe", "Iron").simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_location(f"Level {i} Mining", player),
|
||||
logic.received(season_per_skill_level["Mining", i]).simplify())
|
||||
logic.received("Month End", month_end_per_skill_level["Mining", i]).simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_location(f"Level {i} Combat", player),
|
||||
(logic.received(season_per_skill_level["Combat", i]) &
|
||||
(logic.received("Month End", month_end_per_skill_level["Combat", i]) &
|
||||
logic.has_any_weapon()).simplify())
|
||||
|
||||
# Bundles
|
||||
for bundle in current_bundles.values():
|
||||
MultiWorldRules.set_rule(multi_world.get_location(bundle.get_name_with_bundle(), player),
|
||||
logic.can_complete_bundle(bundle.requirements, bundle.number_required).simplify())
|
||||
location = multi_world.get_location(bundle.get_name_with_bundle(), player)
|
||||
rules = logic.can_complete_bundle(bundle.requirements, bundle.number_required)
|
||||
simplified_rules = rules.simplify()
|
||||
MultiWorldRules.set_rule(location, simplified_rules)
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Complete Crafts Room", player),
|
||||
_And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.CRAFTS_ROOM_BUNDLE]).simplify())
|
||||
And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.CRAFTS_ROOM_BUNDLE]).simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Complete Pantry", player),
|
||||
_And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.PANTRY_BUNDLE]).simplify())
|
||||
And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.PANTRY_BUNDLE]).simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Complete Fish Tank", player),
|
||||
_And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.FISH_TANK_BUNDLE]).simplify())
|
||||
And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.FISH_TANK_BUNDLE]).simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Complete Boiler Room", player),
|
||||
_And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.BOILER_ROOM_BUNDLE]).simplify())
|
||||
And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.BOILER_ROOM_BUNDLE]).simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Complete Bulletin Board", player),
|
||||
_And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.BULLETIN_BOARD_BUNDLE]).simplify())
|
||||
And(logic.can_reach_location(bundle.name)
|
||||
for bundle
|
||||
in locations.locations_by_tag[LocationTags.BULLETIN_BOARD_BUNDLE]).simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Complete Vault", player),
|
||||
_And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.VAULT_BUNDLE]).simplify())
|
||||
And(logic.can_reach_location(bundle.name)
|
||||
for bundle in locations.locations_by_tag[LocationTags.VAULT_BUNDLE]).simplify())
|
||||
|
||||
# Buildings
|
||||
if world_options[options.BuildingProgression] != options.BuildingProgression.option_vanilla:
|
||||
|
@ -122,7 +127,7 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: options.Stard
|
|||
for i in range(1, desired_number_help_wanted + 1):
|
||||
prefix = "Help Wanted:"
|
||||
delivery = "Item Delivery"
|
||||
rule = logic.received(help_wanted_per_season[min(5, i)])
|
||||
rule = logic.received("Month End", i - 1)
|
||||
fishing_rule = rule & logic.can_fish()
|
||||
slay_rule = rule & logic.has_any_weapon()
|
||||
for j in range(i, i + 4):
|
||||
|
@ -136,6 +141,21 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: options.Stard
|
|||
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Slay Monsters {i}", player),
|
||||
slay_rule.simplify())
|
||||
|
||||
set_fishsanity_rules(all_location_names, logic, multi_world, player)
|
||||
set_museumsanity_rules(all_location_names, logic, multi_world, player, world_options)
|
||||
set_friendsanity_rules(all_location_names, logic, multi_world, player)
|
||||
set_backpack_rules(logic, multi_world, player, world_options)
|
||||
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Old Master Cannoli", player),
|
||||
logic.has("Sweet Gem Berry").simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Galaxy Sword Shrine", player),
|
||||
logic.has("Prismatic Shard").simplify())
|
||||
|
||||
set_traveling_merchant_rules(logic, multi_world, player)
|
||||
set_arcade_machine_rules(logic, multi_world, player, world_options)
|
||||
|
||||
|
||||
def set_fishsanity_rules(all_location_names: List[str], logic: StardewLogic, multi_world: MultiWorld, player: int):
|
||||
fish_prefix = "Fishsanity: "
|
||||
for fish_location in locations.locations_by_tag[LocationTags.FISHSANITY]:
|
||||
if fish_location.name in all_location_names:
|
||||
|
@ -143,27 +163,78 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: options.Stard
|
|||
MultiWorldRules.set_rule(multi_world.get_location(fish_location.name, player),
|
||||
logic.has(fish_name).simplify())
|
||||
|
||||
if world_options[options.BuildingProgression] == options.BuildingProgression.option_progressive_early_shipping_bin:
|
||||
summer.access_rule = summer.access_rule & logic.received("Shipping Bin")
|
||||
|
||||
# Backpacks
|
||||
def set_museumsanity_rules(all_location_names: List[str], logic: StardewLogic, multi_world: MultiWorld, player: int,
|
||||
world_options: StardewOptions):
|
||||
museum_prefix = "Museumsanity: "
|
||||
if world_options[options.Museumsanity] == options.Museumsanity.option_milestones:
|
||||
for museum_milestone in locations.locations_by_tag[LocationTags.MUSEUM_MILESTONES]:
|
||||
set_museum_milestone_rule(logic, multi_world, museum_milestone, museum_prefix, player)
|
||||
elif world_options[options.Museumsanity] != options.Museumsanity.option_none:
|
||||
set_museum_individual_donations_rules(all_location_names, logic, multi_world, museum_prefix, player)
|
||||
|
||||
|
||||
def set_museum_individual_donations_rules(all_location_names, logic, multi_world, museum_prefix, player):
|
||||
all_donations = sorted(locations.locations_by_tag[LocationTags.MUSEUM_DONATIONS],
|
||||
key=lambda x: all_museum_items_by_name[x.name[len(museum_prefix):]].difficulty, reverse=True)
|
||||
counter = 0
|
||||
number_donations = len(all_donations)
|
||||
for museum_location in all_donations:
|
||||
if museum_location.name in all_location_names:
|
||||
donation_name = museum_location.name[len(museum_prefix):]
|
||||
required_detectors = counter * 5 // number_donations
|
||||
rule = logic.has(donation_name) & logic.received("Traveling Merchant Metal Detector", required_detectors)
|
||||
MultiWorldRules.set_rule(multi_world.get_location(museum_location.name, player),
|
||||
rule.simplify())
|
||||
counter += 1
|
||||
|
||||
|
||||
def set_museum_milestone_rule(logic: StardewLogic, multi_world: MultiWorld, museum_milestone, museum_prefix: str,
|
||||
player: int):
|
||||
milestone_name = museum_milestone.name[len(museum_prefix):]
|
||||
donations_suffix = " Donations"
|
||||
minerals_suffix = " Minerals"
|
||||
artifacts_suffix = " Artifacts"
|
||||
metal_detector = "Traveling Merchant Metal Detector"
|
||||
rule = None
|
||||
if milestone_name.endswith(donations_suffix):
|
||||
rule = get_museum_item_count_rule(logic, donations_suffix, milestone_name, all_museum_items)
|
||||
elif milestone_name.endswith(minerals_suffix):
|
||||
rule = get_museum_item_count_rule(logic, minerals_suffix, milestone_name, all_mineral_items)
|
||||
elif milestone_name.endswith(artifacts_suffix):
|
||||
rule = get_museum_item_count_rule(logic, artifacts_suffix, milestone_name, all_artifact_items)
|
||||
elif milestone_name == "Dwarf Scrolls":
|
||||
rule = logic.has([item.name for item in dwarf_scrolls]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Skeleton Front":
|
||||
rule = logic.has([item.name for item in skeleton_front]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Skeleton Middle":
|
||||
rule = logic.has([item.name for item in skeleton_middle]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Skeleton Back":
|
||||
rule = logic.has([item.name for item in skeleton_back]) & logic.received(metal_detector, 4)
|
||||
elif milestone_name == "Ancient Seed":
|
||||
rule = logic.has("Ancient Seed") & logic.received(metal_detector, 4)
|
||||
if rule is None:
|
||||
return
|
||||
MultiWorldRules.set_rule(multi_world.get_location(museum_milestone.name, player), rule.simplify())
|
||||
|
||||
|
||||
def get_museum_item_count_rule(logic, suffix, milestone_name, accepted_items):
|
||||
metal_detector = "Traveling Merchant Metal Detector"
|
||||
num = int(milestone_name[:milestone_name.index(suffix)])
|
||||
required_detectors = (num - 1) * 5 // len(accepted_items)
|
||||
rule = logic.has([item.name for item in accepted_items], num) & logic.received(metal_detector, required_detectors)
|
||||
return rule
|
||||
|
||||
|
||||
def set_backpack_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options):
|
||||
if world_options[options.BackpackProgression] != options.BackpackProgression.option_vanilla:
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Large Pack", player),
|
||||
MultiWorldRules.set_rule(multi_world.get_location("Large Pack", player),
|
||||
logic.can_spend_money(2000).simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Deluxe Pack", player),
|
||||
logic.can_spend_money(10000).simplify())
|
||||
MultiWorldRules.set_rule(multi_world.get_location("Deluxe Pack", player),
|
||||
(logic.can_spend_money(10000) & logic.received("Progressive Backpack")).simplify())
|
||||
|
||||
if world_options[options.BackpackProgression] == options.BackpackProgression.option_early_progressive:
|
||||
summer.access_rule = summer.access_rule & logic.received("Progressive Backpack")
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Winter", player),
|
||||
logic.received("Progressive Backpack", 2).simplify())
|
||||
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Old Master Cannoli", player),
|
||||
logic.has("Sweet Gem Berry").simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Galaxy Sword Shrine", player),
|
||||
logic.has("Prismatic Shard").simplify())
|
||||
|
||||
# Traveling Merchant
|
||||
def set_traveling_merchant_rules(logic: StardewLogic, multi_world: MultiWorld, player: int):
|
||||
for day in week_days:
|
||||
item_for_day = f"Traveling Merchant: {day}"
|
||||
for i in range(1, 4):
|
||||
|
@ -171,6 +242,8 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: options.Stard
|
|||
MultiWorldRules.set_rule(multi_world.get_location(location_name, player),
|
||||
logic.received(item_for_day))
|
||||
|
||||
|
||||
def set_arcade_machine_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options):
|
||||
if world_options[options.ArcadeMachineLocations] == options.ArcadeMachineLocations.option_full_shuffling:
|
||||
MultiWorldRules.add_rule(multi_world.get_entrance("Play Junimo Kart", player),
|
||||
(logic.received("Skull Key") & logic.has("Junimo Kart Small Buff")).simplify())
|
||||
|
@ -188,3 +261,18 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: options.Stard
|
|||
logic.has("JotPK Big Buff").simplify())
|
||||
MultiWorldRules.add_rule(multi_world.get_location("Journey of the Prairie King Victory", player),
|
||||
logic.has("JotPK Max Buff").simplify())
|
||||
|
||||
|
||||
def set_friendsanity_rules(all_location_names: List[str], logic: StardewLogic, multi_world: MultiWorld, player: int):
|
||||
friend_prefix = "Friendsanity: "
|
||||
friend_suffix = " <3"
|
||||
for friend_location in locations.locations_by_tag[LocationTags.FRIENDSANITY]:
|
||||
if not friend_location.name in all_location_names:
|
||||
continue
|
||||
friend_location_without_prefix = friend_location.name[len(friend_prefix):]
|
||||
friend_location_trimmed = friend_location_without_prefix[:friend_location_without_prefix.index(friend_suffix)]
|
||||
parts = friend_location_trimmed.split(" ")
|
||||
friend_name = parts[0]
|
||||
num_hearts = int(parts[1])
|
||||
MultiWorldRules.set_rule(multi_world.get_location(friend_location.name, player),
|
||||
logic.can_earn_relationship(friend_name, num_hearts).simplify())
|
||||
|
|
|
@ -0,0 +1,360 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from typing import Iterable, Dict, List, Union, FrozenSet
|
||||
|
||||
from BaseClasses import CollectionState, ItemClassification
|
||||
from .items import item_table
|
||||
|
||||
MISSING_ITEM = "THIS ITEM IS MISSING"
|
||||
|
||||
|
||||
class StardewRule:
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def __or__(self, other) -> StardewRule:
|
||||
if isinstance(other, Or):
|
||||
return Or(self, *other.rules)
|
||||
|
||||
return Or(self, other)
|
||||
|
||||
def __and__(self, other) -> StardewRule:
|
||||
if isinstance(other, And):
|
||||
return And(other.rules.union({self}))
|
||||
|
||||
return And(self, other)
|
||||
|
||||
def get_difficulty(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def simplify(self) -> StardewRule:
|
||||
return self
|
||||
|
||||
|
||||
class True_(StardewRule): # noqa
|
||||
|
||||
def __new__(cls, _cache=[]): # noqa
|
||||
# Only one single instance will be ever created.
|
||||
if not _cache:
|
||||
_cache.append(super(True_, cls).__new__(cls))
|
||||
return _cache[0]
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
return True
|
||||
|
||||
def __or__(self, other) -> StardewRule:
|
||||
return self
|
||||
|
||||
def __and__(self, other) -> StardewRule:
|
||||
return other
|
||||
|
||||
def __repr__(self):
|
||||
return "True"
|
||||
|
||||
def get_difficulty(self):
|
||||
return 0
|
||||
|
||||
|
||||
class False_(StardewRule): # noqa
|
||||
|
||||
def __new__(cls, _cache=[]): # noqa
|
||||
# Only one single instance will be ever created.
|
||||
if not _cache:
|
||||
_cache.append(super(False_, cls).__new__(cls))
|
||||
return _cache[0]
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
return False
|
||||
|
||||
def __or__(self, other) -> StardewRule:
|
||||
return other
|
||||
|
||||
def __and__(self, other) -> StardewRule:
|
||||
return self
|
||||
|
||||
def __repr__(self):
|
||||
return "False"
|
||||
|
||||
def get_difficulty(self):
|
||||
return 999999999
|
||||
|
||||
|
||||
class Or(StardewRule):
|
||||
rules: FrozenSet[StardewRule]
|
||||
|
||||
def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
|
||||
rules_list = set()
|
||||
if isinstance(rule, Iterable):
|
||||
rules_list.update(rule)
|
||||
else:
|
||||
rules_list.add(rule)
|
||||
|
||||
if rules is not None:
|
||||
rules_list.update(rules)
|
||||
|
||||
assert rules_list, "Can't create a Or conditions without rules"
|
||||
|
||||
new_rules = set()
|
||||
for rule in rules_list:
|
||||
if isinstance(rule, Or):
|
||||
new_rules.update(rule.rules)
|
||||
else:
|
||||
new_rules.add(rule)
|
||||
rules_list = new_rules
|
||||
|
||||
self.rules = frozenset(rules_list)
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
return any(rule(state) for rule in self.rules)
|
||||
|
||||
def __repr__(self):
|
||||
return f"({' | '.join(repr(rule) for rule in self.rules)})"
|
||||
|
||||
def __or__(self, other):
|
||||
if isinstance(other, True_):
|
||||
return other
|
||||
if isinstance(other, False_):
|
||||
return self
|
||||
if isinstance(other, Or):
|
||||
return Or(self.rules.union(other.rules))
|
||||
|
||||
return Or(self.rules.union({other}))
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, self.__class__) and other.rules == self.rules
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.rules)
|
||||
|
||||
def get_difficulty(self):
|
||||
return min(rule.get_difficulty() for rule in self.rules)
|
||||
|
||||
def simplify(self) -> StardewRule:
|
||||
if any(isinstance(rule, True_) for rule in self.rules):
|
||||
return True_()
|
||||
|
||||
simplified_rules = {rule.simplify() for rule in self.rules}
|
||||
simplified_rules = {rule for rule in simplified_rules if rule is not False_()}
|
||||
|
||||
if not simplified_rules:
|
||||
return False_()
|
||||
|
||||
if len(simplified_rules) == 1:
|
||||
return next(iter(simplified_rules))
|
||||
|
||||
return Or(simplified_rules)
|
||||
|
||||
|
||||
class And(StardewRule):
|
||||
rules: FrozenSet[StardewRule]
|
||||
|
||||
def __init__(self, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
|
||||
rules_list = set()
|
||||
if isinstance(rule, Iterable):
|
||||
rules_list.update(rule)
|
||||
else:
|
||||
rules_list.add(rule)
|
||||
|
||||
if rules is not None:
|
||||
rules_list.update(rules)
|
||||
|
||||
assert rules_list, "Can't create a And conditions without rules"
|
||||
|
||||
new_rules = set()
|
||||
for rule in rules_list:
|
||||
if isinstance(rule, And):
|
||||
new_rules.update(rule.rules)
|
||||
else:
|
||||
new_rules.add(rule)
|
||||
rules_list = new_rules
|
||||
|
||||
self.rules = frozenset(rules_list)
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
return all(rule(state) for rule in self.rules)
|
||||
|
||||
def __repr__(self):
|
||||
return f"({' & '.join(repr(rule) for rule in self.rules)})"
|
||||
|
||||
def __and__(self, other):
|
||||
if isinstance(other, True_):
|
||||
return self
|
||||
if isinstance(other, False_):
|
||||
return other
|
||||
if isinstance(other, And):
|
||||
return And(self.rules.union(other.rules))
|
||||
|
||||
return And(self.rules.union({other}))
|
||||
|
||||
def __eq__(self, other):
|
||||
return isinstance(other, self.__class__) and other.rules == self.rules
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.rules)
|
||||
|
||||
def get_difficulty(self):
|
||||
return max(rule.get_difficulty() for rule in self.rules)
|
||||
|
||||
def simplify(self) -> StardewRule:
|
||||
if any(isinstance(rule, False_) for rule in self.rules):
|
||||
return False_()
|
||||
|
||||
simplified_rules = {rule.simplify() for rule in self.rules}
|
||||
simplified_rules = {rule for rule in simplified_rules if rule is not True_()}
|
||||
|
||||
if not simplified_rules:
|
||||
return True_()
|
||||
|
||||
if len(simplified_rules) == 1:
|
||||
return next(iter(simplified_rules))
|
||||
|
||||
return And(simplified_rules)
|
||||
|
||||
|
||||
class Count(StardewRule):
|
||||
count: int
|
||||
rules: List[StardewRule]
|
||||
|
||||
def __init__(self, count: int, rule: Union[StardewRule, Iterable[StardewRule]], *rules: StardewRule):
|
||||
rules_list = []
|
||||
if isinstance(rule, Iterable):
|
||||
rules_list.extend(rule)
|
||||
else:
|
||||
rules_list.append(rule)
|
||||
|
||||
if rules is not None:
|
||||
rules_list.extend(rules)
|
||||
|
||||
assert rules_list, "Can't create a Count conditions without rules"
|
||||
assert len(rules_list) >= count, "Count need at least as many rules at the count"
|
||||
|
||||
self.rules = rules_list
|
||||
self.count = count
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
c = 0
|
||||
for r in self.rules:
|
||||
if r(state):
|
||||
c += 1
|
||||
if c >= self.count:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return f"Received {self.count} {repr(self.rules)}"
|
||||
|
||||
def get_difficulty(self):
|
||||
rules_sorted_by_difficulty = sorted(self.rules, key=lambda x: x.get_difficulty())
|
||||
easiest_n_rules = rules_sorted_by_difficulty[0:self.count]
|
||||
return max(rule.get_difficulty() for rule in easiest_n_rules)
|
||||
|
||||
def simplify(self):
|
||||
return Count(self.count, [rule.simplify() for rule in self.rules])
|
||||
|
||||
|
||||
class TotalReceived(StardewRule):
|
||||
count: int
|
||||
items: Iterable[str]
|
||||
player: int
|
||||
|
||||
def __init__(self, count: int, items: Union[str, Iterable[str]], player: int):
|
||||
items_list = []
|
||||
if isinstance(items, Iterable):
|
||||
items_list.extend(items)
|
||||
else:
|
||||
items_list.append(items)
|
||||
|
||||
assert items_list, "Can't create a Total Received conditions without items"
|
||||
for item in items_list:
|
||||
assert item_table[item].classification & ItemClassification.progression, \
|
||||
"Item has to be progression to be used in logic"
|
||||
|
||||
self.player = player
|
||||
self.items = items_list
|
||||
self.count = count
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
c = 0
|
||||
for item in self.items:
|
||||
c += state.count(item, self.player)
|
||||
if c >= self.count:
|
||||
return True
|
||||
return False
|
||||
|
||||
def __repr__(self):
|
||||
return f"Received {self.count} {self.items}"
|
||||
|
||||
def get_difficulty(self):
|
||||
return self.count
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Received(StardewRule):
|
||||
item: str
|
||||
player: int
|
||||
count: int
|
||||
|
||||
def __post_init__(self):
|
||||
assert item_table[self.item].classification & ItemClassification.progression, \
|
||||
"Item has to be progression to be used in logic"
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
return state.has(self.item, self.player, self.count)
|
||||
|
||||
def __repr__(self):
|
||||
if self.count == 1:
|
||||
return f"Received {self.item}"
|
||||
return f"Received {self.count} {self.item}"
|
||||
|
||||
def get_difficulty(self):
|
||||
if self.item == "Spring":
|
||||
return 0
|
||||
if self.item == "Summer":
|
||||
return 1
|
||||
if self.item == "Fall":
|
||||
return 2
|
||||
if self.item == "Winter":
|
||||
return 3
|
||||
return self.count
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Reach(StardewRule):
|
||||
spot: str
|
||||
resolution_hint: str
|
||||
player: int
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
return state.can_reach(self.spot, self.resolution_hint, self.player)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Reach {self.resolution_hint} {self.spot}"
|
||||
|
||||
def get_difficulty(self):
|
||||
return 1
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Has(StardewRule):
|
||||
item: str
|
||||
# For sure there is a better way than just passing all the rules everytime
|
||||
other_rules: Dict[str, StardewRule]
|
||||
|
||||
def __call__(self, state: CollectionState) -> bool:
|
||||
if isinstance(self.item, str):
|
||||
return self.other_rules[self.item](state)
|
||||
|
||||
def __repr__(self):
|
||||
if not self.item in self.other_rules:
|
||||
return f"Has {self.item} -> {MISSING_ITEM}"
|
||||
return f"Has {self.item} -> {repr(self.other_rules[self.item])}"
|
||||
|
||||
def get_difficulty(self):
|
||||
return self.other_rules[self.item].get_difficulty() + 1
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.item)
|
||||
|
||||
def simplify(self) -> StardewRule:
|
||||
return self.other_rules[self.item].simplify()
|
|
@ -1,53 +0,0 @@
|
|||
import unittest
|
||||
|
||||
from test.general import setup_solo_multiworld
|
||||
from .. import StardewValleyWorld
|
||||
from ..bundle_data import all_bundle_items_except_money
|
||||
from ..logic import MISSING_ITEM, _False
|
||||
|
||||
|
||||
class TestAllLogicalItem(unittest.TestCase):
|
||||
multi_world = setup_solo_multiworld(StardewValleyWorld)
|
||||
world = multi_world.worlds[1]
|
||||
logic = world.logic
|
||||
|
||||
def setUp(self) -> None:
|
||||
for item in self.multi_world.get_items():
|
||||
self.multi_world.state.collect(item, event=True)
|
||||
|
||||
def test_given_bundle_item_then_is_available_in_logic(self):
|
||||
for bundle_item in all_bundle_items_except_money:
|
||||
with self.subTest(bundle_item=bundle_item):
|
||||
assert bundle_item.item.name in self.logic.item_rules
|
||||
|
||||
def test_given_item_rule_then_can_be_resolved(self):
|
||||
for item in self.logic.item_rules.keys():
|
||||
with self.subTest(item=item):
|
||||
rule = self.logic.item_rules[item]
|
||||
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == _False() or rule(self.multi_world.state), f"Could not resolve rule for {item} {rule}"
|
||||
|
||||
def test_given_building_rule_then_can_be_resolved(self):
|
||||
for item in self.logic.building_rules.keys():
|
||||
with self.subTest(item=item):
|
||||
rule = self.logic.building_rules[item]
|
||||
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == _False() or rule(self.multi_world.state), f"Could not resolve rule for {item} {rule}"
|
||||
|
||||
def test_given_quest_rule_then_can_be_resolved(self):
|
||||
for item in self.logic.quest_rules.keys():
|
||||
with self.subTest(item=item):
|
||||
rule = self.logic.quest_rules[item]
|
||||
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == _False() or rule(self.multi_world.state), f"Could not resolve rule for {item} {rule}"
|
||||
|
||||
def test_given_location_rule_then_can_be_resolved(self):
|
||||
for location in self.multi_world.get_locations(1):
|
||||
with self.subTest(location=location):
|
||||
rule = location.access_rule
|
||||
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == _False() or rule(self.multi_world.state), f"Could not resolve rule for {location} {rule}"
|
|
@ -1,6 +1,6 @@
|
|||
import unittest
|
||||
|
||||
from ..bundle_data import all_bundle_items
|
||||
from ..data.bundle_data import all_bundle_items
|
||||
|
||||
|
||||
class TestBundles(unittest.TestCase):
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from BaseClasses import ItemClassification
|
||||
from . import SVTestBase
|
||||
from .. import locations, items, location_table, options
|
||||
from ..data.villagers_data import all_villagers_by_name
|
||||
from ..items import items_by_group, Group
|
||||
from ..locations import LocationTags
|
||||
|
||||
|
@ -113,15 +114,157 @@ class TestLocationGeneration(SVTestBase):
|
|||
|
||||
class TestLocationAndItemCount(SVTestBase):
|
||||
options = {
|
||||
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
|
||||
options.SeedShuffle.internal_name: options.SeedShuffle.option_shuffled,
|
||||
options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
|
||||
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
|
||||
options.TheMinesElevatorsProgression.internal_name: options.TheMinesElevatorsProgression.option_vanilla,
|
||||
options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
|
||||
options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
|
||||
options.TheMinesElevatorsProgression.internal_name: options.TheMinesElevatorsProgression.option_vanilla,
|
||||
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
|
||||
options.HelpWantedLocations.internal_name: 0,
|
||||
options.Fishsanity.internal_name: options.Fishsanity.option_none,
|
||||
options.Museumsanity.internal_name: options.Museumsanity.option_none,
|
||||
options.Friendsanity.internal_name: options.Museumsanity.option_none,
|
||||
options.NumberOfPlayerBuffs.internal_name: 12,
|
||||
}
|
||||
|
||||
def test_minimal_location_maximal_items_still_valid(self):
|
||||
assert len(self.multiworld.get_locations()) >= len(self.multiworld.get_items())
|
||||
|
||||
|
||||
class TestFriendsanityNone(SVTestBase):
|
||||
options = {
|
||||
options.Friendsanity.internal_name: options.Friendsanity.option_none,
|
||||
}
|
||||
|
||||
def test_no_friendsanity_items(self):
|
||||
for item in self.multiworld.get_items():
|
||||
assert not item.name.endswith(": 1 <3")
|
||||
|
||||
def test_no_friendsanity_locations(self):
|
||||
for location in self.multiworld.get_locations():
|
||||
assert not location.name.startswith("Friendsanity")
|
||||
|
||||
|
||||
class TestFriendsanityBachelors(SVTestBase):
|
||||
options = {
|
||||
options.Friendsanity.internal_name: options.Friendsanity.option_bachelors,
|
||||
}
|
||||
bachelors = {"Harvey", "Elliott", "Sam", "Alex", "Shane", "Sebastian", "Emily", "Haley", "Leah", "Abigail", "Penny",
|
||||
"Maru"}
|
||||
|
||||
def test_friendsanity_only_bachelor_items(self):
|
||||
suffix = ": 1 <3"
|
||||
for item in self.multiworld.get_items():
|
||||
if item.name.endswith(suffix):
|
||||
villager_name = item.name[:item.name.index(suffix)]
|
||||
assert villager_name in self.bachelors
|
||||
|
||||
def test_friendsanity_only_bachelor_locations(self):
|
||||
prefix = "Friendsanity: "
|
||||
suffix = " <3"
|
||||
for location in self.multiworld.get_locations():
|
||||
if location.name.startswith(prefix):
|
||||
name_no_prefix = location.name[len(prefix):]
|
||||
name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
|
||||
parts = name_trimmed.split(" ")
|
||||
name = parts[0]
|
||||
hearts = parts[1]
|
||||
assert name in self.bachelors
|
||||
assert int(hearts) <= 8
|
||||
|
||||
|
||||
class TestFriendsanityStartingNpcs(SVTestBase):
|
||||
options = {
|
||||
options.Friendsanity.internal_name: options.Friendsanity.option_starting_npcs,
|
||||
}
|
||||
excluded_npcs = {"Leo", "Krobus", "Dwarf", "Sandy", "Kent"}
|
||||
|
||||
def test_friendsanity_only_starting_npcs_items(self):
|
||||
suffix = ": 1 <3"
|
||||
for item in self.multiworld.get_items():
|
||||
if item.name.endswith(suffix):
|
||||
villager_name = item.name[:item.name.index(suffix)]
|
||||
assert villager_name not in self.excluded_npcs
|
||||
|
||||
def test_friendsanity_only_starting_npcs_locations(self):
|
||||
prefix = "Friendsanity: "
|
||||
suffix = " <3"
|
||||
for location in self.multiworld.get_locations():
|
||||
if location.name.startswith(prefix):
|
||||
name_no_prefix = location.name[len(prefix):]
|
||||
name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
|
||||
parts = name_trimmed.split(" ")
|
||||
name = parts[0]
|
||||
hearts = parts[1]
|
||||
assert name not in self.excluded_npcs
|
||||
assert name in all_villagers_by_name or name == "Pet"
|
||||
if name == "Pet":
|
||||
assert int(hearts) <= 5
|
||||
elif all_villagers_by_name[name].bachelor:
|
||||
assert int(hearts) <= 8
|
||||
else:
|
||||
assert int(hearts) <= 10
|
||||
|
||||
|
||||
class TestFriendsanityAllNpcs(SVTestBase):
|
||||
options = {
|
||||
options.Friendsanity.internal_name: options.Friendsanity.option_all,
|
||||
}
|
||||
|
||||
def test_friendsanity_all_items(self):
|
||||
suffix = ": 1 <3"
|
||||
for item in self.multiworld.get_items():
|
||||
if item.name.endswith(suffix):
|
||||
villager_name = item.name[:item.name.index(suffix)]
|
||||
assert villager_name in all_villagers_by_name or villager_name == "Pet"
|
||||
|
||||
def test_friendsanity_all_locations(self):
|
||||
prefix = "Friendsanity: "
|
||||
suffix = " <3"
|
||||
for location in self.multiworld.get_locations():
|
||||
if location.name.startswith(prefix):
|
||||
name_no_prefix = location.name[len(prefix):]
|
||||
name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
|
||||
parts = name_trimmed.split(" ")
|
||||
name = parts[0]
|
||||
hearts = parts[1]
|
||||
assert name in all_villagers_by_name or name == "Pet"
|
||||
if name == "Pet":
|
||||
assert int(hearts) <= 5
|
||||
elif all_villagers_by_name[name].bachelor:
|
||||
assert int(hearts) <= 8
|
||||
else:
|
||||
assert int(hearts) <= 10
|
||||
|
||||
|
||||
class TestFriendsanityAllNpcsWithMarriage(SVTestBase):
|
||||
options = {
|
||||
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
|
||||
}
|
||||
|
||||
def test_friendsanity_all_with_marriage_items(self):
|
||||
suffix = ": 1 <3"
|
||||
for item in self.multiworld.get_items():
|
||||
if item.name.endswith(suffix):
|
||||
villager_name = item.name[:item.name.index(suffix)]
|
||||
assert villager_name in all_villagers_by_name or villager_name == "Pet"
|
||||
|
||||
def test_friendsanity_all_with_marriage_locations(self):
|
||||
prefix = "Friendsanity: "
|
||||
suffix = " <3"
|
||||
for location in self.multiworld.get_locations():
|
||||
if location.name.startswith(prefix):
|
||||
name_no_prefix = location.name[len(prefix):]
|
||||
name_trimmed = name_no_prefix[:name_no_prefix.index(suffix)]
|
||||
parts = name_trimmed.split(" ")
|
||||
name = parts[0]
|
||||
hearts = parts[1]
|
||||
assert name in all_villagers_by_name or name == "Pet"
|
||||
if name == "Pet":
|
||||
assert int(hearts) <= 5
|
||||
elif all_villagers_by_name[name].bachelor:
|
||||
assert int(hearts) <= 14
|
||||
else:
|
||||
assert int(hearts) <= 10
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import itertools
|
||||
import math
|
||||
import unittest
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from .. import StardewValleyWorld
|
||||
from ..items import item_table
|
||||
|
||||
from BaseClasses import ItemClassification, MultiWorld
|
||||
from .. import ItemData, StardewValleyWorld
|
||||
from ..items import Group, ResourcePackData, item_table
|
||||
|
||||
|
||||
class TestItems(unittest.TestCase):
|
||||
|
@ -24,3 +27,70 @@ class TestItems(unittest.TestCase):
|
|||
|
||||
assert item_with_lowest_id.code >= 717000
|
||||
assert item_with_highest_id.code < 727000
|
||||
|
||||
|
||||
class TestResourcePacks:
|
||||
def test_can_transform_resource_pack_data_into_idem_data(self):
|
||||
resource_pack = ResourcePackData("item name", 1, 1, ItemClassification.filler, frozenset())
|
||||
|
||||
items = resource_pack.as_item_data(itertools.count())
|
||||
|
||||
assert ItemData(0, "Resource Pack: 1 item name", ItemClassification.filler, {Group.RESOURCE_PACK}) in items
|
||||
assert ItemData(1, "Resource Pack: 2 item name", ItemClassification.filler, {Group.RESOURCE_PACK}) in items
|
||||
assert len(items) == 2
|
||||
|
||||
def test_when_scale_quantity_then_generate_a_possible_quantity_from_minimal_scaling_to_double(self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=4, scaling_factor=2)
|
||||
|
||||
quantities = resource_pack.scale_quantity.items()
|
||||
|
||||
assert (50, 2) in quantities
|
||||
assert (100, 4) in quantities
|
||||
assert (150, 6) in quantities
|
||||
assert (200, 8) in quantities
|
||||
assert len(quantities) == (4 / 2) * 2
|
||||
|
||||
def test_given_scaling_not_multiple_of_default_amount_when_scale_quantity_then_double_is_added_at_200_scaling(self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=5, scaling_factor=3)
|
||||
|
||||
quantities = resource_pack.scale_quantity.items()
|
||||
|
||||
assert (40, 2) in quantities
|
||||
assert (100, 5) in quantities
|
||||
assert (160, 8) in quantities
|
||||
assert (200, 10) in quantities
|
||||
assert len(quantities) == math.ceil(5 / 3) * 2
|
||||
|
||||
def test_given_large_default_amount_multiple_of_scaling_factor_when_scale_quantity_then_scaled_amount_multiple(self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=500, scaling_factor=50)
|
||||
|
||||
quantities = resource_pack.scale_quantity.items()
|
||||
|
||||
assert (10, 50) in quantities
|
||||
assert (20, 100) in quantities
|
||||
assert (30, 150) in quantities
|
||||
assert (40, 200) in quantities
|
||||
assert (50, 250) in quantities
|
||||
assert (60, 300) in quantities
|
||||
assert (70, 350) in quantities
|
||||
assert (80, 400) in quantities
|
||||
assert (90, 450) in quantities
|
||||
assert (100, 500) in quantities
|
||||
assert (110, 550) in quantities
|
||||
assert (120, 600) in quantities
|
||||
assert (130, 650) in quantities
|
||||
assert (140, 700) in quantities
|
||||
assert (150, 750) in quantities
|
||||
assert (160, 800) in quantities
|
||||
assert (170, 850) in quantities
|
||||
assert (180, 900) in quantities
|
||||
assert (190, 950) in quantities
|
||||
assert (200, 1000) in quantities
|
||||
assert len(quantities) == math.ceil(500 / 50) * 2
|
||||
|
||||
def test_given_smallest_multiplier_possible_when_generate_resource_pack_name_then_quantity_is_not_0(self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=10, scaling_factor=5)
|
||||
|
||||
name = resource_pack.create_name_from_multiplier(1)
|
||||
|
||||
assert name == "Resource Pack: 5 item name"
|
||||
|
|
|
@ -1,293 +1,57 @@
|
|||
from . import SVTestBase
|
||||
from .. import options
|
||||
import pytest
|
||||
|
||||
from test.general import setup_solo_multiworld
|
||||
from .. import StardewValleyWorld, StardewLocation
|
||||
from ..data.bundle_data import BundleItem, all_bundle_items_except_money
|
||||
from ..stardew_rule import MISSING_ITEM, False_
|
||||
|
||||
multi_world = setup_solo_multiworld(StardewValleyWorld)
|
||||
world = multi_world.worlds[1]
|
||||
logic = world.logic
|
||||
|
||||
|
||||
class TestProgressiveToolsLogic(SVTestBase):
|
||||
options = {
|
||||
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
|
||||
}
|
||||
|
||||
def test_sturgeon(self):
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
summer = self.get_item_by_name("Summer")
|
||||
self.multiworld.state.collect(summer, event=True)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
fishing_rod = self.get_item_by_name("Progressive Fishing Rod")
|
||||
self.multiworld.state.collect(fishing_rod, event=True)
|
||||
self.multiworld.state.collect(fishing_rod, event=True)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
fishing_level = self.get_item_by_name("Fishing Level")
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
assert self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
self.remove(summer)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
winter = self.get_item_by_name("Winter")
|
||||
self.multiworld.state.collect(winter, event=True)
|
||||
assert self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
self.remove(fishing_rod)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
def test_old_master_cannoli(self):
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Axe"), event=True)
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Axe"), event=True)
|
||||
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
fall = self.get_item_by_name("Fall")
|
||||
self.multiworld.state.collect(fall, event=True)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
tuesday = self.get_item_by_name("Traveling Merchant: Tuesday")
|
||||
self.multiworld.state.collect(tuesday, event=True)
|
||||
assert self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
self.remove(fall)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
self.remove(tuesday)
|
||||
|
||||
green_house = self.get_item_by_name("Greenhouse")
|
||||
self.multiworld.state.collect(green_house, event=True)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
friday = self.get_item_by_name("Traveling Merchant: Friday")
|
||||
self.multiworld.state.collect(friday, event=True)
|
||||
assert self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
self.remove(green_house)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
self.remove(friday)
|
||||
def collect_all(mw):
|
||||
for item in mw.get_items():
|
||||
mw.state.collect(item, event=True)
|
||||
|
||||
|
||||
class TestBundlesLogic(SVTestBase):
|
||||
options = {
|
||||
}
|
||||
|
||||
def test_vault_2500g_bundle(self):
|
||||
assert not self.world.logic.can_reach_location("2,500g Bundle")(self.multiworld.state)
|
||||
|
||||
summer = self.get_item_by_name("Summer")
|
||||
self.multiworld.state.collect(summer, event=True)
|
||||
assert self.world.logic.can_reach_location("2,500g Bundle")(self.multiworld.state)
|
||||
collect_all(multi_world)
|
||||
|
||||
|
||||
class TestBuildingLogic(SVTestBase):
|
||||
options = {
|
||||
options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive_early_shipping_bin
|
||||
}
|
||||
|
||||
def test_coop_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
summer = self.get_item_by_name("Summer")
|
||||
self.multiworld.state.collect(summer, event=True)
|
||||
assert self.world.logic.can_reach_location("Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
def test_big_coop_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.get_item_by_name("Fall"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Coop"), event=True)
|
||||
assert self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}"
|
||||
|
||||
def test_deluxe_big_coop_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(self.get_item_by_name("Year Two"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Coop"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Coop"), event=True)
|
||||
assert self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
def test_big_shed_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.get_item_by_name("Year Two"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Shed"), event=True)
|
||||
assert self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}"
|
||||
@pytest.mark.parametrize("bundle_item", all_bundle_items_except_money,
|
||||
ids=[i.item.name for i in all_bundle_items_except_money])
|
||||
def test_given_bundle_item_then_is_available_in_logic(bundle_item: BundleItem):
|
||||
assert bundle_item.item.name in logic.item_rules
|
||||
|
||||
|
||||
class TestArcadeMachinesLogic(SVTestBase):
|
||||
options = {
|
||||
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling,
|
||||
}
|
||||
@pytest.mark.parametrize("item", logic.item_rules.keys(), ids=logic.item_rules.keys())
|
||||
def test_given_item_rule_then_can_be_resolved(item: str):
|
||||
rule = logic.item_rules[item]
|
||||
|
||||
def test_prairie_king(self):
|
||||
assert not self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
|
||||
boots = self.get_item_by_name("JotPK: Progressive Boots")
|
||||
gun = self.get_item_by_name("JotPK: Progressive Gun")
|
||||
ammo = self.get_item_by_name("JotPK: Progressive Ammo")
|
||||
life = self.get_item_by_name("JotPK: Extra Life")
|
||||
drop = self.get_item_by_name("JotPK: Increased Drop Rate")
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(boots)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(life, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
self.remove(ammo)
|
||||
self.remove(life)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(life, event=True)
|
||||
self.multiworld.state.collect(drop, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(ammo)
|
||||
self.remove(ammo)
|
||||
self.remove(life)
|
||||
self.remove(drop)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(life, event=True)
|
||||
self.multiworld.state.collect(drop, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(ammo)
|
||||
self.remove(ammo)
|
||||
self.remove(ammo)
|
||||
self.remove(life)
|
||||
self.remove(drop)
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}"
|
||||
|
||||
|
||||
class TestWeaponsLogic(SVTestBase):
|
||||
options = {
|
||||
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
|
||||
options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
|
||||
}
|
||||
@pytest.mark.parametrize("item", logic.building_rules.keys(), ids=logic.building_rules.keys())
|
||||
def test_given_building_rule_then_can_be_resolved(item: str):
|
||||
rule = logic.building_rules[item]
|
||||
|
||||
def test_mine(self):
|
||||
self.collect(self.get_item_by_name("Adventurer's Guild"))
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Pickaxe"), event=True)
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Pickaxe"), event=True)
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Pickaxe"), event=True)
|
||||
self.multiworld.state.collect(self.get_item_by_name("Progressive Pickaxe"), event=True)
|
||||
self.collect([self.get_item_by_name("Combat Level")] * 10)
|
||||
self.collect([self.get_item_by_name("Progressive Mine Elevator")] * 24)
|
||||
self.multiworld.state.collect(self.get_item_by_name("Bus Repair"), event=True)
|
||||
self.multiworld.state.collect(self.get_item_by_name("Skull Key"), event=True)
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}"
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Rusty Sword", 1)
|
||||
self.GiveItemAndCheckReachableMine("Wooden Blade", 1)
|
||||
self.GiveItemAndCheckReachableMine("Elf Blade", 1)
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Silver Saber", 2)
|
||||
self.GiveItemAndCheckReachableMine("Crystal Dagger", 2)
|
||||
@pytest.mark.parametrize("item", logic.quest_rules.keys(), ids=logic.quest_rules.keys())
|
||||
def test_given_quest_rule_then_can_be_resolved(item: str):
|
||||
rule = logic.quest_rules[item]
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Claymore", 3)
|
||||
self.GiveItemAndCheckReachableMine("Obsidian Edge", 3)
|
||||
self.GiveItemAndCheckReachableMine("Bone Sword", 3)
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {item} {rule}"
|
||||
|
||||
self.GiveItemAndCheckReachableMine("The Slammer", 4)
|
||||
self.GiveItemAndCheckReachableMine("Lava Katana", 4)
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Galaxy Sword", 5)
|
||||
self.GiveItemAndCheckReachableMine("Galaxy Hammer", 5)
|
||||
self.GiveItemAndCheckReachableMine("Galaxy Dagger", 5)
|
||||
@pytest.mark.parametrize("location", multi_world.get_locations(1),
|
||||
ids=[loc.name for loc in multi_world.get_locations(1)])
|
||||
def test_given_location_rule_then_can_be_resolved(location: StardewLocation):
|
||||
rule = location.access_rule
|
||||
|
||||
def GiveItemAndCheckReachableMine(self, item_name: str, reachable_level: int):
|
||||
item = self.multiworld.create_item(item_name, self.player)
|
||||
self.multiworld.state.collect(item, event=True)
|
||||
if reachable_level > 0:
|
||||
assert self.world.logic.can_mine_in_the_mines_floor_1_40()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_mines_floor_1_40()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 1:
|
||||
assert self.world.logic.can_mine_in_the_mines_floor_41_80()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_mines_floor_41_80()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 2:
|
||||
assert self.world.logic.can_mine_in_the_mines_floor_81_120()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_mines_floor_81_120()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 3:
|
||||
assert self.world.logic.can_mine_in_the_skull_cavern()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_skull_cavern()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 4:
|
||||
assert self.world.logic.can_mine_perfectly_in_the_skull_cavern()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_perfectly_in_the_skull_cavern()(self.multiworld.state)
|
||||
|
||||
self.remove(item)
|
||||
assert MISSING_ITEM not in repr(rule)
|
||||
assert rule == False_() or rule(multi_world.state), f"Could not resolve rule for {location} {rule}"
|
||||
|
|
|
@ -1,52 +1,65 @@
|
|||
import unittest
|
||||
|
||||
from .. import _True
|
||||
from ..logic import _Received, _Has, _False, _And, _Or
|
||||
from .. import True_
|
||||
from ..logic import Received, Has, False_, And, Or, StardewLogic
|
||||
from ..options import default_options, StardewOptions
|
||||
|
||||
|
||||
class TestLogicSimplification(unittest.TestCase):
|
||||
def test_simplify_true_in_and(self):
|
||||
rules = {
|
||||
"Wood": _True(),
|
||||
"Rock": _True(),
|
||||
}
|
||||
summer = _Received("Summer", 0, 1)
|
||||
assert (_Has("Wood", rules) & summer & _Has("Rock", rules)).simplify() == summer
|
||||
def test_simplify_true_in_and():
|
||||
rules = {
|
||||
"Wood": True_(),
|
||||
"Rock": True_(),
|
||||
}
|
||||
summer = Received("Summer", 0, 1)
|
||||
assert (Has("Wood", rules) & summer & Has("Rock", rules)).simplify() == summer
|
||||
|
||||
def test_simplify_false_in_or(self):
|
||||
rules = {
|
||||
"Wood": _False(),
|
||||
"Rock": _False(),
|
||||
}
|
||||
summer = _Received("Summer", 0, 1)
|
||||
assert (_Has("Wood", rules) | summer | _Has("Rock", rules)).simplify() == summer
|
||||
|
||||
def test_simplify_and_in_and(self):
|
||||
rule = _And(_And(_Received("Summer", 0, 1), _Received("Fall", 0, 1)),
|
||||
_And(_Received("Winter", 0, 1), _Received("Spring", 0, 1)))
|
||||
assert rule.simplify() == _And(_Received("Summer", 0, 1), _Received("Fall", 0, 1), _Received("Winter", 0, 1),
|
||||
_Received("Spring", 0, 1))
|
||||
def test_simplify_false_in_or():
|
||||
rules = {
|
||||
"Wood": False_(),
|
||||
"Rock": False_(),
|
||||
}
|
||||
summer = Received("Summer", 0, 1)
|
||||
assert (Has("Wood", rules) | summer | Has("Rock", rules)).simplify() == summer
|
||||
|
||||
def test_simplify_duplicated_and(self):
|
||||
rule = _And(_And(_Received("Summer", 0, 1), _Received("Fall", 0, 1)),
|
||||
_And(_Received("Summer", 0, 1), _Received("Fall", 0, 1)))
|
||||
assert rule.simplify() == _And(_Received("Summer", 0, 1), _Received("Fall", 0, 1))
|
||||
|
||||
def test_simplify_or_in_or(self):
|
||||
rule = _Or(_Or(_Received("Summer", 0, 1), _Received("Fall", 0, 1)),
|
||||
_Or(_Received("Winter", 0, 1), _Received("Spring", 0, 1)))
|
||||
assert rule.simplify() == _Or(_Received("Summer", 0, 1), _Received("Fall", 0, 1), _Received("Winter", 0, 1),
|
||||
_Received("Spring", 0, 1))
|
||||
def test_simplify_and_in_and():
|
||||
rule = And(And(Received('Summer', 0, 1), Received('Fall', 0, 1)),
|
||||
And(Received('Winter', 0, 1), Received('Spring', 0, 1)))
|
||||
assert rule.simplify() == And(Received('Summer', 0, 1), Received('Fall', 0, 1), Received('Winter', 0, 1),
|
||||
Received('Spring', 0, 1))
|
||||
|
||||
def test_simplify_duplicated_or(self):
|
||||
rule = _And(_Or(_Received("Summer", 0, 1), _Received("Fall", 0, 1)),
|
||||
_Or(_Received("Summer", 0, 1), _Received("Fall", 0, 1)))
|
||||
assert rule.simplify() == _Or(_Received("Summer", 0, 1), _Received("Fall", 0, 1))
|
||||
|
||||
def test_simplify_true_in_or(self):
|
||||
rule = _Or(_True(), _Received("Summer", 0, 1))
|
||||
assert rule.simplify() == _True()
|
||||
def test_simplify_duplicated_and():
|
||||
rule = And(And(Received('Summer', 0, 1), Received('Fall', 0, 1)),
|
||||
And(Received('Summer', 0, 1), Received('Fall', 0, 1)))
|
||||
assert rule.simplify() == And(Received('Summer', 0, 1), Received('Fall', 0, 1))
|
||||
|
||||
def test_simplify_false_in_and(self):
|
||||
rule = _And(_False(), _Received("Summer", 0, 1))
|
||||
assert rule.simplify() == _False()
|
||||
|
||||
def test_simplify_or_in_or():
|
||||
rule = Or(Or(Received('Summer', 0, 1), Received('Fall', 0, 1)),
|
||||
Or(Received('Winter', 0, 1), Received('Spring', 0, 1)))
|
||||
assert rule.simplify() == Or(Received('Summer', 0, 1), Received('Fall', 0, 1), Received('Winter', 0, 1),
|
||||
Received('Spring', 0, 1))
|
||||
|
||||
|
||||
def test_simplify_duplicated_or():
|
||||
rule = And(Or(Received('Summer', 0, 1), Received('Fall', 0, 1)),
|
||||
Or(Received('Summer', 0, 1), Received('Fall', 0, 1)))
|
||||
assert rule.simplify() == Or(Received('Summer', 0, 1), Received('Fall', 0, 1))
|
||||
|
||||
|
||||
def test_simplify_true_in_or():
|
||||
rule = Or(True_(), Received('Summer', 0, 1))
|
||||
assert rule.simplify() == True_()
|
||||
|
||||
|
||||
def test_simplify_false_in_and():
|
||||
rule = And(False_(), Received('Summer', 0, 1))
|
||||
assert rule.simplify() == False_()
|
||||
|
||||
|
||||
def test_simplify_coffee():
|
||||
logic = StardewLogic(1, StardewOptions(default_options))
|
||||
|
||||
simplified_coffee = logic.has("Coffee").simplify()
|
||||
|
||||
assert simplified_coffee == True_()
|
||||
|
|
|
@ -1,8 +1,159 @@
|
|||
from worlds.stardew_valley.test import SVTestBase
|
||||
import itertools
|
||||
|
||||
import pytest
|
||||
|
||||
from BaseClasses import ItemClassification, MultiWorld
|
||||
from Options import SpecialRange
|
||||
from . import setup_solo_multiworld
|
||||
from .. import StardewItem, options
|
||||
from ..options import StardewOption, stardew_valley_option_classes
|
||||
|
||||
SEASONS = {"Spring", "Summer", "Fall", "Winter"}
|
||||
TOOLS = {"Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can", "Fishing Rod"}
|
||||
|
||||
|
||||
class TestMasterAnglerVanillaTools(SVTestBase):
|
||||
options = {
|
||||
"goal": "master_angler",
|
||||
"tool_progression": "vanilla",
|
||||
}
|
||||
def basic_checks(multi_world: MultiWorld):
|
||||
assert StardewItem("Victory", ItemClassification.progression, None, 1) in multi_world.get_items()
|
||||
assert_can_win(multi_world)
|
||||
assert len(multi_world.itempool) == len(
|
||||
[location for location in multi_world.get_locations() if not location.event])
|
||||
|
||||
|
||||
def assert_can_win(multi_world: MultiWorld):
|
||||
for item in multi_world.get_items():
|
||||
multi_world.state.collect(item)
|
||||
|
||||
assert multi_world.find_item("Victory", 1).can_reach(multi_world.state)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("option, value", [(option, value)
|
||||
for option in stardew_valley_option_classes
|
||||
if issubclass(option, SpecialRange)
|
||||
for value in option.special_range_names])
|
||||
def test_given_special_range_when_generate_then_basic_checks(option: (SpecialRange, StardewOption), value):
|
||||
multi_world = setup_solo_multiworld({option.internal_name: option.special_range_names[value]})
|
||||
|
||||
basic_checks(multi_world)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("option, value", [(option, value)
|
||||
for option in stardew_valley_option_classes
|
||||
if option.options
|
||||
for value in option.options])
|
||||
def test_given_choice_when_generate_then_basic_checks(option, value):
|
||||
multi_world = setup_solo_multiworld({option.internal_name: option.options[value]})
|
||||
|
||||
basic_checks(multi_world)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("option_combination",
|
||||
[{options.Goal.internal_name: options.Goal.option_master_angler,
|
||||
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}],
|
||||
ids=["Master Angler + Vanilla tools"])
|
||||
def test_given_option_combination_when_generate_then_basic_checks(option_combination):
|
||||
multi_world = setup_solo_multiworld(option_combination)
|
||||
|
||||
basic_checks(multi_world)
|
||||
|
||||
|
||||
class TestGoal:
|
||||
@pytest.mark.parametrize("goal,location", [("community_center", "Complete Community Center"),
|
||||
("grandpa_evaluation", "Succeed Grandpa's Evaluation"),
|
||||
("bottom_of_the_mines", "Reach the Bottom of The Mines"),
|
||||
("cryptic_note", "Complete Quest Cryptic Note"),
|
||||
("master_angler", "Catch Every Fish")])
|
||||
def test_given_goal_when_generate_then_victory_is_in_correct_location(self, goal, location):
|
||||
multi_world = setup_solo_multiworld({options.Goal.internal_name: options.Goal.options[goal]})
|
||||
victory = multi_world.find_item("Victory", 1)
|
||||
|
||||
assert victory.name == location
|
||||
|
||||
|
||||
class TestSeasonRandomization:
|
||||
def test_given_disabled_when_generate_then_all_seasons_are_precollected(self):
|
||||
multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name:
|
||||
options.SeasonRandomization.option_disabled})
|
||||
|
||||
precollected_items = {item.name for item in multi_world.precollected_items[1]}
|
||||
assert all([season in precollected_items for season in SEASONS])
|
||||
|
||||
@pytest.mark.parametrize("value", [value for value in options.SeasonRandomization.options if "randomized" in value])
|
||||
def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self, value):
|
||||
multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name:
|
||||
options.SeasonRandomization.options[value]})
|
||||
|
||||
precollected_items = {item.name for item in multi_world.precollected_items[1]}
|
||||
items = {item.name for item in multi_world.get_items()} | precollected_items
|
||||
assert all([season in items for season in SEASONS])
|
||||
assert len(SEASONS.intersection(precollected_items)) == 1
|
||||
|
||||
def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_pool(self):
|
||||
multi_world = setup_solo_multiworld({options.SeasonRandomization.internal_name:
|
||||
options.SeasonRandomization.option_progressive})
|
||||
|
||||
items = [item.name for item in multi_world.get_items()]
|
||||
assert items.count("Progressive Season") == 3
|
||||
|
||||
|
||||
class TestBackpackProgression:
|
||||
def test_given_vanilla_when_generate_then_no_backpack_in_pool(self):
|
||||
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name:
|
||||
options.BackpackProgression.option_vanilla})
|
||||
|
||||
assert "Progressive Backpack" not in {item.name for item in multi_world.get_items()}
|
||||
|
||||
@pytest.mark.parametrize("value",
|
||||
[value for value in options.BackpackProgression.options if "progressive" in value])
|
||||
def test_given_progressive_when_generate_then_progressive_backpack_is_in_pool_two_times(self, value):
|
||||
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name:
|
||||
options.BackpackProgression.options[value]})
|
||||
|
||||
items = [item.name for item in multi_world.get_items()]
|
||||
assert items.count("Progressive Backpack") == 2
|
||||
|
||||
@pytest.mark.parametrize("value",
|
||||
[value for value in options.BackpackProgression.options if "progressive" in value])
|
||||
def test_given_progressive_when_generate_then_backpack_upgrades_are_locations(self, value):
|
||||
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name:
|
||||
options.BackpackProgression.options[value]})
|
||||
|
||||
locations = {locations.name for locations in multi_world.get_locations(1)}
|
||||
assert "Large Pack" in locations
|
||||
assert "Deluxe Pack" in locations
|
||||
|
||||
def test_given_early_progressive_when_generate_then_progressive_backpack_is_in_early_pool(self):
|
||||
multi_world = setup_solo_multiworld({options.BackpackProgression.internal_name:
|
||||
options.BackpackProgression.option_early_progressive})
|
||||
|
||||
assert "Progressive Backpack" in multi_world.early_items[1]
|
||||
|
||||
|
||||
class TestToolProgression:
|
||||
def test_given_vanilla_when_generate_then_no_tool_in_pool(self):
|
||||
multi_world = setup_solo_multiworld({options.ToolProgression.internal_name:
|
||||
options.ToolProgression.option_vanilla})
|
||||
|
||||
items = {item.name for item in multi_world.get_items()}
|
||||
for tool in TOOLS:
|
||||
assert tool not in items
|
||||
|
||||
def test_given_progressive_when_generate_then_progressive_tool_of_each_is_in_pool_four_times(self):
|
||||
multi_world = setup_solo_multiworld({options.ToolProgression.internal_name:
|
||||
options.ToolProgression.option_progressive})
|
||||
|
||||
items = [item.name for item in multi_world.get_items()]
|
||||
for tool in TOOLS:
|
||||
assert items.count("Progressive " + tool) == 4
|
||||
|
||||
def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self):
|
||||
multi_world = setup_solo_multiworld({options.ToolProgression.internal_name:
|
||||
options.ToolProgression.option_progressive})
|
||||
|
||||
locations = {locations.name for locations in multi_world.get_locations(1)}
|
||||
for material, tool in itertools.product(["Copper", "Iron", "Gold", "Iridium"],
|
||||
["Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can"]):
|
||||
assert f"{material} {tool} Upgrade" in locations
|
||||
assert "Purchase Training Rod" in locations
|
||||
assert "Bamboo Pole Cutscene" in locations
|
||||
assert "Purchase Fiberglass Rod" in locations
|
||||
assert "Purchase Iridium Rod" in locations
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
import itertools
|
||||
import math
|
||||
import unittest
|
||||
|
||||
from BaseClasses import ItemClassification
|
||||
from .. import ItemData
|
||||
from ..items import Group, ResourcePackData
|
||||
|
||||
|
||||
class TestResourcePack(unittest.TestCase):
|
||||
|
||||
def test_can_transform_resource_pack_data_into_idem_data(self):
|
||||
resource_pack = ResourcePackData("item name", 1, 1, ItemClassification.filler, frozenset())
|
||||
|
||||
items = resource_pack.as_item_data(itertools.count())
|
||||
|
||||
assert ItemData(0, "Resource Pack: 1 item name", ItemClassification.filler, {Group.RESOURCE_PACK}) in items
|
||||
assert ItemData(1, "Resource Pack: 2 item name", ItemClassification.filler, {Group.RESOURCE_PACK}) in items
|
||||
assert len(items) == 2
|
||||
|
||||
def test_when_scale_quantity_then_generate_a_possible_quantity_from_minimal_scaling_to_double(self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=4, scaling_factor=2)
|
||||
|
||||
quantities = resource_pack.scale_quantity.items()
|
||||
|
||||
assert (50, 2) in quantities
|
||||
assert (100, 4) in quantities
|
||||
assert (150, 6) in quantities
|
||||
assert (200, 8) in quantities
|
||||
assert len(quantities) == (4 / 2) * 2
|
||||
|
||||
def test_given_scaling_not_multiple_of_default_amount_when_scale_quantity_then_double_is_added_at_200_scaling(self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=5, scaling_factor=3)
|
||||
|
||||
quantities = resource_pack.scale_quantity.items()
|
||||
|
||||
assert (40, 2) in quantities
|
||||
assert (100, 5) in quantities
|
||||
assert (160, 8) in quantities
|
||||
assert (200, 10) in quantities
|
||||
assert len(quantities) == math.ceil(5 / 3) * 2
|
||||
|
||||
def test_given_large_default_amount_multiple_of_scaling_factor_when_scale_quantity_then_scaled_amount_multiple(
|
||||
self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=500, scaling_factor=50)
|
||||
|
||||
quantities = resource_pack.scale_quantity.items()
|
||||
|
||||
assert (10, 50) in quantities
|
||||
assert (20, 100) in quantities
|
||||
assert (30, 150) in quantities
|
||||
assert (40, 200) in quantities
|
||||
assert (50, 250) in quantities
|
||||
assert (60, 300) in quantities
|
||||
assert (70, 350) in quantities
|
||||
assert (80, 400) in quantities
|
||||
assert (90, 450) in quantities
|
||||
assert (100, 500) in quantities
|
||||
assert (110, 550) in quantities
|
||||
assert (120, 600) in quantities
|
||||
assert (130, 650) in quantities
|
||||
assert (140, 700) in quantities
|
||||
assert (150, 750) in quantities
|
||||
assert (160, 800) in quantities
|
||||
assert (170, 850) in quantities
|
||||
assert (180, 900) in quantities
|
||||
assert (190, 950) in quantities
|
||||
assert (200, 1000) in quantities
|
||||
assert len(quantities) == math.ceil(500 / 50) * 2
|
||||
|
||||
def test_given_smallest_multiplier_possible_when_generate_resource_pack_name_then_quantity_is_not_0(self):
|
||||
resource_pack = ResourcePackData("item name", default_amount=10, scaling_factor=5)
|
||||
|
||||
name = resource_pack.create_name_from_multiplier(1)
|
||||
|
||||
assert name == "Resource Pack: 5 item name"
|
|
@ -0,0 +1,309 @@
|
|||
from collections import Counter
|
||||
|
||||
from . import SVTestBase
|
||||
from .. import options
|
||||
|
||||
|
||||
class TestProgressiveToolsLogic(SVTestBase):
|
||||
options = {
|
||||
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
|
||||
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.multiworld.state.prog_items = Counter()
|
||||
|
||||
def test_sturgeon(self):
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
summer = self.world.create_item("Summer")
|
||||
self.multiworld.state.collect(summer, event=True)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
fishing_rod = self.world.create_item("Progressive Fishing Rod")
|
||||
self.multiworld.state.collect(fishing_rod, event=True)
|
||||
self.multiworld.state.collect(fishing_rod, event=True)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
fishing_level = self.world.create_item("Fishing Level")
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
self.multiworld.state.collect(fishing_level, event=True)
|
||||
assert self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
self.remove(summer)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
winter = self.world.create_item("Winter")
|
||||
self.multiworld.state.collect(winter, event=True)
|
||||
assert self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
self.remove(fishing_rod)
|
||||
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
|
||||
|
||||
def test_old_master_cannoli(self):
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Axe"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Summer"), event=True)
|
||||
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
fall = self.world.create_item("Fall")
|
||||
self.multiworld.state.collect(fall, event=True)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
tuesday = self.world.create_item("Traveling Merchant: Tuesday")
|
||||
self.multiworld.state.collect(tuesday, event=True)
|
||||
assert self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
self.remove(fall)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
self.remove(tuesday)
|
||||
|
||||
green_house = self.world.create_item("Greenhouse")
|
||||
self.multiworld.state.collect(green_house, event=True)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
|
||||
friday = self.world.create_item("Traveling Merchant: Friday")
|
||||
self.multiworld.state.collect(friday, event=True)
|
||||
assert self.multiworld.get_location("Old Master Cannoli", 1).access_rule(self.multiworld.state)
|
||||
|
||||
self.remove(green_house)
|
||||
assert not self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state)
|
||||
self.remove(friday)
|
||||
|
||||
|
||||
class TestBundlesLogic(SVTestBase):
|
||||
options = {
|
||||
}
|
||||
|
||||
def test_vault_2500g_bundle(self):
|
||||
assert self.world.logic.can_reach_location("2,500g Bundle")(self.multiworld.state)
|
||||
|
||||
|
||||
class TestBuildingLogic(SVTestBase):
|
||||
options = {
|
||||
options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive_early_shipping_bin
|
||||
}
|
||||
|
||||
def test_coop_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
assert self.world.logic.can_reach_location("Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
def test_big_coop_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Coop"), event=True)
|
||||
assert self.world.logic.can_reach_location("Big Coop Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Coop Blueprint', self.player).access_rule)}"
|
||||
|
||||
def test_deluxe_coop_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Coop"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Coop"), event=True)
|
||||
assert self.world.logic.can_reach_location("Deluxe Coop Blueprint")(self.multiworld.state)
|
||||
|
||||
def test_big_shed_blueprint(self):
|
||||
assert not self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Month End"), event=True)
|
||||
assert not self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}"
|
||||
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Shed"), event=True)
|
||||
assert self.world.logic.can_reach_location("Big Shed Blueprint")(self.multiworld.state), \
|
||||
f"Rule is {repr(self.multiworld.get_location('Big Shed Blueprint', self.player).access_rule)}"
|
||||
|
||||
|
||||
class TestArcadeMachinesLogic(SVTestBase):
|
||||
options = {
|
||||
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling,
|
||||
}
|
||||
|
||||
def test_prairie_king(self):
|
||||
assert not self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
|
||||
boots = self.world.create_item("JotPK: Progressive Boots")
|
||||
gun = self.world.create_item("JotPK: Progressive Gun")
|
||||
ammo = self.world.create_item("JotPK: Progressive Ammo")
|
||||
life = self.world.create_item("JotPK: Extra Life")
|
||||
drop = self.world.create_item("JotPK: Increased Drop Rate")
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(boots)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(life, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
self.remove(ammo)
|
||||
self.remove(life)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(life, event=True)
|
||||
self.multiworld.state.collect(drop, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert not self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(ammo)
|
||||
self.remove(ammo)
|
||||
self.remove(life)
|
||||
self.remove(drop)
|
||||
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(boots, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(gun, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(ammo, event=True)
|
||||
self.multiworld.state.collect(life, event=True)
|
||||
self.multiworld.state.collect(drop, event=True)
|
||||
assert self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state)
|
||||
assert self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state)
|
||||
self.remove(boots)
|
||||
self.remove(boots)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(gun)
|
||||
self.remove(ammo)
|
||||
self.remove(ammo)
|
||||
self.remove(ammo)
|
||||
self.remove(life)
|
||||
self.remove(drop)
|
||||
|
||||
|
||||
class TestWeaponsLogic(SVTestBase):
|
||||
options = {
|
||||
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
|
||||
options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
|
||||
}
|
||||
|
||||
def test_mine(self):
|
||||
self.collect(self.world.create_item("Adventurer's Guild"))
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Progressive Pickaxe"), event=True)
|
||||
self.collect([self.world.create_item("Combat Level")] * 10)
|
||||
self.collect([self.world.create_item("Progressive Mine Elevator")] * 24)
|
||||
self.multiworld.state.collect(self.world.create_item("Bus Repair"), event=True)
|
||||
self.multiworld.state.collect(self.world.create_item("Skull Key"), event=True)
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Rusty Sword", 1)
|
||||
self.GiveItemAndCheckReachableMine("Wooden Blade", 1)
|
||||
self.GiveItemAndCheckReachableMine("Elf Blade", 1)
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Silver Saber", 2)
|
||||
self.GiveItemAndCheckReachableMine("Crystal Dagger", 2)
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Claymore", 3)
|
||||
self.GiveItemAndCheckReachableMine("Obsidian Edge", 3)
|
||||
self.GiveItemAndCheckReachableMine("Bone Sword", 3)
|
||||
|
||||
self.GiveItemAndCheckReachableMine("The Slammer", 4)
|
||||
self.GiveItemAndCheckReachableMine("Lava Katana", 4)
|
||||
|
||||
self.GiveItemAndCheckReachableMine("Galaxy Sword", 5)
|
||||
self.GiveItemAndCheckReachableMine("Galaxy Hammer", 5)
|
||||
self.GiveItemAndCheckReachableMine("Galaxy Dagger", 5)
|
||||
|
||||
def GiveItemAndCheckReachableMine(self, item_name: str, reachable_level: int):
|
||||
item = self.multiworld.create_item(item_name, self.player)
|
||||
self.multiworld.state.collect(item, event=True)
|
||||
if reachable_level > 0:
|
||||
assert self.world.logic.can_mine_in_the_mines_floor_1_40()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_mines_floor_1_40()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 1:
|
||||
assert self.world.logic.can_mine_in_the_mines_floor_41_80()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_mines_floor_41_80()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 2:
|
||||
assert self.world.logic.can_mine_in_the_mines_floor_81_120()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_mines_floor_81_120()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 3:
|
||||
assert self.world.logic.can_mine_in_the_skull_cavern()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_in_the_skull_cavern()(self.multiworld.state)
|
||||
|
||||
if reachable_level > 4:
|
||||
assert self.world.logic.can_mine_perfectly_in_the_skull_cavern()(self.multiworld.state)
|
||||
else:
|
||||
assert not self.world.logic.can_mine_perfectly_in_the_skull_cavern()(self.multiworld.state)
|
||||
|
||||
self.remove(item)
|
|
@ -1,7 +1,11 @@
|
|||
from typing import ClassVar
|
||||
from argparse import Namespace
|
||||
from typing import Dict, FrozenSet, Tuple, Any, ClassVar
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from test.TestBase import WorldTestBase
|
||||
from test.general import gen_steps
|
||||
from .. import StardewValleyWorld
|
||||
from ...AutoWorld import call_all
|
||||
|
||||
|
||||
class SVTestBase(WorldTestBase):
|
||||
|
@ -12,9 +16,41 @@ class SVTestBase(WorldTestBase):
|
|||
def world_setup(self, *args, **kwargs):
|
||||
super().world_setup(*args, **kwargs)
|
||||
if self.constructed:
|
||||
self.world = self.multiworld.worlds[self.player]
|
||||
self.world = self.multiworld.worlds[self.player] # noqa
|
||||
|
||||
@property
|
||||
def run_default_tests(self) -> bool:
|
||||
# world_setup is overridden, so it'd always run default tests when importing SVTestBase
|
||||
return type(self) is not SVTestBase and super().run_default_tests
|
||||
|
||||
|
||||
pre_generated_worlds = {}
|
||||
|
||||
|
||||
# Mostly a copy of test.general.setup_solo_multiworld, I just don't want to change the core.
|
||||
def setup_solo_multiworld(test_options=None,
|
||||
_cache: Dict[FrozenSet[Tuple[str, Any]], MultiWorld] = {}) -> MultiWorld: # noqa
|
||||
if test_options is None:
|
||||
test_options = {}
|
||||
|
||||
# Yes I reuse the worlds generated between tests, its speeds the execution by a couple seconds
|
||||
frozen_options = frozenset(test_options.items())
|
||||
if frozen_options in _cache:
|
||||
return _cache[frozen_options]
|
||||
|
||||
multiworld = MultiWorld(1)
|
||||
multiworld.game[1] = StardewValleyWorld.game
|
||||
multiworld.player_name = {1: "Tester"}
|
||||
multiworld.set_seed()
|
||||
args = Namespace()
|
||||
for name, option in StardewValleyWorld.option_definitions.items():
|
||||
value = option(test_options[name]) if name in test_options else option.from_any(option.default)
|
||||
setattr(args, name, {1: value})
|
||||
multiworld.set_options(args)
|
||||
multiworld.set_default_common_options()
|
||||
for step in gen_steps:
|
||||
call_all(multiworld, step)
|
||||
|
||||
_cache[frozen_options] = multiworld
|
||||
|
||||
return multiworld
|
||||
|
|
Loading…
Reference in New Issue