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:
agilbert1412 2023-04-10 19:44:59 -04:00 committed by GitHub
parent 0c1e3097c3
commit 5eadbc9840
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
38 changed files with 4055 additions and 1759 deletions

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,2 @@
from .crops_data import CropItem, SeedItem, all_crops, all_purchasable_seeds
from .fish_data import FishItem, all_fish

View File

@ -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]

View File

@ -0,0 +1,9 @@
fishing_chest = "Fishing Chest"
secret_note = "Secret Note"
quality_dict = {
0: "",
1: "Silver",
2: "Gold",
3: "Iridium"
}

View File

@ -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"
1 crop farm_growth_seasons seed seed_seasons seed_regions
2 Amaranth Fall Amaranth Seeds Fall Pierre's General Store,JojaMart
3 Artichoke Fall Artichoke Seeds Fall Pierre's General Store,JojaMart
4 Beet Fall Beet Seeds Fall The Desert
5 Blue Jazz Spring Jazz Seeds Spring Pierre's General Store,JojaMart
6 Blueberry Summer Blueberry Seeds Summer Pierre's General Store,JojaMart
7 Bok Choy Fall Bok Choy Seeds Fall Pierre's General Store,JojaMart
8 Cactus Fruit Cactus Seeds The Desert
9 Cauliflower Spring Cauliflower Seeds Spring Pierre's General Store,JojaMart
10 Corn Summer,Fall Corn Seeds Summer,Fall Pierre's General Store,JojaMart
11 Cranberries Fall Cranberry Seeds Fall Pierre's General Store,JojaMart
12 Eggplant Fall Eggplant Seeds Fall Pierre's General Store,JojaMart
13 Fairy Rose Fall Fairy Seeds Fall Pierre's General Store,JojaMart
14 Garlic Spring Garlic Seeds Spring Pierre's General Store,JojaMart
15 Grape Fall Grape Starter Fall Pierre's General Store,JojaMart
16 Green Bean Spring Bean Starter Spring Pierre's General Store,JojaMart
17 Hops Summer Hops Starter Summer Pierre's General Store,JojaMart
18 Hot Pepper Summer Pepper Seeds Summer Pierre's General Store,JojaMart
19 Kale Spring Kale Seeds Spring Pierre's General Store,JojaMart
20 Melon Summer Melon Seeds Summer Pierre's General Store,JojaMart
21 Parsnip Spring Parsnip Seeds Spring Pierre's General Store,JojaMart
22 Poppy Summer Poppy Seeds Summer Pierre's General Store,JojaMart
23 Potato Spring Potato Seeds Spring Pierre's General Store,JojaMart
24 Pumpkin Fall Pumpkin Seeds Fall Pierre's General Store,JojaMart
25 Radish Summer Radish Seeds Summer Pierre's General Store,JojaMart
26 Red Cabbage Summer Red Cabbage Seeds Summer Pierre's General Store,JojaMart
27 Rhubarb Spring Rhubarb Seeds Spring The Desert
28 Starfruit Summer Starfruit Seeds Summer The Desert
29 Strawberry Spring Strawberry Seeds Spring Pierre's General Store,JojaMart
30 Summer Spangle Summer Spangle Seeds Summer Pierre's General Store,JojaMart
31 Sunflower Summer,Fall Sunflower Seeds Summer,Fall Pierre's General Store,JojaMart
32 Sweet Gem Berry Fall Rare Seed Spring,Summer Traveling Cart
33 Tomato Summer Tomato Seeds Summer Pierre's General Store,JojaMart
34 Tulip Spring Tulip Bulb Spring Pierre's General Store,JojaMart
35 Unmilled Rice Spring Rice Shoot Spring Pierre's General Store,JojaMart
36 Wheat Summer,Fall Wheat Seeds Summer,Fall Pierre's General Store,JojaMart
37 Yam Fall Yam Seeds Fall Pierre's General Store,JojaMart

View File

@ -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()

View File

@ -0,0 +1,2 @@
class Entrance:
to_stardew_valley = "To Stardew Valley"

View File

@ -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]

View File

@ -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

View File

@ -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

1 id name classification groups
2 0 Joja Cola filler TRASH
3 15 Rusty Key progression MUSEUM
4 16 Dwarvish Translation Guide progression MUSEUM
5 17 Bridge Repair progression COMMUNITY_REWARD
6 18 Greenhouse progression COMMUNITY_REWARD
7 19 Glittering Boulder Removed progression COMMUNITY_REWARD
98 110 Luck Bonus useful
99 111 Lava Katana progression MINES_FLOOR_110,WEAPON
100 112 Progressive House progression
101 113 Traveling Merchant: Sunday progression TRAVELING_MERCHANT_DAY
102 114 Traveling Merchant: Monday progression TRAVELING_MERCHANT_DAY
103 115 Traveling Merchant: Tuesday progression TRAVELING_MERCHANT_DAY
104 116 Traveling Merchant: Wednesday progression TRAVELING_MERCHANT_DAY
105 117 Traveling Merchant: Thursday progression TRAVELING_MERCHANT_DAY
106 118 Traveling Merchant: Friday progression TRAVELING_MERCHANT_DAY
107 119 Traveling Merchant: Saturday progression TRAVELING_MERCHANT_DAY
108 120 Traveling Merchant Stock Size progression
109 121 Traveling Merchant Discount progression
110 122 Return Scepter useful
111 5000 123 Resource Pack: 500 Money Progressive Season useful progression BASE_RESOURCE,RESOURCE_PACK
112 5001 124 Resource Pack: 1000 Money Spring useful progression BASE_RESOURCE,RESOURCE_PACK SEASON
113 5002 125 Resource Pack: 1500 Money Summer useful progression BASE_RESOURCE,RESOURCE_PACK SEASON
114 5003 126 Resource Pack: 2000 Money Fall useful progression BASE_RESOURCE,RESOURCE_PACK SEASON
115 127 Winter progression SEASON
116 128 Amaranth Seeds progression SEED_SHUFFLE
117 129 Artichoke Seeds progression SEED_SHUFFLE
118 130 Beet Seeds progression SEED_SHUFFLE
119 131 Jazz Seeds progression SEED_SHUFFLE
120 132 Blueberry Seeds progression SEED_SHUFFLE
121 133 Bok Choy Seeds progression SEED_SHUFFLE
122 134 Cauliflower Seeds progression SEED_SHUFFLE
123 135 Corn Seeds progression SEED_SHUFFLE
124 136 Cranberry Seeds progression SEED_SHUFFLE
125 137 Eggplant Seeds progression SEED_SHUFFLE
126 138 Fairy Seeds progression SEED_SHUFFLE
127 139 Garlic Seeds progression SEED_SHUFFLE
128 140 Grape Starter progression SEED_SHUFFLE
129 141 Bean Starter progression SEED_SHUFFLE
130 142 Hops Starter progression SEED_SHUFFLE
131 143 Pepper Seeds progression SEED_SHUFFLE
132 144 Kale Seeds progression SEED_SHUFFLE
133 145 Melon Seeds progression SEED_SHUFFLE
134 146 Parsnip Seeds progression SEED_SHUFFLE
135 147 Poppy Seeds progression SEED_SHUFFLE
136 148 Potato Seeds progression SEED_SHUFFLE
137 149 Pumpkin Seeds progression SEED_SHUFFLE
138 150 Radish Seeds progression SEED_SHUFFLE
139 151 Red Cabbage Seeds progression SEED_SHUFFLE
140 152 Rhubarb Seeds progression SEED_SHUFFLE
141 153 Starfruit Seeds progression SEED_SHUFFLE
142 154 Strawberry Seeds progression SEED_SHUFFLE
143 155 Spangle Seeds progression SEED_SHUFFLE
144 156 Sunflower Seeds progression SEED_SHUFFLE
145 157 Tomato Seeds progression SEED_SHUFFLE
146 158 Tulip Bulb progression SEED_SHUFFLE
147 159 Rice Shoot progression SEED_SHUFFLE
148 160 Wheat Seeds progression SEED_SHUFFLE
149 161 Yam Seeds progression SEED_SHUFFLE
150 162 Cactus Seeds progression SEED_SHUFFLE
151 163 Magic Rock Candy useful MUSEUM
152 164 Ancient Seeds Recipe progression MUSEUM
153 165 Ancient Seeds useful MUSEUM
154 166 Traveling Merchant Metal Detector progression MUSEUM
155 167 Alex: 1 <3 progression FRIENDSANITY
156 168 Elliott: 1 <3 progression FRIENDSANITY
157 169 Harvey: 1 <3 progression FRIENDSANITY
158 170 Sam: 1 <3 progression FRIENDSANITY
159 171 Sebastian: 1 <3 progression FRIENDSANITY
160 172 Shane: 1 <3 progression FRIENDSANITY
161 173 Abigail: 1 <3 progression FRIENDSANITY
162 174 Emily: 1 <3 progression FRIENDSANITY
163 175 Haley: 1 <3 progression FRIENDSANITY
164 176 Leah: 1 <3 progression FRIENDSANITY
165 177 Maru: 1 <3 progression FRIENDSANITY
166 178 Penny: 1 <3 progression FRIENDSANITY
167 179 Caroline: 1 <3 progression FRIENDSANITY
168 180 Clint: 1 <3 progression FRIENDSANITY
169 181 Demetrius: 1 <3 progression FRIENDSANITY
170 182 Dwarf: 1 <3 progression FRIENDSANITY
171 183 Evelyn: 1 <3 progression FRIENDSANITY
172 184 George: 1 <3 progression FRIENDSANITY
173 185 Gus: 1 <3 progression FRIENDSANITY
174 186 Jas: 1 <3 progression FRIENDSANITY
175 187 Jodi: 1 <3 progression FRIENDSANITY
176 188 Kent: 1 <3 progression FRIENDSANITY
177 189 Krobus: 1 <3 progression FRIENDSANITY
178 190 Leo: 1 <3 progression FRIENDSANITY
179 191 Lewis: 1 <3 progression FRIENDSANITY
180 192 Linus: 1 <3 progression FRIENDSANITY
181 193 Marnie: 1 <3 progression FRIENDSANITY
182 194 Pam: 1 <3 progression FRIENDSANITY
183 195 Pierre: 1 <3 progression FRIENDSANITY
184 196 Robin: 1 <3 progression FRIENDSANITY
185 197 Sandy: 1 <3 progression FRIENDSANITY
186 198 Vincent: 1 <3 progression FRIENDSANITY
187 199 Willy: 1 <3 progression FRIENDSANITY
188 200 Wizard: 1 <3 progression FRIENDSANITY
189 201 Pet: 1 <3 progression FRIENDSANITY
190 5000 Resource Pack: 500 Money filler BASE_RESOURCE,RESOURCE_PACK
191 5001 Resource Pack: 1000 Money filler BASE_RESOURCE,RESOURCE_PACK
192 5002 Resource Pack: 1500 Money filler BASE_RESOURCE,RESOURCE_PACK
193 5003 Resource Pack: 2000 Money filler BASE_RESOURCE,RESOURCE_PACK
194 5004 Resource Pack: 25 Stone filler BASE_RESOURCE,RESOURCE_PACK
195 5005 Resource Pack: 50 Stone filler BASE_RESOURCE,RESOURCE_PACK
196 5006 Resource Pack: 75 Stone filler BASE_RESOURCE,RESOURCE_PACK
199 5009 Resource Pack: 50 Wood filler BASE_RESOURCE,RESOURCE_PACK
200 5010 Resource Pack: 75 Wood filler BASE_RESOURCE,RESOURCE_PACK
201 5011 Resource Pack: 100 Wood filler BASE_RESOURCE,RESOURCE_PACK
202 5012 Resource Pack: 5 Hardwood useful filler BASE_RESOURCE,RESOURCE_PACK
203 5013 Resource Pack: 10 Hardwood useful filler BASE_RESOURCE,RESOURCE_PACK
204 5014 Resource Pack: 15 Hardwood useful filler BASE_RESOURCE,RESOURCE_PACK
205 5015 Resource Pack: 20 Hardwood useful filler BASE_RESOURCE,RESOURCE_PACK
206 5016 Resource Pack: 15 Fiber filler BASE_RESOURCE,RESOURCE_PACK
207 5017 Resource Pack: 30 Fiber filler BASE_RESOURCE,RESOURCE_PACK
208 5018 Resource Pack: 45 Fiber filler BASE_RESOURCE,RESOURCE_PACK
257 5067 Resource Pack: 6 Magma Geode filler GEODE,RESOURCE_PACK
258 5068 Resource Pack: 9 Magma Geode filler GEODE,RESOURCE_PACK
259 5069 Resource Pack: 12 Magma Geode filler GEODE,RESOURCE_PACK
260 5070 Resource Pack: 2 Omni Geode useful filler GEODE,RESOURCE_PACK
261 5071 Resource Pack: 4 Omni Geode useful filler GEODE,RESOURCE_PACK
262 5072 Resource Pack: 6 Omni Geode useful filler GEODE,RESOURCE_PACK
263 5073 Resource Pack: 8 Omni Geode useful filler GEODE,RESOURCE_PACK
264 5074 Resource Pack: 25 Copper Ore filler ORE,RESOURCE_PACK
265 5075 Resource Pack: 50 Copper Ore filler ORE,RESOURCE_PACK
266 5076 Resource Pack: 75 Copper Ore filler ORE,RESOURCE_PACK
271 5081 Resource Pack: 50 Iron Ore filler ORE,RESOURCE_PACK
272 5082 Resource Pack: 75 Iron Ore filler ORE,RESOURCE_PACK
273 5083 Resource Pack: 100 Iron Ore filler ORE,RESOURCE_PACK
274 5084 Resource Pack: 12 Gold Ore useful filler ORE,RESOURCE_PACK
275 5085 Resource Pack: 25 Gold Ore useful filler ORE,RESOURCE_PACK
276 5086 Resource Pack: 38 Gold Ore useful filler ORE,RESOURCE_PACK
277 5087 Resource Pack: 50 Gold Ore useful filler ORE,RESOURCE_PACK
278 5088 Resource Pack: 5 Iridium Ore useful filler ORE,RESOURCE_PACK
279 5089 Resource Pack: 10 Iridium Ore useful filler ORE,RESOURCE_PACK
280 5090 Resource Pack: 15 Iridium Ore useful filler ORE,RESOURCE_PACK
281 5091 Resource Pack: 20 Iridium Ore useful filler ORE,RESOURCE_PACK
282 5092 Resource Pack: 5 Quartz filler ORE,RESOURCE_PACK
283 5093 Resource Pack: 10 Quartz filler ORE,RESOURCE_PACK
284 5094 Resource Pack: 15 Quartz filler ORE,RESOURCE_PACK
319 5129 Resource Pack: 28 Deluxe Speed-Gro filler FERTILIZER,RESOURCE_PACK
320 5130 Resource Pack: 36 Deluxe Speed-Gro filler FERTILIZER,RESOURCE_PACK
321 5131 Resource Pack: 40 Deluxe Speed-Gro filler FERTILIZER,RESOURCE_PACK
322 5132 Resource Pack: 2 Deluxe Fertilizer useful filler FERTILIZER,RESOURCE_PACK
323 5133 Resource Pack: 6 Deluxe Fertilizer useful filler FERTILIZER,RESOURCE_PACK
324 5134 Resource Pack: 10 Deluxe Fertilizer useful filler FERTILIZER,RESOURCE_PACK
325 5135 Resource Pack: 14 Deluxe Fertilizer useful filler FERTILIZER,RESOURCE_PACK
326 5136 Resource Pack: 18 Deluxe Fertilizer useful filler FERTILIZER,RESOURCE_PACK
327 5137 Resource Pack: 20 Deluxe Fertilizer useful filler FERTILIZER,RESOURCE_PACK
328 5138 Resource Pack: 2 Deluxe Retaining Soil useful filler FERTILIZER,RESOURCE_PACK
329 5139 Resource Pack: 6 Deluxe Retaining Soil useful filler FERTILIZER,RESOURCE_PACK
330 5140 Resource Pack: 10 Deluxe Retaining Soil useful filler FERTILIZER,RESOURCE_PACK
331 5141 Resource Pack: 14 Deluxe Retaining Soil useful filler FERTILIZER,RESOURCE_PACK
332 5142 Resource Pack: 18 Deluxe Retaining Soil useful filler FERTILIZER,RESOURCE_PACK
333 5143 Resource Pack: 20 Deluxe Retaining Soil useful filler FERTILIZER,RESOURCE_PACK
334 5144 Resource Pack: 2 Hyper Speed-Gro useful filler FERTILIZER,RESOURCE_PACK
335 5145 Resource Pack: 6 Hyper Speed-Gro useful filler FERTILIZER,RESOURCE_PACK
336 5146 Resource Pack: 10 Hyper Speed-Gro useful filler FERTILIZER,RESOURCE_PACK
337 5147 Resource Pack: 14 Hyper Speed-Gro useful filler FERTILIZER,RESOURCE_PACK
338 5148 Resource Pack: 18 Hyper Speed-Gro useful filler FERTILIZER,RESOURCE_PACK
339 5149 Resource Pack: 20 Hyper Speed-Gro useful filler FERTILIZER,RESOURCE_PACK
340 5150 Resource Pack: 2 Tree Fertilizer filler FERTILIZER,RESOURCE_PACK
341 5151 Resource Pack: 6 Tree Fertilizer filler FERTILIZER,RESOURCE_PACK
342 5152 Resource Pack: 10 Tree Fertilizer filler FERTILIZER,RESOURCE_PACK
385 5195 Resource Pack: 4 Crab Pot filler FISHING_RESOURCE,RESOURCE_PACK
386 5196 Resource Pack: 5 Crab Pot filler FISHING_RESOURCE,RESOURCE_PACK
387 5197 Resource Pack: 6 Crab Pot filler FISHING_RESOURCE,RESOURCE_PACK
388 5198 Friendship Bonus (1 <3) useful filler FRIENDSHIP_PACK
389 5199 Friendship Bonus (2 <3) useful filler FRIENDSHIP_PACK
390 5200 Friendship Bonus (3 <3) useful filler FRIENDSHIP_PACK
391 5201 Friendship Bonus (4 <3) useful filler FRIENDSHIP_PACK

View File

@ -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

1 id region name tags
377 1063 Beach Fishsanity: Mussel FISHSANITY
378 1064 Beach Fishsanity: Shrimp FISHSANITY
379 1065 Beach Fishsanity: Oyster FISHSANITY
380 1100 Stardew Valley Museumsanity: 5 Donations MUSEUM_MILESTONES
381 1101 Stardew Valley Museumsanity: 10 Donations MUSEUM_MILESTONES
382 1102 Stardew Valley Museumsanity: 15 Donations MUSEUM_MILESTONES
383 1103 Stardew Valley Museumsanity: 20 Donations MUSEUM_MILESTONES
384 1104 Stardew Valley Museumsanity: 25 Donations MUSEUM_MILESTONES
385 1105 Stardew Valley Museumsanity: 30 Donations MUSEUM_MILESTONES
386 1106 Stardew Valley Museumsanity: 35 Donations MUSEUM_MILESTONES
387 1107 Stardew Valley Museumsanity: 40 Donations MUSEUM_MILESTONES
388 1108 Stardew Valley Museumsanity: 50 Donations MUSEUM_MILESTONES
389 1109 Stardew Valley Museumsanity: 60 Donations MUSEUM_MILESTONES
390 1110 Stardew Valley Museumsanity: 70 Donations MUSEUM_MILESTONES
391 1111 Stardew Valley Museumsanity: 80 Donations MUSEUM_MILESTONES
392 1112 Stardew Valley Museumsanity: 90 Donations MUSEUM_MILESTONES
393 1113 Stardew Valley Museumsanity: 95 Donations MUSEUM_MILESTONES
394 1114 Stardew Valley Museumsanity: 11 Minerals MUSEUM_MILESTONES
395 1115 Stardew Valley Museumsanity: 21 Minerals MUSEUM_MILESTONES
396 1116 Stardew Valley Museumsanity: 31 Minerals MUSEUM_MILESTONES
397 1117 Stardew Valley Museumsanity: 41 Minerals MUSEUM_MILESTONES
398 1118 Stardew Valley Museumsanity: 50 Minerals MUSEUM_MILESTONES
399 1119 Stardew Valley Museumsanity: 3 Artifacts MUSEUM_MILESTONES
400 1120 Stardew Valley Museumsanity: 6 Artifacts MUSEUM_MILESTONES
401 1121 Stardew Valley Museumsanity: 9 Artifacts MUSEUM_MILESTONES
402 1122 Stardew Valley Museumsanity: 11 Artifacts MUSEUM_MILESTONES
403 1123 Stardew Valley Museumsanity: 15 Artifacts MUSEUM_MILESTONES
404 1124 Stardew Valley Museumsanity: 20 Artifacts MUSEUM_MILESTONES
405 1125 Stardew Valley Museumsanity: Dwarf Scrolls MUSEUM_MILESTONES
406 1126 Stardew Valley Museumsanity: Skeleton Front MUSEUM_MILESTONES
407 1127 Stardew Valley Museumsanity: Skeleton Middle MUSEUM_MILESTONES
408 1128 Stardew Valley Museumsanity: Skeleton Back MUSEUM_MILESTONES
409 1201 The Mines - Floor 20 Museumsanity: Dwarf Scroll I MUSEUM_DONATIONS
410 1202 The Mines - Floor 20 Museumsanity: Dwarf Scroll II MUSEUM_DONATIONS
411 1203 The Mines - Floor 60 Museumsanity: Dwarf Scroll III MUSEUM_DONATIONS
412 1204 The Mines - Floor 100 Museumsanity: Dwarf Scroll IV MUSEUM_DONATIONS
413 1205 Town Museumsanity: Chipped Amphora MUSEUM_DONATIONS
414 1206 Forest Museumsanity: Arrowhead MUSEUM_DONATIONS
415 1207 Forest Museumsanity: Ancient Doll MUSEUM_DONATIONS
416 1208 Forest Museumsanity: Elvish Jewelry MUSEUM_DONATIONS
417 1209 Forest Museumsanity: Chewing Stick MUSEUM_DONATIONS
418 1210 Forest Museumsanity: Ornamental Fan MUSEUM_DONATIONS
419 1211 Mountain Museumsanity: Dinosaur Egg MUSEUM_DONATIONS
420 1212 Stardew Valley Museumsanity: Rare Disc MUSEUM_DONATIONS
421 1213 Forest Museumsanity: Ancient Sword MUSEUM_DONATIONS
422 1214 Town Museumsanity: Rusty Spoon MUSEUM_DONATIONS
423 1215 Farm Museumsanity: Rusty Spur MUSEUM_DONATIONS
424 1216 Mountain Museumsanity: Rusty Cog MUSEUM_DONATIONS
425 1217 Farm Museumsanity: Chicken Statue MUSEUM_DONATIONS
426 1218 Forest Museumsanity: Ancient Seed MUSEUM_DONATIONS,MUSEUM_MILESTONES
427 1219 Forest Museumsanity: Prehistoric Tool MUSEUM_DONATIONS
428 1220 Beach Museumsanity: Dried Starfish MUSEUM_DONATIONS
429 1221 Beach Museumsanity: Anchor MUSEUM_DONATIONS
430 1222 Beach Museumsanity: Glass Shards MUSEUM_DONATIONS
431 1223 Forest Museumsanity: Bone Flute MUSEUM_DONATIONS
432 1224 Forest Museumsanity: Prehistoric Handaxe MUSEUM_DONATIONS
433 1225 The Mines - Floor 20 Museumsanity: Dwarvish Helm MUSEUM_DONATIONS
434 1226 The Mines - Floor 60 Museumsanity: Dwarf Gadget MUSEUM_DONATIONS
435 1227 Forest Museumsanity: Ancient Drum MUSEUM_DONATIONS
436 1228 The Desert Museumsanity: Golden Mask MUSEUM_DONATIONS
437 1229 The Desert Museumsanity: Golden Relic MUSEUM_DONATIONS
438 1230 Town Museumsanity: Strange Doll (Green) MUSEUM_DONATIONS
439 1231 The Desert Museumsanity: Strange Doll MUSEUM_DONATIONS
440 1232 Forest Museumsanity: Prehistoric Scapula MUSEUM_DONATIONS
441 1233 Forest Museumsanity: Prehistoric Tibia MUSEUM_DONATIONS
442 1234 Ginger Island Museumsanity: Prehistoric Skull MUSEUM_DONATIONS
443 1235 Ginger Island Museumsanity: Skeletal Hand MUSEUM_DONATIONS
444 1236 Ginger Island Museumsanity: Prehistoric Rib MUSEUM_DONATIONS
445 1237 Ginger Island Museumsanity: Prehistoric Vertebra MUSEUM_DONATIONS
446 1238 Ginger Island Museumsanity: Skeletal Tail MUSEUM_DONATIONS
447 1239 Ginger Island Museumsanity: Nautilus Fossil MUSEUM_DONATIONS
448 1240 Forest Museumsanity: Amphibian Fossil MUSEUM_DONATIONS
449 1241 Forest Museumsanity: Palm Fossil MUSEUM_DONATIONS
450 1242 Forest Museumsanity: Trilobite MUSEUM_DONATIONS
451 1243 The Mines - Floor 20 Museumsanity: Quartz MUSEUM_DONATIONS
452 1244 The Mines - Floor 100 Museumsanity: Fire Quartz MUSEUM_DONATIONS
453 1245 The Mines - Floor 60 Museumsanity: Frozen Tear MUSEUM_DONATIONS
454 1246 The Mines - Floor 20 Museumsanity: Earth Crystal MUSEUM_DONATIONS
455 1247 The Mines - Floor 100 Museumsanity: Emerald MUSEUM_DONATIONS
456 1248 The Mines - Floor 60 Museumsanity: Aquamarine MUSEUM_DONATIONS
457 1249 The Mines - Floor 100 Museumsanity: Ruby MUSEUM_DONATIONS
458 1250 The Mines - Floor 20 Museumsanity: Amethyst MUSEUM_DONATIONS
459 1251 The Mines - Floor 20 Museumsanity: Topaz MUSEUM_DONATIONS
460 1252 The Mines - Floor 60 Museumsanity: Jade MUSEUM_DONATIONS
461 1253 The Mines - Floor 60 Museumsanity: Diamond MUSEUM_DONATIONS
462 1254 Skull Cavern Floor 100 Museumsanity: Prismatic Shard MUSEUM_DONATIONS
463 1255 Town Museumsanity: Alamite MUSEUM_DONATIONS
464 1256 Town Museumsanity: Bixite MUSEUM_DONATIONS
465 1257 Town Museumsanity: Baryte MUSEUM_DONATIONS
466 1258 Town Museumsanity: Aerinite MUSEUM_DONATIONS
467 1259 Town Museumsanity: Calcite MUSEUM_DONATIONS
468 1260 Town Museumsanity: Dolomite MUSEUM_DONATIONS
469 1261 Town Museumsanity: Esperite MUSEUM_DONATIONS
470 1262 Town Museumsanity: Fluorapatite MUSEUM_DONATIONS
471 1263 Town Museumsanity: Geminite MUSEUM_DONATIONS
472 1264 Town Museumsanity: Helvite MUSEUM_DONATIONS
473 1265 Town Museumsanity: Jamborite MUSEUM_DONATIONS
474 1266 Town Museumsanity: Jagoite MUSEUM_DONATIONS
475 1267 Town Museumsanity: Kyanite MUSEUM_DONATIONS
476 1268 Town Museumsanity: Lunarite MUSEUM_DONATIONS
477 1269 Town Museumsanity: Malachite MUSEUM_DONATIONS
478 1270 Town Museumsanity: Neptunite MUSEUM_DONATIONS
479 1271 Town Museumsanity: Lemon Stone MUSEUM_DONATIONS
480 1272 Town Museumsanity: Nekoite MUSEUM_DONATIONS
481 1273 Town Museumsanity: Orpiment MUSEUM_DONATIONS
482 1274 Town Museumsanity: Petrified Slime MUSEUM_DONATIONS
483 1275 Town Museumsanity: Thunder Egg MUSEUM_DONATIONS
484 1276 Town Museumsanity: Pyrite MUSEUM_DONATIONS
485 1277 Town Museumsanity: Ocean Stone MUSEUM_DONATIONS
486 1278 Town Museumsanity: Ghost Crystal MUSEUM_DONATIONS
487 1279 Town Museumsanity: Tigerseye MUSEUM_DONATIONS
488 1280 Town Museumsanity: Jasper MUSEUM_DONATIONS
489 1281 Town Museumsanity: Opal MUSEUM_DONATIONS
490 1282 Town Museumsanity: Fire Opal MUSEUM_DONATIONS
491 1283 Town Museumsanity: Celestine MUSEUM_DONATIONS
492 1284 Town Museumsanity: Marble MUSEUM_DONATIONS
493 1285 Town Museumsanity: Sandstone MUSEUM_DONATIONS
494 1286 Town Museumsanity: Granite MUSEUM_DONATIONS
495 1287 Town Museumsanity: Basalt MUSEUM_DONATIONS
496 1288 Town Museumsanity: Limestone MUSEUM_DONATIONS
497 1289 Town Museumsanity: Soapstone MUSEUM_DONATIONS
498 1290 Town Museumsanity: Hematite MUSEUM_DONATIONS
499 1291 Town Museumsanity: Mudstone MUSEUM_DONATIONS
500 1292 Town Museumsanity: Obsidian MUSEUM_DONATIONS
501 1293 Town Museumsanity: Slate MUSEUM_DONATIONS
502 1294 Town Museumsanity: Fairy Stone MUSEUM_DONATIONS
503 1295 Town Museumsanity: Star Shards MUSEUM_DONATIONS
504 1301 Town Friendsanity: Alex 1 <3 FRIENDSANITY
505 1302 Town Friendsanity: Alex 2 <3 FRIENDSANITY
506 1303 Town Friendsanity: Alex 3 <3 FRIENDSANITY
507 1304 Town Friendsanity: Alex 4 <3 FRIENDSANITY
508 1305 Town Friendsanity: Alex 5 <3 FRIENDSANITY
509 1306 Town Friendsanity: Alex 6 <3 FRIENDSANITY
510 1307 Town Friendsanity: Alex 7 <3 FRIENDSANITY
511 1308 Town Friendsanity: Alex 8 <3 FRIENDSANITY
512 1309 Town Friendsanity: Alex 9 <3 FRIENDSANITY
513 1310 Town Friendsanity: Alex 10 <3 FRIENDSANITY
514 1311 Town Friendsanity: Alex 11 <3 FRIENDSANITY
515 1312 Town Friendsanity: Alex 12 <3 FRIENDSANITY
516 1313 Town Friendsanity: Alex 13 <3 FRIENDSANITY
517 1314 Town Friendsanity: Alex 14 <3 FRIENDSANITY
518 1315 Beach Friendsanity: Elliott 1 <3 FRIENDSANITY
519 1316 Beach Friendsanity: Elliott 2 <3 FRIENDSANITY
520 1317 Beach Friendsanity: Elliott 3 <3 FRIENDSANITY
521 1318 Beach Friendsanity: Elliott 4 <3 FRIENDSANITY
522 1319 Beach Friendsanity: Elliott 5 <3 FRIENDSANITY
523 1320 Beach Friendsanity: Elliott 6 <3 FRIENDSANITY
524 1321 Beach Friendsanity: Elliott 7 <3 FRIENDSANITY
525 1322 Beach Friendsanity: Elliott 8 <3 FRIENDSANITY
526 1323 Beach Friendsanity: Elliott 9 <3 FRIENDSANITY
527 1324 Beach Friendsanity: Elliott 10 <3 FRIENDSANITY
528 1325 Beach Friendsanity: Elliott 11 <3 FRIENDSANITY
529 1326 Beach Friendsanity: Elliott 12 <3 FRIENDSANITY
530 1327 Beach Friendsanity: Elliott 13 <3 FRIENDSANITY
531 1328 Beach Friendsanity: Elliott 14 <3 FRIENDSANITY
532 1329 Town Friendsanity: Harvey 1 <3 FRIENDSANITY
533 1330 Town Friendsanity: Harvey 2 <3 FRIENDSANITY
534 1331 Town Friendsanity: Harvey 3 <3 FRIENDSANITY
535 1332 Town Friendsanity: Harvey 4 <3 FRIENDSANITY
536 1333 Town Friendsanity: Harvey 5 <3 FRIENDSANITY
537 1334 Town Friendsanity: Harvey 6 <3 FRIENDSANITY
538 1335 Town Friendsanity: Harvey 7 <3 FRIENDSANITY
539 1336 Town Friendsanity: Harvey 8 <3 FRIENDSANITY
540 1337 Town Friendsanity: Harvey 9 <3 FRIENDSANITY
541 1338 Town Friendsanity: Harvey 10 <3 FRIENDSANITY
542 1339 Town Friendsanity: Harvey 11 <3 FRIENDSANITY
543 1340 Town Friendsanity: Harvey 12 <3 FRIENDSANITY
544 1341 Town Friendsanity: Harvey 13 <3 FRIENDSANITY
545 1342 Town Friendsanity: Harvey 14 <3 FRIENDSANITY
546 1343 Town Friendsanity: Sam 1 <3 FRIENDSANITY
547 1344 Town Friendsanity: Sam 2 <3 FRIENDSANITY
548 1345 Town Friendsanity: Sam 3 <3 FRIENDSANITY
549 1346 Town Friendsanity: Sam 4 <3 FRIENDSANITY
550 1347 Town Friendsanity: Sam 5 <3 FRIENDSANITY
551 1348 Town Friendsanity: Sam 6 <3 FRIENDSANITY
552 1349 Town Friendsanity: Sam 7 <3 FRIENDSANITY
553 1350 Town Friendsanity: Sam 8 <3 FRIENDSANITY
554 1351 Town Friendsanity: Sam 9 <3 FRIENDSANITY
555 1352 Town Friendsanity: Sam 10 <3 FRIENDSANITY
556 1353 Town Friendsanity: Sam 11 <3 FRIENDSANITY
557 1354 Town Friendsanity: Sam 12 <3 FRIENDSANITY
558 1355 Town Friendsanity: Sam 13 <3 FRIENDSANITY
559 1356 Town Friendsanity: Sam 14 <3 FRIENDSANITY
560 1357 Carpenter Shop Friendsanity: Sebastian 1 <3 FRIENDSANITY
561 1358 Carpenter Shop Friendsanity: Sebastian 2 <3 FRIENDSANITY
562 1359 Carpenter Shop Friendsanity: Sebastian 3 <3 FRIENDSANITY
563 1360 Carpenter Shop Friendsanity: Sebastian 4 <3 FRIENDSANITY
564 1361 Carpenter Shop Friendsanity: Sebastian 5 <3 FRIENDSANITY
565 1362 Carpenter Shop Friendsanity: Sebastian 6 <3 FRIENDSANITY
566 1363 Carpenter Shop Friendsanity: Sebastian 7 <3 FRIENDSANITY
567 1364 Carpenter Shop Friendsanity: Sebastian 8 <3 FRIENDSANITY
568 1365 Carpenter Shop Friendsanity: Sebastian 9 <3 FRIENDSANITY
569 1366 Carpenter Shop Friendsanity: Sebastian 10 <3 FRIENDSANITY
570 1367 Carpenter Shop Friendsanity: Sebastian 11 <3 FRIENDSANITY
571 1368 Carpenter Shop Friendsanity: Sebastian 12 <3 FRIENDSANITY
572 1369 Carpenter Shop Friendsanity: Sebastian 13 <3 FRIENDSANITY
573 1370 Carpenter Shop Friendsanity: Sebastian 14 <3 FRIENDSANITY
574 1371 Marnie's Ranch Friendsanity: Shane 1 <3 FRIENDSANITY
575 1372 Marnie's Ranch Friendsanity: Shane 2 <3 FRIENDSANITY
576 1373 Marnie's Ranch Friendsanity: Shane 3 <3 FRIENDSANITY
577 1374 Marnie's Ranch Friendsanity: Shane 4 <3 FRIENDSANITY
578 1375 Marnie's Ranch Friendsanity: Shane 5 <3 FRIENDSANITY
579 1376 Marnie's Ranch Friendsanity: Shane 6 <3 FRIENDSANITY
580 1377 Marnie's Ranch Friendsanity: Shane 7 <3 FRIENDSANITY
581 1378 Marnie's Ranch Friendsanity: Shane 8 <3 FRIENDSANITY
582 1379 Marnie's Ranch Friendsanity: Shane 9 <3 FRIENDSANITY
583 1380 Marnie's Ranch Friendsanity: Shane 10 <3 FRIENDSANITY
584 1381 Marnie's Ranch Friendsanity: Shane 11 <3 FRIENDSANITY
585 1382 Marnie's Ranch Friendsanity: Shane 12 <3 FRIENDSANITY
586 1383 Marnie's Ranch Friendsanity: Shane 13 <3 FRIENDSANITY
587 1384 Marnie's Ranch Friendsanity: Shane 14 <3 FRIENDSANITY
588 1385 Town Friendsanity: Abigail 1 <3 FRIENDSANITY
589 1386 Town Friendsanity: Abigail 2 <3 FRIENDSANITY
590 1387 Town Friendsanity: Abigail 3 <3 FRIENDSANITY
591 1388 Town Friendsanity: Abigail 4 <3 FRIENDSANITY
592 1389 Town Friendsanity: Abigail 5 <3 FRIENDSANITY
593 1390 Town Friendsanity: Abigail 6 <3 FRIENDSANITY
594 1391 Town Friendsanity: Abigail 7 <3 FRIENDSANITY
595 1392 Town Friendsanity: Abigail 8 <3 FRIENDSANITY
596 1393 Town Friendsanity: Abigail 9 <3 FRIENDSANITY
597 1394 Town Friendsanity: Abigail 10 <3 FRIENDSANITY
598 1395 Town Friendsanity: Abigail 11 <3 FRIENDSANITY
599 1396 Town Friendsanity: Abigail 12 <3 FRIENDSANITY
600 1397 Town Friendsanity: Abigail 13 <3 FRIENDSANITY
601 1398 Town Friendsanity: Abigail 14 <3 FRIENDSANITY
602 1399 Town Friendsanity: Emily 1 <3 FRIENDSANITY
603 1400 Town Friendsanity: Emily 2 <3 FRIENDSANITY
604 1401 Town Friendsanity: Emily 3 <3 FRIENDSANITY
605 1402 Town Friendsanity: Emily 4 <3 FRIENDSANITY
606 1403 Town Friendsanity: Emily 5 <3 FRIENDSANITY
607 1404 Town Friendsanity: Emily 6 <3 FRIENDSANITY
608 1405 Town Friendsanity: Emily 7 <3 FRIENDSANITY
609 1406 Town Friendsanity: Emily 8 <3 FRIENDSANITY
610 1407 Town Friendsanity: Emily 9 <3 FRIENDSANITY
611 1408 Town Friendsanity: Emily 10 <3 FRIENDSANITY
612 1409 Town Friendsanity: Emily 11 <3 FRIENDSANITY
613 1410 Town Friendsanity: Emily 12 <3 FRIENDSANITY
614 1411 Town Friendsanity: Emily 13 <3 FRIENDSANITY
615 1412 Town Friendsanity: Emily 14 <3 FRIENDSANITY
616 1413 Town Friendsanity: Haley 1 <3 FRIENDSANITY
617 1414 Town Friendsanity: Haley 2 <3 FRIENDSANITY
618 1415 Town Friendsanity: Haley 3 <3 FRIENDSANITY
619 1416 Town Friendsanity: Haley 4 <3 FRIENDSANITY
620 1417 Town Friendsanity: Haley 5 <3 FRIENDSANITY
621 1418 Town Friendsanity: Haley 6 <3 FRIENDSANITY
622 1419 Town Friendsanity: Haley 7 <3 FRIENDSANITY
623 1420 Town Friendsanity: Haley 8 <3 FRIENDSANITY
624 1421 Town Friendsanity: Haley 9 <3 FRIENDSANITY
625 1422 Town Friendsanity: Haley 10 <3 FRIENDSANITY
626 1423 Town Friendsanity: Haley 11 <3 FRIENDSANITY
627 1424 Town Friendsanity: Haley 12 <3 FRIENDSANITY
628 1425 Town Friendsanity: Haley 13 <3 FRIENDSANITY
629 1426 Town Friendsanity: Haley 14 <3 FRIENDSANITY
630 1427 Forest Friendsanity: Leah 1 <3 FRIENDSANITY
631 1428 Forest Friendsanity: Leah 2 <3 FRIENDSANITY
632 1429 Forest Friendsanity: Leah 3 <3 FRIENDSANITY
633 1430 Forest Friendsanity: Leah 4 <3 FRIENDSANITY
634 1431 Forest Friendsanity: Leah 5 <3 FRIENDSANITY
635 1432 Forest Friendsanity: Leah 6 <3 FRIENDSANITY
636 1433 Forest Friendsanity: Leah 7 <3 FRIENDSANITY
637 1434 Forest Friendsanity: Leah 8 <3 FRIENDSANITY
638 1435 Forest Friendsanity: Leah 9 <3 FRIENDSANITY
639 1436 Forest Friendsanity: Leah 10 <3 FRIENDSANITY
640 1437 Forest Friendsanity: Leah 11 <3 FRIENDSANITY
641 1438 Forest Friendsanity: Leah 12 <3 FRIENDSANITY
642 1439 Forest Friendsanity: Leah 13 <3 FRIENDSANITY
643 1440 Forest Friendsanity: Leah 14 <3 FRIENDSANITY
644 1441 Carpenter Shop Friendsanity: Maru 1 <3 FRIENDSANITY
645 1442 Carpenter Shop Friendsanity: Maru 2 <3 FRIENDSANITY
646 1443 Carpenter Shop Friendsanity: Maru 3 <3 FRIENDSANITY
647 1444 Carpenter Shop Friendsanity: Maru 4 <3 FRIENDSANITY
648 1445 Carpenter Shop Friendsanity: Maru 5 <3 FRIENDSANITY
649 1446 Carpenter Shop Friendsanity: Maru 6 <3 FRIENDSANITY
650 1447 Carpenter Shop Friendsanity: Maru 7 <3 FRIENDSANITY
651 1448 Carpenter Shop Friendsanity: Maru 8 <3 FRIENDSANITY
652 1449 Carpenter Shop Friendsanity: Maru 9 <3 FRIENDSANITY
653 1450 Carpenter Shop Friendsanity: Maru 10 <3 FRIENDSANITY
654 1451 Carpenter Shop Friendsanity: Maru 11 <3 FRIENDSANITY
655 1452 Carpenter Shop Friendsanity: Maru 12 <3 FRIENDSANITY
656 1453 Carpenter Shop Friendsanity: Maru 13 <3 FRIENDSANITY
657 1454 Carpenter Shop Friendsanity: Maru 14 <3 FRIENDSANITY
658 1455 Town Friendsanity: Penny 1 <3 FRIENDSANITY
659 1456 Town Friendsanity: Penny 2 <3 FRIENDSANITY
660 1457 Town Friendsanity: Penny 3 <3 FRIENDSANITY
661 1458 Town Friendsanity: Penny 4 <3 FRIENDSANITY
662 1459 Town Friendsanity: Penny 5 <3 FRIENDSANITY
663 1460 Town Friendsanity: Penny 6 <3 FRIENDSANITY
664 1461 Town Friendsanity: Penny 7 <3 FRIENDSANITY
665 1462 Town Friendsanity: Penny 8 <3 FRIENDSANITY
666 1463 Town Friendsanity: Penny 9 <3 FRIENDSANITY
667 1464 Town Friendsanity: Penny 10 <3 FRIENDSANITY
668 1465 Town Friendsanity: Penny 11 <3 FRIENDSANITY
669 1466 Town Friendsanity: Penny 12 <3 FRIENDSANITY
670 1467 Town Friendsanity: Penny 13 <3 FRIENDSANITY
671 1468 Town Friendsanity: Penny 14 <3 FRIENDSANITY
672 1469 Town Friendsanity: Caroline 1 <3 FRIENDSANITY
673 1470 Town Friendsanity: Caroline 2 <3 FRIENDSANITY
674 1471 Town Friendsanity: Caroline 3 <3 FRIENDSANITY
675 1472 Town Friendsanity: Caroline 4 <3 FRIENDSANITY
676 1473 Town Friendsanity: Caroline 5 <3 FRIENDSANITY
677 1474 Town Friendsanity: Caroline 6 <3 FRIENDSANITY
678 1475 Town Friendsanity: Caroline 7 <3 FRIENDSANITY
679 1476 Town Friendsanity: Caroline 8 <3 FRIENDSANITY
680 1477 Town Friendsanity: Caroline 9 <3 FRIENDSANITY
681 1478 Town Friendsanity: Caroline 10 <3 FRIENDSANITY
682 1480 Town Friendsanity: Clint 1 <3 FRIENDSANITY
683 1481 Town Friendsanity: Clint 2 <3 FRIENDSANITY
684 1482 Town Friendsanity: Clint 3 <3 FRIENDSANITY
685 1483 Town Friendsanity: Clint 4 <3 FRIENDSANITY
686 1484 Town Friendsanity: Clint 5 <3 FRIENDSANITY
687 1485 Town Friendsanity: Clint 6 <3 FRIENDSANITY
688 1486 Town Friendsanity: Clint 7 <3 FRIENDSANITY
689 1487 Town Friendsanity: Clint 8 <3 FRIENDSANITY
690 1488 Town Friendsanity: Clint 9 <3 FRIENDSANITY
691 1489 Town Friendsanity: Clint 10 <3 FRIENDSANITY
692 1491 Carpenter Shop Friendsanity: Demetrius 1 <3 FRIENDSANITY
693 1492 Carpenter Shop Friendsanity: Demetrius 2 <3 FRIENDSANITY
694 1493 Carpenter Shop Friendsanity: Demetrius 3 <3 FRIENDSANITY
695 1494 Carpenter Shop Friendsanity: Demetrius 4 <3 FRIENDSANITY
696 1495 Carpenter Shop Friendsanity: Demetrius 5 <3 FRIENDSANITY
697 1496 Carpenter Shop Friendsanity: Demetrius 6 <3 FRIENDSANITY
698 1497 Carpenter Shop Friendsanity: Demetrius 7 <3 FRIENDSANITY
699 1498 Carpenter Shop Friendsanity: Demetrius 8 <3 FRIENDSANITY
700 1499 Carpenter Shop Friendsanity: Demetrius 9 <3 FRIENDSANITY
701 1500 Carpenter Shop Friendsanity: Demetrius 10 <3 FRIENDSANITY
702 1502 The Mines Friendsanity: Dwarf 1 <3 FRIENDSANITY
703 1503 The Mines Friendsanity: Dwarf 2 <3 FRIENDSANITY
704 1504 The Mines Friendsanity: Dwarf 3 <3 FRIENDSANITY
705 1505 The Mines Friendsanity: Dwarf 4 <3 FRIENDSANITY
706 1506 The Mines Friendsanity: Dwarf 5 <3 FRIENDSANITY
707 1507 The Mines Friendsanity: Dwarf 6 <3 FRIENDSANITY
708 1508 The Mines Friendsanity: Dwarf 7 <3 FRIENDSANITY
709 1509 The Mines Friendsanity: Dwarf 8 <3 FRIENDSANITY
710 1510 The Mines Friendsanity: Dwarf 9 <3 FRIENDSANITY
711 1511 The Mines Friendsanity: Dwarf 10 <3 FRIENDSANITY
712 1513 Town Friendsanity: Evelyn 1 <3 FRIENDSANITY
713 1514 Town Friendsanity: Evelyn 2 <3 FRIENDSANITY
714 1515 Town Friendsanity: Evelyn 3 <3 FRIENDSANITY
715 1516 Town Friendsanity: Evelyn 4 <3 FRIENDSANITY
716 1517 Town Friendsanity: Evelyn 5 <3 FRIENDSANITY
717 1518 Town Friendsanity: Evelyn 6 <3 FRIENDSANITY
718 1519 Town Friendsanity: Evelyn 7 <3 FRIENDSANITY
719 1520 Town Friendsanity: Evelyn 8 <3 FRIENDSANITY
720 1521 Town Friendsanity: Evelyn 9 <3 FRIENDSANITY
721 1522 Town Friendsanity: Evelyn 10 <3 FRIENDSANITY
722 1524 Town Friendsanity: George 1 <3 FRIENDSANITY
723 1525 Town Friendsanity: George 2 <3 FRIENDSANITY
724 1526 Town Friendsanity: George 3 <3 FRIENDSANITY
725 1527 Town Friendsanity: George 4 <3 FRIENDSANITY
726 1528 Town Friendsanity: George 5 <3 FRIENDSANITY
727 1529 Town Friendsanity: George 6 <3 FRIENDSANITY
728 1530 Town Friendsanity: George 7 <3 FRIENDSANITY
729 1531 Town Friendsanity: George 8 <3 FRIENDSANITY
730 1532 Town Friendsanity: George 9 <3 FRIENDSANITY
731 1533 Town Friendsanity: George 10 <3 FRIENDSANITY
732 1535 Town Friendsanity: Gus 1 <3 FRIENDSANITY
733 1536 Town Friendsanity: Gus 2 <3 FRIENDSANITY
734 1537 Town Friendsanity: Gus 3 <3 FRIENDSANITY
735 1538 Town Friendsanity: Gus 4 <3 FRIENDSANITY
736 1539 Town Friendsanity: Gus 5 <3 FRIENDSANITY
737 1540 Town Friendsanity: Gus 6 <3 FRIENDSANITY
738 1541 Town Friendsanity: Gus 7 <3 FRIENDSANITY
739 1542 Town Friendsanity: Gus 8 <3 FRIENDSANITY
740 1543 Town Friendsanity: Gus 9 <3 FRIENDSANITY
741 1544 Town Friendsanity: Gus 10 <3 FRIENDSANITY
742 1546 Marnie's Ranch Friendsanity: Jas 1 <3 FRIENDSANITY
743 1547 Marnie's Ranch Friendsanity: Jas 2 <3 FRIENDSANITY
744 1548 Marnie's Ranch Friendsanity: Jas 3 <3 FRIENDSANITY
745 1549 Marnie's Ranch Friendsanity: Jas 4 <3 FRIENDSANITY
746 1550 Marnie's Ranch Friendsanity: Jas 5 <3 FRIENDSANITY
747 1551 Marnie's Ranch Friendsanity: Jas 6 <3 FRIENDSANITY
748 1552 Marnie's Ranch Friendsanity: Jas 7 <3 FRIENDSANITY
749 1553 Marnie's Ranch Friendsanity: Jas 8 <3 FRIENDSANITY
750 1554 Marnie's Ranch Friendsanity: Jas 9 <3 FRIENDSANITY
751 1555 Marnie's Ranch Friendsanity: Jas 10 <3 FRIENDSANITY
752 1557 Town Friendsanity: Jodi 1 <3 FRIENDSANITY
753 1558 Town Friendsanity: Jodi 2 <3 FRIENDSANITY
754 1559 Town Friendsanity: Jodi 3 <3 FRIENDSANITY
755 1560 Town Friendsanity: Jodi 4 <3 FRIENDSANITY
756 1561 Town Friendsanity: Jodi 5 <3 FRIENDSANITY
757 1562 Town Friendsanity: Jodi 6 <3 FRIENDSANITY
758 1563 Town Friendsanity: Jodi 7 <3 FRIENDSANITY
759 1564 Town Friendsanity: Jodi 8 <3 FRIENDSANITY
760 1565 Town Friendsanity: Jodi 9 <3 FRIENDSANITY
761 1566 Town Friendsanity: Jodi 10 <3 FRIENDSANITY
762 1568 Town Friendsanity: Kent 1 <3 FRIENDSANITY
763 1569 Town Friendsanity: Kent 2 <3 FRIENDSANITY
764 1570 Town Friendsanity: Kent 3 <3 FRIENDSANITY
765 1571 Town Friendsanity: Kent 4 <3 FRIENDSANITY
766 1572 Town Friendsanity: Kent 5 <3 FRIENDSANITY
767 1573 Town Friendsanity: Kent 6 <3 FRIENDSANITY
768 1574 Town Friendsanity: Kent 7 <3 FRIENDSANITY
769 1575 Town Friendsanity: Kent 8 <3 FRIENDSANITY
770 1576 Town Friendsanity: Kent 9 <3 FRIENDSANITY
771 1577 Town Friendsanity: Kent 10 <3 FRIENDSANITY
772 1579 Sewers Friendsanity: Krobus 1 <3 FRIENDSANITY
773 1580 Sewers Friendsanity: Krobus 2 <3 FRIENDSANITY
774 1581 Sewers Friendsanity: Krobus 3 <3 FRIENDSANITY
775 1582 Sewers Friendsanity: Krobus 4 <3 FRIENDSANITY
776 1583 Sewers Friendsanity: Krobus 5 <3 FRIENDSANITY
777 1584 Sewers Friendsanity: Krobus 6 <3 FRIENDSANITY
778 1585 Sewers Friendsanity: Krobus 7 <3 FRIENDSANITY
779 1586 Sewers Friendsanity: Krobus 8 <3 FRIENDSANITY
780 1587 Sewers Friendsanity: Krobus 9 <3 FRIENDSANITY
781 1588 Sewers Friendsanity: Krobus 10 <3 FRIENDSANITY
782 1590 Ginger Island Friendsanity: Leo 1 <3 FRIENDSANITY
783 1591 Ginger Island Friendsanity: Leo 2 <3 FRIENDSANITY
784 1592 Ginger Island Friendsanity: Leo 3 <3 FRIENDSANITY
785 1593 Ginger Island Friendsanity: Leo 4 <3 FRIENDSANITY
786 1594 Ginger Island Friendsanity: Leo 5 <3 FRIENDSANITY
787 1595 Ginger Island Friendsanity: Leo 6 <3 FRIENDSANITY
788 1596 Ginger Island Friendsanity: Leo 7 <3 FRIENDSANITY
789 1597 Ginger Island Friendsanity: Leo 8 <3 FRIENDSANITY
790 1598 Ginger Island Friendsanity: Leo 9 <3 FRIENDSANITY
791 1599 Ginger Island Friendsanity: Leo 10 <3 FRIENDSANITY
792 1601 Town Friendsanity: Lewis 1 <3 FRIENDSANITY
793 1602 Town Friendsanity: Lewis 2 <3 FRIENDSANITY
794 1603 Town Friendsanity: Lewis 3 <3 FRIENDSANITY
795 1604 Town Friendsanity: Lewis 4 <3 FRIENDSANITY
796 1605 Town Friendsanity: Lewis 5 <3 FRIENDSANITY
797 1606 Town Friendsanity: Lewis 6 <3 FRIENDSANITY
798 1607 Town Friendsanity: Lewis 7 <3 FRIENDSANITY
799 1608 Town Friendsanity: Lewis 8 <3 FRIENDSANITY
800 1609 Town Friendsanity: Lewis 9 <3 FRIENDSANITY
801 1610 Town Friendsanity: Lewis 10 <3 FRIENDSANITY
802 1612 Mountain Friendsanity: Linus 1 <3 FRIENDSANITY
803 1613 Mountain Friendsanity: Linus 2 <3 FRIENDSANITY
804 1614 Mountain Friendsanity: Linus 3 <3 FRIENDSANITY
805 1615 Mountain Friendsanity: Linus 4 <3 FRIENDSANITY
806 1616 Mountain Friendsanity: Linus 5 <3 FRIENDSANITY
807 1617 Mountain Friendsanity: Linus 6 <3 FRIENDSANITY
808 1618 Mountain Friendsanity: Linus 7 <3 FRIENDSANITY
809 1619 Mountain Friendsanity: Linus 8 <3 FRIENDSANITY
810 1620 Mountain Friendsanity: Linus 9 <3 FRIENDSANITY
811 1621 Mountain Friendsanity: Linus 10 <3 FRIENDSANITY
812 1623 Marnie's Ranch Friendsanity: Marnie 1 <3 FRIENDSANITY
813 1624 Marnie's Ranch Friendsanity: Marnie 2 <3 FRIENDSANITY
814 1625 Marnie's Ranch Friendsanity: Marnie 3 <3 FRIENDSANITY
815 1626 Marnie's Ranch Friendsanity: Marnie 4 <3 FRIENDSANITY
816 1627 Marnie's Ranch Friendsanity: Marnie 5 <3 FRIENDSANITY
817 1628 Marnie's Ranch Friendsanity: Marnie 6 <3 FRIENDSANITY
818 1629 Marnie's Ranch Friendsanity: Marnie 7 <3 FRIENDSANITY
819 1630 Marnie's Ranch Friendsanity: Marnie 8 <3 FRIENDSANITY
820 1631 Marnie's Ranch Friendsanity: Marnie 9 <3 FRIENDSANITY
821 1632 Marnie's Ranch Friendsanity: Marnie 10 <3 FRIENDSANITY
822 1634 Town Friendsanity: Pam 1 <3 FRIENDSANITY
823 1635 Town Friendsanity: Pam 2 <3 FRIENDSANITY
824 1636 Town Friendsanity: Pam 3 <3 FRIENDSANITY
825 1637 Town Friendsanity: Pam 4 <3 FRIENDSANITY
826 1638 Town Friendsanity: Pam 5 <3 FRIENDSANITY
827 1639 Town Friendsanity: Pam 6 <3 FRIENDSANITY
828 1640 Town Friendsanity: Pam 7 <3 FRIENDSANITY
829 1641 Town Friendsanity: Pam 8 <3 FRIENDSANITY
830 1642 Town Friendsanity: Pam 9 <3 FRIENDSANITY
831 1643 Town Friendsanity: Pam 10 <3 FRIENDSANITY
832 1645 Town Friendsanity: Pierre 1 <3 FRIENDSANITY
833 1646 Town Friendsanity: Pierre 2 <3 FRIENDSANITY
834 1647 Town Friendsanity: Pierre 3 <3 FRIENDSANITY
835 1648 Town Friendsanity: Pierre 4 <3 FRIENDSANITY
836 1649 Town Friendsanity: Pierre 5 <3 FRIENDSANITY
837 1650 Town Friendsanity: Pierre 6 <3 FRIENDSANITY
838 1651 Town Friendsanity: Pierre 7 <3 FRIENDSANITY
839 1652 Town Friendsanity: Pierre 8 <3 FRIENDSANITY
840 1653 Town Friendsanity: Pierre 9 <3 FRIENDSANITY
841 1654 Town Friendsanity: Pierre 10 <3 FRIENDSANITY
842 1656 Carpenter Shop Friendsanity: Robin 1 <3 FRIENDSANITY
843 1657 Carpenter Shop Friendsanity: Robin 2 <3 FRIENDSANITY
844 1658 Carpenter Shop Friendsanity: Robin 3 <3 FRIENDSANITY
845 1659 Carpenter Shop Friendsanity: Robin 4 <3 FRIENDSANITY
846 1660 Carpenter Shop Friendsanity: Robin 5 <3 FRIENDSANITY
847 1661 Carpenter Shop Friendsanity: Robin 6 <3 FRIENDSANITY
848 1662 Carpenter Shop Friendsanity: Robin 7 <3 FRIENDSANITY
849 1663 Carpenter Shop Friendsanity: Robin 8 <3 FRIENDSANITY
850 1664 Carpenter Shop Friendsanity: Robin 9 <3 FRIENDSANITY
851 1665 Carpenter Shop Friendsanity: Robin 10 <3 FRIENDSANITY
852 1667 The Desert Friendsanity: Sandy 1 <3 FRIENDSANITY
853 1668 The Desert Friendsanity: Sandy 2 <3 FRIENDSANITY
854 1669 The Desert Friendsanity: Sandy 3 <3 FRIENDSANITY
855 1670 The Desert Friendsanity: Sandy 4 <3 FRIENDSANITY
856 1671 The Desert Friendsanity: Sandy 5 <3 FRIENDSANITY
857 1672 The Desert Friendsanity: Sandy 6 <3 FRIENDSANITY
858 1673 The Desert Friendsanity: Sandy 7 <3 FRIENDSANITY
859 1674 The Desert Friendsanity: Sandy 8 <3 FRIENDSANITY
860 1675 The Desert Friendsanity: Sandy 9 <3 FRIENDSANITY
861 1676 The Desert Friendsanity: Sandy 10 <3 FRIENDSANITY
862 1678 Town Friendsanity: Vincent 1 <3 FRIENDSANITY
863 1679 Town Friendsanity: Vincent 2 <3 FRIENDSANITY
864 1680 Town Friendsanity: Vincent 3 <3 FRIENDSANITY
865 1681 Town Friendsanity: Vincent 4 <3 FRIENDSANITY
866 1682 Town Friendsanity: Vincent 5 <3 FRIENDSANITY
867 1683 Town Friendsanity: Vincent 6 <3 FRIENDSANITY
868 1684 Town Friendsanity: Vincent 7 <3 FRIENDSANITY
869 1685 Town Friendsanity: Vincent 8 <3 FRIENDSANITY
870 1686 Town Friendsanity: Vincent 9 <3 FRIENDSANITY
871 1687 Town Friendsanity: Vincent 10 <3 FRIENDSANITY
872 1689 Beach Friendsanity: Willy 1 <3 FRIENDSANITY
873 1690 Beach Friendsanity: Willy 2 <3 FRIENDSANITY
874 1691 Beach Friendsanity: Willy 3 <3 FRIENDSANITY
875 1692 Beach Friendsanity: Willy 4 <3 FRIENDSANITY
876 1693 Beach Friendsanity: Willy 5 <3 FRIENDSANITY
877 1694 Beach Friendsanity: Willy 6 <3 FRIENDSANITY
878 1695 Beach Friendsanity: Willy 7 <3 FRIENDSANITY
879 1696 Beach Friendsanity: Willy 8 <3 FRIENDSANITY
880 1697 Beach Friendsanity: Willy 9 <3 FRIENDSANITY
881 1698 Beach Friendsanity: Willy 10 <3 FRIENDSANITY
882 1700 Forest Friendsanity: Wizard 1 <3 FRIENDSANITY
883 1701 Forest Friendsanity: Wizard 2 <3 FRIENDSANITY
884 1702 Forest Friendsanity: Wizard 3 <3 FRIENDSANITY
885 1703 Forest Friendsanity: Wizard 4 <3 FRIENDSANITY
886 1704 Forest Friendsanity: Wizard 5 <3 FRIENDSANITY
887 1705 Forest Friendsanity: Wizard 6 <3 FRIENDSANITY
888 1706 Forest Friendsanity: Wizard 7 <3 FRIENDSANITY
889 1707 Forest Friendsanity: Wizard 8 <3 FRIENDSANITY
890 1708 Forest Friendsanity: Wizard 9 <3 FRIENDSANITY
891 1709 Forest Friendsanity: Wizard 10 <3 FRIENDSANITY
892 1710 Farm Friendsanity: Pet 1 <3 FRIENDSANITY
893 1711 Farm Friendsanity: Pet 2 <3 FRIENDSANITY
894 1712 Farm Friendsanity: Pet 3 <3 FRIENDSANITY
895 1713 Farm Friendsanity: Pet 4 <3 FRIENDSANITY
896 1714 Farm Friendsanity: Pet 5 <3 FRIENDSANITY
897 1715 Farm Friendsanity: Friend 1 <3 FRIENDSANITY
898 1716 Farm Friendsanity: Friend 2 <3 FRIENDSANITY
899 1717 Farm Friendsanity: Friend 3 <3 FRIENDSANITY
900 1718 Farm Friendsanity: Friend 4 <3 FRIENDSANITY
901 1719 Farm Friendsanity: Friend 5 <3 FRIENDSANITY
902 1720 Farm Friendsanity: Friend 6 <3 FRIENDSANITY
903 1721 Farm Friendsanity: Friend 7 <3 FRIENDSANITY
904 1722 Farm Friendsanity: Friend 8 <3 FRIENDSANITY
905 1723 Farm Friendsanity: Suitor 9 <3 FRIENDSANITY
906 1724 Farm Friendsanity: Suitor 10 <3 FRIENDSANITY
907 1725 Farm Friendsanity: Spouse 11 <3 FRIENDSANITY
908 1726 Farm Friendsanity: Spouse 12 <3 FRIENDSANITY
909 1727 Farm Friendsanity: Spouse 13 <3 FRIENDSANITY
910 1728 Farm Friendsanity: Spouse 14 <3 FRIENDSANITY

View File

@ -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,)

View File

@ -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}

View File

@ -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"

View File

@ -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)

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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}

View File

@ -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}) "

View File

@ -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,

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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())

View File

@ -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()

View File

@ -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}"

View File

@ -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):

View File

@ -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

View File

@ -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"

View File

@ -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}"

View File

@ -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_()

View File

@ -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

View File

@ -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"

View File

@ -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)

View File

@ -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