Stardew Valley: 4.x.x - The Ginger Update (#1931)

## What is this fixing or adding?
Major content update for Stardew Valley

## How was this tested?
One large-scale public Beta on the archipelago server, plus several smaller private asyncs and test runs

You can go to https://github.com/agilbert1412/StardewArchipelago/releases to grab the mod (latest 4.x.x version), the supported mods and the apworld, to test this PR

## New Features:
- Festival Checks [Easy mode or Hard Mode]
- Special Orders [Both Board and Qi]
- Willy's Boat
- Ginger Island Parrots
- TV Channels
- Trap Items [Available in various difficulty levels]
- Entrance Randomizer: Buildings and Chaos
- New Fishsanity options: Exclude Legendaries, Exclude Hard fish, Only easy fish
- Resource Pack overhaul [Resource packs are now more enjoyable and varied]
- Goal: Greatest Walnut Hunter [Find every single Golden Walnut]
- Goal: Perfection [Achieve Perfection]
- Option: Profit Margin [Multiplier over all earnings]
- Option: Friendsanity Heart Size [Reduce clutter from friendsanity hearts]
- Option: Exclude Ginger Island - will exclude many locations and items to generate a playthrough that does not go to the island
- Mod Support [Curated list of mods]

## New Contributors:
@Witchybun for the mod support

---------

Co-authored-by: Witchybun <embenham05@gmail.com>
Co-authored-by: Exempt-Medic <60412657+Exempt-Medic@users.noreply.github.com>
Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com>
This commit is contained in:
agilbert1412 2023-07-19 14:26:38 -04:00 committed by GitHub
parent 1f6db12797
commit 62657df3fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
94 changed files with 8320 additions and 3104 deletions

View File

@ -237,7 +237,8 @@ class WorldTestBase(unittest.TestCase):
for location in self.multiworld.get_locations():
if location.name not in excluded:
with self.subTest("Location should be reached", location=location):
self.assertTrue(location.can_reach(state), f"{location.name} unreachable")
reachable = location.can_reach(state)
self.assertTrue(reachable, f"{location.name} unreachable")
with self.subTest("Beatable"):
self.multiworld.state = state
self.assertBeatable(True)

View File

@ -1,16 +1,19 @@
from typing import Dict, Any, Iterable, Optional, Union
import logging
from typing import Dict, Any, Iterable, Optional, Union, Set
from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState
from BaseClasses import Region, Entrance, Location, Item, Tutorial, CollectionState, ItemClassification, MultiWorld
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, items_by_group
from .locations import location_table, create_locations, LocationData
from .logic import StardewLogic, StardewRule, True_
from .logic import StardewLogic, StardewRule, True_, MAX_MONTHS
from .options import stardew_valley_options, StardewOptions, fetch_options
from .regions import create_regions
from .rules import set_rules
from worlds.generic.Rules import set_rule
from .mods.mod_data import mod_versions
from .strings.goal_names import Goal
client_version = 0
@ -37,7 +40,7 @@ class StardewWebWorld(WebWorld):
"English",
"setup_en.md",
"setup/en",
["KaitoKid", "Jouramie"]
["KaitoKid", "Jouramie", "Witchybun (Mod Support)", "Exempt-Medic (Proofreading)"]
)]
@ -62,15 +65,33 @@ class StardewValleyWorld(World):
web = StardewWebWorld()
modified_bundles: Dict[str, Bundle]
randomized_entrances: Dict[str, str]
all_progression_items: Set[str]
def __init__(self, world: MultiWorld, player: int):
super().__init__(world, player)
self.all_progression_items = set()
def generate_early(self):
self.options = fetch_options(self.multiworld, self.player)
self.force_change_options_if_incompatible()
self.logic = StardewLogic(self.player, self.options)
self.modified_bundles = get_all_bundles(self.multiworld.random,
self.logic,
self.options[options.BundleRandomization],
self.options[options.BundlePrice])
def force_change_options_if_incompatible(self):
goal_is_walnut_hunter = self.options[options.Goal] == options.Goal.option_greatest_walnut_hunter
goal_is_perfection = self.options[options.Goal] == options.Goal.option_perfection
goal_is_island_related = goal_is_walnut_hunter or goal_is_perfection
exclude_ginger_island = self.options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true
if goal_is_island_related and exclude_ginger_island:
self.options[options.ExcludeGingerIsland] = options.ExcludeGingerIsland.option_false
goal = options.Goal.name_lookup[self.options[options.Goal]]
player_name = self.multiworld.player_name[self.player]
logging.warning(f"Goal '{goal}' requires Ginger Island. Exclude Ginger Island setting forced to 'False' for player {self.player} ({player_name})")
def create_regions(self):
def create_region(name: str, exits: Iterable[str]) -> Region:
region = Region(name, self.player, self.multiworld)
@ -142,7 +163,7 @@ class StardewValleyWorld(World):
self.multiworld.early_items[self.player]["Progressive Backpack"] = 1
def setup_month_events(self):
for i in range(0, 8):
for i in range(0, MAX_MONTHS):
month_end = LocationData(None, "Stardew Valley", f"Month End {i + 1}")
if i == 0:
self.create_event_location(month_end, True_(), "Month End")
@ -152,32 +173,40 @@ class StardewValleyWorld(World):
def setup_victory(self):
if self.options[options.Goal] == options.Goal.option_community_center:
self.create_event_location(location_table["Complete Community Center"],
self.create_event_location(location_table[Goal.community_center],
self.logic.can_complete_community_center().simplify(),
"Victory")
elif self.options[options.Goal] == options.Goal.option_grandpa_evaluation:
self.create_event_location(location_table["Succeed Grandpa's Evaluation"],
self.create_event_location(location_table[Goal.grandpa_evaluation],
self.logic.can_finish_grandpa_evaluation().simplify(),
"Victory")
elif self.options[options.Goal] == options.Goal.option_bottom_of_the_mines:
self.create_event_location(location_table["Reach the Bottom of The Mines"],
self.create_event_location(location_table[Goal.bottom_of_the_mines],
self.logic.can_mine_to_floor(120).simplify(),
"Victory")
elif self.options[options.Goal] == options.Goal.option_cryptic_note:
self.create_event_location(location_table["Complete Quest Cryptic Note"],
self.create_event_location(location_table[Goal.cryptic_note],
self.logic.can_complete_quest("Cryptic Note").simplify(),
"Victory")
elif self.options[options.Goal] == options.Goal.option_master_angler:
self.create_event_location(location_table["Catch Every Fish"],
self.create_event_location(location_table[Goal.master_angler],
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.create_event_location(location_table[Goal.complete_museum],
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(),
self.create_event_location(location_table[Goal.full_house],
(self.logic.has_children(2) & self.logic.can_reproduce()).simplify(),
"Victory")
elif self.options[options.Goal] == options.Goal.option_greatest_walnut_hunter:
self.create_event_location(location_table[Goal.greatest_walnut_hunter],
self.logic.has_walnut(130).simplify(),
"Victory")
elif self.options[options.Goal] == options.Goal.option_perfection:
self.create_event_location(location_table[Goal.perfection],
self.logic.has_everything(self.all_progression_items).simplify(),
"Victory")
self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player)
@ -186,6 +215,8 @@ class StardewValleyWorld(World):
if isinstance(item, str):
item = item_table[item]
if item.classification == ItemClassification.progression:
self.all_progression_items.add(item.name)
return StardewItem(item.name, item.classification, item.code, self.player)
def create_event_location(self, location_data: LocationData, rule: StardewRule, item: Optional[str] = None):
@ -245,8 +276,13 @@ class StardewValleyWorld(World):
key, value = self.modified_bundles[bundle_key].to_pair()
modified_bundles[key] = value
excluded_options = [options.ResourcePackMultiplier, options.BundleRandomization, options.BundlePrice,
options.NumberOfPlayerBuffs]
instance_mod_versions = {}
for mod in mod_versions:
if mod in self.options[options.Mods]:
instance_mod_versions[mod] = mod_versions[mod]
excluded_options = [options.BundleRandomization, options.BundlePrice,
options.NumberOfMovementBuffs, options.NumberOfLuckBuffs, options.Mods]
slot_data = dict(self.options.options)
for option in excluded_options:
slot_data.pop(option.internal_name)
@ -254,7 +290,8 @@ class StardewValleyWorld(World):
"seed": self.multiworld.per_slot_randoms[self.player].randrange(1000000000), # Seed should be max 9 digits
"randomized_entrances": self.randomized_entrances,
"modified_bundles": modified_bundles,
"client_version": "3.0.0",
"client_version": "4.0.0",
"mod_versions": instance_mod_versions,
})
return slot_data

View File

@ -5,7 +5,6 @@ from .common_data import quality_dict
from .game_item import GameItem
from .museum_data import Mineral
@dataclass(frozen=True)
class BundleItem:
item: GameItem
@ -36,6 +35,9 @@ class BundleItem:
amount = 1
return self.as_gold_quality().as_amount(amount)
def is_gold_quality(self) -> bool:
return self.quality >= 2
def __repr__(self):
return f"{self.amount} {quality_dict[self.quality]} {self.item.name}"

View File

@ -1,11 +1,11 @@
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
Beet,Fall,Beet Seeds,Fall,Oasis
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
Cactus Fruit,,Cactus Seeds,,Oasis
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"
@ -19,17 +19,19 @@ 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"
Pineapple,Summer,Pineapple Seeds,Summer,"Island Trader"
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
Rhubarb,Spring,Rhubarb Seeds,Spring,Oasis
Starfruit,Summer,Starfruit Seeds,Summer,Oasis
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
Taro Root,Summer,Taro Tuber,Summer,"Island Trader"
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"

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 Oasis
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 Oasis
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
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 Pineapple Summer Pineapple Seeds Summer Island Trader
23 Poppy Summer Poppy Seeds Summer Pierre's General Store,JojaMart
24 Potato Spring Potato Seeds Spring Pierre's General Store,JojaMart
25 Pumpkin Fall Pumpkin Seeds Fall Pierre's General Store,JojaMart
26 Radish Summer Radish Seeds Summer Pierre's General Store,JojaMart
27 Red Cabbage Summer Red Cabbage Seeds Summer Pierre's General Store,JojaMart
28 Rhubarb Spring Rhubarb Seeds Spring The Desert Oasis
29 Starfruit Summer Starfruit Seeds Summer The Desert Oasis
30 Strawberry Spring Strawberry Seeds Spring Pierre's General Store,JojaMart
31 Summer Spangle Summer Spangle Seeds Summer Pierre's General Store,JojaMart
32 Sunflower Summer,Fall Sunflower Seeds Summer,Fall Pierre's General Store,JojaMart
33 Sweet Gem Berry Fall Rare Seed Spring,Summer Traveling Cart
34 Taro Root Summer Taro Tuber Summer Island Trader
35 Tomato Summer Tomato Seeds Summer Pierre's General Store,JojaMart
36 Tulip Spring Tulip Bulb Spring Pierre's General Store,JojaMart
37 Unmilled Rice Spring Rice Shoot Spring Pierre's General Store,JojaMart

View File

@ -45,3 +45,4 @@ def load_crop_csv():
# TODO Those two should probably be split to we can include rest of seeds
all_crops, all_purchasable_seeds = load_crop_csv()
crops_by_name = {crop.name: crop for crop in all_crops}

View File

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

View File

@ -1,9 +1,9 @@
from dataclasses import dataclass
from typing import List, Tuple, Union
from typing import List, Tuple, Union, Optional
from . import season_data as season
from .game_item import GameItem
from .region_data import SVRegion
from ..strings.region_names import Region
@dataclass(frozen=True)
@ -11,41 +11,43 @@ class FishItem(GameItem):
locations: Tuple[str]
seasons: Tuple[str]
difficulty: int
mod_name: Optional[str]
def __repr__(self):
return f"{self.name} [{self.item_id}] (Locations: {self.locations} |" \
f" Seasons: {self.seasons} |" \
f" Difficulty: {self.difficulty}) "
f" Difficulty: {self.difficulty}) |" \
f"Mod: {self.mod_name}"
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,)
fresh_water = (Region.farm, Region.forest, Region.town, Region.mountain)
ocean = (Region.beach,)
town_river = (Region.town,)
mountain_lake = (Region.mountain,)
forest_pond = (Region.forest,)
forest_river = (Region.forest,)
secret_woods = (Region.secret_woods,)
mines_floor_20 = (Region.mines_floor_20,)
mines_floor_60 = (Region.mines_floor_60,)
mines_floor_100 = (Region.mines_floor_100,)
sewers = (Region.sewer,)
desert = (Region.desert,)
mutant_bug_lair = (Region.mutant_bug_lair,)
witch_swamp = (Region.witch_swamp,)
night_market = (Region.beach,)
ginger_island_ocean = (Region.island_south, Region.island_west)
ginger_island_river = (Region.island_west,)
pirate_cove = (Region.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:
difficulty: int, mod_name: Optional[str] = None) -> FishItem:
if isinstance(seasons, str):
seasons = (seasons,)
fish_item = FishItem(name, item_id, locations, seasons, difficulty)
fish_item = FishItem(name, item_id, locations, seasons, difficulty, mod_name)
all_fish.append(fish_item)
return fish_item
@ -94,6 +96,7 @@ sunfish = create_fish("Sunfish", 145, town_river + forest_river, (season.spring,
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 has different seasons on ginger island. Should be changed when the whole fish thing is refactored
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)
@ -122,3 +125,6 @@ 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]
island_fish = [lionfish, blue_discus, stingray]
all_fish_by_name = {fish.name: fish for fish in all_fish}

View File

@ -1,391 +1,598 @@
id,name,classification,groups
0,Joja Cola,filler,TRASH
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
20,Minecarts Repair,useful,COMMUNITY_REWARD
21,Bus Repair,progression,COMMUNITY_REWARD
22,Movie Theater,useful,
23,Stardrop,useful,
24,Progressive Backpack,progression,
25,Rusty Sword,progression,WEAPON
26,Leather Boots,progression,"FOOTWEAR,MINES_FLOOR_10"
27,Work Boots,useful,"FOOTWEAR,MINES_FLOOR_10"
28,Wooden Blade,progression,"MINES_FLOOR_10,WEAPON"
29,Iron Dirk,progression,"MINES_FLOOR_10,WEAPON"
30,Wind Spire,progression,"MINES_FLOOR_10,WEAPON"
31,Femur,progression,"MINES_FLOOR_10,WEAPON"
32,Steel Smallsword,progression,"MINES_FLOOR_20,WEAPON"
33,Wood Club,progression,"MINES_FLOOR_20,WEAPON"
34,Elf Blade,progression,"MINES_FLOOR_20,WEAPON"
35,Glow Ring,useful,"MINES_FLOOR_20,RING"
36,Magnet Ring,useful,"MINES_FLOOR_20,RING"
37,Slingshot,progression,WEAPON
38,Tundra Boots,useful,"FOOTWEAR,MINES_FLOOR_50"
39,Thermal Boots,useful,"FOOTWEAR,MINES_FLOOR_50"
40,Combat Boots,useful,"FOOTWEAR,MINES_FLOOR_50"
41,Silver Saber,progression,"MINES_FLOOR_50,WEAPON"
42,Pirate's Sword,progression,"MINES_FLOOR_50,WEAPON"
43,Crystal Dagger,progression,"MINES_FLOOR_60,WEAPON"
44,Cutlass,progression,"MINES_FLOOR_60,WEAPON"
45,Iron Edge,progression,"MINES_FLOOR_60,WEAPON"
46,Burglar's Shank,progression,"MINES_FLOOR_60,WEAPON"
47,Wood Mallet,progression,"MINES_FLOOR_60,WEAPON"
48,Master Slingshot,progression,WEAPON
49,Firewalker Boots,useful,"FOOTWEAR,MINES_FLOOR_80"
50,Dark Boots,useful,"FOOTWEAR,MINES_FLOOR_80"
51,Claymore,progression,"MINES_FLOOR_80,WEAPON"
52,Templar's Blade,progression,"MINES_FLOOR_80,WEAPON"
53,Kudgel,progression,"MINES_FLOOR_80,WEAPON"
54,Shadow Dagger,progression,"MINES_FLOOR_80,WEAPON"
55,Obsidian Edge,progression,"MINES_FLOOR_90,WEAPON"
56,Tempered Broadsword,progression,"MINES_FLOOR_90,WEAPON"
57,Wicked Kris,progression,"MINES_FLOOR_90,WEAPON"
58,Bone Sword,progression,"MINES_FLOOR_90,WEAPON"
59,Ossified Blade,progression,"MINES_FLOOR_90,WEAPON"
60,Space Boots,useful,"FOOTWEAR,MINES_FLOOR_110"
61,Crystal Shoes,useful,"FOOTWEAR,MINES_FLOOR_110"
62,Steel Falchion,progression,"MINES_FLOOR_110,WEAPON"
63,The Slammer,progression,"MINES_FLOOR_110,WEAPON"
64,Skull Key,progression,
65,Progressive Hoe,progression,PROGRESSIVE_TOOLS
66,Progressive Pickaxe,progression,PROGRESSIVE_TOOLS
67,Progressive Axe,progression,PROGRESSIVE_TOOLS
68,Progressive Watering Can,progression,PROGRESSIVE_TOOLS
69,Progressive Trash Can,progression,PROGRESSIVE_TOOLS
70,Progressive Fishing Rod,progression,PROGRESSIVE_TOOLS
71,Golden Scythe,useful,
72,Progressive Mine Elevator,progression,
73,Farming Level,progression,SKILL_LEVEL_UP
74,Fishing Level,progression,SKILL_LEVEL_UP
75,Foraging Level,progression,SKILL_LEVEL_UP
76,Mining Level,progression,SKILL_LEVEL_UP
77,Combat Level,progression,SKILL_LEVEL_UP
78,Earth Obelisk,useful,
79,Water Obelisk,useful,
80,Desert Obelisk,progression,
81,Island Obelisk,progression,
82,Junimo Hut,useful,
83,Gold Clock,useful,
84,Progressive Coop,progression,
85,Progressive Barn,progression,
86,Well,useful,
87,Silo,progression,
88,Mill,progression,
89,Progressive Shed,progression,
90,Fish Pond,progression,
91,Stable,useful,
92,Slime Hutch,useful,
93,Shipping Bin,progression,
94,Beach Bridge,progression,
95,Adventurer's Guild,progression,
96,Club Card,progression,
97,Magnifying Glass,progression,
98,Bear's Knowledge,progression,
99,Iridium Snake Milk,progression,
100,JotPK: Progressive Boots,progression,ARCADE_MACHINE_BUFFS
101,JotPK: Progressive Gun,progression,ARCADE_MACHINE_BUFFS
102,JotPK: Progressive Ammo,progression,ARCADE_MACHINE_BUFFS
103,JotPK: Extra Life,progression,ARCADE_MACHINE_BUFFS
104,JotPK: Increased Drop Rate,progression,ARCADE_MACHINE_BUFFS
105,Junimo Kart: Extra Life,progression,ARCADE_MACHINE_BUFFS
106,Galaxy Sword,progression,"GALAXY_WEAPONS,WEAPON"
107,Galaxy Dagger,progression,"GALAXY_WEAPONS,WEAPON"
108,Galaxy Hammer,progression,"GALAXY_WEAPONS,WEAPON"
109,Movement Speed Bonus,useful,
110,Luck Bonus,useful,
111,Lava Katana,progression,"MINES_FLOOR_110,WEAPON"
112,Progressive House,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,
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"
5007,Resource Pack: 100 Stone,filler,"BASE_RESOURCE,RESOURCE_PACK"
5008,Resource Pack: 25 Wood,filler,"BASE_RESOURCE,RESOURCE_PACK"
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,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"
5019,Resource Pack: 60 Fiber,filler,"BASE_RESOURCE,RESOURCE_PACK"
5020,Resource Pack: 5 Coal,filler,"BASE_RESOURCE,RESOURCE_PACK"
5021,Resource Pack: 10 Coal,filler,"BASE_RESOURCE,RESOURCE_PACK"
5022,Resource Pack: 15 Coal,filler,"BASE_RESOURCE,RESOURCE_PACK"
5023,Resource Pack: 20 Coal,filler,"BASE_RESOURCE,RESOURCE_PACK"
5024,Resource Pack: 5 Clay,filler,"BASE_RESOURCE,RESOURCE_PACK"
5025,Resource Pack: 10 Clay,filler,"BASE_RESOURCE,RESOURCE_PACK"
5026,Resource Pack: 15 Clay,filler,"BASE_RESOURCE,RESOURCE_PACK"
5027,Resource Pack: 20 Clay,filler,"BASE_RESOURCE,RESOURCE_PACK"
5028,Resource Pack: 1 Warp Totem: Beach,filler,"RESOURCE_PACK,WARP_TOTEM"
5029,Resource Pack: 3 Warp Totem: Beach,filler,"RESOURCE_PACK,WARP_TOTEM"
5030,Resource Pack: 5 Warp Totem: Beach,filler,"RESOURCE_PACK,WARP_TOTEM"
5031,Resource Pack: 7 Warp Totem: Beach,filler,"RESOURCE_PACK,WARP_TOTEM"
5032,Resource Pack: 9 Warp Totem: Beach,filler,"RESOURCE_PACK,WARP_TOTEM"
5033,Resource Pack: 10 Warp Totem: Beach,filler,"RESOURCE_PACK,WARP_TOTEM"
5034,Resource Pack: 1 Warp Totem: Desert,filler,"RESOURCE_PACK,WARP_TOTEM"
5035,Resource Pack: 3 Warp Totem: Desert,filler,"RESOURCE_PACK,WARP_TOTEM"
5036,Resource Pack: 5 Warp Totem: Desert,filler,"RESOURCE_PACK,WARP_TOTEM"
5037,Resource Pack: 7 Warp Totem: Desert,filler,"RESOURCE_PACK,WARP_TOTEM"
5038,Resource Pack: 9 Warp Totem: Desert,filler,"RESOURCE_PACK,WARP_TOTEM"
5039,Resource Pack: 10 Warp Totem: Desert,filler,"RESOURCE_PACK,WARP_TOTEM"
5040,Resource Pack: 1 Warp Totem: Farm,filler,"RESOURCE_PACK,WARP_TOTEM"
5041,Resource Pack: 3 Warp Totem: Farm,filler,"RESOURCE_PACK,WARP_TOTEM"
5042,Resource Pack: 5 Warp Totem: Farm,filler,"RESOURCE_PACK,WARP_TOTEM"
5043,Resource Pack: 7 Warp Totem: Farm,filler,"RESOURCE_PACK,WARP_TOTEM"
5044,Resource Pack: 9 Warp Totem: Farm,filler,"RESOURCE_PACK,WARP_TOTEM"
5045,Resource Pack: 10 Warp Totem: Farm,filler,"RESOURCE_PACK,WARP_TOTEM"
5046,Resource Pack: 1 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM"
5047,Resource Pack: 3 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM"
5048,Resource Pack: 5 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM"
5049,Resource Pack: 7 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM"
5050,Resource Pack: 9 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM"
5051,Resource Pack: 10 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM"
5052,Resource Pack: 1 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM"
5053,Resource Pack: 3 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM"
5054,Resource Pack: 5 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM"
5055,Resource Pack: 7 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM"
5056,Resource Pack: 9 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM"
5057,Resource Pack: 10 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM"
5058,Resource Pack: 6 Geode,filler,"GEODE,RESOURCE_PACK"
5059,Resource Pack: 12 Geode,filler,"GEODE,RESOURCE_PACK"
5060,Resource Pack: 18 Geode,filler,"GEODE,RESOURCE_PACK"
5061,Resource Pack: 24 Geode,filler,"GEODE,RESOURCE_PACK"
5062,Resource Pack: 4 Frozen Geode,filler,"GEODE,RESOURCE_PACK"
5063,Resource Pack: 8 Frozen Geode,filler,"GEODE,RESOURCE_PACK"
5064,Resource Pack: 12 Frozen Geode,filler,"GEODE,RESOURCE_PACK"
5065,Resource Pack: 16 Frozen Geode,filler,"GEODE,RESOURCE_PACK"
5066,Resource Pack: 3 Magma Geode,filler,"GEODE,RESOURCE_PACK"
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,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"
5077,Resource Pack: 100 Copper Ore,filler,"ORE,RESOURCE_PACK"
5078,Resource Pack: 125 Copper Ore,filler,"ORE,RESOURCE_PACK"
5079,Resource Pack: 150 Copper Ore,filler,"ORE,RESOURCE_PACK"
5080,Resource Pack: 25 Iron Ore,filler,"ORE,RESOURCE_PACK"
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,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"
5095,Resource Pack: 20 Quartz,filler,"ORE,RESOURCE_PACK"
5096,Resource Pack: 10 Basic Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5097,Resource Pack: 20 Basic Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5098,Resource Pack: 30 Basic Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5099,Resource Pack: 40 Basic Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5100,Resource Pack: 50 Basic Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5101,Resource Pack: 60 Basic Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5102,Resource Pack: 10 Basic Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5103,Resource Pack: 20 Basic Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5104,Resource Pack: 30 Basic Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5105,Resource Pack: 40 Basic Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5106,Resource Pack: 50 Basic Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5107,Resource Pack: 60 Basic Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5108,Resource Pack: 10 Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5109,Resource Pack: 20 Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5110,Resource Pack: 30 Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5111,Resource Pack: 40 Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5112,Resource Pack: 50 Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5113,Resource Pack: 60 Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5114,Resource Pack: 4 Quality Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5115,Resource Pack: 12 Quality Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5116,Resource Pack: 20 Quality Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5117,Resource Pack: 28 Quality Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5118,Resource Pack: 36 Quality Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5119,Resource Pack: 40 Quality Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5120,Resource Pack: 4 Quality Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5121,Resource Pack: 12 Quality Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5122,Resource Pack: 20 Quality Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5123,Resource Pack: 28 Quality Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5124,Resource Pack: 36 Quality Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5125,Resource Pack: 40 Quality Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK"
5126,Resource Pack: 4 Deluxe Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5127,Resource Pack: 12 Deluxe Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
5128,Resource Pack: 20 Deluxe Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK"
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,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"
5153,Resource Pack: 14 Tree Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5154,Resource Pack: 18 Tree Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5155,Resource Pack: 20 Tree Fertilizer,filler,"FERTILIZER,RESOURCE_PACK"
5156,Resource Pack: 10 Spring Seeds,filler,"RESOURCE_PACK,SEED"
5157,Resource Pack: 20 Spring Seeds,filler,"RESOURCE_PACK,SEED"
5158,Resource Pack: 30 Spring Seeds,filler,"RESOURCE_PACK,SEED"
5159,Resource Pack: 40 Spring Seeds,filler,"RESOURCE_PACK,SEED"
5160,Resource Pack: 50 Spring Seeds,filler,"RESOURCE_PACK,SEED"
5161,Resource Pack: 60 Spring Seeds,filler,"RESOURCE_PACK,SEED"
5162,Resource Pack: 10 Summer Seeds,filler,"RESOURCE_PACK,SEED"
5163,Resource Pack: 20 Summer Seeds,filler,"RESOURCE_PACK,SEED"
5164,Resource Pack: 30 Summer Seeds,filler,"RESOURCE_PACK,SEED"
5165,Resource Pack: 40 Summer Seeds,filler,"RESOURCE_PACK,SEED"
5166,Resource Pack: 50 Summer Seeds,filler,"RESOURCE_PACK,SEED"
5167,Resource Pack: 60 Summer Seeds,filler,"RESOURCE_PACK,SEED"
5168,Resource Pack: 10 Fall Seeds,filler,"RESOURCE_PACK,SEED"
5169,Resource Pack: 20 Fall Seeds,filler,"RESOURCE_PACK,SEED"
5170,Resource Pack: 30 Fall Seeds,filler,"RESOURCE_PACK,SEED"
5171,Resource Pack: 40 Fall Seeds,filler,"RESOURCE_PACK,SEED"
5172,Resource Pack: 50 Fall Seeds,filler,"RESOURCE_PACK,SEED"
5173,Resource Pack: 60 Fall Seeds,filler,"RESOURCE_PACK,SEED"
5174,Resource Pack: 10 Winter Seeds,filler,"RESOURCE_PACK,SEED"
5175,Resource Pack: 20 Winter Seeds,filler,"RESOURCE_PACK,SEED"
5176,Resource Pack: 30 Winter Seeds,filler,"RESOURCE_PACK,SEED"
5177,Resource Pack: 40 Winter Seeds,filler,"RESOURCE_PACK,SEED"
5178,Resource Pack: 50 Winter Seeds,filler,"RESOURCE_PACK,SEED"
5179,Resource Pack: 60 Winter Seeds,filler,"RESOURCE_PACK,SEED"
5180,Resource Pack: 1 Mahogany Seed,filler,"RESOURCE_PACK,SEED"
5181,Resource Pack: 3 Mahogany Seed,filler,"RESOURCE_PACK,SEED"
5182,Resource Pack: 5 Mahogany Seed,filler,"RESOURCE_PACK,SEED"
5183,Resource Pack: 7 Mahogany Seed,filler,"RESOURCE_PACK,SEED"
5184,Resource Pack: 9 Mahogany Seed,filler,"RESOURCE_PACK,SEED"
5185,Resource Pack: 10 Mahogany Seed,filler,"RESOURCE_PACK,SEED"
5186,Resource Pack: 10 Bait,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5187,Resource Pack: 20 Bait,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5188,Resource Pack: 30 Bait,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5189,Resource Pack: 40 Bait,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5190,Resource Pack: 50 Bait,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5191,Resource Pack: 60 Bait,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5192,Resource Pack: 1 Crab Pot,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5193,Resource Pack: 2 Crab Pot,filler,"FISHING_RESOURCE,RESOURCE_PACK"
5194,Resource Pack: 3 Crab Pot,filler,"FISHING_RESOURCE,RESOURCE_PACK"
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),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
id,name,classification,groups,mod_name
0,Joja Cola,filler,TRASH,
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,
20,Minecarts Repair,useful,COMMUNITY_REWARD,
21,Bus Repair,progression,COMMUNITY_REWARD,
22,Movie Theater,useful,,
23,Stardrop,progression,,
24,Progressive Backpack,progression,,
25,Rusty Sword,progression,WEAPON,
26,Leather Boots,progression,"FOOTWEAR,MINES_FLOOR_10",
27,Work Boots,useful,"FOOTWEAR,MINES_FLOOR_10",
28,Wooden Blade,progression,"MINES_FLOOR_10,WEAPON",
29,Iron Dirk,progression,"MINES_FLOOR_10,WEAPON",
30,Wind Spire,progression,"MINES_FLOOR_10,WEAPON",
31,Femur,progression,"MINES_FLOOR_10,WEAPON",
32,Steel Smallsword,progression,"MINES_FLOOR_20,WEAPON",
33,Wood Club,progression,"MINES_FLOOR_20,WEAPON",
34,Elf Blade,progression,"MINES_FLOOR_20,WEAPON",
35,Glow Ring,useful,"MINES_FLOOR_20,RING",
36,Magnet Ring,useful,"MINES_FLOOR_20,RING",
37,Slingshot,progression,WEAPON,
38,Tundra Boots,useful,"FOOTWEAR,MINES_FLOOR_50",
39,Thermal Boots,useful,"FOOTWEAR,MINES_FLOOR_50",
40,Combat Boots,useful,"FOOTWEAR,MINES_FLOOR_50",
41,Silver Saber,progression,"MINES_FLOOR_50,WEAPON",
42,Pirate's Sword,progression,"MINES_FLOOR_50,WEAPON",
43,Crystal Dagger,progression,"MINES_FLOOR_60,WEAPON",
44,Cutlass,progression,"MINES_FLOOR_60,WEAPON",
45,Iron Edge,progression,"MINES_FLOOR_60,WEAPON",
46,Burglar's Shank,progression,"MINES_FLOOR_60,WEAPON",
47,Wood Mallet,progression,"MINES_FLOOR_60,WEAPON",
48,Master Slingshot,progression,WEAPON,
49,Firewalker Boots,useful,"FOOTWEAR,MINES_FLOOR_80",
50,Dark Boots,useful,"FOOTWEAR,MINES_FLOOR_80",
51,Claymore,progression,"MINES_FLOOR_80,WEAPON",
52,Templar's Blade,progression,"MINES_FLOOR_80,WEAPON",
53,Kudgel,progression,"MINES_FLOOR_80,WEAPON",
54,Shadow Dagger,progression,"MINES_FLOOR_80,WEAPON",
55,Obsidian Edge,progression,"MINES_FLOOR_90,WEAPON",
56,Tempered Broadsword,progression,"MINES_FLOOR_90,WEAPON",
57,Wicked Kris,progression,"MINES_FLOOR_90,WEAPON",
58,Bone Sword,progression,"MINES_FLOOR_90,WEAPON",
59,Ossified Blade,progression,"MINES_FLOOR_90,WEAPON",
60,Space Boots,useful,"FOOTWEAR,MINES_FLOOR_110",
61,Crystal Shoes,useful,"FOOTWEAR,MINES_FLOOR_110",
62,Steel Falchion,progression,"MINES_FLOOR_110,WEAPON",
63,The Slammer,progression,"MINES_FLOOR_110,WEAPON",
64,Skull Key,progression,,
65,Progressive Hoe,progression,PROGRESSIVE_TOOLS,
66,Progressive Pickaxe,progression,PROGRESSIVE_TOOLS,
67,Progressive Axe,progression,PROGRESSIVE_TOOLS,
68,Progressive Watering Can,progression,PROGRESSIVE_TOOLS,
69,Progressive Trash Can,progression,PROGRESSIVE_TOOLS,
70,Progressive Fishing Rod,progression,PROGRESSIVE_TOOLS,
71,Golden Scythe,useful,,
72,Progressive Mine Elevator,progression,,
73,Farming Level,progression,SKILL_LEVEL_UP,
74,Fishing Level,progression,SKILL_LEVEL_UP,
75,Foraging Level,progression,SKILL_LEVEL_UP,
76,Mining Level,progression,SKILL_LEVEL_UP,
77,Combat Level,progression,SKILL_LEVEL_UP,
78,Earth Obelisk,progression,,
79,Water Obelisk,progression,,
80,Desert Obelisk,progression,,
81,Island Obelisk,progression,GINGER_ISLAND,
82,Junimo Hut,useful,,
83,Gold Clock,progression,,
84,Progressive Coop,progression,,
85,Progressive Barn,progression,,
86,Well,useful,,
87,Silo,progression,,
88,Mill,progression,,
89,Progressive Shed,progression,,
90,Fish Pond,progression,,
91,Stable,useful,,
92,Slime Hutch,useful,,
93,Shipping Bin,progression,,
94,Beach Bridge,progression,,
95,Adventurer's Guild,progression,,
96,Club Card,progression,,
97,Magnifying Glass,progression,,
98,Bear's Knowledge,progression,,
99,Iridium Snake Milk,progression,,
100,JotPK: Progressive Boots,progression,ARCADE_MACHINE_BUFFS,
101,JotPK: Progressive Gun,progression,ARCADE_MACHINE_BUFFS,
102,JotPK: Progressive Ammo,progression,ARCADE_MACHINE_BUFFS,
103,JotPK: Extra Life,progression,ARCADE_MACHINE_BUFFS,
104,JotPK: Increased Drop Rate,progression,ARCADE_MACHINE_BUFFS,
105,Junimo Kart: Extra Life,progression,ARCADE_MACHINE_BUFFS,
106,Galaxy Sword,progression,"GALAXY_WEAPONS,WEAPON",
107,Galaxy Dagger,progression,"GALAXY_WEAPONS,WEAPON",
108,Galaxy Hammer,progression,"GALAXY_WEAPONS,WEAPON",
109,Movement Speed Bonus,progression,,
110,Luck Bonus,progression,,
111,Lava Katana,progression,"MINES_FLOOR_110,WEAPON",
112,Progressive House,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,,
123,Progressive Season,progression,,
124,Spring,progression,SEASON,
125,Summer,progression,SEASON,
126,Fall,progression,SEASON,
127,Winter,progression,SEASON,
128,Amaranth Seeds,progression,CROPSANITY,
129,Artichoke Seeds,progression,CROPSANITY,
130,Beet Seeds,progression,CROPSANITY,
131,Jazz Seeds,progression,CROPSANITY,
132,Blueberry Seeds,progression,CROPSANITY,
133,Bok Choy Seeds,progression,CROPSANITY,
134,Cauliflower Seeds,progression,CROPSANITY,
135,Corn Seeds,progression,CROPSANITY,
136,Cranberry Seeds,progression,CROPSANITY,
137,Eggplant Seeds,progression,CROPSANITY,
138,Fairy Seeds,progression,CROPSANITY,
139,Garlic Seeds,progression,CROPSANITY,
140,Grape Starter,progression,CROPSANITY,
141,Bean Starter,progression,CROPSANITY,
142,Hops Starter,progression,CROPSANITY,
143,Pepper Seeds,progression,CROPSANITY,
144,Kale Seeds,progression,CROPSANITY,
145,Melon Seeds,progression,CROPSANITY,
146,Parsnip Seeds,progression,CROPSANITY,
147,Poppy Seeds,progression,CROPSANITY,
148,Potato Seeds,progression,CROPSANITY,
149,Pumpkin Seeds,progression,CROPSANITY,
150,Radish Seeds,progression,CROPSANITY,
151,Red Cabbage Seeds,progression,CROPSANITY,
152,Rhubarb Seeds,progression,CROPSANITY,
153,Starfruit Seeds,progression,CROPSANITY,
154,Strawberry Seeds,progression,CROPSANITY,
155,Spangle Seeds,progression,CROPSANITY,
156,Sunflower Seeds,progression,CROPSANITY,
157,Tomato Seeds,progression,CROPSANITY,
158,Tulip Bulb,progression,CROPSANITY,
159,Rice Shoot,progression,CROPSANITY,
160,Wheat Seeds,progression,CROPSANITY,
161,Yam Seeds,progression,CROPSANITY,
162,Cactus Seeds,progression,CROPSANITY,
163,Magic Rock Candy,useful,"MUSEUM,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
164,Ancient Seeds Recipe,progression,MUSEUM,
165,Ancient Seeds,progression,"MUSEUM,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
166,Traveling Merchant Metal Detector,progression,MUSEUM,
167,Alex <3,progression,FRIENDSANITY,
168,Elliott <3,progression,FRIENDSANITY,
169,Harvey <3,progression,FRIENDSANITY,
170,Sam <3,progression,FRIENDSANITY,
171,Sebastian <3,progression,FRIENDSANITY,
172,Shane <3,progression,FRIENDSANITY,
173,Abigail <3,progression,FRIENDSANITY,
174,Emily <3,progression,FRIENDSANITY,
175,Haley <3,progression,FRIENDSANITY,
176,Leah <3,progression,FRIENDSANITY,
177,Maru <3,progression,FRIENDSANITY,
178,Penny <3,progression,FRIENDSANITY,
179,Caroline <3,progression,FRIENDSANITY,
180,Clint <3,progression,FRIENDSANITY,
181,Demetrius <3,progression,FRIENDSANITY,
182,Dwarf <3,progression,FRIENDSANITY,
183,Evelyn <3,progression,FRIENDSANITY,
184,George <3,progression,FRIENDSANITY,
185,Gus <3,progression,FRIENDSANITY,
186,Jas <3,progression,FRIENDSANITY,
187,Jodi <3,progression,FRIENDSANITY,
188,Kent <3,progression,FRIENDSANITY,
189,Krobus <3,progression,FRIENDSANITY,
190,Leo <3,progression,"FRIENDSANITY,GINGER_ISLAND",
191,Lewis <3,progression,FRIENDSANITY,
192,Linus <3,progression,FRIENDSANITY,
193,Marnie <3,progression,FRIENDSANITY,
194,Pam <3,progression,FRIENDSANITY,
195,Pierre <3,progression,FRIENDSANITY,
196,Robin <3,progression,FRIENDSANITY,
197,Sandy <3,progression,FRIENDSANITY,
198,Vincent <3,progression,FRIENDSANITY,
199,Willy <3,progression,FRIENDSANITY,
200,Wizard <3,progression,FRIENDSANITY,
201,Pet <3,progression,FRIENDSANITY,
202,Rarecrow #1,progression,"FESTIVAL,RARECROW",
203,Rarecrow #2,progression,"FESTIVAL,RARECROW",
204,Rarecrow #3,progression,"FESTIVAL,RARECROW",
205,Rarecrow #4,progression,"FESTIVAL,RARECROW",
206,Rarecrow #5,progression,"FESTIVAL,RARECROW",
207,Rarecrow #6,progression,"FESTIVAL,RARECROW",
208,Rarecrow #7,progression,"FESTIVAL,RARECROW",
209,Rarecrow #8,progression,"FESTIVAL,RARECROW",
210,Straw Hat,filler,FESTIVAL,
211,Golden Pumpkin,useful,FESTIVAL,
212,Barbed Hook,useful,FESTIVAL,
213,Dressed Spinner,useful,FESTIVAL,
214,Magnet,useful,FESTIVAL,
215,Sailor's Cap,filler,FESTIVAL,
216,Pearl,useful,FESTIVAL,
217,Cone Hat,filler,FESTIVAL,
218,Iridium Fireplace,filler,FESTIVAL,
219,Lupini: Red Eagle,filler,FESTIVAL,
220,Lupini: Portrait Of A Mermaid,filler,FESTIVAL,
221,Lupini: Solar Kingdom,filler,FESTIVAL,
222,Lupini: Clouds,filler,FESTIVAL,
223,Lupini: 1000 Years From Now,filler,FESTIVAL,
224,Lupini: Three Trees,filler,FESTIVAL,
225,Lupini: The Serpent,filler,FESTIVAL,
226,Lupini: 'Tropical Fish #173',filler,FESTIVAL,
227,Lupini: Land Of Clay,filler,FESTIVAL,
228,Special Order Board,progression,SPECIAL_ORDER_BOARD,
229,Solar Panel Recipe,progression,SPECIAL_ORDER_BOARD,
230,Geode Crusher Recipe,progression,SPECIAL_ORDER_BOARD,
231,Farm Computer Recipe,progression,SPECIAL_ORDER_BOARD,
232,Bone Mill Recipe,progression,SPECIAL_ORDER_BOARD,
233,Fiber Seeds Recipe,progression,SPECIAL_ORDER_BOARD,
234,Stone Chest Recipe,progression,SPECIAL_ORDER_BOARD,
235,Quality Bobber Recipe,progression,SPECIAL_ORDER_BOARD,
236,Mini-Obelisk Recipe,progression,SPECIAL_ORDER_BOARD,
237,Monster Musk Recipe,progression,SPECIAL_ORDER_BOARD,
239,Sewing Machine,progression,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
240,Coffee Maker,progression,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
241,Mini-Fridge,useful,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
242,Mini-Shipping Bin,progression,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
243,Deluxe Fish Tank,useful,"RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD",
244,10 Qi Gems,progression,"GINGER_ISLAND,SPECIAL_ORDER_QI",
245,20 Qi Gems,progression,"GINGER_ISLAND,SPECIAL_ORDER_QI",
246,25 Qi Gems,progression,"GINGER_ISLAND,SPECIAL_ORDER_QI",
247,35 Qi Gems,progression,"GINGER_ISLAND,SPECIAL_ORDER_QI",
248,40 Qi Gems,progression,"GINGER_ISLAND,SPECIAL_ORDER_QI",
249,50 Qi Gems,progression,"GINGER_ISLAND,SPECIAL_ORDER_QI",
250,100 Qi Gems,progression,"GINGER_ISLAND,SPECIAL_ORDER_QI",
251,Rare Seed,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
252,Apple Sapling,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
253,Apricot Sapling,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
254,Cherry Sapling,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
255,Orange Sapling,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
256,Pomegranate Sapling,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
257,Peach Sapling,progression,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
258,Banana Sapling,progression,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
259,Mango Sapling,progression,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY",
260,Boat Repair,progression,GINGER_ISLAND,
261,Open Professor Snail Cave,progression,"GINGER_ISLAND",
262,Island North Turtle,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
263,Island West Turtle,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
264,Island Farmhouse,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
265,Island Mailbox,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
266,Farm Obelisk,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
267,Dig Site Bridge,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
268,Island Trader,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
269,Volcano Bridge,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
270,Volcano Exit Shortcut,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
271,Island Resort,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
272,Parrot Express,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
273,Qi Walnut Room,progression,"GINGER_ISLAND,WALNUT_PURCHASE",
274,Pineapple Seeds,progression,"GINGER_ISLAND,CROPSANITY",
275,Taro Tuber,progression,"GINGER_ISLAND,CROPSANITY",
276,Weather Report,useful,"TV_CHANNEL",
277,Fortune Teller,useful,"TV_CHANNEL",
278,Livin' Off The Land,useful,"TV_CHANNEL",
279,The Queen of Sauce,progression,"TV_CHANNEL",
280,Fishing Information Broadcasting Service,useful,"TV_CHANNEL",
281,Sinister Signal,useful,"TV_CHANNEL",
282,Dark Talisman,progression,,
283,Ostrich Incubator Recipe,progression,"GINGER_ISLAND",
284,Cute Baby,progression,"BABY",
285,Ugly Baby,progression,"BABY",
286,Deluxe Scarecrow Recipe,progression,"FESTIVAL,RARECROW",
4001,Burnt,trap,TRAP,
4002,Darkness,trap,TRAP,
4003,Frozen,trap,TRAP,
4004,Jinxed,trap,TRAP,
4005,Nauseated,trap,TRAP,
4006,Slimed,trap,TRAP,
4007,Weakness,trap,TRAP,
4008,Taxes,trap,TRAP,
4009,Random Teleport,trap,TRAP,
4010,The Crows,trap,TRAP,
4011,Monsters,trap,TRAP,
4012,Entrance Reshuffle,trap,"TRAP,DEPRECATED",
4013,Debris,trap,TRAP,
4014,Shuffle,trap,TRAP,
4015,Temporary Winter,trap,"TRAP,DEPRECATED",
4016,Pariah,trap,TRAP,
4017,Drought,trap,TRAP,
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",
5004,Resource Pack: 25 Stone,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5005,Resource Pack: 50 Stone,filler,"BASE_RESOURCE,RESOURCE_PACK",
5006,Resource Pack: 75 Stone,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5007,Resource Pack: 100 Stone,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5008,Resource Pack: 25 Wood,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5009,Resource Pack: 50 Wood,filler,"BASE_RESOURCE,RESOURCE_PACK",
5010,Resource Pack: 75 Wood,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5011,Resource Pack: 100 Wood,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5012,Resource Pack: 5 Hardwood,useful,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5013,Resource Pack: 10 Hardwood,useful,"BASE_RESOURCE,RESOURCE_PACK",
5014,Resource Pack: 15 Hardwood,useful,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5015,Resource Pack: 20 Hardwood,useful,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5016,Resource Pack: 15 Fiber,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5017,Resource Pack: 30 Fiber,filler,"BASE_RESOURCE,RESOURCE_PACK",
5018,Resource Pack: 45 Fiber,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5019,Resource Pack: 60 Fiber,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5020,Resource Pack: 5 Coal,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5021,Resource Pack: 10 Coal,filler,"BASE_RESOURCE,RESOURCE_PACK",
5022,Resource Pack: 15 Coal,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5023,Resource Pack: 20 Coal,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5024,Resource Pack: 5 Clay,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5025,Resource Pack: 10 Clay,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5026,Resource Pack: 15 Clay,filler,"DEPRECATED,BASE_RESOURCE,RESOURCE_PACK",
5027,Resource Pack: 20 Clay,filler,"BASE_RESOURCE,RESOURCE_PACK",
5028,Resource Pack: 1 Warp Totem: Beach,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5029,Resource Pack: 3 Warp Totem: Beach,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5030,Resource Pack: 5 Warp Totem: Beach,filler,"RESOURCE_PACK,WARP_TOTEM",
5031,Resource Pack: 7 Warp Totem: Beach,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5032,Resource Pack: 9 Warp Totem: Beach,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5033,Resource Pack: 10 Warp Totem: Beach,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5034,Resource Pack: 1 Warp Totem: Desert,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5035,Resource Pack: 3 Warp Totem: Desert,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5036,Resource Pack: 5 Warp Totem: Desert,filler,"RESOURCE_PACK,WARP_TOTEM",
5037,Resource Pack: 7 Warp Totem: Desert,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5038,Resource Pack: 9 Warp Totem: Desert,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5039,Resource Pack: 10 Warp Totem: Desert,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5040,Resource Pack: 1 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5041,Resource Pack: 3 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5042,Resource Pack: 5 Warp Totem: Farm,filler,"RESOURCE_PACK,WARP_TOTEM",
5043,Resource Pack: 7 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5044,Resource Pack: 9 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5045,Resource Pack: 10 Warp Totem: Farm,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5046,Resource Pack: 1 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5047,Resource Pack: 3 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5048,Resource Pack: 5 Warp Totem: Island,filler,"RESOURCE_PACK,WARP_TOTEM",
5049,Resource Pack: 7 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5050,Resource Pack: 9 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5051,Resource Pack: 10 Warp Totem: Island,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5052,Resource Pack: 1 Warp Totem: Mountains,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5053,Resource Pack: 3 Warp Totem: Mountains,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5054,Resource Pack: 5 Warp Totem: Mountains,filler,"RESOURCE_PACK,WARP_TOTEM",
5055,Resource Pack: 7 Warp Totem: Mountains,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5056,Resource Pack: 9 Warp Totem: Mountains,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5057,Resource Pack: 10 Warp Totem: Mountains,filler,"DEPRECATED,RESOURCE_PACK,WARP_TOTEM",
5058,Resource Pack: 6 Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5059,Resource Pack: 12 Geode,filler,"GEODE,RESOURCE_PACK",
5060,Resource Pack: 18 Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5061,Resource Pack: 24 Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5062,Resource Pack: 4 Frozen Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5063,Resource Pack: 8 Frozen Geode,filler,"GEODE,RESOURCE_PACK",
5064,Resource Pack: 12 Frozen Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5065,Resource Pack: 16 Frozen Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5066,Resource Pack: 3 Magma Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5067,Resource Pack: 6 Magma Geode,filler,"GEODE,RESOURCE_PACK",
5068,Resource Pack: 9 Magma Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5069,Resource Pack: 12 Magma Geode,filler,"DEPRECATED,GEODE,RESOURCE_PACK",
5070,Resource Pack: 2 Omni Geode,useful,"DEPRECATED,GEODE,RESOURCE_PACK",
5071,Resource Pack: 4 Omni Geode,useful,"GEODE,RESOURCE_PACK",
5072,Resource Pack: 6 Omni Geode,useful,"DEPRECATED,GEODE,RESOURCE_PACK",
5073,Resource Pack: 8 Omni Geode,useful,"DEPRECATED,GEODE,RESOURCE_PACK",
5074,Resource Pack: 25 Copper Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5075,Resource Pack: 50 Copper Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5076,Resource Pack: 75 Copper Ore,filler,"ORE,RESOURCE_PACK",
5077,Resource Pack: 100 Copper Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5078,Resource Pack: 125 Copper Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5079,Resource Pack: 150 Copper Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5080,Resource Pack: 25 Iron Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5081,Resource Pack: 50 Iron Ore,filler,"ORE,RESOURCE_PACK",
5082,Resource Pack: 75 Iron Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5083,Resource Pack: 100 Iron Ore,filler,"DEPRECATED,ORE,RESOURCE_PACK",
5084,Resource Pack: 12 Gold Ore,useful,"DEPRECATED,ORE,RESOURCE_PACK",
5085,Resource Pack: 25 Gold Ore,useful,"ORE,RESOURCE_PACK",
5086,Resource Pack: 38 Gold Ore,useful,"DEPRECATED,ORE,RESOURCE_PACK",
5087,Resource Pack: 50 Gold Ore,useful,"DEPRECATED,ORE,RESOURCE_PACK",
5088,Resource Pack: 5 Iridium Ore,useful,"DEPRECATED,ORE,RESOURCE_PACK",
5089,Resource Pack: 10 Iridium Ore,useful,"ORE,RESOURCE_PACK",
5090,Resource Pack: 15 Iridium Ore,useful,"DEPRECATED,ORE,RESOURCE_PACK",
5091,Resource Pack: 20 Iridium Ore,useful,"DEPRECATED,ORE,RESOURCE_PACK",
5092,Resource Pack: 5 Quartz,filler,"ORE,RESOURCE_PACK",
5093,Resource Pack: 10 Quartz,filler,"ORE,DEPRECATED,RESOURCE_PACK",
5094,Resource Pack: 15 Quartz,filler,"ORE,DEPRECATED,RESOURCE_PACK",
5095,Resource Pack: 20 Quartz,filler,"ORE,DEPRECATED,RESOURCE_PACK",
5096,Resource Pack: 10 Basic Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5097,Resource Pack: 20 Basic Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5098,Resource Pack: 30 Basic Fertilizer,filler,"FERTILIZER,RESOURCE_PACK",
5099,Resource Pack: 40 Basic Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5100,Resource Pack: 50 Basic Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5101,Resource Pack: 60 Basic Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5102,Resource Pack: 10 Basic Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5103,Resource Pack: 20 Basic Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5104,Resource Pack: 30 Basic Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK",
5105,Resource Pack: 40 Basic Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5106,Resource Pack: 50 Basic Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5107,Resource Pack: 60 Basic Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5108,Resource Pack: 10 Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5109,Resource Pack: 20 Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5110,Resource Pack: 30 Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK",
5111,Resource Pack: 40 Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5112,Resource Pack: 50 Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5113,Resource Pack: 60 Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5114,Resource Pack: 4 Quality Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5115,Resource Pack: 12 Quality Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5116,Resource Pack: 20 Quality Fertilizer,filler,"FERTILIZER,RESOURCE_PACK",
5117,Resource Pack: 28 Quality Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5118,Resource Pack: 36 Quality Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5119,Resource Pack: 40 Quality Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5120,Resource Pack: 4 Quality Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5121,Resource Pack: 12 Quality Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5122,Resource Pack: 20 Quality Retaining Soil,filler,"FERTILIZER,RESOURCE_PACK",
5123,Resource Pack: 28 Quality Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5124,Resource Pack: 36 Quality Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5125,Resource Pack: 40 Quality Retaining Soil,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5126,Resource Pack: 4 Deluxe Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5127,Resource Pack: 12 Deluxe Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5128,Resource Pack: 20 Deluxe Speed-Gro,filler,"FERTILIZER,RESOURCE_PACK",
5129,Resource Pack: 28 Deluxe Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5130,Resource Pack: 36 Deluxe Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5131,Resource Pack: 40 Deluxe Speed-Gro,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5132,Resource Pack: 2 Deluxe Fertilizer,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5133,Resource Pack: 6 Deluxe Fertilizer,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5134,Resource Pack: 10 Deluxe Fertilizer,useful,"FERTILIZER,RESOURCE_PACK",
5135,Resource Pack: 14 Deluxe Fertilizer,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5136,Resource Pack: 18 Deluxe Fertilizer,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5137,Resource Pack: 20 Deluxe Fertilizer,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5138,Resource Pack: 2 Deluxe Retaining Soil,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5139,Resource Pack: 6 Deluxe Retaining Soil,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5140,Resource Pack: 10 Deluxe Retaining Soil,useful,"FERTILIZER,RESOURCE_PACK",
5141,Resource Pack: 14 Deluxe Retaining Soil,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5142,Resource Pack: 18 Deluxe Retaining Soil,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5143,Resource Pack: 20 Deluxe Retaining Soil,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5144,Resource Pack: 2 Hyper Speed-Gro,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5145,Resource Pack: 6 Hyper Speed-Gro,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5146,Resource Pack: 10 Hyper Speed-Gro,useful,"FERTILIZER,RESOURCE_PACK",
5147,Resource Pack: 14 Hyper Speed-Gro,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5148,Resource Pack: 18 Hyper Speed-Gro,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5149,Resource Pack: 20 Hyper Speed-Gro,useful,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5150,Resource Pack: 2 Tree Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5151,Resource Pack: 6 Tree Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5152,Resource Pack: 10 Tree Fertilizer,filler,"FERTILIZER,RESOURCE_PACK",
5153,Resource Pack: 14 Tree Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5154,Resource Pack: 18 Tree Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5155,Resource Pack: 20 Tree Fertilizer,filler,"DEPRECATED,FERTILIZER,RESOURCE_PACK",
5156,Resource Pack: 10 Spring Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5157,Resource Pack: 20 Spring Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5158,Resource Pack: 30 Spring Seeds,filler,"RESOURCE_PACK,SEED",
5159,Resource Pack: 40 Spring Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5160,Resource Pack: 50 Spring Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5161,Resource Pack: 60 Spring Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5162,Resource Pack: 10 Summer Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5163,Resource Pack: 20 Summer Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5164,Resource Pack: 30 Summer Seeds,filler,"RESOURCE_PACK,SEED",
5165,Resource Pack: 40 Summer Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5166,Resource Pack: 50 Summer Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5167,Resource Pack: 60 Summer Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5168,Resource Pack: 10 Fall Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5169,Resource Pack: 20 Fall Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5170,Resource Pack: 30 Fall Seeds,filler,"RESOURCE_PACK,SEED",
5171,Resource Pack: 40 Fall Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5172,Resource Pack: 50 Fall Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5173,Resource Pack: 60 Fall Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5174,Resource Pack: 10 Winter Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5175,Resource Pack: 20 Winter Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5176,Resource Pack: 30 Winter Seeds,filler,"RESOURCE_PACK,SEED",
5177,Resource Pack: 40 Winter Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5178,Resource Pack: 50 Winter Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5179,Resource Pack: 60 Winter Seeds,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5180,Resource Pack: 1 Mahogany Seed,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5181,Resource Pack: 3 Mahogany Seed,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5182,Resource Pack: 5 Mahogany Seed,filler,"RESOURCE_PACK,SEED",
5183,Resource Pack: 7 Mahogany Seed,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5184,Resource Pack: 9 Mahogany Seed,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5185,Resource Pack: 10 Mahogany Seed,filler,"DEPRECATED,RESOURCE_PACK,SEED",
5186,Resource Pack: 10 Bait,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5187,Resource Pack: 20 Bait,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5188,Resource Pack: 30 Bait,filler,"FISHING_RESOURCE,RESOURCE_PACK",
5189,Resource Pack: 40 Bait,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5190,Resource Pack: 50 Bait,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5191,Resource Pack: 60 Bait,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5192,Resource Pack: 1 Crab Pot,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5193,Resource Pack: 2 Crab Pot,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5194,Resource Pack: 3 Crab Pot,filler,"FISHING_RESOURCE,RESOURCE_PACK",
5195,Resource Pack: 4 Crab Pot,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5196,Resource Pack: 5 Crab Pot,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5197,Resource Pack: 6 Crab Pot,filler,"DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK",
5198,Friendship Bonus (1 <3),useful,"DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK",
5199,Friendship Bonus (2 <3),useful,"FRIENDSHIP_PACK,COMMUNITY_REWARD",
5200,Friendship Bonus (3 <3),useful,"DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK",
5201,Friendship Bonus (4 <3),useful,"DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK",
5202,30 Qi Gems,useful,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5203,Solar Panel,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5204,Geode Crusher,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5205,Farm Computer,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5206,Bone Mill,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5207,Fiber Seeds,filler,RESOURCE_PACK,
5208,Chest,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5209,Stone Chest,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5210,Quality Bobber,filler,RESOURCE_PACK,
5211,Mini-Obelisk,filler,"EXACTLY_TWO,RESOURCE_PACK",
5212,Monster Musk,filler,RESOURCE_PACK,
5213,Sprinkler,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5214,Quality Sprinkler,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5215,Iridium Sprinkler,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5216,Scarecrow,filler,RESOURCE_PACK,
5217,Deluxe Scarecrow,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5218,Furnace,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5219,Charcoal Kiln,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5220,Lightning Rod,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5221,Resource Pack: 5000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5222,Resource Pack: 10000 Money,useful,"BASE_RESOURCE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5223,Junimo Chest,filler,"EXACTLY_TWO,RESOURCE_PACK",
5224,Horse Flute,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5225,Pierre's Missing Stocklist,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5226,Hopper,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5227,Enricher,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5228,Pressure Nozzle,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5229,Deconstructor,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5230,Key To The Town,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5231,Galaxy Soul,filler,"GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5232,Mushroom Tree Seed,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5233,Resource Pack: 20 Magic Bait,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5234,Resource Pack: 10 Qi Seasoning,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5235,Mr. Qi's Hat,filler,"MAXIMUM_ONE,RESOURCE_PACK",
5236,Aquatic Sanctuary,filler,RESOURCE_PACK,
5237,Heavy Tapper Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5238,Hyper Speed-Gro Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5239,Deluxe Fertilizer Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5240,Hopper Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5241,Magic Bait Recipe,filler,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5242,Exotic Double Bed,filler,RESOURCE_PACK,
5243,Resource Pack: 2 Qi Gem,filler,"GINGER_ISLAND,RESOURCE_PACK",
5244,Golden Egg,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5245,Golden Walnut,filler,"RESOURCE_PACK,RESOURCE_PACK_USEFUL,GINGER_ISLAND",
5246,Fairy Dust Recipe,useful,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5247,Fairy Dust,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5248,Seed Maker,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5249,Keg,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5250,Cask,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5251,Preserves Jar,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5252,Bee House,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5253,Garden Pot,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5254,Cheese Press,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5255,Mayonnaise Machine,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5256,Loom,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5257,Oil Maker,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5258,Recycling Machine,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5259,Worm Bin,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5260,Tapper,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5261,Heavy Tapper,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5262,Slime Incubator,useful,"RESOURCE_PACK",
5263,Slime Egg-Press,useful,"RESOURCE_PACK",
5264,Crystalarium,useful,"RESOURCE_PACK,RESOURCE_PACK_USEFUL",
5265,Ostrich Incubator,useful,"MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL",
10001,Luck Level,progression,SKILL_LEVEL_UP,Luck Skill
10002,Magic Level,progression,SKILL_LEVEL_UP,Magic
10003,Socializing Level,progression,SKILL_LEVEL_UP,Socializing Skill
10004,Archaeology Level,progression,SKILL_LEVEL_UP,Archaeology
10005,Cooking Level,progression,SKILL_LEVEL_UP,Cooking Skill
10006,Binning Level,progression,SKILL_LEVEL_UP,Binning Skill
10007,Tractor Garage,useful,,Tractor Mod
10008,Woods Obelisk,progression,,DeepWoods
10009,Spell: Clear Debris,progression,MAGIC_SPELL,Magic
10010,Spell: Till,useful,MAGIC_SPELL,Magic
10011,Spell: Water,progression,MAGIC_SPELL,Magic
10012,Spell: Blink,progression,MAGIC_SPELL,Magic
10013,Spell: Evac,useful,MAGIC_SPELL,Magic
10014,Spell: Haste,filler,MAGIC_SPELL,Magic
10015,Spell: Heal,progression,MAGIC_SPELL,Magic
10016,Spell: Buff,useful,MAGIC_SPELL,Magic
10017,Spell: Shockwave,progression,MAGIC_SPELL,Magic
10018,Spell: Fireball,progression,MAGIC_SPELL,Magic
10019,Spell: Frostbite,progression,MAGIC_SPELL,Magic
10020,Spell: Teleport,progression,MAGIC_SPELL,Magic
10021,Spell: Lantern,filler,MAGIC_SPELL,Magic
10022,Spell: Tendrils,progression,MAGIC_SPELL,Magic
10023,Spell: Photosynthesis,useful,MAGIC_SPELL,Magic
10024,Spell: Descend,progression,MAGIC_SPELL,Magic
10025,Spell: Meteor,progression,MAGIC_SPELL,Magic
10026,Spell: Bloodmana,useful,MAGIC_SPELL,Magic
10027,Spell: Lucksteal,useful,MAGIC_SPELL,Magic
10028,Spell: Spirit,progression,MAGIC_SPELL,Magic
10029,Spell: Rewind,useful,MAGIC_SPELL,Magic
10101,Juna <3,progression,FRIENDSANITY,Juna - Roommate NPC
10102,Jasper <3,progression,FRIENDSANITY,Professor Jasper Thomas
10103,Alec <3,progression,FRIENDSANITY,Alec Revisited
10104,Yoba <3,progression,FRIENDSANITY,Custom NPC - Yoba
10105,Eugene <3,progression,FRIENDSANITY,Custom NPC Eugene
10106,Wellwick <3,progression,FRIENDSANITY,'Prophet' Wellwick
10107,Mr. Ginger <3,progression,FRIENDSANITY,Mister Ginger (cat npc)
10108,Shiko <3,progression,FRIENDSANITY,Shiko - New Custom NPC
10109,Delores <3,progression,FRIENDSANITY,Delores - Custom NPC
10110,Ayeisha <3,progression,FRIENDSANITY,Ayeisha - The Postal Worker (Custom NPC)
10111,Riley <3,progression,FRIENDSANITY,Custom NPC - Riley
10301,Progressive Wood Obelisk Sigils,progression,,DeepWoods
10302,Progressive Skull Cavern Elevator,progression,,Skull Cavern Elevator

1 id name classification groups mod_name
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
8 20 Minecarts Repair useful COMMUNITY_REWARD
9 21 Bus Repair progression COMMUNITY_REWARD
10 22 Movie Theater useful
11 23 Stardrop useful progression
12 24 Progressive Backpack progression
13 25 Rusty Sword progression WEAPON
14 26 Leather Boots progression FOOTWEAR,MINES_FLOOR_10
15 27 Work Boots useful FOOTWEAR,MINES_FLOOR_10
16 28 Wooden Blade progression MINES_FLOOR_10,WEAPON
17 29 Iron Dirk progression MINES_FLOOR_10,WEAPON
18 30 Wind Spire progression MINES_FLOOR_10,WEAPON
19 31 Femur progression MINES_FLOOR_10,WEAPON
20 32 Steel Smallsword progression MINES_FLOOR_20,WEAPON
21 33 Wood Club progression MINES_FLOOR_20,WEAPON
22 34 Elf Blade progression MINES_FLOOR_20,WEAPON
23 35 Glow Ring useful MINES_FLOOR_20,RING
24 36 Magnet Ring useful MINES_FLOOR_20,RING
25 37 Slingshot progression WEAPON
26 38 Tundra Boots useful FOOTWEAR,MINES_FLOOR_50
27 39 Thermal Boots useful FOOTWEAR,MINES_FLOOR_50
28 40 Combat Boots useful FOOTWEAR,MINES_FLOOR_50
29 41 Silver Saber progression MINES_FLOOR_50,WEAPON
30 42 Pirate's Sword progression MINES_FLOOR_50,WEAPON
31 43 Crystal Dagger progression MINES_FLOOR_60,WEAPON
32 44 Cutlass progression MINES_FLOOR_60,WEAPON
33 45 Iron Edge progression MINES_FLOOR_60,WEAPON
34 46 Burglar's Shank progression MINES_FLOOR_60,WEAPON
35 47 Wood Mallet progression MINES_FLOOR_60,WEAPON
36 48 Master Slingshot progression WEAPON
37 49 Firewalker Boots useful FOOTWEAR,MINES_FLOOR_80
38 50 Dark Boots useful FOOTWEAR,MINES_FLOOR_80
39 51 Claymore progression MINES_FLOOR_80,WEAPON
40 52 Templar's Blade progression MINES_FLOOR_80,WEAPON
41 53 Kudgel progression MINES_FLOOR_80,WEAPON
42 54 Shadow Dagger progression MINES_FLOOR_80,WEAPON
43 55 Obsidian Edge progression MINES_FLOOR_90,WEAPON
44 56 Tempered Broadsword progression MINES_FLOOR_90,WEAPON
45 57 Wicked Kris progression MINES_FLOOR_90,WEAPON
46 58 Bone Sword progression MINES_FLOOR_90,WEAPON
47 59 Ossified Blade progression MINES_FLOOR_90,WEAPON
48 60 Space Boots useful FOOTWEAR,MINES_FLOOR_110
49 61 Crystal Shoes useful FOOTWEAR,MINES_FLOOR_110
50 62 Steel Falchion progression MINES_FLOOR_110,WEAPON
51 63 The Slammer progression MINES_FLOOR_110,WEAPON
52 64 Skull Key progression
53 65 Progressive Hoe progression PROGRESSIVE_TOOLS
54 66 Progressive Pickaxe progression PROGRESSIVE_TOOLS
55 67 Progressive Axe progression PROGRESSIVE_TOOLS
56 68 Progressive Watering Can progression PROGRESSIVE_TOOLS
57 69 Progressive Trash Can progression PROGRESSIVE_TOOLS
58 70 Progressive Fishing Rod progression PROGRESSIVE_TOOLS
59 71 Golden Scythe useful
60 72 Progressive Mine Elevator progression
61 73 Farming Level progression SKILL_LEVEL_UP
62 74 Fishing Level progression SKILL_LEVEL_UP
63 75 Foraging Level progression SKILL_LEVEL_UP
64 76 Mining Level progression SKILL_LEVEL_UP
65 77 Combat Level progression SKILL_LEVEL_UP
66 78 Earth Obelisk useful progression
67 79 Water Obelisk useful progression
68 80 Desert Obelisk progression
69 81 Island Obelisk progression GINGER_ISLAND
70 82 Junimo Hut useful
71 83 Gold Clock useful progression
72 84 Progressive Coop progression
73 85 Progressive Barn progression
74 86 Well useful
75 87 Silo progression
76 88 Mill progression
77 89 Progressive Shed progression
78 90 Fish Pond progression
79 91 Stable useful
80 92 Slime Hutch useful
81 93 Shipping Bin progression
82 94 Beach Bridge progression
83 95 Adventurer's Guild progression
84 96 Club Card progression
85 97 Magnifying Glass progression
86 98 Bear's Knowledge progression
87 99 Iridium Snake Milk progression
88 100 JotPK: Progressive Boots progression ARCADE_MACHINE_BUFFS
89 101 JotPK: Progressive Gun progression ARCADE_MACHINE_BUFFS
90 102 JotPK: Progressive Ammo progression ARCADE_MACHINE_BUFFS
91 103 JotPK: Extra Life progression ARCADE_MACHINE_BUFFS
92 104 JotPK: Increased Drop Rate progression ARCADE_MACHINE_BUFFS
93 105 Junimo Kart: Extra Life progression ARCADE_MACHINE_BUFFS
94 106 Galaxy Sword progression GALAXY_WEAPONS,WEAPON
95 107 Galaxy Dagger progression GALAXY_WEAPONS,WEAPON
96 108 Galaxy Hammer progression GALAXY_WEAPONS,WEAPON
97 109 Movement Speed Bonus useful progression
98 110 Luck Bonus useful progression
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 123 Progressive Season progression
112 124 Spring progression SEASON
113 125 Summer progression SEASON
114 126 Fall progression SEASON
115 127 Winter progression SEASON
116 128 Amaranth Seeds progression SEED_SHUFFLE CROPSANITY
117 129 Artichoke Seeds progression SEED_SHUFFLE CROPSANITY
118 130 Beet Seeds progression SEED_SHUFFLE CROPSANITY
119 131 Jazz Seeds progression SEED_SHUFFLE CROPSANITY
120 132 Blueberry Seeds progression SEED_SHUFFLE CROPSANITY
121 133 Bok Choy Seeds progression SEED_SHUFFLE CROPSANITY
122 134 Cauliflower Seeds progression SEED_SHUFFLE CROPSANITY
123 135 Corn Seeds progression SEED_SHUFFLE CROPSANITY
124 136 Cranberry Seeds progression SEED_SHUFFLE CROPSANITY
125 137 Eggplant Seeds progression SEED_SHUFFLE CROPSANITY
126 138 Fairy Seeds progression SEED_SHUFFLE CROPSANITY
127 139 Garlic Seeds progression SEED_SHUFFLE CROPSANITY
128 140 Grape Starter progression SEED_SHUFFLE CROPSANITY
129 141 Bean Starter progression SEED_SHUFFLE CROPSANITY
130 142 Hops Starter progression SEED_SHUFFLE CROPSANITY
131 143 Pepper Seeds progression SEED_SHUFFLE CROPSANITY
132 144 Kale Seeds progression SEED_SHUFFLE CROPSANITY
133 145 Melon Seeds progression SEED_SHUFFLE CROPSANITY
134 146 Parsnip Seeds progression SEED_SHUFFLE CROPSANITY
135 147 Poppy Seeds progression SEED_SHUFFLE CROPSANITY
136 148 Potato Seeds progression SEED_SHUFFLE CROPSANITY
137 149 Pumpkin Seeds progression SEED_SHUFFLE CROPSANITY
138 150 Radish Seeds progression SEED_SHUFFLE CROPSANITY
139 151 Red Cabbage Seeds progression SEED_SHUFFLE CROPSANITY
140 152 Rhubarb Seeds progression SEED_SHUFFLE CROPSANITY
141 153 Starfruit Seeds progression SEED_SHUFFLE CROPSANITY
142 154 Strawberry Seeds progression SEED_SHUFFLE CROPSANITY
143 155 Spangle Seeds progression SEED_SHUFFLE CROPSANITY
144 156 Sunflower Seeds progression SEED_SHUFFLE CROPSANITY
145 157 Tomato Seeds progression SEED_SHUFFLE CROPSANITY
146 158 Tulip Bulb progression SEED_SHUFFLE CROPSANITY
147 159 Rice Shoot progression SEED_SHUFFLE CROPSANITY
148 160 Wheat Seeds progression SEED_SHUFFLE CROPSANITY
149 161 Yam Seeds progression SEED_SHUFFLE CROPSANITY
150 162 Cactus Seeds progression SEED_SHUFFLE CROPSANITY
151 163 Magic Rock Candy useful MUSEUM MUSEUM,RESOURCE_PACK,RESOURCE_PACK_USEFUL
152 164 Ancient Seeds Recipe progression MUSEUM
153 165 Ancient Seeds useful progression MUSEUM MUSEUM,RESOURCE_PACK,RESOURCE_PACK_USEFUL
154 166 Traveling Merchant Metal Detector progression MUSEUM
155 167 Alex: 1 <3 Alex <3 progression FRIENDSANITY
156 168 Elliott: 1 <3 Elliott <3 progression FRIENDSANITY
157 169 Harvey: 1 <3 Harvey <3 progression FRIENDSANITY
158 170 Sam: 1 <3 Sam <3 progression FRIENDSANITY
159 171 Sebastian: 1 <3 Sebastian <3 progression FRIENDSANITY
160 172 Shane: 1 <3 Shane <3 progression FRIENDSANITY
161 173 Abigail: 1 <3 Abigail <3 progression FRIENDSANITY
162 174 Emily: 1 <3 Emily <3 progression FRIENDSANITY
163 175 Haley: 1 <3 Haley <3 progression FRIENDSANITY
164 176 Leah: 1 <3 Leah <3 progression FRIENDSANITY
165 177 Maru: 1 <3 Maru <3 progression FRIENDSANITY
166 178 Penny: 1 <3 Penny <3 progression FRIENDSANITY
167 179 Caroline: 1 <3 Caroline <3 progression FRIENDSANITY
168 180 Clint: 1 <3 Clint <3 progression FRIENDSANITY
169 181 Demetrius: 1 <3 Demetrius <3 progression FRIENDSANITY
170 182 Dwarf: 1 <3 Dwarf <3 progression FRIENDSANITY
171 183 Evelyn: 1 <3 Evelyn <3 progression FRIENDSANITY
172 184 George: 1 <3 George <3 progression FRIENDSANITY
173 185 Gus: 1 <3 Gus <3 progression FRIENDSANITY
174 186 Jas: 1 <3 Jas <3 progression FRIENDSANITY
175 187 Jodi: 1 <3 Jodi <3 progression FRIENDSANITY
176 188 Kent: 1 <3 Kent <3 progression FRIENDSANITY
177 189 Krobus: 1 <3 Krobus <3 progression FRIENDSANITY
178 190 Leo: 1 <3 Leo <3 progression FRIENDSANITY FRIENDSANITY,GINGER_ISLAND
179 191 Lewis: 1 <3 Lewis <3 progression FRIENDSANITY
180 192 Linus: 1 <3 Linus <3 progression FRIENDSANITY
181 193 Marnie: 1 <3 Marnie <3 progression FRIENDSANITY
182 194 Pam: 1 <3 Pam <3 progression FRIENDSANITY
183 195 Pierre: 1 <3 Pierre <3 progression FRIENDSANITY
184 196 Robin: 1 <3 Robin <3 progression FRIENDSANITY
185 197 Sandy: 1 <3 Sandy <3 progression FRIENDSANITY
186 198 Vincent: 1 <3 Vincent <3 progression FRIENDSANITY
187 199 Willy: 1 <3 Willy <3 progression FRIENDSANITY
188 200 Wizard: 1 <3 Wizard <3 progression FRIENDSANITY
189 201 Pet: 1 <3 Pet <3 progression FRIENDSANITY
190 5000 202 Resource Pack: 500 Money Rarecrow #1 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
191 5001 203 Resource Pack: 1000 Money Rarecrow #2 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
192 5002 204 Resource Pack: 1500 Money Rarecrow #3 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
193 5003 205 Resource Pack: 2000 Money Rarecrow #4 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
194 5004 206 Resource Pack: 25 Stone Rarecrow #5 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
195 5005 207 Resource Pack: 50 Stone Rarecrow #6 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
196 5006 208 Resource Pack: 75 Stone Rarecrow #7 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
197 5007 209 Resource Pack: 100 Stone Rarecrow #8 filler progression BASE_RESOURCE,RESOURCE_PACK FESTIVAL,RARECROW
198 5008 210 Resource Pack: 25 Wood Straw Hat filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
199 5009 211 Resource Pack: 50 Wood Golden Pumpkin filler useful BASE_RESOURCE,RESOURCE_PACK FESTIVAL
200 5010 212 Resource Pack: 75 Wood Barbed Hook filler useful BASE_RESOURCE,RESOURCE_PACK FESTIVAL
201 5011 213 Resource Pack: 100 Wood Dressed Spinner filler useful BASE_RESOURCE,RESOURCE_PACK FESTIVAL
202 5012 214 Resource Pack: 5 Hardwood Magnet filler useful BASE_RESOURCE,RESOURCE_PACK FESTIVAL
203 5013 215 Resource Pack: 10 Hardwood Sailor's Cap filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
204 5014 216 Resource Pack: 15 Hardwood Pearl filler useful BASE_RESOURCE,RESOURCE_PACK FESTIVAL
205 5015 217 Resource Pack: 20 Hardwood Cone Hat filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
206 5016 218 Resource Pack: 15 Fiber Iridium Fireplace filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
207 5017 219 Resource Pack: 30 Fiber Lupini: Red Eagle filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
208 5018 220 Resource Pack: 45 Fiber Lupini: Portrait Of A Mermaid filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
209 5019 221 Resource Pack: 60 Fiber Lupini: Solar Kingdom filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
210 5020 222 Resource Pack: 5 Coal Lupini: Clouds filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
211 5021 223 Resource Pack: 10 Coal Lupini: 1000 Years From Now filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
212 5022 224 Resource Pack: 15 Coal Lupini: Three Trees filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
213 5023 225 Resource Pack: 20 Coal Lupini: The Serpent filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
214 5024 226 Resource Pack: 5 Clay Lupini: 'Tropical Fish #173' filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
215 5025 227 Resource Pack: 10 Clay Lupini: Land Of Clay filler BASE_RESOURCE,RESOURCE_PACK FESTIVAL
216 5026 228 Resource Pack: 15 Clay Special Order Board filler progression BASE_RESOURCE,RESOURCE_PACK SPECIAL_ORDER_BOARD
217 5027 229 Resource Pack: 20 Clay Solar Panel Recipe filler progression BASE_RESOURCE,RESOURCE_PACK SPECIAL_ORDER_BOARD
218 5028 230 Resource Pack: 1 Warp Totem: Beach Geode Crusher Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
219 5029 231 Resource Pack: 3 Warp Totem: Beach Farm Computer Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
220 5030 232 Resource Pack: 5 Warp Totem: Beach Bone Mill Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
221 5031 233 Resource Pack: 7 Warp Totem: Beach Fiber Seeds Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
222 5032 234 Resource Pack: 9 Warp Totem: Beach Stone Chest Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
223 5033 235 Resource Pack: 10 Warp Totem: Beach Quality Bobber Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
224 5034 236 Resource Pack: 1 Warp Totem: Desert Mini-Obelisk Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
225 5035 237 Resource Pack: 3 Warp Totem: Desert Monster Musk Recipe filler progression RESOURCE_PACK,WARP_TOTEM SPECIAL_ORDER_BOARD
226 5036 239 Resource Pack: 5 Warp Totem: Desert Sewing Machine filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD
227 5037 240 Resource Pack: 7 Warp Totem: Desert Coffee Maker filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD
228 5038 241 Resource Pack: 9 Warp Totem: Desert Mini-Fridge filler useful RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD
229 5039 242 Resource Pack: 10 Warp Totem: Desert Mini-Shipping Bin filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD
230 5040 243 Resource Pack: 1 Warp Totem: Farm Deluxe Fish Tank filler useful RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK_USEFUL,SPECIAL_ORDER_BOARD
231 5041 244 Resource Pack: 3 Warp Totem: Farm 10 Qi Gems filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,SPECIAL_ORDER_QI
232 5042 245 Resource Pack: 5 Warp Totem: Farm 20 Qi Gems filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,SPECIAL_ORDER_QI
233 5043 246 Resource Pack: 7 Warp Totem: Farm 25 Qi Gems filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,SPECIAL_ORDER_QI
234 5044 247 Resource Pack: 9 Warp Totem: Farm 35 Qi Gems filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,SPECIAL_ORDER_QI
235 5045 248 Resource Pack: 10 Warp Totem: Farm 40 Qi Gems filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,SPECIAL_ORDER_QI
236 5046 249 Resource Pack: 1 Warp Totem: Island 50 Qi Gems filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,SPECIAL_ORDER_QI
237 5047 250 Resource Pack: 3 Warp Totem: Island 100 Qi Gems filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,SPECIAL_ORDER_QI
238 5048 251 Resource Pack: 5 Warp Totem: Island Rare Seed filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
239 5049 252 Resource Pack: 7 Warp Totem: Island Apple Sapling filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
240 5050 253 Resource Pack: 9 Warp Totem: Island Apricot Sapling filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
241 5051 254 Resource Pack: 10 Warp Totem: Island Cherry Sapling filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
242 5052 255 Resource Pack: 1 Warp Totem: Mountains Orange Sapling filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
243 5053 256 Resource Pack: 3 Warp Totem: Mountains Pomegranate Sapling filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
244 5054 257 Resource Pack: 5 Warp Totem: Mountains Peach Sapling filler progression RESOURCE_PACK,WARP_TOTEM RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
245 5055 258 Resource Pack: 7 Warp Totem: Mountains Banana Sapling filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
246 5056 259 Resource Pack: 9 Warp Totem: Mountains Mango Sapling filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL,CROPSANITY
247 5057 260 Resource Pack: 10 Warp Totem: Mountains Boat Repair filler progression RESOURCE_PACK,WARP_TOTEM GINGER_ISLAND
248 5058 261 Resource Pack: 6 Geode Open Professor Snail Cave filler progression GEODE,RESOURCE_PACK GINGER_ISLAND
249 5059 262 Resource Pack: 12 Geode Island North Turtle filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
250 5060 263 Resource Pack: 18 Geode Island West Turtle filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
251 5061 264 Resource Pack: 24 Geode Island Farmhouse filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
252 5062 265 Resource Pack: 4 Frozen Geode Island Mailbox filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
253 5063 266 Resource Pack: 8 Frozen Geode Farm Obelisk filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
254 5064 267 Resource Pack: 12 Frozen Geode Dig Site Bridge filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
255 5065 268 Resource Pack: 16 Frozen Geode Island Trader filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
256 5066 269 Resource Pack: 3 Magma Geode Volcano Bridge filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
257 5067 270 Resource Pack: 6 Magma Geode Volcano Exit Shortcut filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
258 5068 271 Resource Pack: 9 Magma Geode Island Resort filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
259 5069 272 Resource Pack: 12 Magma Geode Parrot Express filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
260 5070 273 Resource Pack: 2 Omni Geode Qi Walnut Room filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,WALNUT_PURCHASE
261 5071 274 Resource Pack: 4 Omni Geode Pineapple Seeds filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,CROPSANITY
262 5072 275 Resource Pack: 6 Omni Geode Taro Tuber filler progression GEODE,RESOURCE_PACK GINGER_ISLAND,CROPSANITY
263 5073 276 Resource Pack: 8 Omni Geode Weather Report filler useful GEODE,RESOURCE_PACK TV_CHANNEL
264 5074 277 Resource Pack: 25 Copper Ore Fortune Teller filler useful ORE,RESOURCE_PACK TV_CHANNEL
265 5075 278 Resource Pack: 50 Copper Ore Livin' Off The Land filler useful ORE,RESOURCE_PACK TV_CHANNEL
266 5076 279 Resource Pack: 75 Copper Ore The Queen of Sauce filler progression ORE,RESOURCE_PACK TV_CHANNEL
267 5077 280 Resource Pack: 100 Copper Ore Fishing Information Broadcasting Service filler useful ORE,RESOURCE_PACK TV_CHANNEL
268 5078 281 Resource Pack: 125 Copper Ore Sinister Signal filler useful ORE,RESOURCE_PACK TV_CHANNEL
269 5079 282 Resource Pack: 150 Copper Ore Dark Talisman filler progression ORE,RESOURCE_PACK
270 5080 283 Resource Pack: 25 Iron Ore Ostrich Incubator Recipe filler progression ORE,RESOURCE_PACK GINGER_ISLAND
271 5081 284 Resource Pack: 50 Iron Ore Cute Baby filler progression ORE,RESOURCE_PACK BABY
272 5082 285 Resource Pack: 75 Iron Ore Ugly Baby filler progression ORE,RESOURCE_PACK BABY
273 5083 286 Resource Pack: 100 Iron Ore Deluxe Scarecrow Recipe filler progression ORE,RESOURCE_PACK FESTIVAL,RARECROW
274 5084 4001 Resource Pack: 12 Gold Ore Burnt filler trap ORE,RESOURCE_PACK TRAP
275 5085 4002 Resource Pack: 25 Gold Ore Darkness filler trap ORE,RESOURCE_PACK TRAP
276 5086 4003 Resource Pack: 38 Gold Ore Frozen filler trap ORE,RESOURCE_PACK TRAP
277 5087 4004 Resource Pack: 50 Gold Ore Jinxed filler trap ORE,RESOURCE_PACK TRAP
278 5088 4005 Resource Pack: 5 Iridium Ore Nauseated filler trap ORE,RESOURCE_PACK TRAP
279 5089 4006 Resource Pack: 10 Iridium Ore Slimed filler trap ORE,RESOURCE_PACK TRAP
280 5090 4007 Resource Pack: 15 Iridium Ore Weakness filler trap ORE,RESOURCE_PACK TRAP
281 5091 4008 Resource Pack: 20 Iridium Ore Taxes filler trap ORE,RESOURCE_PACK TRAP
282 5092 4009 Resource Pack: 5 Quartz Random Teleport filler trap ORE,RESOURCE_PACK TRAP
283 5093 4010 Resource Pack: 10 Quartz The Crows filler trap ORE,RESOURCE_PACK TRAP
284 5094 4011 Resource Pack: 15 Quartz Monsters filler trap ORE,RESOURCE_PACK TRAP
285 5095 4012 Resource Pack: 20 Quartz Entrance Reshuffle filler trap ORE,RESOURCE_PACK TRAP,DEPRECATED
286 5096 4013 Resource Pack: 10 Basic Fertilizer Debris filler trap FERTILIZER,RESOURCE_PACK TRAP
287 5097 4014 Resource Pack: 20 Basic Fertilizer Shuffle filler trap FERTILIZER,RESOURCE_PACK TRAP
288 5098 4015 Resource Pack: 30 Basic Fertilizer Temporary Winter filler trap FERTILIZER,RESOURCE_PACK TRAP,DEPRECATED
289 5099 4016 Resource Pack: 40 Basic Fertilizer Pariah filler trap FERTILIZER,RESOURCE_PACK TRAP
290 5100 4017 Resource Pack: 50 Basic Fertilizer Drought filler trap FERTILIZER,RESOURCE_PACK TRAP
291 5101 5000 Resource Pack: 60 Basic Fertilizer Resource Pack: 500 Money filler useful FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
292 5102 5001 Resource Pack: 10 Basic Retaining Soil Resource Pack: 1000 Money filler useful FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
293 5103 5002 Resource Pack: 20 Basic Retaining Soil Resource Pack: 1500 Money filler useful FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
294 5104 5003 Resource Pack: 30 Basic Retaining Soil Resource Pack: 2000 Money filler useful FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
295 5105 5004 Resource Pack: 40 Basic Retaining Soil Resource Pack: 25 Stone filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
296 5106 5005 Resource Pack: 50 Basic Retaining Soil Resource Pack: 50 Stone filler FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
297 5107 5006 Resource Pack: 60 Basic Retaining Soil Resource Pack: 75 Stone filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
298 5108 5007 Resource Pack: 10 Speed-Gro Resource Pack: 100 Stone filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
299 5109 5008 Resource Pack: 20 Speed-Gro Resource Pack: 25 Wood filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
300 5110 5009 Resource Pack: 30 Speed-Gro Resource Pack: 50 Wood filler FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
301 5111 5010 Resource Pack: 40 Speed-Gro Resource Pack: 75 Wood filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
302 5112 5011 Resource Pack: 50 Speed-Gro Resource Pack: 100 Wood filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
303 5113 5012 Resource Pack: 60 Speed-Gro Resource Pack: 5 Hardwood filler useful FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
304 5114 5013 Resource Pack: 4 Quality Fertilizer Resource Pack: 10 Hardwood filler useful FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
305 5115 5014 Resource Pack: 12 Quality Fertilizer Resource Pack: 15 Hardwood filler useful FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
306 5116 5015 Resource Pack: 20 Quality Fertilizer Resource Pack: 20 Hardwood filler useful FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
307 5117 5016 Resource Pack: 28 Quality Fertilizer Resource Pack: 15 Fiber filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
308 5118 5017 Resource Pack: 36 Quality Fertilizer Resource Pack: 30 Fiber filler FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
309 5119 5018 Resource Pack: 40 Quality Fertilizer Resource Pack: 45 Fiber filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
310 5120 5019 Resource Pack: 4 Quality Retaining Soil Resource Pack: 60 Fiber filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
311 5121 5020 Resource Pack: 12 Quality Retaining Soil Resource Pack: 5 Coal filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
312 5122 5021 Resource Pack: 20 Quality Retaining Soil Resource Pack: 10 Coal filler FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
313 5123 5022 Resource Pack: 28 Quality Retaining Soil Resource Pack: 15 Coal filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
314 5124 5023 Resource Pack: 36 Quality Retaining Soil Resource Pack: 20 Coal filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
315 5125 5024 Resource Pack: 40 Quality Retaining Soil Resource Pack: 5 Clay filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
316 5126 5025 Resource Pack: 4 Deluxe Speed-Gro Resource Pack: 10 Clay filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
317 5127 5026 Resource Pack: 12 Deluxe Speed-Gro Resource Pack: 15 Clay filler FERTILIZER,RESOURCE_PACK DEPRECATED,BASE_RESOURCE,RESOURCE_PACK
318 5128 5027 Resource Pack: 20 Deluxe Speed-Gro Resource Pack: 20 Clay filler FERTILIZER,RESOURCE_PACK BASE_RESOURCE,RESOURCE_PACK
319 5129 5028 Resource Pack: 28 Deluxe Speed-Gro Resource Pack: 1 Warp Totem: Beach filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
320 5130 5029 Resource Pack: 36 Deluxe Speed-Gro Resource Pack: 3 Warp Totem: Beach filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
321 5131 5030 Resource Pack: 40 Deluxe Speed-Gro Resource Pack: 5 Warp Totem: Beach filler FERTILIZER,RESOURCE_PACK RESOURCE_PACK,WARP_TOTEM
322 5132 5031 Resource Pack: 2 Deluxe Fertilizer Resource Pack: 7 Warp Totem: Beach filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
323 5133 5032 Resource Pack: 6 Deluxe Fertilizer Resource Pack: 9 Warp Totem: Beach filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
324 5134 5033 Resource Pack: 10 Deluxe Fertilizer Resource Pack: 10 Warp Totem: Beach filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
325 5135 5034 Resource Pack: 14 Deluxe Fertilizer Resource Pack: 1 Warp Totem: Desert filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
326 5136 5035 Resource Pack: 18 Deluxe Fertilizer Resource Pack: 3 Warp Totem: Desert filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
327 5137 5036 Resource Pack: 20 Deluxe Fertilizer Resource Pack: 5 Warp Totem: Desert filler FERTILIZER,RESOURCE_PACK RESOURCE_PACK,WARP_TOTEM
328 5138 5037 Resource Pack: 2 Deluxe Retaining Soil Resource Pack: 7 Warp Totem: Desert filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
329 5139 5038 Resource Pack: 6 Deluxe Retaining Soil Resource Pack: 9 Warp Totem: Desert filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
330 5140 5039 Resource Pack: 10 Deluxe Retaining Soil Resource Pack: 10 Warp Totem: Desert filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
331 5141 5040 Resource Pack: 14 Deluxe Retaining Soil Resource Pack: 1 Warp Totem: Farm filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
332 5142 5041 Resource Pack: 18 Deluxe Retaining Soil Resource Pack: 3 Warp Totem: Farm filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
333 5143 5042 Resource Pack: 20 Deluxe Retaining Soil Resource Pack: 5 Warp Totem: Farm filler FERTILIZER,RESOURCE_PACK RESOURCE_PACK,WARP_TOTEM
334 5144 5043 Resource Pack: 2 Hyper Speed-Gro Resource Pack: 7 Warp Totem: Farm filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
335 5145 5044 Resource Pack: 6 Hyper Speed-Gro Resource Pack: 9 Warp Totem: Farm filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
336 5146 5045 Resource Pack: 10 Hyper Speed-Gro Resource Pack: 10 Warp Totem: Farm filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
337 5147 5046 Resource Pack: 14 Hyper Speed-Gro Resource Pack: 1 Warp Totem: Island filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
338 5148 5047 Resource Pack: 18 Hyper Speed-Gro Resource Pack: 3 Warp Totem: Island filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
339 5149 5048 Resource Pack: 20 Hyper Speed-Gro Resource Pack: 5 Warp Totem: Island filler FERTILIZER,RESOURCE_PACK RESOURCE_PACK,WARP_TOTEM
340 5150 5049 Resource Pack: 2 Tree Fertilizer Resource Pack: 7 Warp Totem: Island filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
341 5151 5050 Resource Pack: 6 Tree Fertilizer Resource Pack: 9 Warp Totem: Island filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
342 5152 5051 Resource Pack: 10 Tree Fertilizer Resource Pack: 10 Warp Totem: Island filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
343 5153 5052 Resource Pack: 14 Tree Fertilizer Resource Pack: 1 Warp Totem: Mountains filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
344 5154 5053 Resource Pack: 18 Tree Fertilizer Resource Pack: 3 Warp Totem: Mountains filler FERTILIZER,RESOURCE_PACK DEPRECATED,RESOURCE_PACK,WARP_TOTEM
345 5155 5054 Resource Pack: 20 Tree Fertilizer Resource Pack: 5 Warp Totem: Mountains filler FERTILIZER,RESOURCE_PACK RESOURCE_PACK,WARP_TOTEM
346 5156 5055 Resource Pack: 10 Spring Seeds Resource Pack: 7 Warp Totem: Mountains filler RESOURCE_PACK,SEED DEPRECATED,RESOURCE_PACK,WARP_TOTEM
347 5157 5056 Resource Pack: 20 Spring Seeds Resource Pack: 9 Warp Totem: Mountains filler RESOURCE_PACK,SEED DEPRECATED,RESOURCE_PACK,WARP_TOTEM
348 5158 5057 Resource Pack: 30 Spring Seeds Resource Pack: 10 Warp Totem: Mountains filler RESOURCE_PACK,SEED DEPRECATED,RESOURCE_PACK,WARP_TOTEM
349 5159 5058 Resource Pack: 40 Spring Seeds Resource Pack: 6 Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
350 5160 5059 Resource Pack: 50 Spring Seeds Resource Pack: 12 Geode filler RESOURCE_PACK,SEED GEODE,RESOURCE_PACK
351 5161 5060 Resource Pack: 60 Spring Seeds Resource Pack: 18 Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
352 5162 5061 Resource Pack: 10 Summer Seeds Resource Pack: 24 Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
353 5163 5062 Resource Pack: 20 Summer Seeds Resource Pack: 4 Frozen Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
354 5164 5063 Resource Pack: 30 Summer Seeds Resource Pack: 8 Frozen Geode filler RESOURCE_PACK,SEED GEODE,RESOURCE_PACK
355 5165 5064 Resource Pack: 40 Summer Seeds Resource Pack: 12 Frozen Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
356 5166 5065 Resource Pack: 50 Summer Seeds Resource Pack: 16 Frozen Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
357 5167 5066 Resource Pack: 60 Summer Seeds Resource Pack: 3 Magma Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
358 5168 5067 Resource Pack: 10 Fall Seeds Resource Pack: 6 Magma Geode filler RESOURCE_PACK,SEED GEODE,RESOURCE_PACK
359 5169 5068 Resource Pack: 20 Fall Seeds Resource Pack: 9 Magma Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
360 5170 5069 Resource Pack: 30 Fall Seeds Resource Pack: 12 Magma Geode filler RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
361 5171 5070 Resource Pack: 40 Fall Seeds Resource Pack: 2 Omni Geode filler useful RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
362 5172 5071 Resource Pack: 50 Fall Seeds Resource Pack: 4 Omni Geode filler useful RESOURCE_PACK,SEED GEODE,RESOURCE_PACK
363 5173 5072 Resource Pack: 60 Fall Seeds Resource Pack: 6 Omni Geode filler useful RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
364 5174 5073 Resource Pack: 10 Winter Seeds Resource Pack: 8 Omni Geode filler useful RESOURCE_PACK,SEED DEPRECATED,GEODE,RESOURCE_PACK
365 5175 5074 Resource Pack: 20 Winter Seeds Resource Pack: 25 Copper Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
366 5176 5075 Resource Pack: 30 Winter Seeds Resource Pack: 50 Copper Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
367 5177 5076 Resource Pack: 40 Winter Seeds Resource Pack: 75 Copper Ore filler RESOURCE_PACK,SEED ORE,RESOURCE_PACK
368 5178 5077 Resource Pack: 50 Winter Seeds Resource Pack: 100 Copper Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
369 5179 5078 Resource Pack: 60 Winter Seeds Resource Pack: 125 Copper Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
370 5180 5079 Resource Pack: 1 Mahogany Seed Resource Pack: 150 Copper Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
371 5181 5080 Resource Pack: 3 Mahogany Seed Resource Pack: 25 Iron Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
372 5182 5081 Resource Pack: 5 Mahogany Seed Resource Pack: 50 Iron Ore filler RESOURCE_PACK,SEED ORE,RESOURCE_PACK
373 5183 5082 Resource Pack: 7 Mahogany Seed Resource Pack: 75 Iron Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
374 5184 5083 Resource Pack: 9 Mahogany Seed Resource Pack: 100 Iron Ore filler RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
375 5185 5084 Resource Pack: 10 Mahogany Seed Resource Pack: 12 Gold Ore filler useful RESOURCE_PACK,SEED DEPRECATED,ORE,RESOURCE_PACK
376 5186 5085 Resource Pack: 10 Bait Resource Pack: 25 Gold Ore filler useful FISHING_RESOURCE,RESOURCE_PACK ORE,RESOURCE_PACK
377 5187 5086 Resource Pack: 20 Bait Resource Pack: 38 Gold Ore filler useful FISHING_RESOURCE,RESOURCE_PACK DEPRECATED,ORE,RESOURCE_PACK
378 5188 5087 Resource Pack: 30 Bait Resource Pack: 50 Gold Ore filler useful FISHING_RESOURCE,RESOURCE_PACK DEPRECATED,ORE,RESOURCE_PACK
379 5189 5088 Resource Pack: 40 Bait Resource Pack: 5 Iridium Ore filler useful FISHING_RESOURCE,RESOURCE_PACK DEPRECATED,ORE,RESOURCE_PACK
380 5190 5089 Resource Pack: 50 Bait Resource Pack: 10 Iridium Ore filler useful FISHING_RESOURCE,RESOURCE_PACK ORE,RESOURCE_PACK
381 5191 5090 Resource Pack: 60 Bait Resource Pack: 15 Iridium Ore filler useful FISHING_RESOURCE,RESOURCE_PACK DEPRECATED,ORE,RESOURCE_PACK
382 5192 5091 Resource Pack: 1 Crab Pot Resource Pack: 20 Iridium Ore filler useful FISHING_RESOURCE,RESOURCE_PACK DEPRECATED,ORE,RESOURCE_PACK
383 5193 5092 Resource Pack: 2 Crab Pot Resource Pack: 5 Quartz filler FISHING_RESOURCE,RESOURCE_PACK ORE,RESOURCE_PACK
384 5194 5093 Resource Pack: 3 Crab Pot Resource Pack: 10 Quartz filler FISHING_RESOURCE,RESOURCE_PACK ORE,DEPRECATED,RESOURCE_PACK
385 5195 5094 Resource Pack: 4 Crab Pot Resource Pack: 15 Quartz filler FISHING_RESOURCE,RESOURCE_PACK ORE,DEPRECATED,RESOURCE_PACK
386 5196 5095 Resource Pack: 5 Crab Pot Resource Pack: 20 Quartz filler FISHING_RESOURCE,RESOURCE_PACK ORE,DEPRECATED,RESOURCE_PACK
387 5197 5096 Resource Pack: 6 Crab Pot Resource Pack: 10 Basic Fertilizer filler FISHING_RESOURCE,RESOURCE_PACK DEPRECATED,FERTILIZER,RESOURCE_PACK
388 5198 5097 Friendship Bonus (1 <3) Resource Pack: 20 Basic Fertilizer filler FRIENDSHIP_PACK DEPRECATED,FERTILIZER,RESOURCE_PACK
389 5199 5098 Friendship Bonus (2 <3) Resource Pack: 30 Basic Fertilizer filler FRIENDSHIP_PACK FERTILIZER,RESOURCE_PACK
390 5200 5099 Friendship Bonus (3 <3) Resource Pack: 40 Basic Fertilizer filler FRIENDSHIP_PACK DEPRECATED,FERTILIZER,RESOURCE_PACK
391 5201 5100 Friendship Bonus (4 <3) Resource Pack: 50 Basic Fertilizer filler FRIENDSHIP_PACK DEPRECATED,FERTILIZER,RESOURCE_PACK
392 5101 Resource Pack: 60 Basic Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
393 5102 Resource Pack: 10 Basic Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
394 5103 Resource Pack: 20 Basic Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
395 5104 Resource Pack: 30 Basic Retaining Soil filler FERTILIZER,RESOURCE_PACK
396 5105 Resource Pack: 40 Basic Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
397 5106 Resource Pack: 50 Basic Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
398 5107 Resource Pack: 60 Basic Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
399 5108 Resource Pack: 10 Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
400 5109 Resource Pack: 20 Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
401 5110 Resource Pack: 30 Speed-Gro filler FERTILIZER,RESOURCE_PACK
402 5111 Resource Pack: 40 Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
403 5112 Resource Pack: 50 Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
404 5113 Resource Pack: 60 Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
405 5114 Resource Pack: 4 Quality Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
406 5115 Resource Pack: 12 Quality Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
407 5116 Resource Pack: 20 Quality Fertilizer filler FERTILIZER,RESOURCE_PACK
408 5117 Resource Pack: 28 Quality Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
409 5118 Resource Pack: 36 Quality Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
410 5119 Resource Pack: 40 Quality Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
411 5120 Resource Pack: 4 Quality Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
412 5121 Resource Pack: 12 Quality Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
413 5122 Resource Pack: 20 Quality Retaining Soil filler FERTILIZER,RESOURCE_PACK
414 5123 Resource Pack: 28 Quality Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
415 5124 Resource Pack: 36 Quality Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
416 5125 Resource Pack: 40 Quality Retaining Soil filler DEPRECATED,FERTILIZER,RESOURCE_PACK
417 5126 Resource Pack: 4 Deluxe Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
418 5127 Resource Pack: 12 Deluxe Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
419 5128 Resource Pack: 20 Deluxe Speed-Gro filler FERTILIZER,RESOURCE_PACK
420 5129 Resource Pack: 28 Deluxe Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
421 5130 Resource Pack: 36 Deluxe Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
422 5131 Resource Pack: 40 Deluxe Speed-Gro filler DEPRECATED,FERTILIZER,RESOURCE_PACK
423 5132 Resource Pack: 2 Deluxe Fertilizer useful DEPRECATED,FERTILIZER,RESOURCE_PACK
424 5133 Resource Pack: 6 Deluxe Fertilizer useful DEPRECATED,FERTILIZER,RESOURCE_PACK
425 5134 Resource Pack: 10 Deluxe Fertilizer useful FERTILIZER,RESOURCE_PACK
426 5135 Resource Pack: 14 Deluxe Fertilizer useful DEPRECATED,FERTILIZER,RESOURCE_PACK
427 5136 Resource Pack: 18 Deluxe Fertilizer useful DEPRECATED,FERTILIZER,RESOURCE_PACK
428 5137 Resource Pack: 20 Deluxe Fertilizer useful DEPRECATED,FERTILIZER,RESOURCE_PACK
429 5138 Resource Pack: 2 Deluxe Retaining Soil useful DEPRECATED,FERTILIZER,RESOURCE_PACK
430 5139 Resource Pack: 6 Deluxe Retaining Soil useful DEPRECATED,FERTILIZER,RESOURCE_PACK
431 5140 Resource Pack: 10 Deluxe Retaining Soil useful FERTILIZER,RESOURCE_PACK
432 5141 Resource Pack: 14 Deluxe Retaining Soil useful DEPRECATED,FERTILIZER,RESOURCE_PACK
433 5142 Resource Pack: 18 Deluxe Retaining Soil useful DEPRECATED,FERTILIZER,RESOURCE_PACK
434 5143 Resource Pack: 20 Deluxe Retaining Soil useful DEPRECATED,FERTILIZER,RESOURCE_PACK
435 5144 Resource Pack: 2 Hyper Speed-Gro useful DEPRECATED,FERTILIZER,RESOURCE_PACK
436 5145 Resource Pack: 6 Hyper Speed-Gro useful DEPRECATED,FERTILIZER,RESOURCE_PACK
437 5146 Resource Pack: 10 Hyper Speed-Gro useful FERTILIZER,RESOURCE_PACK
438 5147 Resource Pack: 14 Hyper Speed-Gro useful DEPRECATED,FERTILIZER,RESOURCE_PACK
439 5148 Resource Pack: 18 Hyper Speed-Gro useful DEPRECATED,FERTILIZER,RESOURCE_PACK
440 5149 Resource Pack: 20 Hyper Speed-Gro useful DEPRECATED,FERTILIZER,RESOURCE_PACK
441 5150 Resource Pack: 2 Tree Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
442 5151 Resource Pack: 6 Tree Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
443 5152 Resource Pack: 10 Tree Fertilizer filler FERTILIZER,RESOURCE_PACK
444 5153 Resource Pack: 14 Tree Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
445 5154 Resource Pack: 18 Tree Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
446 5155 Resource Pack: 20 Tree Fertilizer filler DEPRECATED,FERTILIZER,RESOURCE_PACK
447 5156 Resource Pack: 10 Spring Seeds filler DEPRECATED,RESOURCE_PACK,SEED
448 5157 Resource Pack: 20 Spring Seeds filler DEPRECATED,RESOURCE_PACK,SEED
449 5158 Resource Pack: 30 Spring Seeds filler RESOURCE_PACK,SEED
450 5159 Resource Pack: 40 Spring Seeds filler DEPRECATED,RESOURCE_PACK,SEED
451 5160 Resource Pack: 50 Spring Seeds filler DEPRECATED,RESOURCE_PACK,SEED
452 5161 Resource Pack: 60 Spring Seeds filler DEPRECATED,RESOURCE_PACK,SEED
453 5162 Resource Pack: 10 Summer Seeds filler DEPRECATED,RESOURCE_PACK,SEED
454 5163 Resource Pack: 20 Summer Seeds filler DEPRECATED,RESOURCE_PACK,SEED
455 5164 Resource Pack: 30 Summer Seeds filler RESOURCE_PACK,SEED
456 5165 Resource Pack: 40 Summer Seeds filler DEPRECATED,RESOURCE_PACK,SEED
457 5166 Resource Pack: 50 Summer Seeds filler DEPRECATED,RESOURCE_PACK,SEED
458 5167 Resource Pack: 60 Summer Seeds filler DEPRECATED,RESOURCE_PACK,SEED
459 5168 Resource Pack: 10 Fall Seeds filler DEPRECATED,RESOURCE_PACK,SEED
460 5169 Resource Pack: 20 Fall Seeds filler DEPRECATED,RESOURCE_PACK,SEED
461 5170 Resource Pack: 30 Fall Seeds filler RESOURCE_PACK,SEED
462 5171 Resource Pack: 40 Fall Seeds filler DEPRECATED,RESOURCE_PACK,SEED
463 5172 Resource Pack: 50 Fall Seeds filler DEPRECATED,RESOURCE_PACK,SEED
464 5173 Resource Pack: 60 Fall Seeds filler DEPRECATED,RESOURCE_PACK,SEED
465 5174 Resource Pack: 10 Winter Seeds filler DEPRECATED,RESOURCE_PACK,SEED
466 5175 Resource Pack: 20 Winter Seeds filler DEPRECATED,RESOURCE_PACK,SEED
467 5176 Resource Pack: 30 Winter Seeds filler RESOURCE_PACK,SEED
468 5177 Resource Pack: 40 Winter Seeds filler DEPRECATED,RESOURCE_PACK,SEED
469 5178 Resource Pack: 50 Winter Seeds filler DEPRECATED,RESOURCE_PACK,SEED
470 5179 Resource Pack: 60 Winter Seeds filler DEPRECATED,RESOURCE_PACK,SEED
471 5180 Resource Pack: 1 Mahogany Seed filler DEPRECATED,RESOURCE_PACK,SEED
472 5181 Resource Pack: 3 Mahogany Seed filler DEPRECATED,RESOURCE_PACK,SEED
473 5182 Resource Pack: 5 Mahogany Seed filler RESOURCE_PACK,SEED
474 5183 Resource Pack: 7 Mahogany Seed filler DEPRECATED,RESOURCE_PACK,SEED
475 5184 Resource Pack: 9 Mahogany Seed filler DEPRECATED,RESOURCE_PACK,SEED
476 5185 Resource Pack: 10 Mahogany Seed filler DEPRECATED,RESOURCE_PACK,SEED
477 5186 Resource Pack: 10 Bait filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
478 5187 Resource Pack: 20 Bait filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
479 5188 Resource Pack: 30 Bait filler FISHING_RESOURCE,RESOURCE_PACK
480 5189 Resource Pack: 40 Bait filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
481 5190 Resource Pack: 50 Bait filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
482 5191 Resource Pack: 60 Bait filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
483 5192 Resource Pack: 1 Crab Pot filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
484 5193 Resource Pack: 2 Crab Pot filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
485 5194 Resource Pack: 3 Crab Pot filler FISHING_RESOURCE,RESOURCE_PACK
486 5195 Resource Pack: 4 Crab Pot filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
487 5196 Resource Pack: 5 Crab Pot filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
488 5197 Resource Pack: 6 Crab Pot filler DEPRECATED,FISHING_RESOURCE,RESOURCE_PACK
489 5198 Friendship Bonus (1 <3) useful DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK
490 5199 Friendship Bonus (2 <3) useful FRIENDSHIP_PACK,COMMUNITY_REWARD
491 5200 Friendship Bonus (3 <3) useful DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK
492 5201 Friendship Bonus (4 <3) useful DEPRECATED,FRIENDSHIP_PACK,RESOURCE_PACK
493 5202 30 Qi Gems useful GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL
494 5203 Solar Panel filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
495 5204 Geode Crusher filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
496 5205 Farm Computer filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
497 5206 Bone Mill filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
498 5207 Fiber Seeds filler RESOURCE_PACK
499 5208 Chest filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
500 5209 Stone Chest filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
501 5210 Quality Bobber filler RESOURCE_PACK
502 5211 Mini-Obelisk filler EXACTLY_TWO,RESOURCE_PACK
503 5212 Monster Musk filler RESOURCE_PACK
504 5213 Sprinkler filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
505 5214 Quality Sprinkler filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
506 5215 Iridium Sprinkler filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
507 5216 Scarecrow filler RESOURCE_PACK
508 5217 Deluxe Scarecrow filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
509 5218 Furnace filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
510 5219 Charcoal Kiln filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
511 5220 Lightning Rod filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
512 5221 Resource Pack: 5000 Money useful BASE_RESOURCE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
513 5222 Resource Pack: 10000 Money useful BASE_RESOURCE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
514 5223 Junimo Chest filler EXACTLY_TWO,RESOURCE_PACK
515 5224 Horse Flute filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
516 5225 Pierre's Missing Stocklist filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
517 5226 Hopper filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
518 5227 Enricher filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
519 5228 Pressure Nozzle filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
520 5229 Deconstructor filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
521 5230 Key To The Town filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
522 5231 Galaxy Soul filler GINGER_ISLAND,RESOURCE_PACK,RESOURCE_PACK_USEFUL
523 5232 Mushroom Tree Seed filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
524 5233 Resource Pack: 20 Magic Bait filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
525 5234 Resource Pack: 10 Qi Seasoning filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
526 5235 Mr. Qi's Hat filler MAXIMUM_ONE,RESOURCE_PACK
527 5236 Aquatic Sanctuary filler RESOURCE_PACK
528 5237 Heavy Tapper Recipe filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
529 5238 Hyper Speed-Gro Recipe filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
530 5239 Deluxe Fertilizer Recipe filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
531 5240 Hopper Recipe filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
532 5241 Magic Bait Recipe filler MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
533 5242 Exotic Double Bed filler RESOURCE_PACK
534 5243 Resource Pack: 2 Qi Gem filler GINGER_ISLAND,RESOURCE_PACK
535 5244 Golden Egg filler RESOURCE_PACK,RESOURCE_PACK_USEFUL
536 5245 Golden Walnut filler RESOURCE_PACK,RESOURCE_PACK_USEFUL,GINGER_ISLAND
537 5246 Fairy Dust Recipe useful MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
538 5247 Fairy Dust useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
539 5248 Seed Maker useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
540 5249 Keg useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
541 5250 Cask useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
542 5251 Preserves Jar useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
543 5252 Bee House useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
544 5253 Garden Pot useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
545 5254 Cheese Press useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
546 5255 Mayonnaise Machine useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
547 5256 Loom useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
548 5257 Oil Maker useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
549 5258 Recycling Machine useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
550 5259 Worm Bin useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
551 5260 Tapper useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
552 5261 Heavy Tapper useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
553 5262 Slime Incubator useful RESOURCE_PACK
554 5263 Slime Egg-Press useful RESOURCE_PACK
555 5264 Crystalarium useful RESOURCE_PACK,RESOURCE_PACK_USEFUL
556 5265 Ostrich Incubator useful MAXIMUM_ONE,RESOURCE_PACK,RESOURCE_PACK_USEFUL
557 10001 Luck Level progression SKILL_LEVEL_UP Luck Skill
558 10002 Magic Level progression SKILL_LEVEL_UP Magic
559 10003 Socializing Level progression SKILL_LEVEL_UP Socializing Skill
560 10004 Archaeology Level progression SKILL_LEVEL_UP Archaeology
561 10005 Cooking Level progression SKILL_LEVEL_UP Cooking Skill
562 10006 Binning Level progression SKILL_LEVEL_UP Binning Skill
563 10007 Tractor Garage useful Tractor Mod
564 10008 Woods Obelisk progression DeepWoods
565 10009 Spell: Clear Debris progression MAGIC_SPELL Magic
566 10010 Spell: Till useful MAGIC_SPELL Magic
567 10011 Spell: Water progression MAGIC_SPELL Magic
568 10012 Spell: Blink progression MAGIC_SPELL Magic
569 10013 Spell: Evac useful MAGIC_SPELL Magic
570 10014 Spell: Haste filler MAGIC_SPELL Magic
571 10015 Spell: Heal progression MAGIC_SPELL Magic
572 10016 Spell: Buff useful MAGIC_SPELL Magic
573 10017 Spell: Shockwave progression MAGIC_SPELL Magic
574 10018 Spell: Fireball progression MAGIC_SPELL Magic
575 10019 Spell: Frostbite progression MAGIC_SPELL Magic
576 10020 Spell: Teleport progression MAGIC_SPELL Magic
577 10021 Spell: Lantern filler MAGIC_SPELL Magic
578 10022 Spell: Tendrils progression MAGIC_SPELL Magic
579 10023 Spell: Photosynthesis useful MAGIC_SPELL Magic
580 10024 Spell: Descend progression MAGIC_SPELL Magic
581 10025 Spell: Meteor progression MAGIC_SPELL Magic
582 10026 Spell: Bloodmana useful MAGIC_SPELL Magic
583 10027 Spell: Lucksteal useful MAGIC_SPELL Magic
584 10028 Spell: Spirit progression MAGIC_SPELL Magic
585 10029 Spell: Rewind useful MAGIC_SPELL Magic
586 10101 Juna <3 progression FRIENDSANITY Juna - Roommate NPC
587 10102 Jasper <3 progression FRIENDSANITY Professor Jasper Thomas
588 10103 Alec <3 progression FRIENDSANITY Alec Revisited
589 10104 Yoba <3 progression FRIENDSANITY Custom NPC - Yoba
590 10105 Eugene <3 progression FRIENDSANITY Custom NPC Eugene
591 10106 Wellwick <3 progression FRIENDSANITY 'Prophet' Wellwick
592 10107 Mr. Ginger <3 progression FRIENDSANITY Mister Ginger (cat npc)
593 10108 Shiko <3 progression FRIENDSANITY Shiko - New Custom NPC
594 10109 Delores <3 progression FRIENDSANITY Delores - Custom NPC
595 10110 Ayeisha <3 progression FRIENDSANITY Ayeisha - The Postal Worker (Custom NPC)
596 10111 Riley <3 progression FRIENDSANITY Custom NPC - Riley
597 10301 Progressive Wood Obelisk Sigils progression DeepWoods
598 10302 Progressive Skull Cavern Elevator progression Skull Cavern Elevator

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,8 @@ 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
from ..strings.region_names import Region
from ..strings.geode_names import Geode
@dataclass(frozen=True)
@ -40,14 +41,6 @@ class MuseumItem(GameItem):
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] = []
@ -92,205 +85,205 @@ def create_mineral(name: str,
class Artifact:
dwarf_scroll_i = create_artifact("Dwarf Scroll I", 96, 5.6, SVRegion.mines_floor_20,
dwarf_scroll_i = create_artifact("Dwarf Scroll I", 96, 5.6, Region.mines_floor_20,
monsters=unlikely)
dwarf_scroll_ii = create_artifact("Dwarf Scroll II", 97, 3, SVRegion.mines_floor_20,
dwarf_scroll_ii = create_artifact("Dwarf Scroll II", 97, 3, Region.mines_floor_20,
monsters=unlikely)
dwarf_scroll_iii = create_artifact("Dwarf Scroll III", 98, 7.5, SVRegion.mines_floor_60,
dwarf_scroll_iii = create_artifact("Dwarf Scroll III", 98, 7.5, Region.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,
dwarf_scroll_iv = create_artifact("Dwarf Scroll IV", 99, 4, Region.mines_floor_100)
chipped_amphora = create_artifact("Chipped Amphora", 100, 6.7, Region.town,
geodes=Geode.artifact_trove)
arrowhead = create_artifact("Arrowhead", 101, 8.5, (SVRegion.mountain, SVRegion.forest, SVRegion.bus_stop),
arrowhead = create_artifact("Arrowhead", 101, 8.5, (Region.mountain, Region.forest, Region.bus_stop),
geodes=Geode.artifact_trove)
ancient_doll = create_artifact("Ancient Doll", 103, 13.1, (SVRegion.mountain, SVRegion.forest, SVRegion.bus_stop),
ancient_doll = create_artifact("Ancient Doll", 103, 13.1, (Region.mountain, Region.forest, Region.bus_stop),
geodes=(Geode.artifact_trove, common.fishing_chest))
elvish_jewelry = create_artifact("Elvish Jewelry", 104, 5.3, SVRegion.forest,
elvish_jewelry = create_artifact("Elvish Jewelry", 104, 5.3, Region.forest,
geodes=(Geode.artifact_trove, common.fishing_chest))
chewing_stick = create_artifact("Chewing Stick", 105, 10.3, (SVRegion.mountain, SVRegion.forest, SVRegion.town),
chewing_stick = create_artifact("Chewing Stick", 105, 10.3, (Region.mountain, Region.forest, Region.town),
geodes=(Geode.artifact_trove, common.fishing_chest))
ornamental_fan = create_artifact("Ornamental Fan", 106, 7.4, (SVRegion.beach, SVRegion.forest, SVRegion.town),
ornamental_fan = create_artifact("Ornamental Fan", 106, 7.4, (Region.beach, Region.forest, Region.town),
geodes=(Geode.artifact_trove, common.fishing_chest))
dinosaur_egg = create_artifact("Dinosaur Egg", 107, 11.4, (SVRegion.mountain, SVRegion.skull_cavern),
dinosaur_egg = create_artifact("Dinosaur Egg", 107, 11.4, (Region.mountain, Region.skull_cavern),
geodes=common.fishing_chest,
monsters=Monster.pepper_rex)
rare_disc = create_artifact("Rare Disc", 108, 5.6, SVRegion.stardew_valley,
rare_disc = create_artifact("Rare Disc", 108, 5.6, Region.stardew_valley,
geodes=(Geode.artifact_trove, common.fishing_chest),
monsters=unlikely)
ancient_sword = create_artifact("Ancient Sword", 109, 5.8, (SVRegion.forest, SVRegion.mountain),
ancient_sword = create_artifact("Ancient Sword", 109, 5.8, (Region.forest, Region.mountain),
geodes=(Geode.artifact_trove, common.fishing_chest))
rusty_spoon = create_artifact("Rusty Spoon", 110, 9.6, SVRegion.town,
rusty_spoon = create_artifact("Rusty Spoon", 110, 9.6, Region.town,
geodes=(Geode.artifact_trove, common.fishing_chest))
rusty_spur = create_artifact("Rusty Spur", 111, 15.6, SVRegion.farm,
rusty_spur = create_artifact("Rusty Spur", 111, 15.6, Region.farm,
geodes=(Geode.artifact_trove, common.fishing_chest))
rusty_cog = create_artifact("Rusty Cog", 112, 9.6, SVRegion.mountain,
rusty_cog = create_artifact("Rusty Cog", 112, 9.6, Region.mountain,
geodes=(Geode.artifact_trove, common.fishing_chest))
chicken_statue = create_artifact("Chicken Statue", 113, 13.5, SVRegion.farm,
chicken_statue = create_artifact("Chicken Statue", 113, 13.5, Region.farm,
geodes=(Geode.artifact_trove, common.fishing_chest))
ancient_seed = create_artifact("Ancient Seed", 114, 8.4, (SVRegion.forest, SVRegion.mountain),
ancient_seed = create_artifact("Ancient Seed", 114, 8.4, (Region.forest, Region.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),
prehistoric_tool = create_artifact("Prehistoric Tool", 115, 11.1, (Region.mountain, Region.forest, Region.bus_stop),
geodes=(Geode.artifact_trove, common.fishing_chest))
dried_starfish = create_artifact("Dried Starfish", 116, 12.5, SVRegion.beach,
dried_starfish = create_artifact("Dried Starfish", 116, 12.5, Region.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,
anchor = create_artifact("Anchor", 117, 8.5, Region.beach, geodes=(Geode.artifact_trove, common.fishing_chest))
glass_shards = create_artifact("Glass Shards", 118, 11.5, Region.beach,
geodes=(Geode.artifact_trove, common.fishing_chest))
bone_flute = create_artifact("Bone Flute", 119, 6.3, (SVRegion.mountain, SVRegion.forest, SVRegion.town),
bone_flute = create_artifact("Bone Flute", 119, 6.3, (Region.mountain, Region.forest, Region.town),
geodes=(Geode.artifact_trove, common.fishing_chest))
prehistoric_handaxe = create_artifact("Prehistoric Handaxe", 120, 13.7,
(SVRegion.mountain, SVRegion.forest, SVRegion.bus_stop),
(Region.mountain, Region.forest, Region.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,
dwarvish_helm = create_artifact("Dwarvish Helm", 121, 8.7, Region.mines_floor_20,
geodes=(Geode.geode, Geode.omni, Geode.artifact_trove))
dwarf_gadget = create_artifact("Dwarf Gadget", 122, 9.7, Region.mines_floor_60,
geodes=(Geode.magma, Geode.omni, Geode.artifact_trove))
ancient_drum = create_artifact("Ancient Drum", 123, 9.5, (Region.bus_stop, Region.forest, Region.town),
geodes=(Geode.frozen, Geode.omni, Geode.artifact_trove))
golden_mask = create_artifact("Golden Mask", 124, 6.7, Region.desert,
geodes=Geode.artifact_trove)
golden_relic = create_artifact("Golden Relic", 125, 9.7, SVRegion.desert,
golden_relic = create_artifact("Golden Relic", 125, 9.7, Region.desert,
geodes=Geode.artifact_trove)
strange_doll_green = create_artifact("Strange Doll (Green)", 126, 10, SVRegion.town,
strange_doll_green = create_artifact("Strange Doll (Green)", 126, 10, Region.town,
geodes=common.secret_note)
strange_doll = create_artifact("Strange Doll", 127, 10, SVRegion.desert,
strange_doll = create_artifact("Strange Doll", 127, 10, Region.desert,
geodes=common.secret_note)
prehistoric_scapula = create_artifact("Prehistoric Scapula", 579, 6.2,
(SVRegion.dig_site, SVRegion.forest, SVRegion.town))
(Region.dig_site, Region.forest, Region.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),
(Region.dig_site, Region.forest, Region.railroad))
prehistoric_skull = create_artifact("Prehistoric Skull", 581, 3.9, (Region.dig_site, Region.mountain))
skeletal_hand = create_artifact("Skeletal Hand", 582, 7.9, (Region.dig_site, Region.backwoods, Region.beach))
prehistoric_rib = create_artifact("Prehistoric Rib", 583, 15, (Region.dig_site, Region.farm, Region.town),
monsters=Monster.pepper_rex)
prehistoric_vertebra = create_artifact("Prehistoric Vertebra", 584, 12.7, (SVRegion.dig_site, SVRegion.bus_stop),
prehistoric_vertebra = create_artifact("Prehistoric Vertebra", 584, 12.7, (Region.dig_site, Region.bus_stop),
monsters=Monster.pepper_rex)
skeletal_tail = create_artifact("Skeletal Tail", 585, 5.1, (SVRegion.dig_site, SVRegion.mines_floor_20),
skeletal_tail = create_artifact("Skeletal Tail", 585, 5.1, (Region.dig_site, Region.mines_floor_20),
geodes=common.fishing_chest)
nautilus_fossil = create_artifact("Nautilus Fossil", 586, 6.9, (SVRegion.dig_site, SVRegion.beach),
nautilus_fossil = create_artifact("Nautilus Fossil", 586, 6.9, (Region.dig_site, Region.beach),
geodes=common.fishing_chest)
amphibian_fossil = create_artifact("Amphibian Fossil", 587, 6.3, (SVRegion.dig_site, SVRegion.forest, SVRegion.mountain),
amphibian_fossil = create_artifact("Amphibian Fossil", 587, 6.3, (Region.dig_site, Region.forest, Region.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))
(Region.dig_site, Region.desert, Region.forest, Region.beach))
trilobite = create_artifact("Trilobite", 589, 7.4, (Region.dig_site, Region.desert, Region.forest, Region.beach))
class Mineral:
quartz = create_mineral("Quartz", 80, SVRegion.mines_floor_20,
quartz = create_mineral("Quartz", 80, Region.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),
fire_quartz = create_mineral("Fire Quartz", 82, Region.mines_floor_100,
geodes=(Geode.magma, Geode.omni, 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),
frozen_tear = create_mineral("Frozen Tear", 84, Region.mines_floor_60,
geodes=(Geode.frozen, Geode.omni, 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),
earth_crystal = create_mineral("Earth Crystal", 86, Region.mines_floor_20,
geodes=(Geode.geode, Geode.omni, common.fishing_chest),
monsters=Monster.duggy,
difficulty=1.0 / 12.0)
emerald = create_mineral("Emerald", 60, SVRegion.mines_floor_100,
emerald = create_mineral("Emerald", 60, Region.mines_floor_100,
geodes=common.fishing_chest)
aquamarine = create_mineral("Aquamarine", 62, SVRegion.mines_floor_60,
aquamarine = create_mineral("Aquamarine", 62, Region.mines_floor_60,
geodes=common.fishing_chest)
ruby = create_mineral("Ruby", 64, SVRegion.mines_floor_100,
ruby = create_mineral("Ruby", 64, Region.mines_floor_100,
geodes=common.fishing_chest)
amethyst = create_mineral("Amethyst", 66, SVRegion.mines_floor_20,
amethyst = create_mineral("Amethyst", 66, Region.mines_floor_20,
geodes=common.fishing_chest)
topaz = create_mineral("Topaz", 68, SVRegion.mines_floor_20,
topaz = create_mineral("Topaz", 68, Region.mines_floor_20,
geodes=common.fishing_chest)
jade = create_mineral("Jade", 70, SVRegion.mines_floor_60,
jade = create_mineral("Jade", 70, Region.mines_floor_60,
geodes=common.fishing_chest)
diamond = create_mineral("Diamond", 72, SVRegion.mines_floor_60,
diamond = create_mineral("Diamond", 72, Region.mines_floor_60,
geodes=common.fishing_chest)
prismatic_shard = create_mineral("Prismatic Shard", 74, SVRegion.perfect_skull_cavern,
prismatic_shard = create_mineral("Prismatic Shard", 74, Region.skull_cavern_100,
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),
alamite = create_mineral("Alamite", 538, Region.town,
geodes=(Geode.geode, Geode.omni))
bixite = create_mineral("Bixite", 539, Region.town,
geodes=(Geode.magma, Geode.omni),
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))
baryte = create_mineral("Baryte", 540, Region.town,
geodes=(Geode.magma, Geode.omni))
aerinite = create_mineral("Aerinite", 541, Region.town,
geodes=(Geode.frozen, Geode.omni))
calcite = create_mineral("Calcite", 542, Region.town,
geodes=(Geode.geode, Geode.omni))
dolomite = create_mineral("Dolomite", 543, Region.town,
geodes=(Geode.magma, Geode.omni))
esperite = create_mineral("Esperite", 544, Region.town,
geodes=(Geode.frozen, Geode.omni))
fluorapatite = create_mineral("Fluorapatite", 545, Region.town,
geodes=(Geode.frozen, Geode.omni))
geminite = create_mineral("Geminite", 546, Region.town,
geodes=(Geode.frozen, Geode.omni))
helvite = create_mineral("Helvite", 547, Region.town,
geodes=(Geode.magma, Geode.omni))
jamborite = create_mineral("Jamborite", 548, Region.town,
geodes=(Geode.geode, Geode.omni))
jagoite = create_mineral("Jagoite", 549, Region.town,
geodes=(Geode.geode, Geode.omni))
kyanite = create_mineral("Kyanite", 550, Region.town,
geodes=(Geode.frozen, Geode.omni))
lunarite = create_mineral("Lunarite", 551, Region.town,
geodes=(Geode.frozen, Geode.omni))
malachite = create_mineral("Malachite", 552, Region.town,
geodes=(Geode.geode, Geode.omni))
neptunite = create_mineral("Neptunite", 553, Region.town,
geodes=(Geode.magma, Geode.omni))
lemon_stone = create_mineral("Lemon Stone", 554, Region.town,
geodes=(Geode.magma, Geode.omni))
nekoite = create_mineral("Nekoite", 555, Region.town,
geodes=(Geode.geode, Geode.omni))
orpiment = create_mineral("Orpiment", 556, Region.town,
geodes=(Geode.geode, Geode.omni))
petrified_slime = create_mineral("Petrified Slime", 557, Region.town,
geodes=(Geode.geode, Geode.omni))
thunder_egg = create_mineral("Thunder Egg", 558, Region.town,
geodes=(Geode.geode, Geode.omni))
pyrite = create_mineral("Pyrite", 559, Region.town,
geodes=(Geode.frozen, Geode.omni))
ocean_stone = create_mineral("Ocean Stone", 560, Region.town,
geodes=(Geode.frozen, Geode.omni))
ghost_crystal = create_mineral("Ghost Crystal", 561, Region.town,
geodes=(Geode.frozen, Geode.omni))
tigerseye = create_mineral("Tigerseye", 562, Region.town,
geodes=(Geode.magma, Geode.omni))
jasper = create_mineral("Jasper", 563, Region.town,
geodes=(Geode.magma, Geode.omni))
opal = create_mineral("Opal", 564, Region.town,
geodes=(Geode.frozen, Geode.omni))
fire_opal = create_mineral("Fire Opal", 565, Region.town,
geodes=(Geode.magma, Geode.omni))
celestine = create_mineral("Celestine", 566, Region.town,
geodes=(Geode.geode, Geode.omni))
marble = create_mineral("Marble", 567, Region.town,
geodes=(Geode.frozen, Geode.omni))
sandstone = create_mineral("Sandstone", 568, Region.town,
geodes=(Geode.geode, Geode.omni))
granite = create_mineral("Granite", 569, Region.town,
geodes=(Geode.geode, Geode.omni))
basalt = create_mineral("Basalt", 570, Region.town,
geodes=(Geode.magma, Geode.omni))
limestone = create_mineral("Limestone", 571, Region.town,
geodes=(Geode.geode, Geode.omni))
soapstone = create_mineral("Soapstone", 572, Region.town,
geodes=(Geode.frozen, Geode.omni))
hematite = create_mineral("Hematite", 573, Region.town,
geodes=(Geode.frozen, Geode.omni))
mudstone = create_mineral("Mudstone", 574, Region.town,
geodes=(Geode.geode, Geode.omni))
obsidian = create_mineral("Obsidian", 575, Region.town,
geodes=(Geode.magma, Geode.omni))
slate = create_mineral("Slate", 576, Region.town,
geodes=(Geode.geode, Geode.omni))
fairy_stone = create_mineral("Fairy Stone", 577, Region.town,
geodes=(Geode.frozen, Geode.omni))
star_shards = create_mineral("Star Shards", 578, Region.town,
geodes=(Geode.magma, Geode.omni))
dwarf_scrolls = (Artifact.dwarf_scroll_i, Artifact.dwarf_scroll_ii, Artifact.dwarf_scroll_iii, Artifact.dwarf_scroll_iv)

View File

@ -0,0 +1,190 @@
from typing import Dict, List
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
from ..strings.crop_names import Fruit, Vegetable
from ..strings.fish_names import Fish, WaterItem
from ..strings.flower_names import Flower
from ..strings.forageable_names import Forageable
from ..strings.ingredient_names import Ingredient
from ..strings.food_names import Meal, Beverage
from ..strings.region_names import Region
from ..strings.season_names import Season
from ..strings.skill_names import Skill
from ..strings.villager_names import NPC
class RecipeSource:
pass
class StarterSource(RecipeSource):
pass
class QueenOfSauceSource(RecipeSource):
year: int
season: str
day: int
def __init__(self, year: int, season: str, day: int):
self.year = year
self.season = season
self.day = day
class FriendshipSource(RecipeSource):
friend: str
hearts: int
def __init__(self, friend: str, hearts: int):
self.friend = friend
self.hearts = hearts
class SkillSource(RecipeSource):
skill: str
level: int
def __init__(self, skill: str, level: int):
self.skill = skill
self.level = level
class ShopSource(RecipeSource):
region: str
price: int
def __init__(self, region: str, price: int):
self.region = region
self.price = price
class ShopTradeSource(ShopSource):
currency: str
class CookingRecipe:
meal: str
ingredients: Dict[str, int]
source: RecipeSource
def __init__(self, meal: str, ingredients: Dict[str, int], source: RecipeSource):
self.meal = meal
self.ingredients = ingredients
self.source = source
def __repr__(self):
return f"{self.meal} (Source: {self.source} |" \
f" Ingredients: {self.ingredients})"
all_cooking_recipes: List[CookingRecipe] = []
def friendship_recipe(name: str, friend: str, hearts: int, ingredients: Dict[str, int]) -> CookingRecipe:
source = FriendshipSource(friend, hearts)
return create_recipe(name, ingredients, source)
def skill_recipe(name: str, skill: str, level: int, ingredients: Dict[str, int]) -> CookingRecipe:
source = SkillSource(skill, level)
return create_recipe(name, ingredients, source)
def shop_recipe(name: str, region: str, price: int, ingredients: Dict[str, int]) -> CookingRecipe:
source = ShopSource(region, price)
return create_recipe(name, ingredients, source)
def queen_of_sauce_recipe(name: str, year: int, season: str, day: int, ingredients: Dict[str, int]) -> CookingRecipe:
source = QueenOfSauceSource(year, season, day)
return create_recipe(name, ingredients, source)
def starter_recipe(name: str, ingredients: Dict[str, int]) -> CookingRecipe:
source = StarterSource()
return create_recipe(name, ingredients, source)
def create_recipe(name: str, ingredients: Dict[str, int], source: RecipeSource) -> CookingRecipe:
recipe = CookingRecipe(name, ingredients, source)
all_cooking_recipes.append(recipe)
return recipe
algae_soup = friendship_recipe(Meal.algae_soup, NPC.clint, 3, {WaterItem.green_algae: 4})
artichoke_dip = queen_of_sauce_recipe(Meal.artichoke_dip, 1, Season.fall, 28, {Vegetable.artichoke: 1, AnimalProduct.cow_milk: 1})
baked_fish = queen_of_sauce_recipe(Meal.baked_fish, 1, Season.summer, 7, {Fish.sunfish: 1, Fish.bream: 1, Ingredient.wheat_flour: 1})
bean_hotpot = friendship_recipe(Meal.bean_hotpot, NPC.clint, 7, {Vegetable.green_bean: 2})
blackberry_cobbler_ingredients = {Forageable.blackberry: 2, Ingredient.sugar: 1, Ingredient.wheat_flour: 1}
blackberry_cobbler_qos = queen_of_sauce_recipe(Meal.blackberry_cobbler, 2, Season.fall, 14, blackberry_cobbler_ingredients)
blueberry_tart = friendship_recipe(Meal.blueberry_tart, NPC.pierre, 3, {Fruit.blueberry: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.any_egg: 1})
bread = queen_of_sauce_recipe(Meal.bread, 1, Season.summer, 28, {Ingredient.wheat_flour: 1})
cheese_cauliflower = friendship_recipe(Meal.cheese_cauliflower, NPC.pam, 3, {Vegetable.cauliflower: 1, ArtisanGood.cheese: 1})
chocolate_cake_ingredients = {Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.chicken_egg: 1}
chocolate_cake_qos = queen_of_sauce_recipe(Meal.chocolate_cake, 1, Season.winter, 14, chocolate_cake_ingredients)
chowder = friendship_recipe(Meal.chowder, NPC.willy, 3, {WaterItem.clam: 1, AnimalProduct.cow_milk: 1})
complete_breakfast = queen_of_sauce_recipe(Meal.complete_breakfast, 2, Season.spring, 21, {Meal.fried_egg: 1, AnimalProduct.milk: 1, Meal.hashbrowns: 1, Meal.pancakes: 1})
crab_cakes_ingredients = {Fish.crab: 1, Ingredient.wheat_flour: 1, AnimalProduct.chicken_egg: 1, Ingredient.oil: 1}
crab_cakes_qos = queen_of_sauce_recipe(Meal.crab_cakes, 2, Season.fall, 21, crab_cakes_ingredients)
cranberry_candy = queen_of_sauce_recipe(Meal.cranberry_candy, 1, Season.winter, 28, {Fruit.cranberries: 1, Fruit.apple: 1, Ingredient.sugar: 1})
crispy_bass = friendship_recipe(Meal.crispy_bass, NPC.kent, 3, {Fish.largemouth_bass: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1})
dish_o_the_sea = skill_recipe(Meal.dish_o_the_sea, Skill.fishing, 3, {Fish.sardine: 2, Meal.hashbrowns: 1})
eggplant_parmesan = friendship_recipe(Meal.eggplant_parmesan, NPC.lewis, 7, {Vegetable.eggplant: 1, Vegetable.tomato: 1})
escargot = friendship_recipe(Meal.escargot, NPC.willy, 5, {Fish.snail: 1, Vegetable.garlic: 1})
farmer_lunch = skill_recipe(Meal.farmer_lunch, Skill.farming, 3, {Meal.omelet: 2, Vegetable.parsnip: 1})
fiddlehead_risotto = queen_of_sauce_recipe(Meal.fiddlehead_risotto, 2, Season.fall, 28, {Ingredient.oil: 1, Forageable.fiddlehead_fern: 1, Vegetable.garlic: 1})
fish_taco = friendship_recipe(Meal.fish_taco, NPC.linus, 7, {Fish.tuna: 1, Meal.tortilla: 1, Vegetable.red_cabbage: 1, ArtisanGood.mayonnaise: 1})
fried_calamari = friendship_recipe(Meal.fried_calamari, NPC.jodi, 3, {Fish.squid: 1, Ingredient.wheat_flour: 1, Ingredient.oil: 1})
fried_eel = friendship_recipe(Meal.fried_eel, NPC.george, 3, {Fish.eel: 1, Ingredient.oil: 1})
fried_egg = starter_recipe(Meal.fried_egg, {AnimalProduct.chicken_egg: 1})
fried_mushroom = friendship_recipe(Meal.fried_mushroom, NPC.demetrius, 3, {Forageable.common_mushroom: 1, Forageable.morel: 1, Ingredient.oil: 1})
fruit_salad = queen_of_sauce_recipe(Meal.fruit_salad, 2, Season.fall, 7, {Fruit.blueberry: 1, Fruit.melon: 1, Fruit.apricot: 1})
ginger_ale = shop_recipe(Beverage.ginger_ale, Region.volcano_dwarf_shop, 1000, {Forageable.ginger: 3, Ingredient.sugar: 1})
glazed_yams = queen_of_sauce_recipe(Meal.glazed_yams, 1, Season.fall, 21, {Vegetable.yam: 1, Ingredient.sugar: 1})
hashbrowns = queen_of_sauce_recipe(Meal.hashbrowns, 2, Season.spring, 14, {Vegetable.potato: 1, Ingredient.oil: 1})
ice_cream = friendship_recipe(Meal.ice_cream, NPC.jodi, 7, {AnimalProduct.cow_milk: 1, Ingredient.sugar: 1})
maki_roll = queen_of_sauce_recipe(Meal.maki_roll, 1, Season.summer, 21, {Fish.any: 1, WaterItem.seaweed: 1, Ingredient.rice: 1})
maple_bar = queen_of_sauce_recipe(Meal.maple_bar, 2, Season.summer, 14, {ArtisanGood.maple_syrup: 1, Ingredient.sugar: 1, Ingredient.wheat_flour: 1})
miners_treat = skill_recipe(Meal.miners_treat, Skill.mining, 3, {Forageable.cave_carrot: 2, Ingredient.sugar: 1, AnimalProduct.cow_milk: 1})
omelet = queen_of_sauce_recipe(Meal.omelet, 1, Season.spring, 28, {AnimalProduct.chicken_egg: 1, AnimalProduct.cow_milk: 1})
pale_broth = friendship_recipe(Meal.pale_broth, NPC.marnie, 3, {WaterItem.white_algae: 2})
pancakes = queen_of_sauce_recipe(Meal.pancakes, 1, Season.summer, 14, {Ingredient.wheat_flour: 1, AnimalProduct.chicken_egg: 1})
parsnip_soup = friendship_recipe(Meal.parsnip_soup, NPC.caroline, 3, {Vegetable.parsnip: 1, AnimalProduct.cow_milk: 1, Ingredient.vinegar: 1})
pepper_poppers = friendship_recipe(Meal.pepper_poppers, NPC.shane, 3, {Fruit.hot_pepper: 1, ArtisanGood.cheese: 1})
pink_cake_ingredients = {Fruit.melon: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.chicken_egg: 1}
pink_cake_qos = queen_of_sauce_recipe(Meal.pink_cake, 2, Season.summer, 21, pink_cake_ingredients)
pizza_ingredients = {Ingredient.wheat_flour: 1, Vegetable.tomato: 1, ArtisanGood.cheese: 1}
pizza_qos = queen_of_sauce_recipe(Meal.pizza, 2, Season.spring, 7, pizza_ingredients)
pizza_saloon = shop_recipe(Meal.pizza, Region.saloon, 150, pizza_ingredients)
plum_pudding = queen_of_sauce_recipe(Meal.plum_pudding, 1, Season.winter, 7, {Forageable.wild_plum: 2, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
poppyseed_muffin = queen_of_sauce_recipe(Meal.poppyseed_muffin, 2, Season.winter, 7, {Flower.poppy: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
pumpkin_pie_ingredients = {Vegetable.pumpkin: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1, AnimalProduct.cow_milk: 1}
pumpkin_pie_qos = queen_of_sauce_recipe(Meal.pumpkin_pie, 1, Season.winter, 21, pumpkin_pie_ingredients)
red_plate = friendship_recipe(Meal.red_plate, NPC.emily, 7, {Vegetable.red_cabbage: 1, Vegetable.radish: 1})
rhubarb_pie = friendship_recipe(Meal.rhubarb_pie, NPC.marnie, 7, {Fruit.rhubarb: 1, Ingredient.wheat_flour: 1, Ingredient.sugar: 1})
rice_pudding = friendship_recipe(Meal.rice_pudding, NPC.evelyn, 7, {AnimalProduct.milk: 1, Ingredient.sugar: 1, Ingredient.rice: 1})
roasted_hazelnuts = queen_of_sauce_recipe(Meal.roasted_hazelnuts, 2, Season.summer, 28, {Forageable.hazelnut: 3})
roots_platter = skill_recipe(Meal.roots_platter, Skill.combat, 3, {Forageable.cave_carrot: 1, Forageable.winter_root: 1})
salad = friendship_recipe(Meal.salad, NPC.emily, 3, {Forageable.leek: 1, Forageable.dandelion: 1, Ingredient.vinegar: 1})
salmon_dinner = friendship_recipe(Meal.salmon_dinner, NPC.gus, 3, {Fish.salmon: 1, Vegetable.amaranth: 1, Vegetable.kale: 1})
sashimi = friendship_recipe(Meal.sashimi, NPC.linus, 3, {Fish.any: 1})
spaghetti = friendship_recipe(Meal.spaghetti, NPC.lewis, 3, {Vegetable.tomato: 1, Ingredient.wheat_flour: 1})
stir_fry_ingredients = {Forageable.cave_carrot: 1, Forageable.common_mushroom: 1, Vegetable.kale: 1, Ingredient.sugar: 1}
stir_fry_qos = queen_of_sauce_recipe(Meal.stir_fry, 1, Season.spring, 7, stir_fry_ingredients)
stuffing = friendship_recipe(Meal.stuffing, NPC.pam, 7, {Meal.bread: 1, Fruit.cranberries: 1, Forageable.hazelnut: 1})
survival_burger = skill_recipe(Meal.survival_burger, Skill.foraging, 2, {Meal.bread: 1, Forageable.cave_carrot: 1, Vegetable.eggplant: 1})
tortilla_ingredients = {Vegetable.corn: 1}
tortilla_qos = queen_of_sauce_recipe(Meal.tortilla, 1, Season.fall, 7, tortilla_ingredients)
tortilla_saloon = shop_recipe(Meal.tortilla, Region.saloon, 100, tortilla_ingredients)
triple_shot_espresso = shop_recipe(Beverage.triple_shot_espresso, Region.saloon, 5000, {Beverage.coffee: 3})
tropical_curry = shop_recipe(Meal.tropical_curry, Region.island_resort, 2000, {Forageable.coconut: 1, Fruit.pineapple: 1, Fruit.hot_pepper: 1})
vegetable_medley = friendship_recipe(Meal.vegetable_medley, NPC.caroline, 7, {Vegetable.tomato: 1, Vegetable.beet: 1})

View File

@ -1,39 +0,0 @@
name,default_amount,scaling_factor,classification,groups
Money,1000,500,useful,BASE_RESOURCE
Stone,50,25,filler,BASE_RESOURCE
Wood,50,25,filler,BASE_RESOURCE
Hardwood,10,5,useful,BASE_RESOURCE
Fiber,30,15,filler,BASE_RESOURCE
Coal,10,5,filler,BASE_RESOURCE
Clay,10,5,filler,BASE_RESOURCE
Warp Totem: Beach,5,2,filler,WARP_TOTEM
Warp Totem: Desert,5,2,filler,WARP_TOTEM
Warp Totem: Farm,5,2,filler,WARP_TOTEM
Warp Totem: Island,5,2,filler,WARP_TOTEM
Warp Totem: Mountains,5,2,filler,WARP_TOTEM
Geode,12,6,filler,GEODE
Frozen Geode,8,4,filler,GEODE
Magma Geode,6,3,filler,GEODE
Omni Geode,4,2,useful,GEODE
Copper Ore,75,25,filler,ORE
Iron Ore,50,25,filler,ORE
Gold Ore,25,13,useful,ORE
Iridium Ore,10,5,useful,ORE
Quartz,10,5,filler,ORE
Basic Fertilizer,30,10,filler,FERTILIZER
Basic Retaining Soil,30,10,filler,FERTILIZER
Speed-Gro,30,10,filler,FERTILIZER
Quality Fertilizer,20,8,filler,FERTILIZER
Quality Retaining Soil,20,8,filler,FERTILIZER
Deluxe Speed-Gro,20,8,filler,FERTILIZER
Deluxe Fertilizer,10,4,useful,FERTILIZER
Deluxe Retaining Soil,10,4,useful,FERTILIZER
Hyper Speed-Gro,10,4,useful,FERTILIZER
Tree Fertilizer,10,4,filler,FERTILIZER
Spring Seeds,30,10,filler,SEED
Summer Seeds,30,10,filler,SEED
Fall Seeds,30,10,filler,SEED
Winter Seeds,30,10,filler,SEED
Mahogany Seed,5,2,filler,SEED
Bait,30,10,filler,FISHING_RESOURCE
Crab Pot,3,1,filler,FISHING_RESOURCE
1 name default_amount scaling_factor classification groups
2 Money 1000 500 useful BASE_RESOURCE
3 Stone 50 25 filler BASE_RESOURCE
4 Wood 50 25 filler BASE_RESOURCE
5 Hardwood 10 5 useful BASE_RESOURCE
6 Fiber 30 15 filler BASE_RESOURCE
7 Coal 10 5 filler BASE_RESOURCE
8 Clay 10 5 filler BASE_RESOURCE
9 Warp Totem: Beach 5 2 filler WARP_TOTEM
10 Warp Totem: Desert 5 2 filler WARP_TOTEM
11 Warp Totem: Farm 5 2 filler WARP_TOTEM
12 Warp Totem: Island 5 2 filler WARP_TOTEM
13 Warp Totem: Mountains 5 2 filler WARP_TOTEM
14 Geode 12 6 filler GEODE
15 Frozen Geode 8 4 filler GEODE
16 Magma Geode 6 3 filler GEODE
17 Omni Geode 4 2 useful GEODE
18 Copper Ore 75 25 filler ORE
19 Iron Ore 50 25 filler ORE
20 Gold Ore 25 13 useful ORE
21 Iridium Ore 10 5 useful ORE
22 Quartz 10 5 filler ORE
23 Basic Fertilizer 30 10 filler FERTILIZER
24 Basic Retaining Soil 30 10 filler FERTILIZER
25 Speed-Gro 30 10 filler FERTILIZER
26 Quality Fertilizer 20 8 filler FERTILIZER
27 Quality Retaining Soil 20 8 filler FERTILIZER
28 Deluxe Speed-Gro 20 8 filler FERTILIZER
29 Deluxe Fertilizer 10 4 useful FERTILIZER
30 Deluxe Retaining Soil 10 4 useful FERTILIZER
31 Hyper Speed-Gro 10 4 useful FERTILIZER
32 Tree Fertilizer 10 4 filler FERTILIZER
33 Spring Seeds 30 10 filler SEED
34 Summer Seeds 30 10 filler SEED
35 Fall Seeds 30 10 filler SEED
36 Winter Seeds 30 10 filler SEED
37 Mahogany Seed 5 2 filler SEED
38 Bait 30 10 filler FISHING_RESOURCE
39 Crab Pot 3 1 filler FISHING_RESOURCE

View File

@ -1,6 +1,9 @@
from dataclasses import dataclass
from typing import Set, List, FrozenSet, Tuple
from .region_data import SVRegion
from typing import List, Tuple, Optional, Dict
from ..strings.region_names import Region
from ..mods.mod_data import ModNames
from ..strings.season_names import Season
from ..strings.villager_names import NPC, ModNPC
@dataclass(frozen=True)
@ -11,28 +14,31 @@ class Villager:
birthday: str
gifts: Tuple[str]
available: bool
mod_name: Optional[str]
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}) "
f" Gifts: {self.gifts}) |" \
f" Mod: {self.mod_name}"
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,)
town = (Region.town,)
beach = (Region.beach,)
forest = (Region.forest,)
mountain = (Region.mountain,)
hospital = (Region.hospital,)
carpenter = (Region.carpenter,)
alex_house = (Region.alex_house,)
elliott_house = (Region.elliott_house,)
ranch = (Region.ranch,)
mines_dwarf_shop = (Region.mines_dwarf_shop,)
desert = (Region.desert,)
oasis = (Region.oasis,)
sewers = (Region.sewer,)
island = (Region.island_east,)
secret_woods = (Region.secret_woods,)
golden_pumpkin = ("Golden Pumpkin",)
# magic_rock_candy = ("Magic Rock Candy",)
@ -202,49 +208,138 @@ super_cucumber = ("Super Cucumber",)
void_essence = ("Void Essence",)
wizard_loves = purple_mushroom + solar_essence + super_cucumber + void_essence
#Custom NPC Items and Loves
blueberry = ("Blueberry",)
chanterelle = ("Chanterelle",)
garlic = ("Garlic",)
omelet = ("Omelet",)
wild_plum = ("Wild Plum",)
rhubarb = ("Rhubarb",)
fried_mushroom = ("Fried Mushroom",)
eggplant_parmesan = ("Eggplant Parmesan",)
maki_roll = ("Maki Roll",)
red_plate = ("Red Plate",)
baked_fish = ("Baked Fish",)
cheese = ("Cheese",)
eel = ("Eel",)
flounder = ("Flounder",)
salmon = ("Salmon",)
sashimi = ("Sashimi",)
tuna = ("Tuna",)
energy_tonic = ("Energy Tonic",)
kale = ("Kale",)
muscle_remedy = ("Muscle Remedy",)
vegetable_medley = ("Vegetable Medley",)
trilobite = ("Trilobite",)
golden_mask = ("Golden Mask",)
rainbow_shell = ("Rainbow Shell",)
blue_jazz = ("Blue Jazz",)
honey = ("Honey",)
apple = ("Apple",)
dwarf_gadget = ("Dwarf Gadget",)
dwarvish_helm = ("Dwarvish Helm",)
fire_quartz = ("Fire Quartz",)
jasper = ("Jasper",)
opal = ("Opal",)
rare_disc = ("Rare Disc",)
ancient_doll = ("Ancient Doll",)
elvish_jewelry = ("Elvish Jewelry",)
dinosaur_egg = ("Dinosaur Egg",)
strange_doll = ("Strange Doll",)
joja_cola = ("Joja Cola",)
hashbrowns = ("Hashbrowns",)
jelly = ("Jelly",)
ghost_crystal = ("Ghost Crystal",)
prehistoric_scapula = ("Prehistoric Scapula",)
cherry = ("Cherry",)
golden_relic = ("Golden Relic",)
ayeisha_loves = blackberry_cobbler + blueberry + chanterelle + emerald + omelet + sweet_pea + wild_plum + rhubarb + \
fried_mushroom + eggplant_parmesan
shiko_loves = maki_roll + red_plate + ruby + salad + wine
wellwick_loves = fairy_rose + solar_essence + void_essence + wine
mister_ginger_loves = baked_fish + cheese + eel + flounder + goat_cheese + lobster + salmon + sashimi + tuna
delores_loves = aquamarine + blueberry + energy_tonic + green_tea + kale + muscle_remedy + red_plate + \
roots_platter + salad + vegetable_medley
yoba_loves = golden_mask + rainbow_shell
eugene_loves = blue_jazz + fairy_rose + green_tea + honey + poppy + poppyseed_muffin + \
salad + summer_spangle + sunflower + tulip
jasper_loves = apple + blueberry + diamond + dwarf_gadget + dwarvish_helm + fire_quartz + jasper + \
miners_treat + opal + rare_disc
juna_loves = ancient_doll + elvish_jewelry + dinosaur_egg + strange_doll + joja_cola + hashbrowns + pancakes + \
pink_cake + jelly + ghost_crystal + prehistoric_scapula + cherry
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)
available: bool, mod_name: Optional[str] = None) -> Villager:
npc = Villager(name, bachelor, locations, birthday, gifts, available, mod_name)
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)
josh = villager(NPC.alex, True, town + alex_house, Season.summer, universal_loves + complete_breakfast + salmon_dinner, True)
elliott = villager(NPC.elliott, True, town + beach + elliott_house, Season.fall, universal_loves + elliott_loves, True)
harvey = villager(NPC.harvey, True, town + hospital, Season.winter, universal_loves + harvey_loves, True)
sam = villager(NPC.sam, True, town, Season.summer, universal_loves + sam_loves, True)
sebastian = villager(NPC.sebastian, True, carpenter, Season.winter, universal_loves + sebastian_loves, True)
shane = villager(NPC.shane, True, ranch, Season.spring, universal_loves + shane_loves, True)
best_girl = villager(NPC.abigail, True, town, Season.fall, universal_loves + abigail_loves, True)
emily = villager(NPC.emily, True, town, Season.spring, universal_loves + emily_loves, True)
hoe = villager(NPC.haley, True, town, Season.spring, universal_loves_no_prismatic_shard + haley_loves, True)
leah = villager(NPC.leah, True, forest, Season.winter, universal_loves + leah_loves, True)
nerd = villager(NPC.maru, True, carpenter + hospital + town, Season.summer, universal_loves + maru_loves, True)
penny = villager(NPC.penny, True, town, Season.fall, universal_loves_no_rabbit_foot + penny_loves, True)
caroline = villager(NPC.caroline, False, town, Season.winter, universal_loves + caroline_loves, True)
clint = villager(NPC.clint, False, town, Season.winter, universal_loves + clint_loves, True)
demetrius = villager(NPC.demetrius, False, carpenter, Season.summer, universal_loves + demetrius_loves, True)
dwarf = villager(NPC.dwarf, False, mines_dwarf_shop, Season.summer, universal_loves + dwarf_loves, False)
gilf = villager(NPC.evelyn, False, town, Season.winter, universal_loves + evelyn_loves, True)
boomer = villager(NPC.george, False, town, Season.fall, universal_loves + george_loves, True)
gus = villager(NPC.gus, False, town, Season.summer, universal_loves + gus_loves, True)
jas = villager(NPC.jas, False, ranch, Season.summer, universal_loves + jas_loves, True)
jodi = villager(NPC.jodi, False, town, Season.fall, universal_loves + jodi_loves, True)
kent = villager(NPC.kent, False, town, Season.spring, universal_loves + kent_loves, False)
krobus = villager(NPC.krobus, False, sewers, Season.winter, universal_loves + krobus_loves, False)
leo = villager(NPC.leo, False, island, Season.summer, universal_loves + leo_loves, False)
lewis = villager(NPC.lewis, False, town, Season.spring, universal_loves + lewis_loves, True)
linus = villager(NPC.linus, False, mountain, Season.winter, universal_loves + linus_loves, True)
marnie = villager(NPC.marnie, False, ranch, Season.fall, universal_loves + marnie_loves, True)
pam = villager(NPC.pam, False, town, Season.spring, universal_loves + pam_loves, True)
pierre = villager(NPC.pierre, False, town, Season.spring, universal_loves + pierre_loves, True)
milf = villager(NPC.robin, False, carpenter, Season.fall, universal_loves + robin_loves, True)
sandy = villager(NPC.sandy, False, oasis, Season.fall, universal_loves + sandy_loves, False)
vincent = villager(NPC.vincent, False, town, Season.spring, universal_loves + vincent_loves, True)
willy = villager(NPC.willy, False, beach, Season.summer, universal_loves + willy_loves, True)
wizard = villager(NPC.wizard, False, forest, Season.winter, universal_loves + wizard_loves, True)
# Custom NPCs
alec = villager(ModNPC.alec, True, forest, Season.winter, universal_loves + trilobite, True, ModNames.alec)
ayeisha = villager(ModNPC.ayeisha, False, town, Season.summer, universal_loves + ayeisha_loves, True, ModNames.ayeisha)
delores = villager(ModNPC.delores, True, forest, Season.winter, universal_loves + delores_loves, True, ModNames.delores)
eugene = villager(ModNPC.eugene, True, forest, Season.spring, universal_loves + eugene_loves, True, ModNames.eugene)
jasper = villager(ModNPC.jasper, True, town, Season.fall, universal_loves + jasper_loves, True, ModNames.jasper)
juna = villager(ModNPC.juna, False, forest, Season.summer, universal_loves + juna_loves, True, ModNames.juna)
kitty = villager(ModNPC.mr_ginger, False, forest, Season.summer, universal_loves + mister_ginger_loves, True, ModNames.ginger)
shiko = villager(ModNPC.shiko, True, town, Season.winter, universal_loves + shiko_loves, True, ModNames.shiko)
wellwick = villager(ModNPC.wellwick, True, forest, Season.winter, universal_loves + wellwick_loves, True, ModNames.shiko)
yoba = villager(ModNPC.yoba, False, secret_woods, Season.spring, universal_loves + yoba_loves, False, ModNames.yoba)
riley = villager(ModNPC.riley, True, town, Season.spring, universal_loves, True, ModNames.riley)
all_villagers_by_name: Dict[str, Villager] = {villager.name: villager for villager in all_villagers}
all_villagers_by_mod: Dict[str, List[Villager]] = {}
all_villagers_by_mod_by_name: Dict[str, Dict[str, Villager]] = {}
for npc in all_villagers:
mod = npc.mod_name
name = npc.name
if mod in all_villagers_by_mod:
all_villagers_by_mod[mod].append(npc)
all_villagers_by_mod_by_name[mod][name] = npc
else:
all_villagers_by_mod[mod] = [npc]
all_villagers_by_mod_by_name[mod] = {}
all_villagers_by_mod_by_name[mod][name] = npc
all_villagers_by_name = {item.name: item for item in all_villagers}

View File

@ -9,27 +9,29 @@ config file.
A vast number of optional objectives in stardew valley can be shuffled around the multiworld. Most of these are optional, and the player can customize their experience in their YAML file.
For these objectives, if they have a vanilla reward, this reward will instead be an item in the multiworld. For the remaining number of such objectives, there are a number of "Resource Pack" items, which are simply a stack of an item that may be useful to the player.
For these objectives, if they have a vanilla reward, this reward will instead be an item in the multiworld. For the remaining number of such objectives, there are a number of "Resource Pack" items, which are simply an item or a stack of items that may be useful to the player.
## What is the goal of Stardew Valley?
The player can choose from a number of goals, using their YAML settings.
- Complete the Community Center
- Succeed Grandpa's Evaluation with 4 lit candles
- 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.
- Complete the [Community Center](https://stardewvalleywiki.com/Bundles)
- Succeed [Grandpa's Evaluation](https://stardewvalleywiki.com/Grandpa) with 4 lit candles
- Reach the bottom of the [Pelican Town Mineshaft](https://stardewvalleywiki.com/The_Mines)
- Complete the [Cryptic Note](https://stardewvalleywiki.com/Secret_Notes#Secret_Note_.2310) quest, by meeting Mr Qi on floor 100 of the Skull Cavern
- Get the achievement [Master Angler](https://stardewvalleywiki.com/Fish), which requires catching every fish in the game
- Get the achievement [A Complete Collection](https://stardewvalleywiki.com/Museum), which requires donating all the artifacts and minerals to the museum
- Get the achievement [Full House](https://stardewvalleywiki.com/Children), which requires getting married and having two kids.
- Get recognized as the [Greatest Walnut Hunter](https://stardewvalleywiki.com/Golden_Walnut) by Mr Qi, which requires finding all 130 golden walnuts on ginger island
- Achieve [Perfection](https://stardewvalleywiki.com/Perfection) in your save file
## What are location check in Stardew Valley?
Location checks in Stardew Valley always include:
- Community Center Bundles
- Mineshaft chest rewards
- Story Quests
- Traveling Merchant items
- Isolated objectives such as the beach bridge, Old Master Cannoli, Grim Reaper Statue, etc
- [Community Center Bundles](https://stardewvalleywiki.com/Bundles)
- [Mineshaft chest rewards](https://stardewvalleywiki.com/The_Mines#Remixed_Rewards)
- [Story Quests](https://stardewvalleywiki.com/Quests#List_of_Story_Quests)
- [Traveling Merchant items](https://stardewvalleywiki.com/Traveling_Cart)
- Isolated objectives such as the [beach bridge](https://stardewvalleywiki.com/The_Beach#Tide_Pools), [Old Master Cannoli](https://stardewvalleywiki.com/Secret_Woods#Old_Master_Cannoli), [Grim Reaper Statue](https://stardewvalleywiki.com/Golden_Scythe), etc
There also are a number of location checks that are optional, and individual players choose to include them or not in their shuffling:
- Tools and Fishing Rod Upgrades
@ -39,6 +41,9 @@ There also are a number of location checks that are optional, and individual pla
- Skill Levels
- Arcade Machines
- Help Wanted quests
- Participating in Festivals
- Special Orders from the town board, or from Mr Qi
- Cropsanity: Growing and harvesting individual crop types
- 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
@ -46,22 +51,24 @@ There also are a number of location checks that are optional, and individual pla
## 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.
For the locations which do not include a normal reward, Resource Packs and traps are instead added to the pool. Traps are optional.
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.
- Cropsanity:
- Every single seed in the game starts off locked and cannot be purchased from any merchant. Their unlocks are received as multiworld items. Growing each seed and harvesting the resulting crop sends a location check
- 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.
- TV Channels
- Babies
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
- [Wizard Buildings](https://stardewvalleywiki.com/Wizard%27s_Tower#Buildings)
- [Return Scepter](https://stardewvalleywiki.com/Return_Scepter)
And lastly, some Archipelago-exclusive items exist in the pool, which are designed around game balance and QoL. These include:
- Arcade Machine buffs (Only if the arcade machines are randomized)
@ -80,6 +87,39 @@ Some items will be directly attached to the letter, while some others will inste
In some cases, like receiving Carpenter and Wizard buildings, the player will still need to go ask Robin to construct the building that they have received, so they can choose its position. This construction will be completely free.
## Mods
Starting in version 4.x.x, some Stardew Valley mods unrelated to Archipelago are officially "supported".
This means that, for these specific mods, if you decide to include them in your yaml settings, the multiworld will be generated with the assumption that you will install and play with these mods.
The multiworld will contain related items and locations for these mods, the specifics will vary from mod to mod
List of supported mods:
- Skills
- [Luck Skill](https://www.nexusmods.com/stardewvalley/mods/521)
- [Magic](https://www.nexusmods.com/stardewvalley/mods/2007)
- [Socializing Skill](https://www.nexusmods.com/stardewvalley/mods/14142)
- [Archaeology](https://www.nexusmods.com/stardewvalley/mods/15793)
- [Cooking Skill](https://www.nexusmods.com/stardewvalley/mods/522)
- [Binning Skill](https://www.nexusmods.com/stardewvalley/mods/14073)
- NPCs
- [Ayeisha - The Postal Worker (Custom NPC)](https://www.nexusmods.com/stardewvalley/mods/6427)
- [Mister Ginger (cat npc)](https://www.nexusmods.com/stardewvalley/mods/5295)
- [Juna - Roommate NPC](https://www.nexusmods.com/stardewvalley/mods/8606)
- [Professor Jasper Thomas](https://www.nexusmods.com/stardewvalley/mods/5599)
- [Alec Revisited](https://www.nexusmods.com/stardewvalley/mods/10697)
- [Custom NPC - Yoba](https://www.nexusmods.com/stardewvalley/mods/14871)
- [Custom NPC Eugene](https://www.nexusmods.com/stardewvalley/mods/9222)
- ['Prophet' Wellwick](https://www.nexusmods.com/stardewvalley/mods/6462)
- [Shiko - New Custom NPC](https://www.nexusmods.com/stardewvalley/mods/3732)
- [Delores - Custom NPC](https://www.nexusmods.com/stardewvalley/mods/5510)
- [Custom NPC - Riley](https://www.nexusmods.com/stardewvalley/mods/5811)
- Other
- [DeepWoods](https://www.nexusmods.com/stardewvalley/mods/2571)
- [Tractor Mod](https://www.nexusmods.com/stardewvalley/mods/1401)
- [Bigger Backpack](https://www.nexusmods.com/stardewvalley/mods/1845)
- [Skull Cavern Elevator](https://www.nexusmods.com/stardewvalley/mods/963)
## Multiplayer
You cannot play an Archipelago Slot in multiplayer at the moment. There is no short-terms plans to support that feature.

View File

@ -4,15 +4,16 @@
- 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 3.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- 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.
- [StardewArchipelago Mod Release 4.x.x](https://github.com/agilbert1412/StardewArchipelago/releases)
- It is important to use a mod release of version 4.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)
- (Only for the TextClient)
- Other Stardew Valley Mods [Nexus Mods](https://www.nexusmods.com/stardewvalley)
- It is **not** recommended to further mod Stardew Valley, altough it is possible to do so. Mod interactions can be unpredictable, and no support will be offered for related bugs.
- The more mods you have, and the bigger they are, the more likely things are to break.
- For Supported mods (see related section in this page), it is recommend to install them from the mods archive available with the StardewArchipelago mod release
- It is **not** recommended to further mod Stardew Valley with unsupported mods, altough it is possible to do so. Mod interactions can be unpredictable, and no support will be offered for related bugs.
- The more unsupported mods you have, and the bigger they are, the more likely things are to break.
## Configuring your YAML file
@ -76,6 +77,46 @@ Lastly, you can also run Archipelago commands `!help` from the in game chat box,
It is important to note that the Stardew Valley chat is fairly limited in its capabilities. For example, it doesn't allow scrolling up to see history that has been pushed off screen. The SMAPI console running alonside your game will have the full history as well and may be better suited to read older messages.
For a better chat experience, you can also use the official Archipelago Text Client, altough it will not allow you to run Stardew-exclusive commands.
### Playing with supported mods
To include supported mods in your multiworld slot, you need to include a section in your yaml settings called "mods".
This section must be an array with the **exact** names of every mod you wish to include. Any improperly typed mod name will be ignored.
![image](https://i.imgur.com/uOHtXmU.png)
These mods will then be included in the multiworld generation, and considered in logic. For example, the Magic mod includes a spell that allow a player to teleport, and, if included, teleporting can be required to reach checks.
Furthermore, as mod development can be unpredictable, the generator and the StardewArchipelago client are designed and tested for a very specific version of any supported mod. When installing them, you must choose the correct version, or you will not be able to play.
A Zip archive of **every supported mod** is included in the [StardewArchipelago Mod Releases](https://github.com/agilbert1412/StardewArchipelago/releases) alongside the main mod, which should all have the correct versions available. The archive also contains recommended configs for customizable mods.
The archive also contains every dependency for these mods, but dependency versions are less strict.
If you can load the supported mod on the correct version, the exact version of a dependency is not important.
#### All supported mod exact names and required versions:
- "DeepWoods" -> 3.0.0-beta
- "Tractor Mod" -> 4.16.4
- "Bigger Backpack" -> 6.0.0
- "Skull Cavern Elevator" -> 1.5.0
- "Luck Skill" -> 1.2.4
- "Magic" -> 0.8.2
- "Socializing Skill" -> 1.1.5
- "Archaeology" -> 1.5.0
- "Cooking Skill" -> 1.4.5
- "Binning Skill" -> 1.2.7
- "Ayeisha - The Postal Worker (Custom NPC)" -> 0.5.0-alpha
- "Mister Ginger (cat npc)" -> 1.5.9
- "Juna - Roommate NPC" -> 2.1.3
- "Professor Jasper Thomas" -> 1.7.6
- "Alec Revisited" -> 2.1.0
- "Custom NPC - Yoba" -> 1.0.0
- "Custom NPC Eugene" -> 1.3.1
- "'Prophet' Wellwick" -> 1.0.0
- "Shiko - New Custom NPC" -> 1.1.0
- "Delores - Custom NPC" -> 1.1.2
- "Custom NPC - Riley" -> 1.2.2
### Multiplayer
You cannot play an Archipelago Slot in multiplayer at the moment. There is no short-terms plans to support that feature.
You cannot play an Archipelago Slot in multiplayer at the moment. There are no short-terms plans to support that feature.

View File

@ -1,21 +1,17 @@
import bisect
import csv
import enum
import itertools
import logging
import math
import typing
from collections import OrderedDict
from dataclasses import dataclass, field
from functools import cached_property
from pathlib import Path
from random import Random
from typing import Dict, List, Protocol, Union, Set, Optional, FrozenSet
from typing import Dict, List, Protocol, Union, Set, Optional
from BaseClasses import Item, ItemClassification
from . import options, data
from .data.villagers_data import all_villagers
from .mods.mod_data import ModNames
from .options import StardewOptions
from .strings.ap_names.buff_names import Buff
ITEM_CODE_OFFSET = 717000
@ -49,12 +45,26 @@ class Group(enum.Enum):
ORE = enum.auto()
FERTILIZER = enum.auto()
SEED = enum.auto()
SEED_SHUFFLE = enum.auto()
CROPSANITY = enum.auto()
FISHING_RESOURCE = enum.auto()
SEASON = enum.auto()
TRAVELING_MERCHANT_DAY = enum.auto()
MUSEUM = enum.auto()
FRIENDSANITY = enum.auto()
FESTIVAL = enum.auto()
RARECROW = enum.auto()
TRAP = enum.auto()
MAXIMUM_ONE = enum.auto()
EXACTLY_TWO = enum.auto
DEPRECATED = enum.auto()
RESOURCE_PACK_USEFUL = enum.auto()
SPECIAL_ORDER_BOARD = enum.auto()
SPECIAL_ORDER_QI = enum.auto()
BABY = enum.auto()
GINGER_ISLAND = enum.auto()
WALNUT_PURCHASE = enum.auto()
TV_CHANNEL = enum.auto()
MAGIC_SPELL = enum.auto()
@dataclass(frozen=True)
@ -62,6 +72,7 @@ class ItemData:
code_without_offset: Optional[int]
name: str
classification: ItemClassification
mod_name: Optional[str] = None
groups: Set[Group] = field(default_factory=frozenset)
def __post_init__(self):
@ -77,59 +88,6 @@ class ItemData:
return bool(groups.intersection(self.groups))
@dataclass(frozen=True)
class ResourcePackData:
name: str
default_amount: int = 1
scaling_factor: int = 1
classification: ItemClassification = ItemClassification.filler
groups: FrozenSet[Group] = frozenset()
def as_item_data(self, counter: itertools.count) -> [ItemData]:
return [ItemData(next(counter), self.create_item_name(quantity), self.classification,
{Group.RESOURCE_PACK} | self.groups)
for quantity in self.scale_quantity.values()]
def create_item_name(self, quantity: int) -> str:
return f"Resource Pack: {quantity} {self.name}"
@cached_property
def scale_quantity(self) -> typing.OrderedDict[int, int]:
"""Discrete scaling of the resource pack quantities.
100 is default, 200 is double, 50 is half (if the scaling_factor allows it).
"""
levels = math.ceil(self.default_amount / self.scaling_factor) * 2
first_level = self.default_amount % self.scaling_factor
if first_level == 0:
first_level = self.scaling_factor
quantities = sorted(set(range(first_level, self.scaling_factor * levels, self.scaling_factor))
| {self.default_amount * 2})
return OrderedDict({round(quantity / self.default_amount * 100): quantity
for quantity in quantities
if quantity <= self.default_amount * 2})
def calculate_quantity(self, multiplier: int) -> int:
scales = list(self.scale_quantity)
left_scale = bisect.bisect_left(scales, multiplier)
closest_scale = min([scales[left_scale], scales[left_scale - 1]],
key=lambda x: abs(multiplier - x))
return self.scale_quantity[closest_scale]
def create_name_from_multiplier(self, multiplier: int) -> str:
return self.create_item_name(self.calculate_quantity(multiplier))
class FriendshipPackData(ResourcePackData):
def create_item_name(self, quantity: int) -> str:
return f"Friendship Bonus ({quantity} <3)"
def as_item_data(self, counter: itertools.count) -> [ItemData]:
item_datas = super().as_item_data(counter)
return [ItemData(item.code_without_offset, item.name, item.classification, {Group.FRIENDSHIP_PACK})
for item in item_datas]
class StardewItemFactory(Protocol):
def __call__(self, name: Union[str, ItemData]) -> Item:
raise NotImplementedError
@ -148,29 +106,11 @@ def load_item_csv():
id = int(item["id"]) if item["id"] else None
classification = ItemClassification[item["classification"]]
groups = {Group[group] for group in item["groups"].split(",") if group}
items.append(ItemData(id, item["name"], classification, groups))
mod_name = str(item["mod_name"]) if item["mod_name"] else None
items.append(ItemData(id, item["name"], classification, mod_name, groups))
return items
def load_resource_pack_csv() -> List[ResourcePackData]:
try:
from importlib.resources import files
except ImportError:
from importlib_resources import files # noqa
resource_packs = []
with files(data).joinpath("resource_packs.csv").open() as file:
resource_pack_reader = csv.DictReader(file)
for resource_pack in resource_pack_reader:
groups = frozenset(Group[group] for group in resource_pack["groups"].split(",") if group)
resource_packs.append(ResourcePackData(resource_pack["name"],
int(resource_pack["default_amount"]),
int(resource_pack["scaling_factor"]),
ItemClassification[resource_pack["classification"]],
groups))
return resource_packs
events = [
ItemData(None, "Victory", ItemClassification.progression),
ItemData(None, "Month End", ItemClassification.progression),
@ -193,26 +133,29 @@ def initialize_item_table():
item_table.update({item.name: item for item in all_items})
friendship_pack = FriendshipPackData("Friendship Bonus", default_amount=2, classification=ItemClassification.useful)
all_resource_packs = load_resource_pack_csv()
initialize_item_table()
initialize_groups()
def create_items(item_factory: StardewItemFactory, locations_count: int, items_to_exclude: List[Item], world_options: StardewOptions,
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)
items = []
unique_items = create_unique_items(item_factory, world_options, random)
for item in items_to_exclude:
if item in items:
items.remove(item)
if item in unique_items:
unique_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")
assert len(unique_items) <= locations_count, f"There should be at least as many locations [{locations_count}] as there are mandatory items [{len(unique_items)}]"
items += unique_items
logger.debug(f"Created {len(unique_items)} unique items")
resource_pack_items = fill_with_resource_packs(item_factory, world_options, random, locations_count - len(items))
unique_filler_items = create_unique_filler_items(item_factory, world_options, random, locations_count - len(items))
items += unique_filler_items
logger.debug(f"Created {len(unique_filler_items)} unique filler items")
resource_pack_items = fill_with_resource_packs_and_traps(item_factory, world_options, random, items, locations_count)
items += resource_pack_items
logger.debug(f"Created {len(resource_pack_items)} resource packs")
@ -226,25 +169,31 @@ def create_unique_items(item_factory: StardewItemFactory, world_options: Stardew
create_backpack_items(item_factory, world_options, items)
create_mine_rewards(item_factory, items, random)
create_mine_elevators(item_factory, world_options, items)
create_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_wizard_buildings(item_factory, world_options, items)
create_carpenter_buildings(item_factory, world_options, items)
items.append(item_factory("Beach Bridge"))
items.append(item_factory("Dark Talisman"))
create_tv_channels(item_factory, items)
create_special_quest_rewards(item_factory, items)
create_stardrops(item_factory, items)
create_stardrops(item_factory, world_options, 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))
create_traveling_merchant_items(item_factory, items)
items.append(item_factory("Return Scepter"))
items.extend(create_seasons(item_factory, world_options))
items.extend(create_seeds(item_factory, world_options))
create_seasons(item_factory, world_options, items)
create_seeds(item_factory, world_options, items)
create_friendsanity_items(item_factory, world_options, items)
create_festival_rewards(item_factory, world_options, items)
create_babies(item_factory, items, random)
create_special_order_board_rewards(item_factory, world_options, items)
create_special_order_qi_rewards(item_factory, world_options, items)
create_walnut_purchase_rewards(item_factory, world_options, items)
create_magic_mod_spells(item_factory, world_options, items)
return items
@ -253,6 +202,8 @@ def create_backpack_items(item_factory: StardewItemFactory, world_options: Stard
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)
if ModNames.big_backpack in world_options[options.Mods]:
items.append(item_factory("Progressive Backpack"))
def create_mine_rewards(item_factory: StardewItemFactory, items: List[Item], random: Random):
@ -269,12 +220,15 @@ 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: StardewOptions, items: List[Item]):
if (world_options[options.TheMinesElevatorsProgression] ==
options.TheMinesElevatorsProgression.option_progressive or
world_options[options.TheMinesElevatorsProgression] ==
options.TheMinesElevatorsProgression.option_progressive_from_previous_floor):
items.extend([item_factory(item) for item in ["Progressive Mine Elevator"] * 24])
def create_elevators(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if world_options[options.ElevatorProgression] == options.ElevatorProgression.option_vanilla:
return
items.extend([item_factory(item) for item in ["Progressive Mine Elevator"] * 24])
if ModNames.deepwoods in world_options[options.Mods]:
items.extend([item_factory(item) for item in ["Progressive Wood Obelisk Sigils"] * 10])
if ModNames.skull_cavern_elevator in world_options[options.Mods]:
items.extend([item_factory(item) for item in ["Progressive Skull Cavern Elevator"] * 8])
def create_tools(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
@ -285,20 +239,25 @@ def create_tools(item_factory: StardewItemFactory, world_options: StardewOptions
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])
for item in items_by_group[Group.SKILL_LEVEL_UP]:
if item.mod_name not in world_options[options.Mods] and item.mod_name is not None:
continue
items.extend(item_factory(item) for item in [item.name] * 10)
def create_wizard_buildings(item_factory: StardewItemFactory, items: List[Item]):
def create_wizard_buildings(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
items.append(item_factory("Earth Obelisk"))
items.append(item_factory("Water Obelisk"))
items.append(item_factory("Desert Obelisk"))
items.append(item_factory("Island Obelisk"))
items.append(item_factory("Junimo Hut"))
items.append(item_factory("Gold Clock"))
if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false:
items.append(item_factory("Island Obelisk"))
if ModNames.deepwoods in world_options[options.Mods]:
items.append(item_factory("Woods Obelisk"))
def create_carpenter_buildings(item_factory: StardewItemFactory, world_options: StardewOptions,
items: List[Item]):
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}:
items.append(item_factory("Progressive Coop"))
@ -319,6 +278,8 @@ def create_carpenter_buildings(item_factory: StardewItemFactory, world_options:
items.append(item_factory("Progressive House"))
items.append(item_factory("Progressive House"))
items.append(item_factory("Progressive House"))
if ModNames.tractor in world_options[options.Mods]:
items.append(item_factory("Tractor Garage"))
def create_special_quest_rewards(item_factory: StardewItemFactory, items: List[Item]):
@ -329,9 +290,13 @@ def create_special_quest_rewards(item_factory: StardewItemFactory, items: List[I
items.append(item_factory("Iridium Snake Milk"))
def create_stardrops(item_factory: StardewItemFactory, items: List[Item]):
def create_stardrops(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
items.append(item_factory("Stardrop")) # The Mines level 100
items.append(item_factory("Stardrop")) # Old Master Cannoli
if world_options[options.Fishsanity] != options.Fishsanity.option_none:
items.append(item_factory("Stardrop")) #Master Angler Stardrop
if ModNames.deepwoods in world_options[options.Mods]:
items.append(item_factory("Stardrop")) # Petting the Unicorn
def create_museum_items(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
@ -352,24 +317,40 @@ def create_friendsanity_items(item_factory: StardewItemFactory, world_options: S
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
include_post_marriage_hearts = world_options[options.Friendsanity] == options.Friendsanity.option_all_with_marriage
exclude_ginger_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true
heart_size = world_options[options.FriendsanityHeartSize]
for villager in all_villagers:
if villager.mod_name not in world_options[options.Mods] and villager.mod_name is not None:
continue
if not villager.available and exclude_locked_villagers:
continue
if not villager.bachelor and exclude_non_bachelors:
continue
if villager.name == "Leo" and exclude_ginger_island:
continue
heart_cap = 8 if villager.bachelor else 10
if include_post_marriage_hearts and villager.bachelor:
heart_cap = 14
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 heart > heart_cap:
break
if heart % heart_size == 0 or heart == heart_cap:
items.append(item_factory(f"{villager.name} <3"))
if not exclude_non_bachelors:
for heart in range(1, 6):
items.append(item_factory(f"Pet: 1 <3"))
if heart % heart_size == 0 or heart == 5:
items.append(item_factory(f"Pet <3"))
def create_arcade_machine_items(item_factory: StardewItemFactory, world_options: StardewOptions,
items: List[Item]):
def create_babies(item_factory: StardewItemFactory, items: List[Item], random: Random):
baby_items = [item for item in items_by_group[Group.BABY]]
for i in range(2):
chosen_baby = random.choice(baby_items)
items.append(item_factory(chosen_baby))
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"))
items.append(item_factory("JotPK: Progressive Boots"))
@ -387,47 +368,160 @@ def create_arcade_machine_items(item_factory: StardewItemFactory, world_options:
def create_player_buffs(item_factory: StardewItemFactory, world_options: options.StardewOptions, items: List[Item]):
number_of_buffs: int = world_options[options.NumberOfPlayerBuffs]
items.extend(item_factory(item) for item in ["Movement Speed Bonus"] * number_of_buffs)
items.extend(item_factory(item) for item in ["Luck Bonus"] * number_of_buffs)
number_of_movement_buffs: int = world_options[options.NumberOfMovementBuffs]
number_of_luck_buffs: int = world_options[options.NumberOfLuckBuffs]
items.extend(item_factory(item) for item in [Buff.movement] * number_of_movement_buffs)
items.extend(item_factory(item) for item in [Buff.luck] * number_of_luck_buffs)
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_traveling_merchant_items(item_factory: StardewItemFactory, items: List[Item]):
items.extend([*(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_seasons(item_factory: StardewItemFactory, world_options: StardewOptions) -> List[Item]:
def create_seasons(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if world_options[options.SeasonRandomization] == options.SeasonRandomization.option_disabled:
return []
return
if world_options[options.SeasonRandomization] == options.SeasonRandomization.option_progressive:
return [item_factory(item) for item in ["Progressive Season"] * 3]
items.extend([item_factory(item) for item in ["Progressive Season"] * 3])
return
return [item_factory(item) for item in items_by_group[Group.SEASON]]
items.extend([item_factory(item) for item in items_by_group[Group.SEASON]])
def create_seeds(item_factory: StardewItemFactory, world_options: StardewOptions) -> List[Item]:
if world_options[options.SeedShuffle] == options.SeedShuffle.option_disabled:
def create_seeds(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if world_options[options.Cropsanity] == options.Cropsanity.option_disabled:
return
include_ginger_island = world_options[options.ExcludeGingerIsland] != options.ExcludeGingerIsland.option_true
seed_items = [item_factory(item) for item in items_by_group[Group.CROPSANITY] if include_ginger_island or Group.GINGER_ISLAND not in item.groups]
items.extend(seed_items)
def create_festival_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if world_options[options.FestivalLocations] == options.FestivalLocations.option_disabled:
return
items.extend([*[item_factory(item) for item in items_by_group[Group.FESTIVAL] if item.classification != ItemClassification.filler],
item_factory("Stardrop")])
def create_walnut_purchase_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true:
return
items.extend([item_factory("Boat Repair"),
item_factory("Open Professor Snail Cave"),
item_factory("Ostrich Incubator Recipe"),
*[item_factory(item) for item in items_by_group[Group.WALNUT_PURCHASE]]])
def create_special_order_board_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled:
return
items.extend([item_factory(item) for item in items_by_group[Group.SPECIAL_ORDER_BOARD]])
def create_special_order_qi_rewards(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if (world_options[options.SpecialOrderLocations] != options.SpecialOrderLocations.option_board_qi or
world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true):
return
qi_gem_rewards = ["100 Qi Gems", "10 Qi Gems", "40 Qi Gems", "25 Qi Gems", "25 Qi Gems",
"40 Qi Gems", "20 Qi Gems", "50 Qi Gems", "40 Qi Gems", "35 Qi Gems"]
qi_gem_items = [item_factory(reward) for reward in qi_gem_rewards]
items.extend(qi_gem_items)
def create_tv_channels(item_factory: StardewItemFactory, items: List[Item]):
items.extend([item_factory(item) for item in items_by_group[Group.TV_CHANNEL]])
def create_filler_festival_rewards(item_factory: StardewItemFactory, world_options: StardewOptions) -> List[Item]:
if world_options[options.FestivalLocations] == options.FestivalLocations.option_disabled:
return []
return [item_factory(item) for item in items_by_group[Group.SEED_SHUFFLE]]
return [item_factory(item) for item in items_by_group[Group.FESTIVAL] if
item.classification == ItemClassification.filler]
def fill_with_resource_packs(item_factory: StardewItemFactory, world_options: options.StardewOptions, random: Random,
required_resource_pack: int) -> List[Item]:
resource_pack_multiplier = world_options[options.ResourcePackMultiplier]
def create_magic_mod_spells(item_factory: StardewItemFactory, world_options: StardewOptions, items: List[Item]):
if ModNames.magic not in world_options[options.Mods]:
return []
items.extend([item_factory(item) for item in items_by_group[Group.MAGIC_SPELL]])
if resource_pack_multiplier == 0:
return [item_factory(cola) for cola in ["Joja Cola"] * required_resource_pack]
def create_unique_filler_items(item_factory: StardewItemFactory, world_options: options.StardewOptions, random: Random,
available_item_slots: int) -> List[Item]:
items = []
for i in range(required_resource_pack):
resource_pack = random.choice(all_resource_packs)
items.append(item_factory(resource_pack.create_name_from_multiplier(resource_pack_multiplier)))
items.extend(create_filler_festival_rewards(item_factory, world_options))
if len(items) > available_item_slots:
items = random.sample(items, available_item_slots)
return items
def fill_with_resource_packs_and_traps(item_factory: StardewItemFactory, world_options: options.StardewOptions, random: Random,
items_already_added: List[Item],
number_locations: int) -> List[Item]:
include_traps = world_options[options.TrapItems] != options.TrapItems.option_no_traps
all_filler_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK]]
all_filler_packs.extend(items_by_group[Group.TRASH])
if include_traps:
all_filler_packs.extend(items_by_group[Group.TRAP])
items_already_added_names = [item.name for item in items_already_added]
useful_resource_packs = [pack for pack in items_by_group[Group.RESOURCE_PACK_USEFUL]
if pack.name not in items_already_added_names]
trap_items = [pack for pack in items_by_group[Group.TRAP]
if pack.name not in items_already_added_names and
(pack.mod_name is None or pack.mod_name in world_options[options.Mods])]
priority_filler_items = []
priority_filler_items.extend(useful_resource_packs)
if include_traps:
priority_filler_items.extend(trap_items)
all_filler_packs = remove_excluded_packs(all_filler_packs, world_options)
priority_filler_items = remove_excluded_packs(priority_filler_items, world_options)
number_priority_items = len(priority_filler_items)
required_resource_pack = number_locations - len(items_already_added)
if required_resource_pack < number_priority_items:
chosen_priority_items = [item_factory(resource_pack) for resource_pack in
random.sample(priority_filler_items, required_resource_pack)]
return chosen_priority_items
items = []
chosen_priority_items = [item_factory(resource_pack) for resource_pack in priority_filler_items]
items.extend(chosen_priority_items)
required_resource_pack -= number_priority_items
all_filler_packs = [filler_pack for filler_pack in all_filler_packs
if Group.MAXIMUM_ONE not in filler_pack.groups or
filler_pack.name not in [priority_item.name for priority_item in priority_filler_items]]
while required_resource_pack > 0:
resource_pack = random.choice(all_filler_packs)
exactly_2 = Group.EXACTLY_TWO in resource_pack.groups
while exactly_2 and required_resource_pack == 1:
resource_pack = random.choice(all_filler_packs)
exactly_2 = Group.EXACTLY_TWO in resource_pack.groups
items.append(item_factory(resource_pack))
required_resource_pack -= 1
if exactly_2:
items.append(item_factory(resource_pack))
required_resource_pack -= 1
if exactly_2 or Group.MAXIMUM_ONE in resource_pack.groups:
all_filler_packs.remove(resource_pack)
return items
def remove_excluded_packs(packs, world_options):
included_packs = [pack for pack in packs if Group.DEPRECATED not in pack.groups]
if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true:
included_packs = [pack for pack in included_packs if Group.GINGER_ISLAND not in pack.groups]
return included_packs

View File

@ -8,6 +8,8 @@ from . import options, data
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
from .strings.goal_names import Goal
from .strings.region_names import Region
LOCATION_CODE_OFFSET = 717000
@ -32,7 +34,8 @@ class LocationTags(enum.Enum):
TRASH_CAN_UPGRADE = enum.auto()
FISHING_ROD_UPGRADE = enum.auto()
THE_MINES_TREASURE = enum.auto()
THE_MINES_ELEVATOR = enum.auto()
CROPSANITY = enum.auto()
ELEVATOR = enum.auto()
SKILL_LEVEL = enum.auto()
FARMING_LEVEL = enum.auto()
FISHING_LEVEL = enum.auto()
@ -51,6 +54,19 @@ class LocationTags(enum.Enum):
MUSEUM_MILESTONES = enum.auto()
MUSEUM_DONATIONS = enum.auto()
FRIENDSANITY = enum.auto()
FESTIVAL = enum.auto()
FESTIVAL_HARD = enum.auto()
SPECIAL_ORDER_BOARD = enum.auto()
SPECIAL_ORDER_QI = enum.auto()
GINGER_ISLAND = enum.auto()
WALNUT_PURCHASE = enum.auto()
# Skill Mods
LUCK_LEVEL = enum.auto()
BINNING_LEVEL = enum.auto()
COOKING_LEVEL = enum.auto()
SOCIALIZING_LEVEL = enum.auto()
MAGIC_LEVEL = enum.auto()
ARCHAEOLOGY_LEVEL = enum.auto()
@dataclass(frozen=True)
@ -58,6 +74,7 @@ class LocationData:
code_without_offset: Optional[int]
region: str
name: str
mod_name: Optional[str] = None
tags: FrozenSet[LocationTags] = frozenset()
@property
@ -81,6 +98,7 @@ def load_location_csv() -> List[LocationData]:
return [LocationData(int(location["id"]) if location["id"] else None,
location["region"],
location["name"],
str(location["mod_name"]) if location["mod_name"] else None,
frozenset(LocationTags[group]
for group in location["tags"].split(",")
if group))
@ -88,13 +106,15 @@ def load_location_csv() -> List[LocationData]:
events_locations = [
LocationData(None, "Stardew Valley", "Succeed Grandpa's Evaluation"),
LocationData(None, "Community Center", "Complete Community Center"),
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", "Complete the Museum Collection"),
LocationData(None, "Stardew Valley", "Full House"),
LocationData(None, Region.farm_house, Goal.grandpa_evaluation),
LocationData(None, Region.community_center, Goal.community_center),
LocationData(None, Region.mines_floor_120, Goal.bottom_of_the_mines),
LocationData(None, Region.skull_cavern_100, Goal.cryptic_note),
LocationData(None, Region.farm, Goal.master_angler),
LocationData(None, Region.museum, Goal.complete_museum),
LocationData(None, Region.farm_house, Goal.full_house),
LocationData(None, Region.island_west, Goal.greatest_walnut_hunter),
LocationData(None, Region.qi_walnut_room, Goal.perfection),
]
all_locations = load_location_csv() + events_locations
@ -113,6 +133,15 @@ def initialize_groups():
initialize_groups()
def extend_cropsanity_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.Cropsanity] == options.Cropsanity.option_disabled:
return
cropsanity_locations = locations_by_tag[LocationTags.CROPSANITY]
cropsanity_locations = filter_ginger_island(world_options, cropsanity_locations)
randomized_locations.extend(cropsanity_locations)
def extend_help_wanted_quests(randomized_locations: List[LocationData], desired_number_of_quests: int):
for i in range(0, desired_number_of_quests):
batch = i // 7
@ -128,19 +157,29 @@ def extend_help_wanted_quests(randomized_locations: List[LocationData], desired_
randomized_locations.append(location_table[f"Help Wanted: Gathering {batch + 1}"])
def extend_fishsanity_locations(randomized_locations: List[LocationData], fishsanity: int, random: Random):
def extend_fishsanity_locations(randomized_locations: List[LocationData], world_options, random: Random):
prefix = "Fishsanity: "
if fishsanity == options.Fishsanity.option_none:
if world_options[options.Fishsanity] == options.Fishsanity.option_none:
return
elif fishsanity == options.Fishsanity.option_legendaries:
elif world_options[options.Fishsanity] == options.Fishsanity.option_legendaries:
randomized_locations.extend(location_table[f"{prefix}{legendary.name}"] for legendary in legendary_fish)
elif fishsanity == options.Fishsanity.option_special:
elif world_options[options.Fishsanity] == options.Fishsanity.option_special:
randomized_locations.extend(location_table[f"{prefix}{special.name}"] for special in special_fish)
elif fishsanity == options.Fishsanity.option_randomized:
randomized_locations.extend(location_table[f"{prefix}{fish.name}"]
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)
elif world_options[options.Fishsanity] == options.Fishsanity.option_randomized:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if random.random() < 0.4]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_all:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_exclude_legendaries:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish not in legendary_fish]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_exclude_hard_fish:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 80]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
elif world_options[options.Fishsanity] == options.Fishsanity.option_only_easy_fish:
fish_locations = [location_table[f"{prefix}{fish.name}"] for fish in all_fish if fish.difficulty < 50]
randomized_locations.extend(filter_ginger_island(world_options, fish_locations))
def extend_museumsanity_locations(randomized_locations: List[LocationData], museumsanity: int, random: Random):
@ -156,26 +195,98 @@ def extend_museumsanity_locations(randomized_locations: List[LocationData], muse
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:
def extend_friendsanity_locations(randomized_locations: List[LocationData], world_options: options.StardewOptions):
if world_options[options.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
exclude_leo = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true
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
include_post_marriage_hearts = world_options[options.Friendsanity] == options.Friendsanity.option_all_with_marriage
heart_size = world_options[options.FriendsanityHeartSize]
for villager in all_villagers:
if villager.mod_name not in world_options[options.Mods] and villager.mod_name is not None:
continue
if not villager.available and exclude_locked_villagers:
continue
if not villager.bachelor and exclude_non_bachelors:
continue
if villager.name == "Leo" and exclude_leo:
continue
heart_cap = 8 if villager.bachelor else 10
if include_post_marriage_hearts and villager.bachelor:
heart_cap = 14
for heart in range(1, 15):
if villager.bachelor and exclude_post_marriage_hearts and heart > 8:
continue
if villager.bachelor or heart < 11:
if heart > heart_cap:
break
if heart % heart_size == 0 or heart == heart_cap:
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"])
if heart % heart_size == 0 or heart == 5:
randomized_locations.append(location_table[f"Friendsanity: Pet {heart} <3"])
def extend_festival_locations(randomized_locations: List[LocationData], festival_option: int):
if festival_option == options.FestivalLocations.option_disabled:
return
festival_locations = locations_by_tag[LocationTags.FESTIVAL]
randomized_locations.extend(festival_locations)
extend_hard_festival_locations(randomized_locations, festival_option)
def extend_hard_festival_locations(randomized_locations, festival_option: int):
if festival_option != options.FestivalLocations.option_hard:
return
hard_festival_locations = locations_by_tag[LocationTags.FESTIVAL_HARD]
randomized_locations.extend(hard_festival_locations)
def extend_special_order_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled:
return
include_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false
board_locations = filter_disabled_locations(world_options, locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD])
randomized_locations.extend(board_locations)
if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_board_qi and include_island:
include_arcade = world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_disabled
qi_orders = [location for location in locations_by_tag[LocationTags.SPECIAL_ORDER_QI] if include_arcade or LocationTags.JUNIMO_KART not in location.tags]
randomized_locations.extend(qi_orders)
def extend_walnut_purchase_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true:
return
randomized_locations.append(location_table["Repair Ticket Machine"])
randomized_locations.append(location_table["Repair Boat Hull"])
randomized_locations.append(location_table["Repair Boat Anchor"])
randomized_locations.append(location_table["Open Professor Snail Cave"])
randomized_locations.append(location_table["Complete Island Field Office"])
randomized_locations.extend(locations_by_tag[LocationTags.WALNUT_PURCHASE])
def extend_mandatory_locations(randomized_locations: List[LocationData], world_options):
mandatory_locations = [location for location in locations_by_tag[LocationTags.MANDATORY]]
filtered_mandatory_locations = filter_disabled_locations(world_options, mandatory_locations)
randomized_locations.extend(filtered_mandatory_locations)
def extend_backpack_locations(randomized_locations: List[LocationData], world_options):
backpack_locations = [location for location in locations_by_tag[LocationTags.BACKPACK]]
filtered_backpack_locations = filter_modded_locations(world_options, backpack_locations)
randomized_locations.extend(filtered_backpack_locations)
def extend_elevator_locations(randomized_locations: List[LocationData], world_options):
if world_options[options.ElevatorProgression] == options.ElevatorProgression.option_vanilla:
return
elevator_locations = [location for location in locations_by_tag[LocationTags.ELEVATOR]]
filtered_elevator_locations = filter_modded_locations(world_options, elevator_locations)
randomized_locations.extend(filtered_elevator_locations)
def create_locations(location_collector: StardewLocationCollector,
@ -183,33 +294,55 @@ def create_locations(location_collector: StardewLocationCollector,
random: Random):
randomized_locations = []
randomized_locations.extend(locations_by_tag[LocationTags.MANDATORY])
if not world_options[options.BackpackProgression] == options.BackpackProgression.option_vanilla:
randomized_locations.extend(locations_by_tag[LocationTags.BACKPACK])
extend_mandatory_locations(randomized_locations, world_options)
extend_backpack_locations(randomized_locations, world_options)
if not world_options[options.ToolProgression] == options.ToolProgression.option_vanilla:
randomized_locations.extend(locations_by_tag[LocationTags.TOOL_UPGRADE])
if not world_options[options.TheMinesElevatorsProgression] == options.TheMinesElevatorsProgression.option_vanilla:
randomized_locations.extend(locations_by_tag[LocationTags.THE_MINES_ELEVATOR])
extend_elevator_locations(randomized_locations, world_options)
if not world_options[options.SkillProgression] == options.SkillProgression.option_vanilla:
randomized_locations.extend(locations_by_tag[LocationTags.SKILL_LEVEL])
for location in locations_by_tag[LocationTags.SKILL_LEVEL]:
if location.mod_name is None or location.mod_name in world_options[options.Mods]:
randomized_locations.append(location_table[location.name])
if not world_options[options.BuildingProgression] == options.BuildingProgression.option_vanilla:
randomized_locations.extend(locations_by_tag[LocationTags.BUILDING_BLUEPRINT])
for location in locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
if location.mod_name is None or location.mod_name in world_options[options.Mods]:
randomized_locations.append(location_table[location.name])
if not world_options[options.ArcadeMachineLocations] == options.ArcadeMachineLocations.option_disabled:
if world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_disabled:
randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE_VICTORY])
if world_options[options.ArcadeMachineLocations] == options.ArcadeMachineLocations.option_full_shuffling:
randomized_locations.extend(locations_by_tag[LocationTags.ARCADE_MACHINE])
extend_cropsanity_locations(randomized_locations, world_options)
extend_help_wanted_quests(randomized_locations, world_options[options.HelpWantedLocations])
extend_fishsanity_locations(randomized_locations, world_options[options.Fishsanity], random)
extend_fishsanity_locations(randomized_locations, world_options, random)
extend_museumsanity_locations(randomized_locations, world_options[options.Museumsanity], random)
extend_friendsanity_locations(randomized_locations, world_options[options.Friendsanity])
extend_friendsanity_locations(randomized_locations, world_options)
extend_festival_locations(randomized_locations, world_options[options.FestivalLocations])
extend_special_order_locations(randomized_locations, world_options)
extend_walnut_purchase_locations(randomized_locations, world_options)
for location_data in randomized_locations:
location_collector(location_data.name, location_data.code, location_data.region)
def filter_ginger_island(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]:
include_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_false
return [location for location in locations if include_island or LocationTags.GINGER_ISLAND not in location.tags]
def filter_modded_locations(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]:
current_mod_names = world_options[options.Mods]
return [location for location in locations if location.mod_name is None or location.mod_name in current_mod_names]
def filter_disabled_locations(world_options: options.StardewOptions, locations: List[LocationData]) -> List[LocationData]:
locations_first_pass = filter_ginger_island(world_options, locations)
locations_second_pass = filter_modded_locations(world_options, locations_first_pass)
return locations_second_pass

File diff suppressed because it is too large Load Diff

View File

View File

@ -0,0 +1,16 @@
from typing import Union
from ...strings.artisan_good_names import ArtisanGood
from ...strings.building_names import ModBuilding
from ..mod_data import ModNames
from ...strings.metal_names import MetalBar
from ...strings.region_names import Region
def get_modded_building_rules(vanilla_logic, active_mods):
buildings = {}
if ModNames.tractor in active_mods:
buildings.update({
ModBuilding.tractor_garage: vanilla_logic.can_spend_money_at(Region.carpenter, 150000) & vanilla_logic.has(MetalBar.iron) &
vanilla_logic.has(MetalBar.iridium) & vanilla_logic.has(ArtisanGood.battery_pack)})
return buildings

View File

@ -0,0 +1,35 @@
from ...strings.craftable_names import Craftable
from ...strings.performance_names import Performance
from ...strings.skill_names import Skill
from ...strings.tool_names import Tool, ToolMaterial
from ...strings.ap_names.transport_names import ModTransportation
from ...stardew_rule import StardewRule, True_, And
from ... import options
def can_reach_woods_depth(vanilla_logic, depth: int) -> StardewRule:
tier = int(depth / 25) + 1
rules = []
if depth > 10:
rules.append(vanilla_logic.has(Craftable.bomb) | vanilla_logic.has_tool(Tool.axe, ToolMaterial.iridium))
if depth > 30:
rules.append(vanilla_logic.received(ModTransportation.woods_obelisk))
if depth > 50:
rules.append(vanilla_logic.can_do_combat_at_level(Performance.great) & vanilla_logic.can_cook() &
vanilla_logic.received(ModTransportation.woods_obelisk))
if vanilla_logic.options[options.SkillProgression] == options.SkillProgression.option_progressive:
combat_tier = min(10, max(0, tier + 5))
rules.append(vanilla_logic.has_skill_level(Skill.combat, combat_tier))
return And(rules)
def has_woods_rune_to_depth(vanilla_logic, floor: int) -> StardewRule:
if vanilla_logic.options[options.ElevatorProgression] == options.ElevatorProgression.option_vanilla:
return True_()
return vanilla_logic.received("Progressive Wood Obelisk Sigils", count=int(floor / 10))
def can_chop_to_depth(vanilla_logic, floor: int) -> StardewRule:
previous_elevator = max(floor - 10, 0)
return (has_woods_rune_to_depth(vanilla_logic, previous_elevator) &
can_reach_woods_depth(vanilla_logic, previous_elevator))

View File

@ -0,0 +1,80 @@
from ...strings.region_names import MagicRegion
from ...mods.mod_data import ModNames
from ...strings.spells import MagicSpell
from ...strings.ap_names.skill_level_names import ModSkillLevel
from ...stardew_rule import Count, StardewRule, False_
from ... import options
def can_use_clear_debris_instead_of_tool_level(vanilla_logic, level: int) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
return vanilla_logic.received(MagicSpell.clear_debris) & can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, level)
def can_use_altar(vanilla_logic) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
return vanilla_logic.can_reach_region(MagicRegion.altar)
def has_any_spell(vanilla_logic) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
return can_use_altar(vanilla_logic)
def has_attack_spell_count(vanilla_logic, count: int) -> StardewRule:
attack_spell_rule = [vanilla_logic.received(MagicSpell.fireball), vanilla_logic.received(
MagicSpell.frostbite), vanilla_logic.received(MagicSpell.shockwave), vanilla_logic.received(MagicSpell.spirit),
vanilla_logic.received(MagicSpell.meteor)
]
return Count(count, attack_spell_rule)
def has_support_spell_count(vanilla_logic, count: int) -> StardewRule:
support_spell_rule = [can_use_altar(vanilla_logic), vanilla_logic.received(ModSkillLevel.magic_level, 2),
vanilla_logic.received(MagicSpell.descend), vanilla_logic.received(MagicSpell.heal),
vanilla_logic.received(MagicSpell.tendrils)]
return Count(count, support_spell_rule)
def has_decent_spells(vanilla_logic) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 2)
magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 1)
return magic_resource_rule & magic_attack_options_rule
def has_good_spells(vanilla_logic) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 4)
magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 2)
magic_support_options_rule = has_support_spell_count(vanilla_logic, 1)
return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
def has_great_spells(vanilla_logic) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 6)
magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 3)
magic_support_options_rule = has_support_spell_count(vanilla_logic, 1)
return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
def has_amazing_spells(vanilla_logic) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
magic_resource_rule = can_use_altar(vanilla_logic) & vanilla_logic.received(ModSkillLevel.magic_level, 8)
magic_attack_options_rule = has_attack_spell_count(vanilla_logic, 4)
magic_support_options_rule = has_support_spell_count(vanilla_logic, 2)
return magic_resource_rule & magic_attack_options_rule & magic_support_options_rule
def can_blink(vanilla_logic) -> StardewRule:
if ModNames.magic not in vanilla_logic.options[options.Mods]:
return False_()
return vanilla_logic.received(MagicSpell.blink) & can_use_altar(vanilla_logic)

View File

@ -0,0 +1,31 @@
from typing import Union
from ...strings.quest_names import ModQuest
from ..mod_data import ModNames
from ...strings.food_names import Meal, Beverage
from ...strings.monster_drop_names import Loot
from ...strings.villager_names import ModNPC
from ...strings.season_names import Season
from ...strings.region_names import Region
def get_modded_quest_rules(vanilla_logic, active_mods):
quests = {}
if ModNames.juna in active_mods:
quests.update({
ModQuest.JunaCola: vanilla_logic.has_relationship(ModNPC.juna, 3) & vanilla_logic.has(Beverage.joja_cola),
ModQuest.JunaSpaghetti: vanilla_logic.has_relationship(ModNPC.juna, 6) & vanilla_logic.has(Meal.spaghetti)
})
if ModNames.ginger in active_mods:
quests.update({
ModQuest.MrGinger: vanilla_logic.has_relationship(ModNPC.mr_ginger, 6) & vanilla_logic.has(Loot.void_essence)
})
if ModNames.ayeisha in active_mods:
quests.update({
ModQuest.AyeishaEnvelope: (vanilla_logic.has_season(Season.spring) | vanilla_logic.has_season(Season.fall)) &
vanilla_logic.can_reach_region(Region.mountain),
ModQuest.AyeishaRing: vanilla_logic.has_season(Season.winter) & vanilla_logic.can_reach_region(Region.forest)
})
return quests

View File

@ -0,0 +1,94 @@
from typing import List, Union
from . import magic
from ...strings.building_names import Building
from ...strings.geode_names import Geode
from ...strings.region_names import Region
from ...strings.skill_names import ModSkill
from ...strings.spells import MagicSpell
from ...strings.machine_names import Machine
from ...strings.tool_names import Tool, ToolMaterial
from ...mods.mod_data import ModNames
from ...data.villagers_data import all_villagers
from ...stardew_rule import Count, StardewRule, False_
from ... import options
def append_mod_skill_level(skills_items: List[str], active_mods):
if ModNames.luck_skill in active_mods:
skills_items.append("Luck Level")
if ModNames.socializing_skill in active_mods:
skills_items.append("Socializing Level")
if ModNames.magic in active_mods:
skills_items.append("Magic Level")
if ModNames.archaeology in active_mods:
skills_items.append("Archaeology Level")
if ModNames.binning_skill in active_mods:
skills_items.append("Binning Level")
if ModNames.cooking_skill in active_mods:
skills_items.append("Cooking Level")
def can_earn_mod_skill_level(logic, skill: str, level: int) -> StardewRule:
if ModNames.luck_skill in logic.options[options.Mods] and skill == ModSkill.luck:
return can_earn_luck_skill_level(logic, level)
if ModNames.magic in logic.options[options.Mods] and skill == ModSkill.magic:
return can_earn_magic_skill_level(logic, level)
if ModNames.socializing_skill in logic.options[options.Mods] and skill == ModSkill.socializing:
return can_earn_socializing_skill_level(logic, level)
if ModNames.archaeology in logic.options[options.Mods] and skill == ModSkill.archaeology:
return can_earn_archaeology_skill_level(logic, level)
if ModNames.cooking_skill in logic.options[options.Mods] and skill == ModSkill.cooking:
return can_earn_cooking_skill_level(logic, level)
if ModNames.binning_skill in logic.options[options.Mods] and skill == ModSkill.binning:
return can_earn_binning_skill_level(logic, level)
return False_()
def can_earn_luck_skill_level(vanilla_logic, level: int) -> StardewRule:
if level >= 6:
return vanilla_logic.can_fish_chests() | vanilla_logic.can_open_geode(Geode.magma)
else:
return vanilla_logic.can_fish_chests() | vanilla_logic.can_open_geode(Geode.geode)
def can_earn_magic_skill_level(vanilla_logic, level: int) -> StardewRule:
spell_count = [vanilla_logic.received(MagicSpell.clear_debris), vanilla_logic.received(MagicSpell.water),
vanilla_logic.received(MagicSpell.blink), vanilla_logic.received(MagicSpell.fireball),
vanilla_logic.received(MagicSpell.frostbite),
vanilla_logic.received(MagicSpell.descend), vanilla_logic.received(MagicSpell.tendrils),
vanilla_logic.received(MagicSpell.shockwave),
vanilla_logic.received(MagicSpell.meteor),
vanilla_logic.received(MagicSpell.spirit)]
return magic.can_use_altar(vanilla_logic) & Count(level, spell_count)
def can_earn_socializing_skill_level(vanilla_logic, level: int) -> StardewRule:
villager_count = []
for villager in all_villagers:
if villager.mod_name in vanilla_logic.options[options.Mods] or villager.mod_name is None:
villager_count.append(vanilla_logic.can_earn_relationship(villager.name, level))
return Count(level * 2, villager_count)
def can_earn_archaeology_skill_level(vanilla_logic, level: int) -> StardewRule:
if level >= 6:
return vanilla_logic.can_do_panning() | vanilla_logic.has_tool(Tool.hoe, ToolMaterial.gold)
else:
return vanilla_logic.can_do_panning() | vanilla_logic.has_tool(Tool.hoe, ToolMaterial.basic)
def can_earn_cooking_skill_level(vanilla_logic, level: int) -> StardewRule:
if level >= 6:
return vanilla_logic.can_cook() & vanilla_logic.can_fish() & vanilla_logic.can_reach_region(Region.saloon) & \
vanilla_logic.has_building(Building.coop) & vanilla_logic.has_building(Building.barn)
else:
return vanilla_logic.can_cook()
def can_earn_binning_skill_level(vanilla_logic, level: int) -> StardewRule:
if level >= 6:
return vanilla_logic.can_reach_region(Region.town) & vanilla_logic.has(Machine.recycling_machine) & \
(vanilla_logic.can_fish() | vanilla_logic.can_crab_pot())
else:
return vanilla_logic.can_reach_region(Region.town) | (vanilla_logic.has(Machine.recycling_machine) &
(vanilla_logic.can_fish() | vanilla_logic.can_crab_pot()))

View File

@ -0,0 +1,10 @@
from ...stardew_rule import Count, StardewRule, True_
from ...mods.mod_data import ModNames
from ... import options
def has_skull_cavern_elevator_to_floor(self, floor: int) -> StardewRule:
if self.options[options.ElevatorProgression] != options.ElevatorProgression.option_vanilla and \
ModNames.skull_cavern_elevator in self.options[options.Mods]:
return self.received("Progressive Skull Cavern Elevator", floor // 25)
return True_()

View File

@ -0,0 +1,24 @@
from typing import Union
from ...strings.craftable_names import Craftable
from ...strings.food_names import Meal
from ...strings.material_names import Material
from ...strings.monster_drop_names import Loot
from ...strings.region_names import Region
from ...strings.special_order_names import SpecialOrder, ModSpecialOrder
from ...strings.villager_names import ModNPC
from ..mod_data import ModNames
def get_modded_special_orders_rules(vanilla_logic, active_mods):
special_orders = {}
if ModNames.juna in active_mods:
special_orders.update({
ModSpecialOrder.junas_monster_mash: vanilla_logic.has_relationship(ModNPC.juna, 4) &
vanilla_logic.can_complete_special_order(SpecialOrder.a_curious_substance) &
vanilla_logic.has_rusty_key() &
vanilla_logic.can_reach_region(Region.forest) & vanilla_logic.has(Craftable.monster_musk) &
vanilla_logic.has("Energy Tonic") & vanilla_logic.has(Material.sap) & vanilla_logic.has(Loot.bug_meat) &
vanilla_logic.has(Craftable.oil_of_garlic) & vanilla_logic.has(Meal.strange_bun)
})
return special_orders

View File

@ -0,0 +1,48 @@
class ModNames:
vanilla = None
deepwoods = "DeepWoods"
tractor = "Tractor Mod"
big_backpack = "Bigger Backpack"
luck_skill = "Luck Skill"
magic = "Magic"
socializing_skill = "Socializing Skill"
archaeology = "Archaeology"
cooking_skill = "Cooking Skill"
binning_skill = "Binning Skill"
juna = "Juna - Roommate NPC"
jasper = "Professor Jasper Thomas"
alec = "Alec Revisited"
yoba = "Custom NPC - Yoba"
eugene = "Custom NPC Eugene"
wellwick = "'Prophet' Wellwick"
ginger = "Mister Ginger (cat npc)"
shiko = "Shiko - New Custom NPC"
delores = "Delores - Custom NPC"
ayeisha = "Ayeisha - The Postal Worker (Custom NPC)"
riley = "Custom NPC - Riley"
skull_cavern_elevator = "Skull Cavern Elevator"
mod_versions = {
ModNames.deepwoods: "3.0.0-beta",
ModNames.tractor: "4.16.4",
ModNames.big_backpack: "6.0.0",
ModNames.luck_skill: "1.2.4",
ModNames.magic: "0.8.2",
ModNames.socializing_skill: "1.1.5",
ModNames.archaeology: "1.5.0",
ModNames.cooking_skill: "1.4.5",
ModNames.binning_skill: "1.2.7",
ModNames.juna: "2.1.3",
ModNames.jasper: "1.7.6",
ModNames.alec: "2.1.0",
ModNames.yoba: "1.0.0",
ModNames.eugene: "1.3.1",
ModNames.wellwick: "1.0.0",
ModNames.ginger: "1.5.9",
ModNames.shiko: "1.1.0",
ModNames.delores: "1.1.2",
ModNames.ayeisha: "0.5.0-alpha",
ModNames.riley: "1.2.2",
ModNames.skull_cavern_elevator: "1.5.0",
}

View File

@ -0,0 +1,144 @@
from ..strings.entrance_names import DeepWoodsEntrance, EugeneEntrance, \
JasperEntrance, AlecEntrance, YobaEntrance, JunaEntrance, MagicEntrance, AyeishaEntrance, RileyEntrance
from ..strings.region_names import Region, DeepWoodsRegion, EugeneRegion, JasperRegion, \
AlecRegion, YobaRegion, JunaRegion, MagicRegion, AyeishaRegion, RileyRegion
from ..region_classes import RegionData, ConnectionData, RandomizationFlag, ModRegionData
from .mod_data import ModNames
deep_woods_regions = [
RegionData(Region.farm, [DeepWoodsEntrance.use_woods_obelisk]),
RegionData(DeepWoodsRegion.woods_obelisk_menu, [DeepWoodsEntrance.deep_woods_depth_1,
DeepWoodsEntrance.deep_woods_depth_10,
DeepWoodsEntrance.deep_woods_depth_20,
DeepWoodsEntrance.deep_woods_depth_30,
DeepWoodsEntrance.deep_woods_depth_40,
DeepWoodsEntrance.deep_woods_depth_50,
DeepWoodsEntrance.deep_woods_depth_60,
DeepWoodsEntrance.deep_woods_depth_70,
DeepWoodsEntrance.deep_woods_depth_80,
DeepWoodsEntrance.deep_woods_depth_90,
DeepWoodsEntrance.deep_woods_depth_100]),
RegionData(Region.secret_woods, [DeepWoodsEntrance.secret_woods_to_deep_woods]),
RegionData(DeepWoodsRegion.main_lichtung, [DeepWoodsEntrance.deep_woods_house]),
RegionData(DeepWoodsRegion.abandoned_home),
RegionData(DeepWoodsRegion.floor_10),
RegionData(DeepWoodsRegion.floor_20),
RegionData(DeepWoodsRegion.floor_30),
RegionData(DeepWoodsRegion.floor_40),
RegionData(DeepWoodsRegion.floor_50),
RegionData(DeepWoodsRegion.floor_60),
RegionData(DeepWoodsRegion.floor_70),
RegionData(DeepWoodsRegion.floor_80),
RegionData(DeepWoodsRegion.floor_90),
RegionData(DeepWoodsRegion.floor_100)
]
deep_woods_entrances = [
ConnectionData(DeepWoodsEntrance.use_woods_obelisk, DeepWoodsRegion.woods_obelisk_menu),
ConnectionData(DeepWoodsEntrance.secret_woods_to_deep_woods, DeepWoodsRegion.main_lichtung),
ConnectionData(DeepWoodsEntrance.deep_woods_house, DeepWoodsRegion.abandoned_home,
flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_1, DeepWoodsRegion.main_lichtung),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_10, DeepWoodsRegion.floor_10),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_20, DeepWoodsRegion.floor_20),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_30, DeepWoodsRegion.floor_30),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_40, DeepWoodsRegion.floor_40),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_50, DeepWoodsRegion.floor_50),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_60, DeepWoodsRegion.floor_60),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_70, DeepWoodsRegion.floor_70),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_80, DeepWoodsRegion.floor_80),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_90, DeepWoodsRegion.floor_90),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_100, DeepWoodsRegion.floor_100)
]
eugene_regions = [
RegionData(Region.forest, [EugeneEntrance.forest_to_garden]),
RegionData(EugeneRegion.eugene_garden, [EugeneEntrance.garden_to_bedroom]),
RegionData(EugeneRegion.eugene_bedroom)
]
eugene_entrances = [
ConnectionData(EugeneEntrance.forest_to_garden, EugeneRegion.eugene_garden,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(EugeneEntrance.garden_to_bedroom, EugeneRegion.eugene_bedroom, flag=RandomizationFlag.BUILDINGS)
]
magic_regions = [
RegionData(Region.pierre_store, [MagicEntrance.store_to_altar]),
RegionData(MagicRegion.altar)
]
magic_entrances = [
ConnectionData(MagicEntrance.store_to_altar, MagicRegion.altar, flag=RandomizationFlag.NOT_RANDOMIZED)
]
jasper_regions = [
RegionData(Region.museum, [JasperEntrance.museum_to_bedroom]),
RegionData(JasperRegion.jasper_bedroom)
]
jasper_entrances = [
ConnectionData(JasperEntrance.museum_to_bedroom, JasperRegion.jasper_bedroom, flag=RandomizationFlag.BUILDINGS)
]
alec_regions = [
RegionData(Region.forest, [AlecEntrance.forest_to_petshop]),
RegionData(AlecRegion.pet_store, [AlecEntrance.petshop_to_bedroom]),
RegionData(AlecRegion.alec_bedroom)
]
alec_entrances = [
ConnectionData(AlecEntrance.forest_to_petshop, AlecRegion.pet_store,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(AlecEntrance.petshop_to_bedroom, AlecRegion.alec_bedroom, flag=RandomizationFlag.BUILDINGS)
]
yoba_regions = [
RegionData(Region.secret_woods, [YobaEntrance.secret_woods_to_clearing]),
RegionData(YobaRegion.yoba_clearing)
]
yoba_entrances = [
ConnectionData(YobaEntrance.secret_woods_to_clearing, YobaRegion.yoba_clearing, flag=RandomizationFlag.BUILDINGS)
]
juna_regions = [
RegionData(Region.forest, [JunaEntrance.forest_to_juna_cave]),
RegionData(JunaRegion.juna_cave)
]
juna_entrances = [
ConnectionData(JunaEntrance.forest_to_juna_cave, JunaRegion.juna_cave,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA)
]
ayeisha_regions = [
RegionData(Region.bus_stop, [AyeishaEntrance.bus_stop_to_mail_van]),
RegionData(AyeishaRegion.mail_van)
]
ayeisha_entrances = [
ConnectionData(AyeishaEntrance.bus_stop_to_mail_van, AyeishaRegion.mail_van,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA)
]
riley_regions = [
RegionData(Region.town, [RileyEntrance.town_to_riley]),
RegionData(RileyRegion.riley_house)
]
riley_entrances = [
ConnectionData(RileyEntrance.town_to_riley, RileyRegion.riley_house,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA)
]
ModDataList = {
ModNames.deepwoods: ModRegionData(ModNames.deepwoods, deep_woods_regions, deep_woods_entrances),
ModNames.eugene: ModRegionData(ModNames.eugene, eugene_regions, eugene_entrances),
ModNames.jasper: ModRegionData(ModNames.jasper, jasper_regions, jasper_entrances),
ModNames.alec: ModRegionData(ModNames.alec, alec_regions, alec_entrances),
ModNames.yoba: ModRegionData(ModNames.yoba, yoba_regions, yoba_entrances),
ModNames.juna: ModRegionData(ModNames.juna, juna_regions, juna_entrances),
ModNames.magic: ModRegionData(ModNames.magic, magic_regions, magic_entrances),
ModNames.ayeisha: ModRegionData(ModNames.ayeisha, ayeisha_regions, ayeisha_entrances),
ModNames.riley: ModRegionData(ModNames.riley, riley_regions, riley_entrances),
}

View File

@ -1,8 +1,8 @@
from dataclasses import dataclass
from typing import Dict, Union, Protocol, runtime_checkable, ClassVar
from Options import Option, Range, DeathLink, SpecialRange, Toggle, Choice
from Options import Option, Range, DeathLink, SpecialRange, Toggle, Choice, OptionSet
from .mods.mod_data import ModNames
@runtime_checkable
class StardewOption(Protocol):
@ -11,14 +11,19 @@ class StardewOption(Protocol):
@dataclass
class StardewOptions:
options: Dict[str, Union[bool, int]]
options: Dict[str, Union[bool, int, str]]
def __getitem__(self, item: Union[str, StardewOption]) -> Union[bool, int]:
def __getitem__(self, item: Union[str, StardewOption]) -> Union[bool, int, str]:
if isinstance(item, StardewOption):
item = item.internal_name
return self.options.get(item, None)
def __setitem__(self, key: Union[str, StardewOption], value: Union[bool, int, str]):
if isinstance(key, StardewOption):
key = key.internal_name
self.options[key] = value
class Goal(Choice):
"""What's your goal with this play-through?
@ -29,6 +34,8 @@ class Goal(Choice):
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.
Greatest Walnut Hunter: The world will be completed once you find all 130 Golden Walnuts
Perfection: The world will be completed once you attain Perfection, based on the vanilla definition.
"""
internal_name = "goal"
display_name = "Goal"
@ -40,6 +47,18 @@ class Goal(Choice):
option_master_angler = 4
option_complete_collection = 5
option_full_house = 6
option_greatest_walnut_hunter = 7
# option_junimo_kart =
# option_prairie_king =
# option_fector_challenge =
# option_craft_master =
# option_mystery_of_the_stardrops =
# option_protector_of_the_valley =
# option_full_shipment =
# option_legend =
# option_beloved_farmer =
# option_master_of_the_five_ways =
option_perfection = 25
@classmethod
def get_option_name(cls, value) -> str:
@ -68,21 +87,22 @@ class StartingMoney(SpecialRange):
}
class ResourcePackMultiplier(SpecialRange):
"""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
range_end = 200
class ProfitMargin(SpecialRange):
"""Multiplier over all gold earned in-game by the player."""
internal_name = "profit_margin"
display_name = "Profit Margin"
range_start = 25
range_end = 400
# step = 25
display_name = "Resource Pack Multiplier"
default = 100
special_range_names = {
"resource packs disabled": 0,
"half packs": 50,
"normal packs": 100,
"double packs": 200,
"quarter": 25,
"half": 50,
"normal": 100,
"double": 200,
"triple": 300,
"quadruple": 400,
}
@ -119,8 +139,9 @@ class EntranceRandomization(Choice):
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
Buildings: All Entrances that Allow you to enter a building using a door are randomized with each other
Chaos: Same as above, but the entrances get reshuffled every single day!
"""
# 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!
@ -133,12 +154,12 @@ class EntranceRandomization(Choice):
option_disabled = 0
option_pelican_town = 1
option_non_progression = 2
# option_buildings = 3
option_buildings = 3
# option_everything = 4
# option_chaos = 4
# option_buildings_one_way = 5
# option_everything_one_way = 6
# option_chaos_one_way = 7
option_chaos = 5
# option_buildings_one_way = 6
# option_everything_one_way = 7
# option_chaos_one_way = 8
class SeasonRandomization(Choice):
@ -158,14 +179,14 @@ class SeasonRandomization(Choice):
option_progressive = 3
class SeedShuffle(Choice):
"""Should seeds be randomized?
class Cropsanity(Choice):
"""Formerly named "Seed Shuffle"
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.
Shuffled: The seeds will be unlocked as Archipelago items
Disabled: All the seeds are unlocked from the start, there are no location checks for growing and harvesting crops
Shuffled: Seeds are unlocked as archipelago item, for each seed there is a location check for growing and harvesting that crop
"""
internal_name = "seed_shuffle"
display_name = "Seed Shuffle"
internal_name = "cropsanity"
display_name = "Cropsanity"
default = 1
option_disabled = 0
option_shuffled = 1
@ -196,13 +217,13 @@ class ToolProgression(Choice):
option_progressive = 1
class TheMinesElevatorsProgression(Choice):
"""How is The Mines' Elevator progression handled?
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.
Progressive from previous floor: Locations are sent for taking the ladder or stairs to every 5
levels, taking the elevator does not count."""
class ElevatorProgression(Choice):
"""How is Elevator progression handled?
Vanilla: You will unlock new elevator floors for yourself.
Progressive: You will randomly find Progressive Mine Elevators to go deeper. Locations are sent for reaching
every elevator level.
Progressive from previous floor: Same as progressive, but you must reach elevator floors on your own,
you cannot use the elevator to check elevator locations"""
internal_name = "elevator_progression"
display_name = "Elevator Progression"
default = 2
@ -238,9 +259,23 @@ class BuildingProgression(Choice):
option_progressive_early_shipping_bin = 2
class FestivalLocations(Choice):
"""Locations for attending and participating in festivals
With Disabled, you do not need to attend festivals
With Easy, there are checks for participating in festivals
With Hard, the festival checks are only granted when the player performs well in the festival
"""
internal_name = "festival_locations"
display_name = "Festival Locations"
default = 1
option_disabled = 0
option_easy = 1
option_hard = 2
class ArcadeMachineLocations(Choice):
"""How are the Arcade Machines handled?
Vanilla: The arcade machines are not included in the Archipelago shuffling.
Disabled: 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.
@ -257,6 +292,20 @@ class ArcadeMachineLocations(Choice):
option_full_shuffling = 3
class SpecialOrderLocations(Choice):
"""How are the Special Orders handled?
Disabled: The special orders are not included in the Archipelago shuffling.
Board Only: The Special Orders on the board in town are location checks
Board and Qi: The Special Orders from Qi's walnut room are checks, as well as the board in town
"""
internal_name = "special_order_locations"
display_name = "Special Order Locations"
default = 1
option_disabled = 0
option_board_only = 1
option_board_qi = 2
class HelpWantedLocations(SpecialRange):
"""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.
@ -284,6 +333,9 @@ class Fishsanity(Choice):
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
Exclude Legendaries: Every fish except legendaries
Exclude Hard Fish: Every fish under difficulty 80
Only Easy Fish: Every fish under difficulty 50
"""
internal_name = "fishsanity"
display_name = "Fishsanity"
@ -294,6 +346,9 @@ class Fishsanity(Choice):
option_randomized = 3
alias_random_selection = option_randomized
option_all = 4
option_exclude_legendaries = 5
option_exclude_hard_fish = 6
option_only_easy_fish = 7
class Museumsanity(Choice):
@ -331,18 +386,64 @@ class Friendsanity(Choice):
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)
and daily luck bonus (0.025 flat value per buff)"""
internal_name = "player_buff_number"
display_name = "Number of Player Buffs"
# Conditional Setting - Friendsanity not None
class FriendsanityHeartSize(Range):
"""If using friendsanity, how many hearts are received per item, and how many hearts must be earned to send a check
A higher value will lead to fewer heart items in the item pool, reducing bloat"""
internal_name = "friendsanity_heart_size"
display_name = "Friendsanity Heart Size"
range_start = 1
range_end = 8
default = 4
# step = 1
class NumberOfMovementBuffs(Range):
"""Number of movement speed buffs to the player that exist as items in the pool.
Each movement speed buff is a +25% multiplier that stacks additively"""
internal_name = "movement_buff_number"
display_name = "Number of Movement Buffs"
range_start = 0
range_end = 12
default = 4
# step = 1
class NumberOfLuckBuffs(Range):
"""Number of luck buffs to the player that exist as items in the pool.
Each luck buff is a bonus to daily luck of 0.025"""
internal_name = "luck_buff_number"
display_name = "Number of Luck Buffs"
range_start = 0
range_end = 12
default = 4
# step = 1
class ExcludeGingerIsland(Toggle):
"""Exclude Ginger Island?
This option will forcefully exclude everything related to Ginger Island from the slot.
If you pick a goal that requires Ginger Island, you cannot exclude it and it will get included anyway"""
internal_name = "exclude_ginger_island"
display_name = "Exclude Ginger Island"
default = 0
class TrapItems(Choice):
"""When rolling filler items, including resource packs, the game can also roll trap items.
This setting is for choosing if traps will be in the item pool, and if so, how punishing they will be.
"""
internal_name = "trap_items"
display_name = "Trap Items"
default = 2
option_no_traps = 0
option_easy = 1
option_medium = 2
option_hard = 3
option_hell = 4
option_nightmare = 5
class MultipleDaySleepEnabled(Toggle):
"""Enable the ability to sleep automatically for multiple days straight?"""
internal_name = "multiple_day_sleep_enabled"
@ -372,7 +473,7 @@ class ExperienceMultiplier(SpecialRange):
internal_name = "experience_multiplier"
display_name = "Experience Multiplier"
range_start = 25
range_end = 400
range_end = 800
# step = 25
default = 200
@ -392,7 +493,7 @@ class FriendshipMultiplier(SpecialRange):
internal_name = "friendship_multiplier"
display_name = "Friendship Multiplier"
range_start = 25
range_end = 400
range_end = 800
# step = 25
default = 200
@ -438,46 +539,46 @@ class Gifting(Toggle):
default = 1
class GiftTax(SpecialRange):
"""Joja Prime will deliver gifts within one business day, for a price!
Sending a gift will cost a percentage of the item's monetary value as a tax on the sender"""
internal_name = "gift_tax"
display_name = "Gift Tax"
range_start = 0
range_end = 400
# step = 20
default = 20
special_range_names = {
"no tax": 0,
"soft tax": 20,
"rough tax": 40,
"full tax": 100,
"oppressive tax": 200,
"nightmare tax": 400,
class Mods(OptionSet):
"""List of mods that will be considered for shuffling."""
internal_name = "mods"
display_name = "Mods"
valid_keys = {
ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack,
ModNames.luck_skill, ModNames.magic, ModNames.socializing_skill, ModNames.archaeology,
ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna,
ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene,
ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores,
ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator
}
stardew_valley_option_classes = [
Goal,
StartingMoney,
ResourcePackMultiplier,
ProfitMargin,
BundleRandomization,
BundlePrice,
EntranceRandomization,
SeasonRandomization,
SeedShuffle,
Cropsanity,
BackpackProgression,
ToolProgression,
SkillProgression,
BuildingProgression,
TheMinesElevatorsProgression,
FestivalLocations,
ElevatorProgression,
ArcadeMachineLocations,
SpecialOrderLocations,
HelpWantedLocations,
Fishsanity,
Museumsanity,
Friendsanity,
NumberOfPlayerBuffs,
FriendsanityHeartSize,
NumberOfMovementBuffs,
NumberOfLuckBuffs,
ExcludeGingerIsland,
TrapItems,
MultipleDaySleepEnabled,
MultipleDaySleepCost,
ExperienceMultiplier,
@ -485,7 +586,7 @@ stardew_valley_option_classes = [
DebrisMultiplier,
QuickStart,
Gifting,
GiftTax,
Mods,
]
stardew_valley_options: Dict[str, type(Option)] = {option.internal_name: option for option in
stardew_valley_option_classes}

View File

@ -0,0 +1,58 @@
from enum import IntFlag
from typing import Optional, List
from dataclasses import dataclass, field
connector_keyword = " to "
class RandomizationFlag(IntFlag):
NOT_RANDOMIZED = 0b0
PELICAN_TOWN = 0b11111
NON_PROGRESSION = 0b11110
BUILDINGS = 0b11100
EVERYTHING = 0b11000
CHAOS = 0b10000
GINGER_ISLAND = 0b0100000
LEAD_TO_OPEN_AREA = 0b1000000
@dataclass(frozen=True)
class RegionData:
name: str
exits: List[str] = field(default_factory=list)
def get_merged_with(self, exits: List[str]):
merged_exits = []
merged_exits.extend(self.exits)
if exits is not None:
merged_exits.extend(exits)
merged_exits = list(set(merged_exits))
return RegionData(self.name, merged_exits)
def get_clone(self):
return self.get_merged_with(None)
@dataclass(frozen=True)
class ConnectionData:
name: str
destination: str
origin: Optional[str] = None
reverse: Optional[str] = None
flag: RandomizationFlag = RandomizationFlag.NOT_RANDOMIZED
def __post_init__(self):
if connector_keyword in self.name:
origin, destination = self.name.split(connector_keyword)
if self.reverse is None:
super().__setattr__("reverse", f"{destination}{connector_keyword}{origin}")
@dataclass(frozen=True)
class ModRegionData:
mod_name: str
regions: List[RegionData]
connections: List[ConnectionData]

View File

@ -1,12 +1,13 @@
from dataclasses import dataclass, field
from enum import IntFlag
from random import Random
from typing import Iterable, Dict, Protocol, Optional, List, Tuple
from typing import Iterable, Dict, Protocol, List, Tuple, Set
from BaseClasses import Region, Entrance
from . import options
from .data.region_data import SVRegion
from .strings.entrance_names import Entrance
from .strings.region_names import Region
from .region_classes import RegionData, ConnectionData, RandomizationFlag
from .options import StardewOptions
from .mods.mod_regions import ModDataList
class RegionFactory(Protocol):
@ -14,299 +15,597 @@ class RegionFactory(Protocol):
raise NotImplementedError
class RandomizationFlag(IntFlag):
NOT_RANDOMIZED = 0b0
PELICAN_TOWN = 0b11111
NON_PROGRESSION = 0b11110
BUILDINGS = 0b11100
EVERYTHING = 0b11000
CHAOS = 0b10000
@dataclass(frozen=True)
class RegionData:
name: str
exits: List[str] = field(default_factory=list)
@dataclass(frozen=True)
class ConnectionData:
name: str
destination: str
reverse: Optional[str] = None
flag: RandomizationFlag = RandomizationFlag.NOT_RANDOMIZED
def __post_init__(self):
if self.reverse is None and " to " in self.name:
origin, destination = self.name.split(" to ")
super().__setattr__("reverse", f"{destination} to {origin}")
stardew_valley_regions = [
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(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(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),
vanilla_regions = [
RegionData(Region.menu, [Entrance.to_stardew_valley]),
RegionData(Region.stardew_valley, [Entrance.to_farmhouse]),
RegionData(Region.farm_house, [Entrance.farmhouse_to_farm, Entrance.downstairs_to_cellar]),
RegionData(Region.cellar),
RegionData(Region.farm,
[Entrance.farm_to_backwoods, Entrance.farm_to_bus_stop, Entrance.farm_to_forest,
Entrance.farm_to_farmcave, Entrance.enter_greenhouse,
Entrance.use_desert_obelisk, Entrance.use_island_obelisk]),
RegionData(Region.backwoods, [Entrance.backwoods_to_mountain]),
RegionData(Region.bus_stop,
[Entrance.bus_stop_to_town, Entrance.take_bus_to_desert, Entrance.bus_stop_to_tunnel_entrance]),
RegionData(Region.forest,
[Entrance.forest_to_town, Entrance.enter_secret_woods, Entrance.forest_to_wizard_tower,
Entrance.forest_to_marnie_ranch,
Entrance.forest_to_leah_cottage, Entrance.forest_to_sewer,
Entrance.buy_from_traveling_merchant]),
RegionData(Region.traveling_cart),
RegionData(Region.farm_cave),
RegionData(Region.greenhouse),
RegionData(Region.mountain,
[Entrance.mountain_to_railroad, Entrance.mountain_to_tent, Entrance.mountain_to_carpenter_shop,
Entrance.mountain_to_the_mines, Entrance.enter_quarry, Entrance.mountain_to_adventurer_guild,
Entrance.mountain_to_town, Entrance.mountain_to_maru_room,
Entrance.mountain_to_leo_treehouse]),
RegionData(Region.leo_treehouse),
RegionData(Region.maru_room),
RegionData(Region.tunnel_entrance, [Entrance.tunnel_entrance_to_bus_tunnel]),
RegionData(Region.bus_tunnel),
RegionData(Region.town,
[Entrance.town_to_community_center, Entrance.town_to_beach, Entrance.town_to_hospital,
Entrance.town_to_pierre_general_store, Entrance.town_to_saloon, Entrance.town_to_alex_house,
Entrance.town_to_trailer,
Entrance.town_to_mayor_manor,
Entrance.town_to_sam_house, Entrance.town_to_haley_house, Entrance.town_to_sewer,
Entrance.town_to_clint_blacksmith,
Entrance.town_to_museum,
Entrance.town_to_jojamart]),
RegionData(Region.beach,
[Entrance.beach_to_willy_fish_shop, Entrance.enter_elliott_house, Entrance.enter_tide_pools]),
RegionData(Region.railroad, [Entrance.enter_bathhouse_entrance, Entrance.enter_witch_warp_cave]),
RegionData(Region.ranch),
RegionData(Region.leah_house),
RegionData(Region.sewer, [Entrance.enter_mutant_bug_lair]),
RegionData(Region.mutant_bug_lair),
RegionData(Region.wizard_tower, [Entrance.enter_wizard_basement]),
RegionData(Region.wizard_basement),
RegionData(Region.tent),
RegionData(Region.carpenter, [Entrance.enter_sebastian_room]),
RegionData(Region.sebastian_room),
RegionData(Region.adventurer_guild),
RegionData(Region.community_center,
[Entrance.access_crafts_room, Entrance.access_pantry, Entrance.access_fish_tank,
Entrance.access_boiler_room, Entrance.access_bulletin_board, Entrance.access_vault]),
RegionData(Region.crafts_room),
RegionData(Region.pantry),
RegionData(Region.fish_tank),
RegionData(Region.boiler_room),
RegionData(Region.bulletin_board),
RegionData(Region.vault),
RegionData(Region.hospital, [Entrance.enter_harvey_room]),
RegionData(Region.harvey_room),
RegionData(Region.pierre_store, [Entrance.enter_sunroom]),
RegionData(Region.sunroom),
RegionData(Region.saloon, [Entrance.play_journey_of_the_prairie_king, Entrance.play_junimo_kart]),
RegionData(Region.alex_house),
RegionData(Region.trailer),
RegionData(Region.mayor_house),
RegionData(Region.sam_house),
RegionData(Region.haley_house),
RegionData(Region.blacksmith),
RegionData(Region.museum),
RegionData(Region.jojamart),
RegionData(Region.fish_shop, [Entrance.fish_shop_to_boat_tunnel]),
RegionData(Region.boat_tunnel, [Entrance.boat_to_ginger_island]),
RegionData(Region.elliott_house),
RegionData(Region.tide_pools),
RegionData(Region.bathhouse_entrance, [Entrance.enter_locker_room]),
RegionData(Region.locker_room, [Entrance.enter_public_bath]),
RegionData(Region.public_bath),
RegionData(Region.witch_warp_cave, [Entrance.enter_witch_swamp]),
RegionData(Region.witch_swamp, [Entrance.enter_witch_hut]),
RegionData(Region.witch_hut, [Entrance.witch_warp_to_wizard_basement]),
RegionData(Region.quarry, [Entrance.enter_quarry_mine_entrance]),
RegionData(Region.quarry_mine_entrance, [Entrance.enter_quarry_mine]),
RegionData(Region.quarry_mine),
RegionData(Region.secret_woods),
RegionData(Region.desert, [Entrance.enter_skull_cavern_entrance, Entrance.enter_oasis]),
RegionData(Region.oasis, [Entrance.enter_casino]),
RegionData(Region.casino),
RegionData(Region.skull_cavern_entrance, [Entrance.enter_skull_cavern]),
RegionData(Region.skull_cavern, [Entrance.mine_to_skull_cavern_floor_25, Entrance.mine_to_skull_cavern_floor_50,
Entrance.mine_to_skull_cavern_floor_75, Entrance.mine_to_skull_cavern_floor_100,
Entrance.mine_to_skull_cavern_floor_125, Entrance.mine_to_skull_cavern_floor_150,
Entrance.mine_to_skull_cavern_floor_175, Entrance.mine_to_skull_cavern_floor_200]),
RegionData(Region.skull_cavern_25),
RegionData(Region.skull_cavern_50),
RegionData(Region.skull_cavern_75),
RegionData(Region.skull_cavern_100),
RegionData(Region.skull_cavern_125),
RegionData(Region.skull_cavern_150),
RegionData(Region.skull_cavern_175),
RegionData(Region.skull_cavern_200),
RegionData(Region.island_south, [Entrance.island_south_to_west, Entrance.island_south_to_north,
Entrance.island_south_to_east, Entrance.island_south_to_southeast,
Entrance.use_island_resort,
Entrance.parrot_express_docks_to_volcano,
Entrance.parrot_express_docks_to_dig_site,
Entrance.parrot_express_docks_to_jungle]),
RegionData(Region.island_resort),
RegionData(Region.island_west,
[Entrance.island_west_to_islandfarmhouse, Entrance.island_west_to_gourmand_cave,
Entrance.island_west_to_crystals_cave, Entrance.island_west_to_shipwreck,
Entrance.island_west_to_qi_walnut_room, Entrance.use_farm_obelisk,
Entrance.parrot_express_jungle_to_docks, Entrance.parrot_express_jungle_to_dig_site,
Entrance.parrot_express_jungle_to_volcano]),
RegionData(Region.island_east, [Entrance.island_east_to_leo_hut, Entrance.island_east_to_island_shrine]),
RegionData(Region.island_shrine),
RegionData(Region.island_south_east, [Entrance.island_southeast_to_pirate_cove]),
RegionData(Region.island_north, [Entrance.talk_to_island_trader, Entrance.island_north_to_field_office,
Entrance.island_north_to_dig_site, Entrance.island_north_to_volcano,
Entrance.parrot_express_volcano_to_dig_site,
Entrance.parrot_express_volcano_to_jungle,
Entrance.parrot_express_volcano_to_docks]),
RegionData(Region.volcano, [Entrance.climb_to_volcano_5, Entrance.volcano_to_secret_beach]),
RegionData(Region.volcano_secret_beach),
RegionData(Region.volcano_floor_5, [Entrance.talk_to_volcano_dwarf, Entrance.climb_to_volcano_10]),
RegionData(Region.volcano_dwarf_shop),
RegionData(Region.volcano_floor_10),
RegionData(Region.island_trader),
RegionData(Region.island_farmhouse),
RegionData(Region.gourmand_frog_cave),
RegionData(Region.colored_crystals_cave),
RegionData(Region.shipwreck),
RegionData(Region.qi_walnut_room),
RegionData(Region.leo_hut),
RegionData(Region.pirate_cove),
RegionData(Region.field_office),
RegionData(Region.dig_site,
[Entrance.dig_site_to_professor_snail_cave, Entrance.parrot_express_dig_site_to_volcano,
Entrance.parrot_express_dig_site_to_docks, Entrance.parrot_express_dig_site_to_jungle]),
RegionData(Region.professor_snail_cave),
RegionData(Region.jotpk_world_1, [Entrance.reach_jotpk_world_2]),
RegionData(Region.jotpk_world_2, [Entrance.reach_jotpk_world_3]),
RegionData(Region.jotpk_world_3),
RegionData(Region.junimo_kart_1, [Entrance.reach_junimo_kart_2]),
RegionData(Region.junimo_kart_2, [Entrance.reach_junimo_kart_3]),
RegionData(Region.junimo_kart_3),
RegionData(Region.mines, [Entrance.talk_to_mines_dwarf,
Entrance.dig_to_mines_floor_5, Entrance.dig_to_mines_floor_10,
Entrance.dig_to_mines_floor_15, Entrance.dig_to_mines_floor_20,
Entrance.dig_to_mines_floor_25, Entrance.dig_to_mines_floor_30,
Entrance.dig_to_mines_floor_35, Entrance.dig_to_mines_floor_40,
Entrance.dig_to_mines_floor_45, Entrance.dig_to_mines_floor_50,
Entrance.dig_to_mines_floor_55, Entrance.dig_to_mines_floor_60,
Entrance.dig_to_mines_floor_65, Entrance.dig_to_mines_floor_70,
Entrance.dig_to_mines_floor_75, Entrance.dig_to_mines_floor_80,
Entrance.dig_to_mines_floor_85, Entrance.dig_to_mines_floor_90,
Entrance.dig_to_mines_floor_95, Entrance.dig_to_mines_floor_100,
Entrance.dig_to_mines_floor_105, Entrance.dig_to_mines_floor_110,
Entrance.dig_to_mines_floor_115, Entrance.dig_to_mines_floor_120]),
RegionData(Region.mines_dwarf_shop),
RegionData(Region.mines_floor_5),
RegionData(Region.mines_floor_10),
RegionData(Region.mines_floor_15),
RegionData(Region.mines_floor_20),
RegionData(Region.mines_floor_25),
RegionData(Region.mines_floor_30),
RegionData(Region.mines_floor_35),
RegionData(Region.mines_floor_40),
RegionData(Region.mines_floor_45),
RegionData(Region.mines_floor_50),
RegionData(Region.mines_floor_55),
RegionData(Region.mines_floor_60),
RegionData(Region.mines_floor_65),
RegionData(Region.mines_floor_70),
RegionData(Region.mines_floor_75),
RegionData(Region.mines_floor_80),
RegionData(Region.mines_floor_85),
RegionData(Region.mines_floor_90),
RegionData(Region.mines_floor_95),
RegionData(Region.mines_floor_100),
RegionData(Region.mines_floor_105),
RegionData(Region.mines_floor_110),
RegionData(Region.mines_floor_115),
RegionData(Region.mines_floor_120),
]
# Exists and where they lead
mandatory_connections = [
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),
vanilla_connections = [
ConnectionData(Entrance.to_stardew_valley, Region.stardew_valley),
ConnectionData(Entrance.to_farmhouse, Region.farm_house),
ConnectionData(Entrance.farmhouse_to_farm, Region.farm),
ConnectionData(Entrance.downstairs_to_cellar, Region.cellar),
ConnectionData(Entrance.farm_to_backwoods, Region.backwoods),
ConnectionData(Entrance.farm_to_bus_stop, Region.bus_stop),
ConnectionData(Entrance.farm_to_forest, Region.forest),
ConnectionData(Entrance.farm_to_farmcave, Region.farm_cave, flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(Entrance.enter_greenhouse, Region.greenhouse),
ConnectionData(Entrance.use_desert_obelisk, Region.desert),
ConnectionData(Entrance.use_island_obelisk, Region.island_south),
ConnectionData(Entrance.use_farm_obelisk, Region.farm),
ConnectionData(Entrance.backwoods_to_mountain, Region.mountain),
ConnectionData(Entrance.bus_stop_to_town, Region.town),
ConnectionData(Entrance.bus_stop_to_tunnel_entrance, Region.tunnel_entrance),
ConnectionData(Entrance.tunnel_entrance_to_bus_tunnel, Region.bus_tunnel, flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(Entrance.take_bus_to_desert, Region.desert),
ConnectionData(Entrance.forest_to_town, Region.town),
ConnectionData(Entrance.forest_to_wizard_tower, Region.wizard_tower,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_wizard_basement, Region.wizard_basement, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.forest_to_marnie_ranch, Region.ranch,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.forest_to_leah_cottage, Region.leah_house,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_secret_woods, Region.secret_woods),
ConnectionData(Entrance.forest_to_sewer, Region.sewer, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.buy_from_traveling_merchant, Region.traveling_cart),
ConnectionData(Entrance.town_to_sewer, Region.sewer, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_mutant_bug_lair, Region.mutant_bug_lair, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.mountain_to_railroad, Region.railroad),
ConnectionData(Entrance.mountain_to_tent, Region.tent,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.mountain_to_leo_treehouse, Region.leo_treehouse,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.mountain_to_carpenter_shop, Region.carpenter,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.mountain_to_maru_room, Region.maru_room,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_sebastian_room, Region.sebastian_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.mountain_to_adventurer_guild, Region.adventurer_guild,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_quarry, Region.quarry),
ConnectionData(Entrance.enter_quarry_mine_entrance, Region.quarry_mine_entrance,
flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_quarry_mine, Region.quarry_mine),
ConnectionData(Entrance.mountain_to_town, Region.town),
ConnectionData(Entrance.town_to_community_center, Region.community_center,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.access_crafts_room, Region.crafts_room),
ConnectionData(Entrance.access_pantry, Region.pantry),
ConnectionData(Entrance.access_fish_tank, Region.fish_tank),
ConnectionData(Entrance.access_boiler_room, Region.boiler_room),
ConnectionData(Entrance.access_bulletin_board, Region.bulletin_board),
ConnectionData(Entrance.access_vault, Region.vault),
ConnectionData(Entrance.town_to_hospital, Region.hospital,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_harvey_room, Region.harvey_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.town_to_pierre_general_store, Region.pierre_store,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_sunroom, Region.sunroom, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.town_to_clint_blacksmith, Region.blacksmith,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_saloon, Region.saloon,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.play_journey_of_the_prairie_king, Region.jotpk_world_1),
ConnectionData(Entrance.reach_jotpk_world_2, Region.jotpk_world_2),
ConnectionData(Entrance.reach_jotpk_world_3, Region.jotpk_world_3),
ConnectionData(Entrance.play_junimo_kart, Region.junimo_kart_1),
ConnectionData(Entrance.reach_junimo_kart_2, Region.junimo_kart_2),
ConnectionData(Entrance.reach_junimo_kart_3, Region.junimo_kart_3),
ConnectionData(Entrance.town_to_sam_house, Region.sam_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_haley_house, Region.haley_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_mayor_manor, Region.mayor_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_alex_house, Region.alex_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_trailer, Region.trailer,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_museum, Region.museum,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_jojamart, Region.jojamart,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_beach, Region.beach),
ConnectionData(Entrance.enter_elliott_house, Region.elliott_house,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.beach_to_willy_fish_shop, Region.fish_shop,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.fish_shop_to_boat_tunnel, Region.boat_tunnel,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.boat_to_ginger_island, Region.island_south),
ConnectionData(Entrance.enter_tide_pools, Region.tide_pools),
ConnectionData(Entrance.mountain_to_the_mines, Region.mines,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.talk_to_mines_dwarf, Region.mines_dwarf_shop),
ConnectionData(Entrance.dig_to_mines_floor_5, Region.mines_floor_5),
ConnectionData(Entrance.dig_to_mines_floor_10, Region.mines_floor_10),
ConnectionData(Entrance.dig_to_mines_floor_15, Region.mines_floor_15),
ConnectionData(Entrance.dig_to_mines_floor_20, Region.mines_floor_20),
ConnectionData(Entrance.dig_to_mines_floor_25, Region.mines_floor_25),
ConnectionData(Entrance.dig_to_mines_floor_30, Region.mines_floor_30),
ConnectionData(Entrance.dig_to_mines_floor_35, Region.mines_floor_35),
ConnectionData(Entrance.dig_to_mines_floor_40, Region.mines_floor_40),
ConnectionData(Entrance.dig_to_mines_floor_45, Region.mines_floor_45),
ConnectionData(Entrance.dig_to_mines_floor_50, Region.mines_floor_50),
ConnectionData(Entrance.dig_to_mines_floor_55, Region.mines_floor_55),
ConnectionData(Entrance.dig_to_mines_floor_60, Region.mines_floor_60),
ConnectionData(Entrance.dig_to_mines_floor_65, Region.mines_floor_65),
ConnectionData(Entrance.dig_to_mines_floor_70, Region.mines_floor_70),
ConnectionData(Entrance.dig_to_mines_floor_75, Region.mines_floor_75),
ConnectionData(Entrance.dig_to_mines_floor_80, Region.mines_floor_80),
ConnectionData(Entrance.dig_to_mines_floor_85, Region.mines_floor_85),
ConnectionData(Entrance.dig_to_mines_floor_90, Region.mines_floor_90),
ConnectionData(Entrance.dig_to_mines_floor_95, Region.mines_floor_95),
ConnectionData(Entrance.dig_to_mines_floor_100, Region.mines_floor_100),
ConnectionData(Entrance.dig_to_mines_floor_105, Region.mines_floor_105),
ConnectionData(Entrance.dig_to_mines_floor_110, Region.mines_floor_110),
ConnectionData(Entrance.dig_to_mines_floor_115, Region.mines_floor_115),
ConnectionData(Entrance.dig_to_mines_floor_120, Region.mines_floor_120),
ConnectionData(Entrance.enter_skull_cavern_entrance, Region.skull_cavern_entrance,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_oasis, Region.oasis,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_casino, Region.casino, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_skull_cavern, Region.skull_cavern),
ConnectionData(Entrance.mine_to_skull_cavern_floor_25, Region.skull_cavern_25),
ConnectionData(Entrance.mine_to_skull_cavern_floor_50, Region.skull_cavern_50),
ConnectionData(Entrance.mine_to_skull_cavern_floor_75, Region.skull_cavern_75),
ConnectionData(Entrance.mine_to_skull_cavern_floor_100, Region.skull_cavern_100),
ConnectionData(Entrance.mine_to_skull_cavern_floor_125, Region.skull_cavern_125),
ConnectionData(Entrance.mine_to_skull_cavern_floor_150, Region.skull_cavern_150),
ConnectionData(Entrance.mine_to_skull_cavern_floor_175, Region.skull_cavern_175),
ConnectionData(Entrance.mine_to_skull_cavern_floor_200, Region.skull_cavern_200),
ConnectionData(Entrance.enter_witch_warp_cave, Region.witch_warp_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_swamp, Region.witch_swamp, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_hut, Region.witch_hut, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.witch_warp_to_wizard_basement, Region.wizard_basement, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_bathhouse_entrance, Region.bathhouse_entrance,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_locker_room, Region.locker_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_public_bath, Region.public_bath, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_south_to_west, Region.island_west, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_south_to_north, Region.island_north, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_south_to_east, Region.island_east, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_south_to_southeast, Region.island_south_east,
flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.use_island_resort, Region.island_resort),
ConnectionData(Entrance.island_west_to_islandfarmhouse, Region.island_farmhouse,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_gourmand_cave, Region.gourmand_frog_cave,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_crystals_cave, Region.colored_crystals_cave,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_shipwreck, Region.shipwreck,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_qi_walnut_room, Region.qi_walnut_room,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_east_to_leo_hut, Region.leo_hut,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_east_to_island_shrine, Region.island_shrine,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_southeast_to_pirate_cove, Region.pirate_cove,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_north_to_field_office, Region.field_office,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_north_to_dig_site, Region.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.dig_site_to_professor_snail_cave, Region.professor_snail_cave, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_north_to_volcano, Region.volcano,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.volcano_to_secret_beach, Region.volcano_secret_beach,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.talk_to_island_trader, Region.island_trader, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.climb_to_volcano_5, Region.volcano_floor_5),
ConnectionData(Entrance.talk_to_volcano_dwarf, Region.volcano_dwarf_shop),
ConnectionData(Entrance.climb_to_volcano_10, Region.volcano_floor_10),
ConnectionData(Entrance.parrot_express_jungle_to_docks, Region.island_south),
ConnectionData(Entrance.parrot_express_dig_site_to_docks, Region.island_south),
ConnectionData(Entrance.parrot_express_volcano_to_docks, Region.island_south),
ConnectionData(Entrance.parrot_express_volcano_to_jungle, Region.island_west),
ConnectionData(Entrance.parrot_express_docks_to_jungle, Region.island_west),
ConnectionData(Entrance.parrot_express_dig_site_to_jungle, Region.island_west),
ConnectionData(Entrance.parrot_express_docks_to_dig_site, Region.dig_site),
ConnectionData(Entrance.parrot_express_volcano_to_dig_site, Region.dig_site),
ConnectionData(Entrance.parrot_express_jungle_to_dig_site, Region.dig_site),
ConnectionData(Entrance.parrot_express_dig_site_to_volcano, Region.island_north),
ConnectionData(Entrance.parrot_express_docks_to_volcano, Region.island_north),
ConnectionData(Entrance.parrot_express_jungle_to_volcano, Region.island_north),
]
def create_final_regions(world_options: StardewOptions) -> List[RegionData]:
final_regions = []
final_regions.extend(vanilla_regions)
if world_options[options.Mods] is None:
return final_regions
for mod in world_options[options.Mods]:
if mod not in ModDataList:
continue
for mod_region in ModDataList[mod].regions:
existing_region = next(
(region for region in final_regions if region.name == mod_region.name), None)
if existing_region:
final_regions.remove(existing_region)
final_regions.append(existing_region.get_merged_with(mod_region.exits))
continue
final_regions.append(mod_region.get_clone())
return final_regions
def create_final_connections(world_options: StardewOptions) -> List[ConnectionData]:
final_connections = []
final_connections.extend(vanilla_connections)
if world_options[options.Mods] is None:
return final_connections
for mod in world_options[options.Mods]:
if mod not in ModDataList:
continue
final_connections.extend(ModDataList[mod].connections)
return final_connections
def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewOptions) -> Tuple[
Iterable[Region], Dict[str, str]]:
final_regions = create_final_regions(world_options)
regions: Dict[str: Region] = {region.name: region_factory(region.name, region.exits) for region in
stardew_valley_regions}
final_regions}
entrances: Dict[str: Entrance] = {entrance.name: entrance
for region in regions.values()
for entrance in region.exits}
connections, randomized_data = randomize_connections(random, world_options)
regions_by_name: Dict[str, RegionData] = {region.name: region for region in final_regions}
connections, randomized_data = randomize_connections(random, world_options, regions_by_name)
for connection in connections:
if connection.name not in entrances:
continue
entrances[connection.name].connect(regions[connection.destination])
if connection.name in entrances:
entrances[connection.name].connect(regions[connection.destination])
return regions.values(), randomized_data
def randomize_connections(random: Random, world_options: StardewOptions) -> Tuple[List[ConnectionData], Dict[str, str]]:
def randomize_connections(random: Random, world_options: StardewOptions, regions_by_name) -> Tuple[
List[ConnectionData], Dict[str, str]]:
connections_to_randomize = []
final_connections = create_final_connections(world_options)
connections_by_name: Dict[str, ConnectionData] = {connection.name: connection for connection in final_connections}
if world_options[options.EntranceRandomization] == options.EntranceRandomization.option_pelican_town:
connections_to_randomize = [connection for connection in mandatory_connections if
connections_to_randomize = [connection for connection in final_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
connections_to_randomize = [connection for connection in final_connections if
RandomizationFlag.NON_PROGRESSION in connection.flag]
random.shuffle(connections_to_randomize)
elif world_options[options.EntranceRandomization] == options.EntranceRandomization.option_buildings:
connections_to_randomize = [connection for connection in final_connections if
RandomizationFlag.BUILDINGS in connection.flag]
elif world_options[options.EntranceRandomization] == options.EntranceRandomization.option_chaos:
connections_to_randomize = [connection for connection in final_connections if
RandomizationFlag.BUILDINGS in connection.flag]
connections_to_randomize = exclude_island_if_necessary(connections_to_randomize, world_options)
# On Chaos, we just add the connections to randomize, unshuffled, and the client does it every day
randomized_data_for_mod = {}
for connection in connections_to_randomize:
randomized_data_for_mod[connection.name] = connection.name
randomized_data_for_mod[connection.reverse] = connection.reverse
return final_connections, randomized_data_for_mod
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, world_options)
random.shuffle(connections_to_randomize)
destination_pool = list(connections_to_randomize)
random.shuffle(destination_pool)
randomized_connections = []
randomized_data = {}
randomized_connections = randomize_chosen_connections(connections_to_randomize, destination_pool)
add_non_randomized_connections(final_connections, connections_to_randomize, randomized_connections)
swap_connections_until_valid(regions_by_name, connections_by_name, randomized_connections, connections_to_randomize, random)
randomized_connections_for_generation = create_connections_for_generation(randomized_connections)
randomized_data_for_mod = create_data_for_mod(randomized_connections, connections_to_randomize)
return randomized_connections_for_generation, randomized_data_for_mod
def remove_excluded_entrances(connections_to_randomize, world_options):
exclude_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true
exclude_sewers = world_options[options.Museumsanity] == options.Museumsanity.option_none
if exclude_island:
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.GINGER_ISLAND not in connection.flag]
if exclude_sewers:
connections_to_randomize = [connection for connection in connections_to_randomize if Region.sewer not in connection.name or Region.sewer not in connection.reverse]
return connections_to_randomize
def exclude_island_if_necessary(connections_to_randomize: List[ConnectionData], world_options) -> List[ConnectionData]:
exclude_island = world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true
if exclude_island:
connections_to_randomize = [connection for connection in connections_to_randomize if
RandomizationFlag.GINGER_ISLAND not in connection.flag]
return connections_to_randomize
def randomize_chosen_connections(connections_to_randomize: List[ConnectionData],
destination_pool: List[ConnectionData]) -> Dict[ConnectionData, ConnectionData]:
randomized_connections = {}
for connection in connections_to_randomize:
destination = destination_pool.pop()
randomized_connections.append(ConnectionData(connection.name, destination.destination, destination.reverse))
randomized_data[connection.name] = destination.name
randomized_data[destination.reverse] = connection.reverse
randomized_connections[connection] = destination
return randomized_connections
return mandatory_connections, randomized_data
def create_connections_for_generation(randomized_connections: Dict[ConnectionData, ConnectionData]) -> List[
ConnectionData]:
connections = []
for connection in randomized_connections:
destination = randomized_connections[connection]
connections.append(ConnectionData(connection.name, destination.destination, destination.reverse))
return connections
def create_data_for_mod(randomized_connections: Dict[ConnectionData, ConnectionData],
connections_to_randomize: List[ConnectionData]) -> Dict[str, str]:
randomized_data_for_mod = {}
for connection in randomized_connections:
if connection not in connections_to_randomize:
continue
destination = randomized_connections[connection]
add_to_mod_data(connection, destination, randomized_data_for_mod)
return randomized_data_for_mod
def add_to_mod_data(connection: ConnectionData, destination: ConnectionData, randomized_data_for_mod: Dict[str, str]):
randomized_data_for_mod[connection.name] = destination.name
randomized_data_for_mod[destination.reverse] = connection.reverse
def add_non_randomized_connections(connections, connections_to_randomize: List[ConnectionData],
randomized_connections: Dict[ConnectionData, ConnectionData]):
for connection in connections:
if connection in connections_to_randomize:
continue
randomized_connections[connection] = connection
def swap_connections_until_valid(regions_by_name, connections_by_name, randomized_connections: Dict[ConnectionData, ConnectionData],
connections_to_randomize: List[ConnectionData], random: Random):
while True:
reachable_regions, unreachable_regions = find_reachable_regions(regions_by_name, connections_by_name, randomized_connections)
if not unreachable_regions:
return randomized_connections
swap_one_connection(regions_by_name, connections_by_name, randomized_connections, reachable_regions,
unreachable_regions, connections_to_randomize, random)
def find_reachable_regions(regions_by_name, connections_by_name,
randomized_connections: Dict[ConnectionData, ConnectionData]):
reachable_regions = {Region.menu}
unreachable_regions = {region for region in regions_by_name.keys()}
unreachable_regions.remove(Region.menu)
exits_to_explore = list(regions_by_name[Region.menu].exits)
while exits_to_explore:
exit_name = exits_to_explore.pop()
exit_connection = connections_by_name[exit_name]
replaced_connection = randomized_connections[exit_connection]
target_region_name = replaced_connection.destination
if target_region_name in reachable_regions:
continue
target_region = regions_by_name[target_region_name]
reachable_regions.add(target_region_name)
unreachable_regions.remove(target_region_name)
exits_to_explore.extend(target_region.exits)
return reachable_regions, unreachable_regions
def swap_one_connection(regions_by_name, connections_by_name,randomized_connections: Dict[ConnectionData, ConnectionData],
reachable_regions: Set[str], unreachable_regions: Set[str],
connections_to_randomize: List[ConnectionData], random: Random):
randomized_connections_already_shuffled = {connection: randomized_connections[connection]
for connection in randomized_connections
if connection != randomized_connections[connection]}
unreachable_regions_names_leading_somewhere = tuple([region for region in unreachable_regions
if len(regions_by_name[region].exits) > 0])
unreachable_regions_leading_somewhere = [regions_by_name[region_name] for region_name in unreachable_regions_names_leading_somewhere]
unreachable_regions_exits_names = [exit_name for region in unreachable_regions_leading_somewhere for exit_name in region.exits]
unreachable_connections = [connections_by_name[exit_name] for exit_name in unreachable_regions_exits_names]
unreachable_connections_that_can_be_randomized = [connection for connection in unreachable_connections if connection in connections_to_randomize]
chosen_unreachable_entrance = random.choice(unreachable_connections_that_can_be_randomized)
chosen_reachable_entrance = None
while chosen_reachable_entrance is None or chosen_reachable_entrance not in randomized_connections_already_shuffled:
chosen_reachable_region_name = random.choice(sorted(reachable_regions))
chosen_reachable_region = regions_by_name[chosen_reachable_region_name]
if not any(chosen_reachable_region.exits):
continue
chosen_reachable_entrance_name = random.choice(chosen_reachable_region.exits)
chosen_reachable_entrance = connections_by_name[chosen_reachable_entrance_name]
reachable_destination = randomized_connections[chosen_reachable_entrance]
unreachable_destination = randomized_connections[chosen_unreachable_entrance]
randomized_connections[chosen_reachable_entrance] = unreachable_destination
randomized_connections[chosen_unreachable_entrance] = reachable_destination

View File

@ -5,85 +5,59 @@ from BaseClasses import MultiWorld
from worlds.generic import Rules as MultiWorldRules
from . import options, locations
from .bundles import Bundle
from .data.crops_data import crops_by_name
from .strings.entrance_names import dig_to_mines_floor, dig_to_skull_floor, Entrance, move_to_woods_depth, \
DeepWoodsEntrance, AlecEntrance, MagicEntrance
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 .strings.region_names import Region
from .mods.mod_data import ModNames
from .mods.logic import magic, skills, deepwoods
from .locations import LocationTags
from .logic import StardewLogic, And, month_end_per_skill_level, tool_prices, week_days
from .logic import StardewLogic, And, tool_upgrade_prices
from .options import StardewOptions
from .strings.ap_names.transport_names import Transportation
from .strings.artisan_good_names import ArtisanGood
from .strings.calendar_names import Weekday
from .strings.craftable_names import Craftable
from .strings.material_names import Material
from .strings.metal_names import MetalBar
from .strings.spells import MagicSpell
from .strings.skill_names import ModSkill, Skill
from .strings.tool_names import Tool, ToolMaterial
from .strings.villager_names import NPC, ModNPC
from .strings.wallet_item_names import Wallet
def set_rules(multi_world: MultiWorld, player: int, world_options: StardewOptions, logic: StardewLogic,
current_bundles: Dict[str, Bundle]):
all_location_names = list(location.name for location in multi_world.get_locations(player))
for floor in range(5, 120 + 5, 5):
MultiWorldRules.set_rule(multi_world.get_entrance(f"Dig to The Mines - Floor {floor}", player),
logic.can_mine_to_floor(floor).simplify())
set_entrance_rules(logic, multi_world, player, world_options)
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.set_rule(multi_world.get_entrance("Enter Secret Woods", player),
logic.has_tool("Axe", "Iron").simplify())
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.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.set_rule(multi_world.get_entrance("Use Desert Obelisk", player),
logic.received("Desert Obelisk").simplify())
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"))
set_ginger_island_rules(logic, multi_world, player, world_options)
# Those checks do not exist if ToolProgression is vanilla
if world_options[options.ToolProgression] != options.ToolProgression.option_vanilla:
MultiWorldRules.add_rule(multi_world.get_location("Purchase Fiberglass Rod", player),
(logic.has_skill_level("Fishing", 2) & logic.can_spend_money(1800)).simplify())
(logic.has_skill_level(Skill.fishing, 2) & logic.can_spend_money(1800)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Purchase Iridium Rod", player),
(logic.has_skill_level("Fishing", 6) & logic.can_spend_money(7500)).simplify())
(logic.has_skill_level(Skill.fishing, 6) & logic.can_spend_money(7500)).simplify())
materials = [None, "Copper", "Iron", "Gold", "Iridium"]
tool = ["Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can"]
tool = [Tool.hoe, Tool.pickaxe, Tool.axe, Tool.watering_can, Tool.watering_can, Tool.trash_can]
for (previous, material), tool in itertools.product(zip(materials[:4], materials[1:]), tool):
if previous is None:
MultiWorldRules.add_rule(multi_world.get_location(f"{material} {tool} Upgrade", player),
(logic.has(f"{material} Ore") &
logic.can_spend_money(tool_prices[material])).simplify())
logic.can_spend_money(tool_upgrade_prices[material])).simplify())
else:
MultiWorldRules.add_rule(multi_world.get_location(f"{material} {tool} Upgrade", player),
(logic.has(f"{material} Ore") & logic.has_tool(tool, previous) &
logic.can_spend_money(tool_prices[material])).simplify())
logic.can_spend_money(tool_upgrade_prices[material])).simplify())
# Skills
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("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("Month End", month_end_per_skill_level["Fishing", i])).simplify())
MultiWorldRules.add_rule(multi_world.get_location(f"Level {i} Foraging", player),
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("Month End", month_end_per_skill_level["Mining", i]).simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"Level {i} Combat", player),
(logic.received("Month End", month_end_per_skill_level["Combat", i]) &
logic.has_any_weapon()).simplify())
set_skills_rules(logic, multi_world, player, world_options)
# Bundles
for bundle in current_bundles.values():
@ -114,46 +88,294 @@ def set_rules(multi_world: MultiWorld, player: int, world_options: StardewOption
# Buildings
if world_options[options.BuildingProgression] != options.BuildingProgression.option_vanilla:
for building in locations.locations_by_tag[LocationTags.BUILDING_BLUEPRINT]:
if building.mod_name is not None and building.mod_name not in world_options[options.Mods]:
continue
MultiWorldRules.set_rule(multi_world.get_location(building.name, player),
logic.building_rules[building.name.replace(" Blueprint", "")].simplify())
# Story Quests
for quest in locations.locations_by_tag[LocationTags.QUEST]:
MultiWorldRules.set_rule(multi_world.get_location(quest.name, player),
logic.quest_rules[quest.name].simplify())
# Help Wanted Quests
desired_number_help_wanted: int = world_options[options.HelpWantedLocations] // 7
for i in range(0, desired_number_help_wanted):
prefix = "Help Wanted:"
delivery = "Item Delivery"
rule = logic.received("Month End", i)
fishing_rule = rule & logic.can_fish()
slay_rule = rule & logic.has_any_weapon()
item_delivery_index = (i * 4) + 1
for j in range(item_delivery_index, item_delivery_index + 4):
location_name = f"{prefix} {delivery} {j}"
MultiWorldRules.set_rule(multi_world.get_location(location_name, player), rule.simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Gathering {i+1}", player),
rule.simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Fishing {i+1}", player),
fishing_rule.simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Slay Monsters {i+1}", player),
slay_rule.simplify())
set_cropsanity_rules(all_location_names, logic, multi_world, player, world_options)
set_story_quests_rules(all_location_names, logic, multi_world, player, world_options)
set_special_order_rules(all_location_names, logic, multi_world, player, world_options)
set_help_wanted_quests_rules(logic, multi_world, player, world_options)
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)
set_festival_rules(all_location_names, logic, multi_world, player)
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())
MultiWorldRules.add_rule(multi_world.get_location("Have a Baby", player),
logic.can_reproduce(1).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Have Another Baby", player),
logic.can_reproduce(2).simplify())
set_traveling_merchant_rules(logic, multi_world, player)
set_arcade_machine_rules(logic, multi_world, player, world_options)
set_deepwoods_rules(logic, multi_world, player, world_options)
set_magic_spell_rules(logic, multi_world, player, world_options)
def set_skills_rules(logic, multi_world, player, world_options):
# Skills
if world_options[options.SkillProgression] != options.SkillProgression.option_vanilla:
for i in range(1, 11):
set_skill_rule(logic, multi_world, player, Skill.farming, i)
set_skill_rule(logic, multi_world, player, Skill.fishing, i)
set_skill_rule(logic, multi_world, player, Skill.foraging, i)
set_skill_rule(logic, multi_world, player, Skill.mining, i)
set_skill_rule(logic, multi_world, player, Skill.combat, i)
# Modded Skills
if ModNames.luck_skill in world_options[options.Mods]:
set_skill_rule(logic, multi_world, player, ModSkill.luck, i)
if ModNames.magic in world_options[options.Mods]:
set_skill_rule(logic, multi_world, player, ModSkill.magic, i)
if ModNames.binning_skill in world_options[options.Mods]:
set_skill_rule(logic, multi_world, player, ModSkill.binning, i)
if ModNames.cooking_skill in world_options[options.Mods]:
set_skill_rule(logic, multi_world, player, ModSkill.cooking, i)
if ModNames.socializing_skill in world_options[options.Mods]:
set_skill_rule(logic, multi_world, player, ModSkill.socializing, i)
if ModNames.archaeology in world_options[options.Mods]:
set_skill_rule(logic, multi_world, player, ModSkill.archaeology, i)
def set_skill_rule(logic, multi_world, player, skill: str, level: int):
location_name = f"Level {level} {skill}"
location = multi_world.get_location(location_name, player)
rule = logic.can_earn_skill_level(skill, level).simplify()
MultiWorldRules.set_rule(location, rule)
def set_entrance_rules(logic, multi_world, player, world_options: StardewOptions):
for floor in range(5, 120 + 5, 5):
MultiWorldRules.set_rule(multi_world.get_entrance(dig_to_mines_floor(floor), player),
logic.can_mine_to_floor(floor).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_tide_pools, player),
logic.received("Beach Bridge") | (magic.can_blink(logic)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_quarry, player),
logic.received("Bridge Repair") | (magic.can_blink(logic)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_secret_woods, player),
logic.has_tool(Tool.axe, "Iron") | (magic.can_blink(logic)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.forest_to_sewer, player),
logic.has_rusty_key().simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.town_to_sewer, player),
logic.has_rusty_key().simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.take_bus_to_desert, player),
logic.received("Bus Repair").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_skull_cavern, player),
logic.received(Wallet.skull_key).simplify())
for floor in range(25, 200 + 25, 25):
MultiWorldRules.set_rule(multi_world.get_entrance(dig_to_skull_floor(floor), player),
logic.can_mine_to_skull_cavern_floor(floor).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.talk_to_mines_dwarf, player),
logic.can_speak_dwarf() & logic.has_tool(Tool.pickaxe, ToolMaterial.iron))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_desert_obelisk, player),
logic.received(Transportation.desert_obelisk).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_island_obelisk, player),
logic.received(Transportation.island_obelisk).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_farm_obelisk, player),
logic.received(Transportation.farm_obelisk).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.buy_from_traveling_merchant, player),
logic.has_traveling_merchant())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_greenhouse, player),
logic.received("Greenhouse"))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_adventurer_guild, player),
logic.received("Adventurer's Guild"))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_railroad, player),
logic.has_lived_months(2))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_witch_warp_cave, player),
logic.received(Wallet.dark_talisman) | (magic.can_blink(logic)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_witch_hut, player),
(logic.has(ArtisanGood.void_mayonnaise) | magic.can_blink(logic)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_mutant_bug_lair, player),
((logic.has_rusty_key() & logic.can_reach_region(Region.railroad) &
logic.can_meet(NPC.krobus) | magic.can_blink(logic)).simplify()))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_harvey_room, player),
logic.has_relationship(NPC.harvey, 2))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_maru_room, player),
logic.has_relationship(NPC.maru, 2))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_sebastian_room, player),
(logic.has_relationship(NPC.sebastian, 2) | magic.can_blink(logic)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.forest_to_leah_cottage, player),
logic.has_relationship(NPC.leah, 2))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_elliott_house, player),
logic.has_relationship(NPC.elliott, 2))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_sunroom, player),
logic.has_relationship(NPC.caroline, 2))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.enter_wizard_basement, player),
logic.has_relationship(NPC.wizard, 4))
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.mountain_to_leo_treehouse, player),
logic.has_relationship(NPC.leo, 6) & logic.can_reach_region(Region.island_south))
if ModNames.alec in world_options[options.Mods]:
MultiWorldRules.set_rule(multi_world.get_entrance(AlecEntrance.petshop_to_bedroom, player),
(logic.has_relationship(ModNPC.alec, 2) | magic.can_blink(logic)).simplify())
def set_ginger_island_rules(logic: StardewLogic, multi_world, player, world_options: StardewOptions):
set_island_entrances_rules(logic, multi_world, player)
if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true:
return
set_boat_repair_rules(logic, multi_world, player)
set_island_parrot_rules(logic, multi_world, player)
MultiWorldRules.add_rule(multi_world.get_location("Open Professor Snail Cave", player),
logic.has(Craftable.cherry_bomb).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Complete Island Field Office", player),
logic.can_complete_field_office().simplify())
def set_boat_repair_rules(logic: StardewLogic, multi_world, player):
MultiWorldRules.add_rule(multi_world.get_location("Repair Boat Hull", player),
logic.has(Material.hardwood).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Repair Boat Anchor", player),
logic.has(MetalBar.iridium).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Repair Ticket Machine", player),
logic.has(ArtisanGood.battery_pack).simplify())
def set_island_entrances_rules(logic: StardewLogic, multi_world, player):
boat_repaired = logic.received(Transportation.boat_repair).simplify()
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.fish_shop_to_boat_tunnel, player),
boat_repaired)
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.boat_to_ginger_island, player),
boat_repaired)
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_south_to_west, player),
logic.received("Island West Turtle").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_south_to_north, player),
logic.received("Island North Turtle").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_west_to_islandfarmhouse, player),
logic.received("Island Farmhouse").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_west_to_gourmand_cave, player),
logic.received("Island Farmhouse").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_north_to_dig_site, player),
logic.received("Dig Site Bridge").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.dig_site_to_professor_snail_cave, player),
logic.received("Open Professor Snail Cave").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.talk_to_island_trader, player),
logic.received("Island Trader").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_south_to_southeast, player),
logic.received("Island Resort").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.use_island_resort, player),
logic.received("Island Resort").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_west_to_qi_walnut_room, player),
logic.received("Qi Walnut Room").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.island_north_to_volcano, player),
(logic.can_water(0) | logic.received("Volcano Bridge") |
magic.can_blink(logic)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.volcano_to_secret_beach, player),
logic.can_water(2).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.climb_to_volcano_5, player),
(logic.can_mine_perfectly() & logic.can_water(1)).simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.talk_to_volcano_dwarf, player),
logic.can_speak_dwarf())
MultiWorldRules.set_rule(multi_world.get_entrance(Entrance.climb_to_volcano_10, player),
(logic.can_mine_perfectly() & logic.can_water(1) & logic.received("Volcano Exit Shortcut")).simplify())
parrots = [Entrance.parrot_express_docks_to_volcano, Entrance.parrot_express_jungle_to_volcano,
Entrance.parrot_express_dig_site_to_volcano, Entrance.parrot_express_docks_to_dig_site,
Entrance.parrot_express_jungle_to_dig_site, Entrance.parrot_express_volcano_to_dig_site,
Entrance.parrot_express_docks_to_jungle, Entrance.parrot_express_dig_site_to_jungle,
Entrance.parrot_express_volcano_to_jungle, Entrance.parrot_express_jungle_to_docks,
Entrance.parrot_express_dig_site_to_docks, Entrance.parrot_express_volcano_to_docks]
for parrot in parrots:
MultiWorldRules.set_rule(multi_world.get_entrance(parrot, player), logic.received(Transportation.parrot_express).simplify())
def set_island_parrot_rules(logic: StardewLogic, multi_world, player):
has_walnut = logic.has_walnut(1).simplify()
has_5_walnut = logic.has_walnut(5).simplify()
has_10_walnut = logic.has_walnut(10).simplify()
has_20_walnut = logic.has_walnut(20).simplify()
MultiWorldRules.add_rule(multi_world.get_location("Leo's Parrot", player),
has_walnut)
MultiWorldRules.add_rule(multi_world.get_location("Island West Turtle", player),
has_10_walnut & logic.received("Island North Turtle"))
MultiWorldRules.add_rule(multi_world.get_location("Island Farmhouse", player),
has_20_walnut)
MultiWorldRules.add_rule(multi_world.get_location("Island Mailbox", player),
has_5_walnut & logic.received("Island Farmhouse"))
MultiWorldRules.add_rule(multi_world.get_location(Transportation.farm_obelisk, player),
has_20_walnut & logic.received("Island Mailbox"))
MultiWorldRules.add_rule(multi_world.get_location("Dig Site Bridge", player),
has_10_walnut & logic.received("Island West Turtle"))
MultiWorldRules.add_rule(multi_world.get_location("Island Trader", player),
has_10_walnut & logic.received("Island Farmhouse"))
MultiWorldRules.add_rule(multi_world.get_location("Volcano Bridge", player),
has_5_walnut & logic.received("Island West Turtle") &
logic.can_reach_region(Region.volcano_floor_10))
MultiWorldRules.add_rule(multi_world.get_location("Volcano Exit Shortcut", player),
has_5_walnut & logic.received("Island West Turtle"))
MultiWorldRules.add_rule(multi_world.get_location("Island Resort", player),
has_20_walnut & logic.received("Island Farmhouse"))
MultiWorldRules.add_rule(multi_world.get_location(Transportation.parrot_express, player),
has_10_walnut)
def set_cropsanity_rules(all_location_names: List[str], logic, multi_world, player, world_options: StardewOptions):
if world_options[options.Cropsanity] == options.Cropsanity.option_disabled:
return
harvest_prefix = "Harvest "
harvest_prefix_length = len(harvest_prefix)
for harvest_location in locations.locations_by_tag[LocationTags.CROPSANITY]:
if harvest_location.name in all_location_names and (harvest_location.mod_name is None or harvest_location.mod_name in world_options[options.Mods]):
crop_name = harvest_location.name[harvest_prefix_length:]
MultiWorldRules.set_rule(multi_world.get_location(harvest_location.name, player),
logic.has(crop_name).simplify())
def set_story_quests_rules(all_location_names: List[str], logic, multi_world, player, world_options: StardewOptions):
for quest in locations.locations_by_tag[LocationTags.QUEST]:
if quest.name in all_location_names and (quest.mod_name is None or quest.mod_name in world_options[options.Mods]):
MultiWorldRules.set_rule(multi_world.get_location(quest.name, player),
logic.quest_rules[quest.name].simplify())
def set_special_order_rules(all_location_names: List[str], logic: StardewLogic, multi_world, player,
world_options: StardewOptions):
if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_disabled:
return
board_rule = logic.received("Special Order Board") & logic.has_lived_months(4)
for board_order in locations.locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]:
if board_order.name in all_location_names:
order_rule = board_rule & logic.special_order_rules[board_order.name]
MultiWorldRules.set_rule(multi_world.get_location(board_order.name, player), order_rule.simplify())
if world_options[options.ExcludeGingerIsland] == options.ExcludeGingerIsland.option_true:
return
if world_options[options.SpecialOrderLocations] == options.SpecialOrderLocations.option_board_only:
return
qi_rule = logic.can_reach_region(Region.qi_walnut_room) & logic.has_lived_months(8)
for qi_order in locations.locations_by_tag[LocationTags.SPECIAL_ORDER_QI]:
if qi_order.name in all_location_names:
order_rule = qi_rule & logic.special_order_rules[qi_order.name]
MultiWorldRules.set_rule(multi_world.get_location(qi_order.name, player), order_rule.simplify())
def set_help_wanted_quests_rules(logic: StardewLogic, multi_world, player, world_options):
desired_number_help_wanted: int = world_options[options.HelpWantedLocations] // 7
for i in range(0, desired_number_help_wanted):
prefix = "Help Wanted:"
delivery = "Item Delivery"
rule = logic.has_lived_months(i).simplify()
fishing_rule = rule & logic.can_fish()
slay_rule = rule & logic.can_do_combat_at_level("Basic")
item_delivery_index = (i * 4) + 1
for j in range(item_delivery_index, item_delivery_index + 4):
location_name = f"{prefix} {delivery} {j}"
MultiWorldRules.set_rule(multi_world.get_location(location_name, player), rule)
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Gathering {i + 1}", player),
rule)
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Fishing {i + 1}", player),
fishing_rule.simplify())
MultiWorldRules.set_rule(multi_world.get_location(f"{prefix} Slay Monsters {i + 1}", player),
slay_rule.simplify())
def set_fishsanity_rules(all_location_names: List[str], logic: StardewLogic, multi_world: MultiWorld, player: int):
@ -175,7 +397,7 @@ def set_museumsanity_rules(all_location_names: List[str], logic: StardewLogic, m
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):
def set_museum_individual_donations_rules(all_location_names, logic: StardewLogic, 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
@ -219,7 +441,7 @@ def set_museum_milestone_rule(logic: StardewLogic, multi_world: MultiWorld, muse
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):
def get_museum_item_count_rule(logic: StardewLogic, 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)
@ -233,10 +455,24 @@ def set_backpack_rules(logic: StardewLogic, multi_world: MultiWorld, player: int
logic.can_spend_money(2000).simplify())
MultiWorldRules.set_rule(multi_world.get_location("Deluxe Pack", player),
(logic.can_spend_money(10000) & logic.received("Progressive Backpack")).simplify())
if ModNames.big_backpack in world_options[options.Mods]:
MultiWorldRules.set_rule(multi_world.get_location("Premium Pack", player),
(logic.can_spend_money(150000) &
logic.received("Progressive Backpack", 2)).simplify())
def set_festival_rules(all_location_names: List[str], logic: StardewLogic, multi_world, player):
festival_locations = []
festival_locations.extend(locations.locations_by_tag[LocationTags.FESTIVAL])
festival_locations.extend(locations.locations_by_tag[LocationTags.FESTIVAL_HARD])
for festival in festival_locations:
if festival.name in all_location_names:
MultiWorldRules.set_rule(multi_world.get_location(festival.name, player),
logic.festival_rules[festival.name].simplify())
def set_traveling_merchant_rules(logic: StardewLogic, multi_world: MultiWorld, player: int):
for day in week_days:
for day in Weekday.all_days:
item_for_day = f"Traveling Merchant: {day}"
for i in range(1, 4):
location_name = f"Traveling Merchant {day} Item {i}"
@ -245,23 +481,27 @@ def set_traveling_merchant_rules(logic: StardewLogic, multi_world: MultiWorld, p
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())
MultiWorldRules.add_rule(multi_world.get_entrance("Reach Junimo Kart 2", player),
logic.has("Junimo Kart Medium Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance("Reach Junimo Kart 3", player),
logic.has("Junimo Kart Big Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_location("Junimo Kart: Sunset Speedway (Victory)", player),
logic.has("Junimo Kart Max Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance("Play Journey of the Prairie King", player),
logic.has("JotPK Small Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance("Reach JotPK World 2", player),
logic.has("JotPK Medium Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance("Reach JotPK World 3", player),
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())
MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.play_junimo_kart, player),
logic.received(Wallet.skull_key).simplify())
if world_options[options.ArcadeMachineLocations] != options.ArcadeMachineLocations.option_full_shuffling:
return
MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.play_junimo_kart, player),
logic.has("Junimo Kart Small Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_junimo_kart_2, player),
logic.has("Junimo Kart Medium Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_junimo_kart_3, player),
logic.has("Junimo Kart Big Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_location("Junimo Kart: Sunset Speedway (Victory)", player),
logic.has("Junimo Kart Max Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.play_journey_of_the_prairie_king, player),
logic.has("JotPK Small Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_jotpk_world_2, player),
logic.has("JotPK Medium Buff").simplify())
MultiWorldRules.add_rule(multi_world.get_entrance(Entrance.reach_jotpk_world_3, player),
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):
@ -272,8 +512,93 @@ def set_friendsanity_rules(all_location_names: List[str], logic: StardewLogic, m
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])
split_index = friend_location_trimmed.rindex(" ")
friend_name = friend_location_trimmed[:split_index]
num_hearts = int(friend_location_trimmed[split_index + 1:])
MultiWorldRules.set_rule(multi_world.get_location(friend_location.name, player),
logic.can_earn_relationship(friend_name, num_hearts).simplify())
def set_deepwoods_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options: StardewOptions):
if ModNames.deepwoods in world_options[options.Mods]:
MultiWorldRules.add_rule(multi_world.get_location("Breaking Up Deep Woods Gingerbread House", player),
logic.has_tool(Tool.axe, "Gold") & deepwoods.can_reach_woods_depth(logic, 50).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Chop Down a Deep Woods Iridium Tree", player),
logic.has_tool(Tool.axe, "Iridium").simplify())
MultiWorldRules.set_rule(multi_world.get_entrance(DeepWoodsEntrance.use_woods_obelisk, player),
logic.received("Woods Obelisk").simplify())
for depth in range(10, 100 + 10, 10):
MultiWorldRules.set_rule(multi_world.get_entrance(move_to_woods_depth(depth), player),
deepwoods.can_chop_to_depth(logic, depth).simplify())
def set_magic_spell_rules(logic: StardewLogic, multi_world: MultiWorld, player: int, world_options: StardewOptions):
if ModNames.magic not in world_options[options.Mods]:
return
MultiWorldRules.set_rule(multi_world.get_entrance(MagicEntrance.store_to_altar, player),
(logic.has_relationship(NPC.wizard, 3) &
logic.can_reach_region(Region.wizard_tower)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Clear Debris", player),
((logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic"))
& magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Till", player),
(logic.has_tool("Hoe", "Basic") & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Water", player),
(logic.has_tool("Watering Can", "Basic") & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze All Toil School Locations", player),
(logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic")
& (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic"))
& magic.can_use_altar(logic)).simplify())
# Do I *want* to add boots into logic when you get them even in vanilla without effort? idk
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Evac", player),
(logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Haste", player),
(logic.has("Coffee") & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Heal", player),
(logic.has("Life Elixir") & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze All Life School Locations", player),
(logic.has("Coffee") & logic.has("Life Elixir")
& logic.can_mine_perfectly() & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Descend", player),
(logic.can_reach_region(Region.mines) & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Fireball", player),
(logic.has("Fire Quartz") & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Frostbite", player),
(logic.can_mine_to_floor(70) & logic.can_fish(85) & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze All Elemental School Locations", player),
(logic.can_reach_region(Region.mines) & logic.has("Fire Quartz")
& logic.can_reach_region(Region.mines_floor_70) & logic.can_fish(85) &
magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Lantern", player),
magic.can_use_altar(logic).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Tendrils", player),
(logic.can_reach_region(Region.farm) & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Shockwave", player),
(logic.has("Earth Crystal") & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze All Nature School Locations", player),
(logic.has("Earth Crystal") & logic.can_reach_region("Farm") &
magic.can_use_altar(logic)).simplify()),
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Meteor", player),
(logic.can_reach_region(Region.farm) & logic.has_lived_months(12)
& magic.can_use_altar(logic)).simplify()),
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Lucksteal", player),
(logic.can_reach_region(Region.witch_hut) & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze: Bloodmana", player),
(logic.can_reach_region(Region.mines_floor_100) & magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze All Eldritch School Locations", player),
(logic.can_reach_region(Region.witch_hut) &
logic.can_reach_region(Region.mines_floor_100) &
logic.can_reach_region(Region.farm) & logic.has_lived_months(12) &
magic.can_use_altar(logic)).simplify())
MultiWorldRules.add_rule(multi_world.get_location("Analyze Every Magic School Location", player),
(logic.has_tool("Watering Can", "Basic") & logic.has_tool("Hoe", "Basic")
& (logic.has_tool("Axe", "Basic") | logic.has_tool("Pickaxe", "Basic")) &
logic.has("Coffee") & logic.has("Life Elixir")
& logic.can_mine_perfectly() & logic.has("Earth Crystal") &
logic.can_reach_region(Region.mines) &
logic.has("Fire Quartz") & logic.can_fish(85) &
logic.can_reach_region(Region.witch_hut) &
logic.can_reach_region(Region.mines_floor_100) &
logic.can_reach_region(Region.farm) & logic.has_lived_months(12) &
magic.can_use_altar(logic)).simplify())

View File

@ -11,7 +11,7 @@ from pathlib import Path
from typing import List
from worlds.stardew_valley import LocationData
from worlds.stardew_valley.items import load_item_csv, Group, ItemData, load_resource_pack_csv, friendship_pack
from worlds.stardew_valley.items import load_item_csv, Group, ItemData
from worlds.stardew_valley.locations import load_location_csv
RESOURCE_PACK_CODE_OFFSET = 5000
@ -53,22 +53,23 @@ if __name__ == "__main__":
for item in loaded_items
if Group.RESOURCE_PACK not in item.groups
and item.code_without_offset is not None) + 1)
resource_pack_counter = itertools.count(max(item.code_without_offset
for item in loaded_items
if Group.RESOURCE_PACK in item.groups
and item.code_without_offset is not None) + 1)
items_to_write = []
for item in loaded_items:
if item.has_any_group(Group.RESOURCE_PACK, Group.FRIENDSHIP_PACK):
continue
if item.code_without_offset is None:
items_to_write.append(ItemData(next(item_counter), item.name, item.classification, item.groups))
if Group.RESOURCE_PACK in item.groups:
new_code = next(resource_pack_counter)
else:
new_code = next(item_counter)
items_to_write.append(ItemData(new_code, item.name, item.classification, item.groups))
continue
items_to_write.append(item)
all_resource_packs = load_resource_pack_csv() + [friendship_pack]
resource_pack_counter = itertools.count(RESOURCE_PACK_CODE_OFFSET)
items_to_write.extend(
item for resource_pack in all_resource_packs for item in resource_pack.as_item_data(resource_pack_counter))
write_item_csv(items_to_write)
loaded_locations = load_location_csv()

View File

@ -159,7 +159,8 @@ class And(StardewRule):
if rules is not None:
rules_list.update(rules)
assert rules_list, "Can't create a And conditions without rules"
if len(rules_list) < 1:
rules_list.add(True_())
new_rules = set()
for rule in rules_list:
@ -297,7 +298,7 @@ class Received(StardewRule):
def __post_init__(self):
assert item_table[self.item].classification & ItemClassification.progression, \
"Item has to be progression to be used in logic"
f"Item [{item_table[self.item].name}] has to be progression to be used in logic"
def __call__(self, state: CollectionState) -> bool:
return state.has(self.item, self.player, self.count)

View File

@ -0,0 +1,25 @@
# Attempt at making this better. Not sure yet
module_names = [
"animal_names",
"animal_product_names",
"building_names",
"crop_names",
"forageable_names",
"fruit_tree_names",
"generic_names",
"geode_names",
"ingredient_names",
"item_names",
"machine_names",
"meal_names",
"metal_names",
"performance_names",
"quest_names",
"region_names",
"Material",
"season_names",
"skill_names",
"tool_names",
"tv_channel_names",
"villager_names",
]

View File

@ -0,0 +1,13 @@
class Animal:
chicken = "Chicken"
cow = "Cow"
pig = "Pig"
duck = "Duck"
sheep = "Sheep"
dinosaur = "Dinosaur"
rabbit = "Rabbit"
goat = "Goat"
ostrich = "Ostrich"
coop_animals = [Animal.chicken, "Rabbit", "Duck", "Dinosaur"]
barn_animals = [Animal.cow, "Sheep", "Pig", "Ostrich"]

View File

@ -0,0 +1,24 @@
class AnimalProduct:
any_egg = "Any Egg"
chicken_egg = "Chicken Egg"
egg = "Egg"
brown_egg = "Egg (Brown)"
large_egg = "Large Egg"
large_brown_egg = "Large Egg (Brown)"
milk = "Milk"
large_milk = "Large Milk"
cow_milk = "Cow Milk"
wool = "Wool"
goat_milk = "Goat Milk"
large_goat_milk = "Large Goat Milk"
duck_egg = "Duck Egg"
duck_feather = "Duck Feather"
void_egg = "Void Egg"
truffle = "Truffle"
rabbit_foot = "Rabbit's Foot"
roe = "Roe"
sturgeon_roe = "Sturgeon Roe"
ostrich_egg = "Ostrich Egg"
dinosaur_egg = "Dinosaur Egg"
squid_ink = "Squid Ink"

View File

@ -0,0 +1,3 @@
class Buff:
movement = "Movement Speed Bonus"
luck = "Luck Bonus"

View File

@ -0,0 +1,2 @@
class ModSkillLevel:
magic_level = "Magic Level"

View File

@ -0,0 +1,10 @@
class Transportation:
boat_repair = "Boat Repair"
island_obelisk = "Island Obelisk"
desert_obelisk = "Desert Obelisk"
farm_obelisk = "Farm Obelisk"
parrot_express = "Parrot Express"
class ModTransportation:
woods_obelisk = "Woods Obelisk"

View File

@ -0,0 +1,23 @@
class ArtisanGood:
honey = "Honey"
oak_resin = "Oak Resin"
pine_tar = "Pine Tar"
maple_syrup = "Maple Syrup"
truffle_oil = "Truffle Oil"
cheese = "Cheese"
goat_cheese = "Goat Cheese"
jelly = "Jelly"
pickles = "Pickles"
wine = "Wine"
juice = "Juice"
cloth = "Cloth"
pale_ale = "Pale Ale"
aged_roe = "Aged Roe"
battery_pack = "Battery Pack"
mayonnaise = "Mayonnaise"
duck_mayonnaise = "Duck Mayonnaise"
dinosaur_mayonnaise = "Dinosaur Mayonnaise"
void_mayonnaise = "Void Mayonnaise"
caviar = "Caviar"
green_tea = "Green Tea"
mead = "Mead"

View File

@ -0,0 +1,23 @@
class Building:
barn = "Barn"
big_barn = "Big Barn"
deluxe_barn = "Deluxe Barn"
coop = "Coop"
big_coop = "Big Coop"
deluxe_coop = "Deluxe Coop"
fish_pond = "Fish Pond"
mill = "Mill"
shed = "Shed"
big_shed = "Big Shed"
silo = "Silo"
slime_hutch = "Slime Hutch"
stable = "Stable"
well = "Well"
shipping_bin = "Shipping Bin"
kitchen = "Kitchen"
kids_room = "Kids Room"
cellar = "Cellar"
class ModBuilding:
tractor_garage = "Tractor Garage"

View File

@ -0,0 +1,11 @@
class Weekday:
sunday = "Sunday"
monday = "Monday"
tuesday = "Tuesday"
wednesday = "Wednesday"
thursday = "Thursday"
friday = "Friday"
saturday = "Saturday"
all_days = [sunday, monday, tuesday, wednesday, thursday, friday, saturday]

View File

@ -0,0 +1,16 @@
class Craftable:
bait = "Bait"
cherry_bomb = "Cherry Bomb"
bomb = "Bomb"
mega_bomb = "Mega Bomb"
staircase = "Staircase"
scarecrow = "Scarecrow"
rain_totem = "Rain Totem"
flute_block = "Flute Block"
life_elixir = "Life Elixir"
monster_musk = "Monster Musk"
oil_of_garlic = "Oil of Garlic"

View File

@ -0,0 +1,59 @@
all_fruits = []
all_vegetables = []
def veggie(name: str) -> str:
all_vegetables.append(name)
return name
def fruity(name: str) -> str:
all_fruits.append(name)
return name
class Fruit:
any = "Any Fruit"
blueberry = fruity("Blueberry")
melon = fruity("Melon")
apple = fruity("Apple")
apricot = fruity("Apricot")
cherry = fruity("Cherry")
orange = fruity("Orange")
peach = fruity("Peach")
pomegranate = fruity("Pomegranate")
banana = fruity("Banana")
mango = fruity("Mango")
pineapple = fruity("Pineapple")
ancient_fruit = fruity("Ancient Fruit")
strawberry = fruity("Strawberry")
starfruit = fruity("Starfruit")
rhubarb = fruity("Rhubarb")
grape = fruity("Grape")
cranberries = fruity("Cranberries")
hot_pepper = fruity("Hot Pepper")
class Vegetable:
any = "Any Vegetable"
parsnip = veggie("Parsnip")
garlic = veggie("Garlic")
wheat = "Wheat"
potato = veggie("Potato")
corn = veggie("Corn")
tomato = veggie("Tomato")
pumpkin = veggie("Pumpkin")
unmilled_rice = veggie("Unmilled Rice")
beet = veggie("Beet")
hops = "Hops"
cauliflower = veggie("Cauliflower")
amaranth = veggie("Amaranth")
kale = veggie("Kale")
artichoke = veggie("Artichoke")
tea_leaves = "Tea Leaves"
eggplant = veggie("Eggplant")
green_bean = veggie("Green Bean")
red_cabbage = veggie("Red Cabbage")
yam = veggie("Yam")
radish = veggie("Radish")
taro_root = veggie("Taro Root")

View File

@ -0,0 +1,217 @@
def dig_to_mines_floor(floor: int) -> str:
return f"Dig to The Mines - Floor {floor}"
def dig_to_skull_floor(floor: int) -> str:
return f"Mine to Skull Cavern Floor {floor}"
def move_to_woods_depth(depth: int) -> str:
return f"Enter Deep Woods Depth {depth}"
class Entrance:
to_stardew_valley = "To Stardew Valley"
to_farmhouse = "To Farmhouse"
farmhouse_to_farm = "Farmhouse to Farm"
downstairs_to_cellar = "Farmhouse to Cellar"
farm_to_backwoods = "Farm to Backwoods"
farm_to_bus_stop = "Farm to Bus Stop"
bus_stop_to_tunnel_entrance = "Bus Stop to Tunnel Entrance"
tunnel_entrance_to_bus_tunnel = "Tunnel Entrance to Bus Tunnel"
farm_to_forest = "Farm to Forest"
farm_to_farmcave = "Farm to Farmcave"
enter_greenhouse = "Farm to Greenhouse"
use_desert_obelisk = "Use Desert Obelisk"
use_island_obelisk = "Use Island Obelisk"
use_farm_obelisk = "Use Farm Obelisk"
backwoods_to_mountain = "Backwoods to Mountain"
bus_stop_to_town = "Bus Stop to Town"
take_bus_to_desert = "Bus Stop to Desert"
forest_to_town = "Forest to Town"
enter_secret_woods = "Forest to Secret Woods"
forest_to_wizard_tower = "Forest to Wizard Tower"
forest_to_marnie_ranch = "Forest to Marnie's Ranch"
forest_to_leah_cottage = "Forest to Leah's Cottage"
forest_to_sewer = "Forest to Sewer"
buy_from_traveling_merchant = "Buy from Traveling Merchant"
mountain_to_railroad = "Mountain to Railroad"
mountain_to_tent = "Mountain to Tent"
mountain_to_carpenter_shop = "Mountain to Carpenter Shop"
mountain_to_maru_room = "Mountain to Maru's Room"
mountain_to_the_mines = "Mountain to The Mines"
enter_quarry = "Mountain to Quarry"
mountain_to_adventurer_guild = "Mountain to Adventurer's Guild"
mountain_to_town = "Mountain to Town"
town_to_community_center = "Town to Community Center"
access_crafts_room = "Access Crafts Room"
access_pantry = "Access Pantry"
access_fish_tank = "Access Fish Tank"
access_boiler_room = "Access Boiler Room"
access_bulletin_board = "Access Bulletin Board"
access_vault = "Access Vault"
town_to_beach = "Town to Beach"
town_to_hospital = "Town to Hospital"
town_to_pierre_general_store = "Town to Pierre's General Store"
town_to_saloon = "Town to Saloon"
town_to_alex_house = "Town to Alex's House"
town_to_trailer = "Town to Trailer"
town_to_mayor_manor = "Town to Mayor's Manor"
town_to_sam_house = "Town to Sam's House"
town_to_haley_house = "Town to Haley's House"
town_to_sewer = "Town to Sewer"
town_to_clint_blacksmith = "Town to Clint's Blacksmith"
town_to_museum = "Town to Museum"
town_to_jojamart = "Town to JojaMart"
beach_to_willy_fish_shop = "Beach to Willy's Fish Shop"
fish_shop_to_boat_tunnel = "Fish Shop to Boat Tunnel"
boat_to_ginger_island = "Take the Boat to Ginger Island"
enter_elliott_house = "Beach to Elliott's House"
enter_tide_pools = "Beach to Tide Pools"
enter_bathhouse_entrance = "Railroad to Bathhouse Entrance"
enter_witch_warp_cave = "Railroad to Witch Warp Cave"
enter_perfection_cutscene_area = "Railroad to Perfection Cutscene Area"
enter_sebastian_room = "Carpenter Shop to Sebastian's Room"
enter_harvey_room = "Hospital to Harvey's Room"
enter_sunroom = "Pierre's General Store to Sunroom"
enter_mutant_bug_lair = "Sewer to Mutant Bug Lair"
enter_wizard_basement = "Wizard Tower to Wizard Basement"
play_journey_of_the_prairie_king = "Play Journey of the Prairie King"
reach_jotpk_world_2 = "Reach JotPK World 2"
reach_jotpk_world_3 = "Reach JotPK World 3"
play_junimo_kart = "Play Junimo Kart"
reach_junimo_kart_2 = "Reach Junimo Kart 2"
reach_junimo_kart_3 = "Reach Junimo Kart 3"
enter_locker_room = "Bathhouse Entrance to Locker Room"
enter_public_bath = "Locker Room to Public Bath"
enter_witch_swamp = "Witch Warp Cave to Witch's Swamp"
enter_witch_hut = "Witch's Swamp to Witch's Hut"
witch_warp_to_wizard_basement = "Witch's Hut to Wizard Basement"
enter_quarry_mine_entrance = "Quarry to Quarry Mine Entrance"
enter_quarry_mine = "Quarry Mine Entrance to Quarry Mine"
enter_oasis = "Desert to Oasis"
enter_casino = "Oasis to Casino"
enter_skull_cavern_entrance = "Desert to Skull Cavern Entrance"
enter_skull_cavern = "Skull Cavern Entrance to Skull Cavern"
mine_to_skull_cavern_floor_25 = dig_to_skull_floor(25)
mine_to_skull_cavern_floor_50 = dig_to_skull_floor(50)
mine_to_skull_cavern_floor_75 = dig_to_skull_floor(75)
mine_to_skull_cavern_floor_100 = dig_to_skull_floor(100)
mine_to_skull_cavern_floor_125 = dig_to_skull_floor(125)
mine_to_skull_cavern_floor_150 = dig_to_skull_floor(150)
mine_to_skull_cavern_floor_175 = dig_to_skull_floor(175)
mine_to_skull_cavern_floor_200 = dig_to_skull_floor(200)
talk_to_mines_dwarf = "Talk to Mines Dwarf"
dig_to_mines_floor_5 = dig_to_mines_floor(5)
dig_to_mines_floor_10 = dig_to_mines_floor(10)
dig_to_mines_floor_15 = dig_to_mines_floor(15)
dig_to_mines_floor_20 = dig_to_mines_floor(20)
dig_to_mines_floor_25 = dig_to_mines_floor(25)
dig_to_mines_floor_30 = dig_to_mines_floor(30)
dig_to_mines_floor_35 = dig_to_mines_floor(35)
dig_to_mines_floor_40 = dig_to_mines_floor(40)
dig_to_mines_floor_45 = dig_to_mines_floor(45)
dig_to_mines_floor_50 = dig_to_mines_floor(50)
dig_to_mines_floor_55 = dig_to_mines_floor(55)
dig_to_mines_floor_60 = dig_to_mines_floor(60)
dig_to_mines_floor_65 = dig_to_mines_floor(65)
dig_to_mines_floor_70 = dig_to_mines_floor(70)
dig_to_mines_floor_75 = dig_to_mines_floor(75)
dig_to_mines_floor_80 = dig_to_mines_floor(80)
dig_to_mines_floor_85 = dig_to_mines_floor(85)
dig_to_mines_floor_90 = dig_to_mines_floor(90)
dig_to_mines_floor_95 = dig_to_mines_floor(95)
dig_to_mines_floor_100 = dig_to_mines_floor(100)
dig_to_mines_floor_105 = dig_to_mines_floor(105)
dig_to_mines_floor_110 = dig_to_mines_floor(110)
dig_to_mines_floor_115 = dig_to_mines_floor(115)
dig_to_mines_floor_120 = dig_to_mines_floor(120)
island_south_to_west = "Island South to West"
island_south_to_north = "Island South to North"
island_south_to_east = "Island South to East"
island_south_to_southeast = "Island South to Southeast"
use_island_resort = "Use Island Resort"
island_west_to_islandfarmhouse = "Island West to Island Farmhouse"
island_west_to_gourmand_cave = "Island West to Gourmand Cave"
island_west_to_crystals_cave = "Island West to Crystal Cave"
island_west_to_shipwreck = "Island West to Shipwreck"
island_west_to_qi_walnut_room = "Island West to Qi Walnut Room"
island_east_to_leo_hut = "Island East to Leo Hut"
mountain_to_leo_treehouse = "Mountain to Leo TreeHouse"
island_east_to_island_shrine = "Island East to Island Shrine"
island_southeast_to_pirate_cove = "Island Southeast to Pirate Cove"
island_north_to_field_office = "Island North to Field Office"
island_north_to_dig_site = "Island North to Dig Site"
dig_site_to_professor_snail_cave = "Dig Site to Professor Snail Cave"
island_north_to_volcano = "Island North to Volcano Entrance"
volcano_to_secret_beach = "Volcano River to Secret Beach"
talk_to_island_trader = "Talk to Island Trader"
climb_to_volcano_5 = "Climb to Volcano Floor 5"
talk_to_volcano_dwarf = "Talk to Volcano Dwarf"
climb_to_volcano_10 = "Climb to Volcano Floor 10"
parrot_express_docks_to_volcano = "Parrot Express Docks to Volcano"
parrot_express_jungle_to_volcano = "Parrot Express Jungle to Volcano"
parrot_express_dig_site_to_volcano = "Parrot Express Dig Site to Volcano"
parrot_express_docks_to_dig_site = "Parrot Express Docks to Dig Site"
parrot_express_jungle_to_dig_site = "Parrot Express Jungle to Dig Site"
parrot_express_volcano_to_dig_site = "Parrot Express Volcano to Dig Site"
parrot_express_docks_to_jungle = "Parrot Express Docks to Jungle"
parrot_express_dig_site_to_jungle = "Parrot Express Dig Site to Jungle"
parrot_express_volcano_to_jungle = "Parrot Express Volcano to Jungle"
parrot_express_jungle_to_docks = "Parrot Express Jungle to Docks"
parrot_express_dig_site_to_docks = "Parrot Express Dig Site to Docks"
parrot_express_volcano_to_docks = "Parrot Express Volcano to Docks"
# Skull Cavern Elevator
class DeepWoodsEntrance:
secret_woods_to_deep_woods = "Woods to Deep Woods"
use_woods_obelisk = "Use Woods Obelisk"
deep_woods_house = "Deep Woods to Deep Woods House"
deep_woods_depth_1 = move_to_woods_depth(1)
deep_woods_depth_10 = move_to_woods_depth(10)
deep_woods_depth_20 = move_to_woods_depth(20)
deep_woods_depth_30 = move_to_woods_depth(30)
deep_woods_depth_40 = move_to_woods_depth(40)
deep_woods_depth_50 = move_to_woods_depth(50)
deep_woods_depth_60 = move_to_woods_depth(60)
deep_woods_depth_70 = move_to_woods_depth(70)
deep_woods_depth_80 = move_to_woods_depth(80)
deep_woods_depth_90 = move_to_woods_depth(90)
deep_woods_depth_100 = move_to_woods_depth(100)
class EugeneEntrance:
forest_to_garden = "Forest to Eugene's Garden"
garden_to_bedroom = "Eugene's Garden to Eugene's Bedroom"
class MagicEntrance:
store_to_altar = "Pierre's General Store to Magic Altar"
class JasperEntrance:
museum_to_bedroom = "Museum to Jasper's Bedroom"
class AlecEntrance:
forest_to_petshop = "Forest to Alec's Pet Shop"
petshop_to_bedroom = "Alec's Pet Shop to Alec's Bedroom"
class YobaEntrance:
secret_woods_to_clearing = "Woods to Yoba's Clearing"
class JunaEntrance:
forest_to_juna_cave = "Forest to Juna's Cave"
class AyeishaEntrance:
bus_stop_to_mail_van = "Bus Stop to Ayeisha's Mail Van"
class RileyEntrance:
town_to_riley = "Town to Riley's House"

View File

@ -0,0 +1,17 @@
class Fertilizer:
basic = "Basic Fertilizer"
quality = "Quality Fertilizer"
deluxe = "Deluxe Fertilizer"
tree = "Tree Fertilizer"
class RetainingSoil:
basic = "Basic Retaining Soil"
quality = "Quality Retaining Soil"
deluxe = "Deluxe Retaining Soil"
class SpeedGro:
basic = "Speed-Gro"
deluxe = "Deluxe Speed-Gro"
hyper = "Hyper Speed-Gro"

View File

@ -0,0 +1,32 @@
class FestivalCheck:
cone_hat = "Cone Hat"
dance = "Dance with someone"
egg_hunt = "Egg Hunt Victory"
fair_stardrop = "Fair Stardrop"
fishing_competition = "Win Fishing Competition"
grange_display = "Grange Display"
iridium_fireplace = "Iridium Fireplace"
luau_soup = "Luau Soup"
lupini_1000_years = "Lupini: 1000 Years From Now"
lupini_clouds = "Lupini: Clouds"
lupini_land_of_clay = "Lupini: Land Of Clay"
lupini_portrait_mermaid = "Lupini: Portrait Of A Mermaid"
lupini_red_eagle = "Lupini: Red Eagle"
lupini_solar_kingdom = "Lupini: Solar Kingdom"
lupini_the_serpent = "Lupini: The Serpent"
lupini_three_trees = "Lupini: Three Trees"
lupini_tropical_fish = "Lupini: 'Tropical Fish #173'"
mermaid_pearl = "Mermaid Pearl"
moonlight_jellies = "Dance of the Moonlight Jellies"
rarecrow_1 = "Rarecrow #1 (Turnip Head)"
rarecrow_2 = "Rarecrow #2 (Witch)"
rarecrow_4 = "Rarecrow #4 (Snowman)"
rarecrow_5 = "Rarecrow #5 (Woman)"
rarecrow_7 = "Rarecrow #7 (Tanuki)"
rarecrow_8 = "Rarecrow #8 (Tribal Mask)"
secret_santa = "Secret Santa"
legend_of_the_winter_star = "The Legend of the Winter Star"
smashing_stone = "Smashing Stone"
spirit_eve_maze = "Spirit's Eve Maze"
strawberry_seeds = "Egg Festival: Strawberry Seeds"
all_rarecrows = "Collect All Rarecrows"

View File

@ -0,0 +1,62 @@
class Fish:
angler = "Angler"
any = "Any Fish"
blobfish = "Blobfish"
blue_discus = "Blue Discus"
bream = "Bream"
catfish = "Catfish"
crab = "Crab"
crayfish = "Crayfish"
crimsonfish = "Crimsonfish"
dorado = "Dorado"
glacierfish = "Glacierfish"
lava_eel = "Lava Eel"
legend = "Legend"
lionfish = "Lionfish"
lobster = "Lobster"
mussel = "Mussel"
mussel_node = "Mussel Node"
mutant_carp = "Mutant Carp"
octopus = "Octopus"
oyster = "Oyster"
pufferfish = "Pufferfish"
spookfish = "Spook Fish"
squid = "Squid"
stingray = "Stingray"
sturgeon = "Sturgeon"
sunfish = "Sunfish"
void_salmon = "Void Salmon"
albacore = "Albacore"
largemouth_bass = "Largemouth Bass"
smallmouth_bass = "Smallmouth Bass"
sardine = "Sardine"
periwinkle = "Periwinkle"
shrimp = "Shrimp"
snail = "Snail"
tuna = "Tuna"
eel = "Eel"
salmon = "Salmon"
class WaterItem:
seaweed = "Seaweed"
green_algae = "Green Algae"
white_algae = "White Algae"
clam = "Clam"
cockle = "Cockle"
coral = "Coral"
nautilus_shell = "Nautilus Shell"
sea_urchin = "Sea Urchin"
class Trash:
driftwood = "Driftwood"
trash = "Trash"
broken_cd = "Broken CD"
broken_glasses = "Broken Glasses"
joja_cola = "Joja Cola"
soggy_newspaper = "Soggy Newspaper"

View File

@ -0,0 +1,3 @@
class Flower:
sunflower = "Sunflower"
poppy = "Poppy"

View File

@ -0,0 +1,67 @@
class Meal:
blueberry_tart = "Blueberry Tart"
bread = "Bread"
fiddlehead_risotto = "Fiddlehead Risotto"
complete_breakfast = "Complete Breakfast"
fried_egg = "Fried Egg"
hashbrowns = "Hashbrowns"
pancakes = "Pancakes"
ice_cream = "Ice Cream"
maki_roll = "Maki Roll"
miners_treat = "Miner's Treat"
omelet = "Omelet"
parsnip_soup = "Parsnip Soup"
pink_cake = "Pink Cake"
pizza = "Pizza"
pumpkin_pie = "Pumpkin Pie"
roasted_hazelnuts = "Roasted Hazelnuts"
salad = "Salad"
spaghetti = "Spaghetti"
tortilla = "Tortilla"
algae_soup = "Algae Soup"
artichoke_dip = "Artichoke Dip"
baked_fish = "Baked Fish"
bean_hotpot = "Bean Hotpot"
blackberry_cobbler = "Blackberry Cobbler"
cheese_cauliflower = "Cheese Cauliflower"
chocolate_cake = "Chocolate Cake"
chowder = "Chowder"
crab_cakes = "Crab Cakes"
cranberry_candy = "Cranberry Candy"
crispy_bass = "Crispy Bass"
dish_o_the_sea = "Dish O' The Sea"
eggplant_parmesan = "Eggplant Parmesan"
escargot = "Escargot"
farmer_lunch = "Farmer's Lunch"
fish_taco = "Fish Taco"
fried_calamari = "Fried Calamari"
fried_eel = "Fried Eel"
fried_mushroom = "Fried Mushroom"
fruit_salad = "Fruit Salad"
glazed_yams = "Glazed Yams"
maple_bar = "Maple Bar"
pale_broth = "Pale Broth"
pepper_poppers = "Pepper Poppers"
plum_pudding = "Plum Pudding"
poppyseed_muffin = "Poppyseed Muffin"
red_plate = "Red Plate"
rhubarb_pie = "Rhubarb Pie"
rice_pudding = "Rice Pudding"
roots_platter = "Roots Platter"
salmon_dinner = "Salmon Dinner"
sashimi = "Sashimi"
stir_fry = "Stir Fry"
strange_bun = "Strange Bun"
stuffing = "Stuffing"
survival_burger = "Survival Burger"
tropical_curry = "Tropical Curry"
vegetable_medley = "Vegetable Medley"
class Beverage:
pina_colada = "Piña Colada"
ginger_ale = "Ginger Ale"
coffee = "Coffee"
triple_shot_espresso = "Triple Shot Espresso"
beer = "Beer"
joja_cola = "Joja Cola"

View File

@ -0,0 +1,35 @@
class Forageable:
blackberry = "Blackberry"
cactus_fruit = "Cactus Fruit"
cave_carrot = "Cave Carrot"
chanterelle = "Chanterelle"
coconut = "Coconut"
common_mushroom = "Common Mushroom"
crocus = "Crocus"
crystal_fruit = "Crystal Fruit"
daffodil = "Daffodil"
dandelion = "Dandelion"
fiddlehead_fern = "Fiddlehead Fern"
ginger = "Ginger"
hay = "Hay"
hazelnut = "Hazelnut"
holly = "Holly"
leek = "Leek"
magma_cap = "Magma Cap"
morel = "Morel"
secret_note = "Secret Note"
spice_berry = "Spice Berry"
sweet_pea = "Sweet Pea"
wild_horseradish = "Wild Horseradish"
wild_plum = "Wild Plum"
winter_root = "Winter Root"
dragon_tooth = "Dragon Tooth"
red_mushroom = "Red Mushroom"
purple_mushroom = "Purple Mushroom"
rainbow_shell = "Rainbow Shell"
salmonberry = "Salmonberry"
snow_yam = "Snow Yam"
spring_onion = "Spring Onion"

View File

@ -0,0 +1,10 @@
class Sapling:
apple = "Apple Sapling"
apricot = "Apricot Sapling"
cherry = "Cherry Sapling"
orange = "Orange Sapling"
peach = "Peach Sapling"
pomegranate = "Pomegranate Sapling"
banana = "Banana Sapling"
mango = "Mango Sapling"
tea = "Tea Sapling"

View File

@ -0,0 +1,4 @@
class Generic:
any = "Any"
all = "All"
bachelor = "Bachelor"

View File

@ -0,0 +1,7 @@
class Geode:
geode = "Geode"
frozen = "Frozen Geode"
magma = "Magma Geode"
omni = "Omni Geode"
artifact_trove = "Artifact Trove"
golden_coconut = "Golden Coconut"

View File

@ -0,0 +1,6 @@
class Gift:
bouquet = "Bouquet"
wilted_bouquet = "Wilted Bouquet"
pearl = "Pearl"
golden_pumpkin = "Golden Pumpkin"
mermaid_pendant = "Mermaid's Pendant"

View File

@ -0,0 +1,10 @@
class Goal:
grandpa_evaluation = "Succeed Grandpa's Evaluation"
community_center = "Complete Community Center"
bottom_of_the_mines = "Reach the Bottom of The Mines"
cryptic_note = "Complete Quest Cryptic Note"
master_angler = "Catch Every Fish"
complete_museum = "Complete the Museum Collection"
full_house = "Full House"
greatest_walnut_hunter = "Greatest Walnut Hunter"
perfection = "Perfection"

View File

@ -0,0 +1,6 @@
class Ingredient:
wheat_flour = "Wheat Flour"
sugar = "Sugar"
oil = "Oil"
rice = "Rice"
vinegar = "Vinegar"

View File

@ -0,0 +1,22 @@
class Machine:
bee_house = "Bee House"
cask = "Cask"
charcoal_kiln = "Charcoal Kiln"
cheese_press = "Cheese Press"
furnace = "Furnace"
geode_crusher = "Geode Crusher"
keg = "Keg"
lightning_rod = "Lightning Rod"
loom = "Loom"
mayonnaise_machine = "Mayonnaise Machine"
oil_maker = "Oil Maker"
preserves_jar = "Preserves Jar"
recycling_machine = "Recycling Machine"
seed_maker = "Seed Maker"
solar_panel = "Solar Panel"
tapper = "Tapper"
worm_bin = "Worm Bin"
coffee_maker = "Coffee Maker"
crab_pot = "Crab Pot"
ostrich_incubator = "Ostrich Incubator"

View File

@ -0,0 +1,9 @@
class Material:
coal = "Coal"
fiber = "Fiber"
hardwood = "Hardwood"
sap = "Sap"
stone = "Stone"
wood = "Wood"
clay = "Clay"
cinder_shard = "Cinder Shard"

View File

@ -0,0 +1,35 @@
class Ore:
copper = "Copper Ore"
iron = "Iron Ore"
gold = "Gold Ore"
iridium = "Iridium Ore"
radioactive = "Radioactive Bar"
class MetalBar:
quartz = "Refined Quartz"
copper = "Copper Bar"
iron = "Iron Bar"
gold = "Gold Bar"
iridium = "Iridium Bar"
radioactive = "Radioactive Ore"
class Mineral:
aquamarine = "Aquamarine"
topaz = "Topaz"
jade = "Jade"
ruby = "Ruby"
emerald = "Emerald"
amethyst = "Amethyst"
class Artifact:
pass # Eventually this will be the artifact names
class Fossil:
bone_fragment = "Bone Fragment"

View File

@ -0,0 +1,6 @@
class Loot:
slime = "Slime"
bug_meat = "Bug Meat"
bat_wing = "Bat Wing"
solar_essence = "Solar Essence"
void_essence = "Void Essence"

View File

@ -0,0 +1,13 @@
class Performance:
basic = "Basic"
decent = "Decent"
good = "Good"
great = "Great"
galaxy = "Galaxy"
maximum = "Maximum"
tiers = {0: basic,
1: decent,
2: good,
3: great,
4: galaxy,
5: maximum}

View File

@ -0,0 +1,57 @@
class Quest:
introductions = "Introductions"
how_to_win_friends = "How To Win Friends"
getting_started = "Getting Started"
to_the_beach = "To The Beach"
raising_animals = "Raising Animals"
advancement = "Advancement"
archaeology = "Archaeology"
meet_the_wizard = "Meet The Wizard"
forging_ahead = "Forging Ahead"
smelting = "Smelting"
initiation = "Initiation"
robins_lost_axe = "Robin's Lost Axe"
jodis_request = "Jodi's Request"
mayors_shorts = "Mayor's \"Shorts\""
blackberry_basket = "Blackberry Basket"
marnies_request = "Marnie's Request"
pam_is_thirsty = "Pam Is Thirsty"
a_dark_reagent = "A Dark Reagent"
cows_delight = "Cow's Delight"
the_skull_key = "The Skull Key"
crop_research = "Crop Research"
knee_therapy = "Knee Therapy"
robins_request = "Robin's Request"
qis_challenge = "Qi's Challenge"
the_mysterious_qi = "The Mysterious Qi"
carving_pumpkins = "Carving Pumpkins"
a_winter_mystery = "A Winter Mystery"
strange_note = "Strange Note"
cryptic_note = "Cryptic Note"
fresh_fruit = "Fresh Fruit"
aquatic_research = "Aquatic Research"
a_soldiers_star = "A Soldier's Star"
mayors_need = "Mayor's Need"
wanted_lobster = "Wanted: Lobster"
pam_needs_juice = "Pam Needs Juice"
fish_casserole = "Fish Casserole"
catch_a_squid = "Catch A Squid"
fish_stew = "Fish Stew"
pierres_notice = "Pierre's Notice"
clints_attempt = "Clint's Attempt"
a_favor_for_clint = "A Favor For Clint"
staff_of_power = "Staff Of Power"
grannys_gift = "Granny's Gift"
exotic_spirits = "Exotic Spirits"
catch_a_lingcod = "Catch a Lingcod"
the_pirates_wife = "The Pirate's Wife"
dark_talisman = "Dark Talisman"
goblin_problem = "Goblin Problem"
magic_ink = "Magic Ink"
class ModQuest:
MrGinger = "Mr.Ginger's request"
AyeishaEnvelope = "Missing Envelope"
AyeishaRing = "Lost Emerald Ring"
JunaCola = "Juna's Drink Request"
JunaSpaghetti = "Juna's BFF Request"

View File

@ -1,4 +1,4 @@
class SVRegion:
class Region:
menu = "Menu"
stardew_valley = "Stardew Valley"
farm_house = "Farmhouse"
@ -10,6 +10,7 @@ class SVRegion:
forest = "Forest"
bus_stop = "Bus Stop"
backwoods = "Backwoods"
bus_tunnel = "Bus Tunnel"
railroad = "Railroad"
secret_woods = "Secret Woods"
community_center = "Community Center"
@ -19,17 +20,49 @@ class SVRegion:
boiler_room = "Boiler Room"
vault = "Vault"
bulletin_board = "Bulletin Board"
desert = "The Desert"
desert = "Desert"
oasis = "Oasis"
casino = "Casino"
mines = "The Mines"
mines_dwarf_shop = "Mines Dwarf Shop"
skull_cavern_entrance = "Skull Cavern Entrance"
skull_cavern = "Skull Cavern"
sewers = "Sewers"
sewer = "Sewer"
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"
witch_hut = "Witch's Hut"
island_south = "Island South"
island_resort = "Island Resort"
island_south_east = "Island Southeast"
pirate_cove = "Pirate Cove"
island_east = "Island East"
island_north = "Island North"
island_shrine = "Island Shrine"
leo_hut = "Leo's Hut"
leo_treehouse = "Leo's TreeHouse"
island_farmhouse = "Island Farmhouse"
dig_site = "Dig Site"
professor_snail_cave = "Professor Snail Cave"
field_office = "Field Office"
volcano = "Volcano Entrance"
volcano_secret_beach = "Volcano Secret Beach"
island_trader = "Island Trader"
volcano_floor_5 = "Volcano - Floor 5"
volcano_dwarf_shop = "Volcano Dwarf Shop"
volcano_floor_10 = "Volcano - Floor 10"
island_west = "Island West"
gourmand_frog_cave = "Gourmand Frog Cave"
colored_crystals_cave = "Colored Crystals Cave"
shipwreck = "Shipwreck"
qi_walnut_room = "Qi's Walnut Room"
skull_cavern_25 = "Skull Cavern Floor 25"
skull_cavern_50 = "Skull Cavern Floor 50"
skull_cavern_75 = "Skull Cavern Floor 75"
skull_cavern_100 = "Skull Cavern Floor 100"
skull_cavern_125 = "Skull Cavern Floor 125"
skull_cavern_150 = "Skull Cavern Floor 150"
skull_cavern_175 = "Skull Cavern Floor 175"
skull_cavern_200 = "Skull Cavern Floor 200"
hospital = "Hospital"
carpenter = "Carpenter Shop"
alex_house = "Alex's House"
@ -38,12 +71,12 @@ class SVRegion:
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"
maru_room = "Maru's Room"
sebastian_room = "Sebastian's Room"
adventurer_guild = "Adventurer's Guild"
quarry = "Quarry"
@ -62,6 +95,7 @@ class SVRegion:
sam_house = "Sam's House"
jojamart = "JojaMart"
fish_shop = "Willy's Fish Shop"
boat_tunnel = "Boat Tunnel"
tide_pools = "Tide Pools"
bathhouse_entrance = "Bathhouse Entrance"
locker_room = "Locker Room"
@ -97,3 +131,52 @@ class SVRegion:
mines_floor_115 = "The Mines - Floor 115"
mines_floor_120 = "The Mines - Floor 120"
class DeepWoodsRegion:
main_lichtung = "Entrance to the Deep Woods"
abandoned_home = "Abandoned Home in Deep Woods"
woods_obelisk_menu = "Woods Obelisk Menu"
floor_10 = "The Deep Woods Depth 10"
floor_20 = "The Deep Woods Depth 20"
floor_30 = "The Deep Woods Depth 30"
floor_40 = "The Deep Woods Depth 40"
floor_50 = "The Deep Woods Depth 50"
floor_60 = "The Deep Woods Depth 60"
floor_70 = "The Deep Woods Depth 70"
floor_80 = "The Deep Woods Depth 80"
floor_90 = "The Deep Woods Depth 90"
floor_100 = "The Deep Woods Depth 100"
class EugeneRegion:
eugene_garden = "Eugene's Garden"
eugene_bedroom = "Eugene's Bedroom"
class MagicRegion:
altar = "Magic Altar"
class JasperRegion:
jasper_bedroom = "Jasper's Bedroom"
class AlecRegion:
pet_store = "Alec's Pet Shop"
alec_bedroom = "Alec's Bedroom"
class YobaRegion:
yoba_clearing = "Yoba's Clearing"
class JunaRegion:
juna_cave = "Juna's Cave"
class AyeishaRegion:
mail_van = "Ayeisha's Mail Van"
class RileyRegion:
riley_house = "Riley's House"

View File

@ -0,0 +1,6 @@
class Season:
spring = "Spring"
summer = "Summer"
fall = "Fall"
winter = "Winter"
progressive = "Progressive Season"

View File

@ -0,0 +1,9 @@
class Seed:
sunflower = "Sunflower Seeds"
tomato = "Tomato Seeds"
melon = "Melon Seeds"
wheat = "Wheat Seeds"
garlic = "Garlic Seeds"
pineapple = "Pineapple Seeds"
taro = "Taro Tuber"
coffee = "Coffee Bean"

View File

@ -0,0 +1,15 @@
class Skill:
farming = "Farming"
foraging = "Foraging"
fishing = "Fishing"
mining = "Mining"
combat = "Combat"
class ModSkill:
luck = "Luck"
binning = "Binning"
archaeology = "Archaeology"
cooking = "Cooking"
magic = "Magic"
socializing = "Socializing"

View File

@ -0,0 +1,33 @@
class SpecialOrder:
island_ingredients = "Island Ingredients"
cave_patrol = "Cave Patrol"
aquatic_overpopulation = "Aquatic Overpopulation"
biome_balance = "Biome Balance"
rock_rejuivenation = "Rock Rejuvenation"
gifts_for_george = "Gifts for George"
fragments_of_the_past = "Fragments of the past"
gus_famous_omelet = "Gus' Famous Omelet"
crop_order = "Crop Order"
community_cleanup = "Community Cleanup"
the_strong_stuff = "The Strong Stuff"
pierres_prime_produce = "Pierre's Prime Produce"
robins_project = "Robin's Project"
robins_resource_rush = "Robin's Resource Rush"
juicy_bugs_wanted_yum = "Juicy Bugs Wanted!"
tropical_fish = "Tropical Fish"
a_curious_substance = "A Curious Substance"
prismatic_jelly = "Prismatic Jelly"
qis_crop = "Qi's Crop"
lets_play_a_game = "Let's Play A Game"
four_precious_stones = "Four Precious Stones"
qis_hungry_challenge = "Qi's Hungry Challenge"
qis_cuisine = "Qi's Cuisine"
qis_kindness = "Qi's Kindness"
extended_family = "Extended Family"
danger_in_the_deep = "Danger In The Deep"
skull_cavern_invasion = "Skull Cavern Invasion"
qis_prismatic_grange = "Qi's Prismatic Grange"
class ModSpecialOrder:
junas_monster_mash = "Juna's Monster Mash"

View File

@ -0,0 +1,22 @@
class MagicSpell:
clear_debris = "Spell: Clear Debris"
till = "Spell: Till"
water = "Spell: Water"
blink = "Spell: Blink"
evac = "Spell: Evac"
haste = "Spell: Haste"
heal = "Spell: Heal"
buff = "Spell: Buff"
shockwave = "Spell: Shockwave"
fireball = "Spell: Fireball"
frostbite = "Spell: Frostbite"
teleport = "Spell: Teleport"
lantern = "Spell: Lantern"
tendrils = "Spell: Tendrils"
photosynthesis = "Spell: Photosynthesis"
descend = "Spell: Descend"
meteor = "Spell: Meteor"
bloodmana = "Spell: Bloodmana"
lucksteal = "Spell: Lucksteal"
spirit = "Spell: Spirit"
rewind = "Spell: Rewind"

View File

@ -0,0 +1,31 @@
class Tool:
pickaxe = "Pickaxe"
axe = "Axe"
hoe = "Hoe"
watering_can = "Watering Can"
trash_can = "Trash Can"
fishing_rod = "Fishing Rod"
scythe = "Scythe"
golden_scythe = "Golden Scythe"
class ToolMaterial:
basic = "Basic"
copper = "Copper"
iron = "Iron"
gold = "Gold"
iridium = "Iridium"
tiers = {0: basic,
1: copper,
2: iron,
3: gold,
4: iridium}
class APTool:
pickaxe = f"Progressive {Tool.pickaxe}"
axe = f"Progressive {Tool.axe}"
hoe = f"Progressive {Tool.hoe}"
watering_can = f"Progressive {Tool.watering_can}"
trash_can = f"Progressive {Tool.trash_can}"
fishing_rod = f"Progressive {Tool.fishing_rod}"

View File

@ -0,0 +1,2 @@
class Channel:
queen_of_sauce = "The Queen of Sauce"

View File

@ -0,0 +1,49 @@
class NPC:
alex = "Alex"
elliott = "Elliott"
harvey = "Harvey"
sam = "Sam"
sebastian = "Sebastian"
shane = "Shane"
abigail = "Abigail"
emily = "Emily"
haley = "Haley"
leah = "Leah"
maru = "Maru"
penny = "Penny"
caroline = "Caroline"
clint = "Clint"
demetrius = "Demetrius"
dwarf = "Dwarf"
evelyn = "Evelyn"
george = "George"
gus = "Gus"
jas = "Jas"
jodi = "Jodi"
kent = "Kent"
krobus = "Krobus"
leo = "Leo"
lewis = "Lewis"
linus = "Linus"
marnie = "Marnie"
pam = "Pam"
pierre = "Pierre"
robin = "Robin"
sandy = "Sandy"
vincent = "Vincent"
willy = "Willy"
wizard = "Wizard"
pet = "Pet"
class ModNPC:
alec = "Alec"
ayeisha = "Ayeisha"
delores = "Delores"
eugene = "Eugene"
jasper = "Jasper"
juna = "Juna"
mr_ginger = "Mr. Ginger"
riley = "Riley"
shiko = "Shiko"
wellwick = "Wellwick"
yoba = "Yoba"

View File

@ -0,0 +1,5 @@
class Wallet:
magnifying_glass = "Magnifying Glass"
rusty_key = "Rusty Key"
skull_key = "Skull Key"
dark_talisman = "Dark Talisman"

View File

@ -0,0 +1,4 @@
class Weapon:
slingshot = "Slingshot"
master_slingshot = "Master Slingshot"
any_slingshot = "Any Slingshot"

View File

@ -1,30 +1,119 @@
from BaseClasses import ItemClassification
from . import SVTestBase
from BaseClasses import ItemClassification, MultiWorld
from . import setup_solo_multiworld, SVTestBase
from .. import locations, items, location_table, options
from ..data.villagers_data import all_villagers_by_name
from ..data.villagers_data import all_villagers_by_name, all_villagers_by_mod_by_name
from ..items import items_by_group, Group
from ..locations import LocationTags
from ..mods.mod_data import ModNames
def get_real_locations(tester: SVTestBase, multiworld: MultiWorld):
return [location for location in multiworld.get_locations(tester.player) if not location.event]
def get_real_location_names(tester: SVTestBase, multiworld: MultiWorld):
return [location.name for location in multiworld.get_locations(tester.player) if not location.event]
class TestBaseItemGeneration(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
}
def test_all_progression_items_are_added_to_the_pool(self):
for classification in [ItemClassification.progression, ItemClassification.useful]:
with self.subTest(classification=classification):
all_classified_items = {self.world.create_item(item)
for item in items.items_by_group[items.Group.COMMUNITY_REWARD]
if item.classification is classification}
for item in all_classified_items:
self.assertIn(item, self.multiworld.itempool)
all_created_items = [item.name for item in self.multiworld.itempool]
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
items_to_ignore.extend(item.name for item in items.all_items if item.mod_name is not None)
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(weapon.name for weapon in items.items_by_group[Group.WEAPON])
items_to_ignore.extend(footwear.name for footwear in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
items_to_ignore.extend(resource_pack.name for resource_pack in items.items_by_group[Group.RESOURCE_PACK])
progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
and item.name not in items_to_ignore]
for progression_item in progression_items:
with self.subTest(f"{progression_item.name}"):
self.assertIn(progression_item.name, all_created_items)
def test_creates_as_many_item_as_non_event_locations(self):
non_event_locations = [location for location in self.multiworld.get_locations(self.player) if
non_event_locations = [location for location in get_real_locations(self, self.multiworld) if
not location.event]
self.assertEqual(len(non_event_locations), len(self.multiworld.itempool))
def test_does_not_create_deprecated_items(self):
all_created_items = [item.name for item in self.multiworld.itempool]
for deprecated_item in items.items_by_group[items.Group.DEPRECATED]:
with self.subTest(f"{deprecated_item.name}"):
self.assertNotIn(deprecated_item.name, all_created_items)
def test_does_not_create_more_than_one_maximum_one_items(self):
all_created_items = [item.name for item in self.multiworld.itempool]
for maximum_one_item in items.items_by_group[items.Group.MAXIMUM_ONE]:
with self.subTest(f"{maximum_one_item.name}"):
self.assertLessEqual(all_created_items.count(maximum_one_item.name), 1)
def test_does_not_create_exactly_two_items(self):
all_created_items = [item.name for item in self.multiworld.itempool]
for exactly_two_item in items.items_by_group[items.Group.EXACTLY_TWO]:
with self.subTest(f"{exactly_two_item.name}"):
count = all_created_items.count(exactly_two_item.name)
self.assertTrue(count == 0 or count == 2)
class TestNoGingerIslandItemGeneration(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true
}
def test_all_progression_items_except_island_are_added_to_the_pool(self):
all_created_items = [item.name for item in self.multiworld.itempool]
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
items_to_ignore.extend(item.name for item in items.all_items if item.mod_name is not None)
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(season.name for season in items.items_by_group[Group.WEAPON])
items_to_ignore.extend(season.name for season in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
and item.name not in items_to_ignore]
for progression_item in progression_items:
with self.subTest(f"{progression_item.name}"):
if Group.GINGER_ISLAND in progression_item.groups:
self.assertNotIn(progression_item.name, all_created_items)
else:
self.assertIn(progression_item.name, all_created_items)
def test_creates_as_many_item_as_non_event_locations(self):
non_event_locations = [location for location in get_real_locations(self, self.multiworld) if
not location.event]
self.assertEqual(len(non_event_locations), len(self.multiworld.itempool))
def test_does_not_create_deprecated_items(self):
all_created_items = [item.name for item in self.multiworld.itempool]
for deprecated_item in items.items_by_group[items.Group.DEPRECATED]:
with self.subTest(f"Deprecated item: {deprecated_item.name}"):
self.assertNotIn(deprecated_item.name, all_created_items)
def test_does_not_create_more_than_one_maximum_one_items(self):
all_created_items = [item.name for item in self.multiworld.itempool]
for maximum_one_item in items.items_by_group[items.Group.MAXIMUM_ONE]:
with self.subTest(f"{maximum_one_item.name}"):
self.assertLessEqual(all_created_items.count(maximum_one_item.name), 1)
def test_does_not_create_exactly_two_items(self):
all_created_items = [item.name for item in self.multiworld.itempool]
for exactly_two_item in items.items_by_group[items.Group.EXACTLY_TWO]:
with self.subTest(f"{exactly_two_item.name}"):
count = all_created_items.count(exactly_two_item.name)
self.assertTrue(count == 0 or count == 2)
class TestGivenProgressiveBackpack(SVTestBase):
options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive}
@ -35,7 +124,8 @@ class TestGivenProgressiveBackpack(SVTestBase):
def test_when_generate_world_then_backpack_locations_are_added(self):
created_locations = {location.name for location in self.multiworld.get_locations(1)}
backpacks_exist = [location.name in created_locations
for location in locations.locations_by_tag[LocationTags.BACKPACK]]
for location in locations.locations_by_tag[LocationTags.BACKPACK]
if location.name != "Premium Pack"]
all_exist = all(backpacks_exist)
self.assertTrue(all_exist)
@ -72,7 +162,7 @@ class TestRemixedMineRewards(SVTestBase):
class TestProgressiveElevator(SVTestBase):
options = {
options.TheMinesElevatorsProgression.internal_name: options.TheMinesElevatorsProgression.option_progressive,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive,
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
}
@ -110,30 +200,44 @@ class TestProgressiveElevator(SVTestBase):
class TestLocationGeneration(SVTestBase):
def test_all_location_created_are_in_location_table(self):
for location in self.multiworld.get_locations(self.player):
for location in get_real_locations(self, self.multiworld):
if not location.event:
self.assertIn(location.name, location_table)
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.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):
self.assertGreaterEqual(len(self.multiworld.get_locations()), len(self.multiworld.get_items()))
min_max_options = self.minimal_locations_maximal_items()
multiworld = setup_solo_multiworld(min_max_options)
valid_locations = get_real_locations(self, multiworld)
self.assertGreaterEqual(len(valid_locations), len(multiworld.itempool))
def test_allsanity_without_mods_has_at_least_locations(self):
expected_locations = 993
allsanity_options = self.allsanity_options_without_mods()
multiworld = setup_solo_multiworld(allsanity_options)
number_locations = len(get_real_locations(self, multiworld))
self.assertGreaterEqual(number_locations, expected_locations)
print(f"Stardew Valley - Allsanity Locations without mods: {number_locations}")
if number_locations != expected_locations:
print(f"\tNew locations detected!"
f"\n\tPlease update test_allsanity_without_mods_has_at_least_locations"
f"\n\t\tExpected: {expected_locations}"
f"\n\t\tActual: {number_locations}")
def test_allsanity_with_mods_has_at_least_locations(self):
expected_locations = 1245
allsanity_options = self.allsanity_options_with_mods()
multiworld = setup_solo_multiworld(allsanity_options)
number_locations = len(get_real_locations(self, multiworld))
self.assertGreaterEqual(number_locations, expected_locations)
print(f"\nStardew Valley - Allsanity Locations with all mods: {number_locations}")
if number_locations != expected_locations:
print(f"\tNew locations detected!"
f"\n\tPlease update test_allsanity_with_mods_has_at_least_locations"
f"\n\t\tExpected: {expected_locations}"
f"\n\t\tActual: {number_locations}")
class TestFriendsanityNone(SVTestBase):
@ -142,24 +246,25 @@ class TestFriendsanityNone(SVTestBase):
}
def test_no_friendsanity_items(self):
for item in self.multiworld.get_items():
self.assertFalse(item.name.endswith(": 1 <3"))
for item in self.multiworld.itempool:
self.assertFalse(item.name.endswith(" <3"))
def test_no_friendsanity_locations(self):
for location in self.multiworld.get_locations():
self.assertFalse(location.name.startswith("Friendsanity"))
for location_name in get_real_location_names(self, self.multiworld):
self.assertFalse(location_name.startswith("Friendsanity"))
class TestFriendsanityBachelors(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_bachelors,
options.FriendsanityHeartSize.internal_name: 1,
}
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():
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertIn(villager_name, self.bachelors)
@ -167,9 +272,9 @@ class TestFriendsanityBachelors(SVTestBase):
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):]
for location_name in get_real_location_names(self, self.multiworld):
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]
@ -181,12 +286,13 @@ class TestFriendsanityBachelors(SVTestBase):
class TestFriendsanityStartingNpcs(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_starting_npcs,
options.FriendsanityHeartSize.internal_name: 1,
}
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():
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertNotIn(villager_name, self.excluded_npcs)
@ -194,15 +300,15 @@ class TestFriendsanityStartingNpcs(SVTestBase):
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):]
for location_name in get_real_location_names(self, self.multiworld):
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]
self.assertNotIn(name, self.excluded_npcs)
self.assertTrue(name in all_villagers_by_name or name == "Pet")
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertLessEqual(int(hearts), 5)
elif all_villagers_by_name[name].bachelor:
@ -214,26 +320,62 @@ class TestFriendsanityStartingNpcs(SVTestBase):
class TestFriendsanityAllNpcs(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all,
options.FriendsanityHeartSize.internal_name: 1,
}
def test_friendsanity_all_items(self):
suffix = ": 1 <3"
for item in self.multiworld.get_items():
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertTrue(villager_name in all_villagers_by_name or villager_name == "Pet")
self.assertTrue(villager_name in all_villagers_by_mod_by_name[ModNames.vanilla] 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):]
for location_name in get_real_location_names(self, self.multiworld):
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]
self.assertTrue(name in all_villagers_by_name or name == "Pet")
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertLessEqual(int(hearts), 5)
elif all_villagers_by_name[name].bachelor:
self.assertLessEqual(int(hearts), 8)
else:
self.assertLessEqual(int(hearts), 10)
class TestFriendsanityAllNpcsExcludingGingerIsland(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all,
options.FriendsanityHeartSize.internal_name: 1,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true
}
def test_friendsanity_all_items(self):
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertNotEqual(villager_name, "Leo")
self.assertTrue(villager_name in all_villagers_by_mod_by_name[ModNames.vanilla] or villager_name == "Pet")
def test_friendsanity_all_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
for location_name in get_real_location_names(self, self.multiworld):
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]
self.assertNotEqual(name, "Leo")
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertLessEqual(int(hearts), 5)
elif all_villagers_by_name[name].bachelor:
@ -245,29 +387,182 @@ class TestFriendsanityAllNpcs(SVTestBase):
class TestFriendsanityAllNpcsWithMarriage(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 1,
}
def test_friendsanity_all_with_marriage_items(self):
suffix = ": 1 <3"
for item in self.multiworld.get_items():
suffix = " <3"
for item in self.multiworld.itempool:
if item.name.endswith(suffix):
villager_name = item.name[:item.name.index(suffix)]
self.assertTrue(villager_name in all_villagers_by_name or villager_name == "Pet")
self.assertTrue(villager_name in all_villagers_by_mod_by_name[ModNames.vanilla] 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):]
for location_name in get_real_location_names(self, self.multiworld):
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]
self.assertTrue(name in all_villagers_by_name or name == "Pet")
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertLessEqual(int(hearts), 5)
elif all_villagers_by_name[name].bachelor:
self.assertLessEqual(int(hearts), 14)
else:
self.assertLessEqual(int(hearts), 10)
class TestFriendsanityAllNpcsWithMarriageHeartSize2(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 2,
}
def test_friendsanity_all_with_marriage_items(self):
suffix = " <3"
item_names = [item.name for item in self.multiworld.itempool]
for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
heart_item_name = f"{villager_name}{suffix}"
number_heart_items = item_names.count(heart_item_name)
if all_villagers_by_name[villager_name].bachelor:
self.assertEqual(number_heart_items, 7)
else:
self.assertEqual(number_heart_items, 5)
self.assertEqual(item_names.count("Pet <3"), 3)
def test_friendsanity_all_with_marriage_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
for location_name in get_real_location_names(self, self.multiworld):
if not location_name.startswith(prefix):
continue
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 = int(parts[1])
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertTrue(hearts == 2 or hearts == 4 or hearts == 5)
elif all_villagers_by_name[name].bachelor:
self.assertTrue(hearts == 2 or hearts == 4 or hearts == 6 or hearts == 8 or hearts == 10 or hearts == 12 or hearts == 14)
else:
self.assertTrue(hearts == 2 or hearts == 4 or hearts == 6 or hearts == 8 or hearts == 10)
class TestFriendsanityAllNpcsWithMarriageHeartSize3(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 3,
}
def test_friendsanity_all_with_marriage_items(self):
suffix = " <3"
item_names = [item.name for item in self.multiworld.itempool]
for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
heart_item_name = f"{villager_name}{suffix}"
number_heart_items = item_names.count(heart_item_name)
if all_villagers_by_name[villager_name].bachelor:
self.assertEqual(number_heart_items, 5)
else:
self.assertEqual(number_heart_items, 4)
self.assertEqual(item_names.count("Pet <3"), 2)
def test_friendsanity_all_with_marriage_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
for location_name in get_real_location_names(self, self.multiworld):
if not location_name.startswith(prefix):
continue
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 = int(parts[1])
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertTrue(hearts == 3 or hearts == 5)
elif all_villagers_by_name[name].bachelor:
self.assertTrue(hearts == 3 or hearts == 6 or hearts == 9 or hearts == 12 or hearts == 14)
else:
self.assertTrue(hearts == 3 or hearts == 6 or hearts == 9 or hearts == 10)
class TestFriendsanityAllNpcsWithMarriageHeartSize4(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 4,
}
def test_friendsanity_all_with_marriage_items(self):
suffix = " <3"
item_names = [item.name for item in self.multiworld.itempool]
for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
heart_item_name = f"{villager_name}{suffix}"
number_heart_items = item_names.count(heart_item_name)
if all_villagers_by_name[villager_name].bachelor:
self.assertEqual(number_heart_items, 4)
else:
self.assertEqual(number_heart_items, 3)
self.assertEqual(item_names.count("Pet <3"), 2)
def test_friendsanity_all_with_marriage_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
for location_name in get_real_location_names(self, self.multiworld):
if not location_name.startswith(prefix):
continue
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 = int(parts[1])
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertTrue(hearts == 4 or hearts == 5)
elif all_villagers_by_name[name].bachelor:
self.assertTrue(hearts == 4 or hearts == 8 or hearts == 12 or hearts == 14)
else:
self.assertTrue(hearts == 4 or hearts == 8 or hearts == 10)
class TestFriendsanityAllNpcsWithMarriageHeartSize5(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 5,
}
def test_friendsanity_all_with_marriage_items(self):
suffix = " <3"
item_names = [item.name for item in self.multiworld.itempool]
for villager_name in all_villagers_by_mod_by_name[ModNames.vanilla]:
heart_item_name = f"{villager_name}{suffix}"
number_heart_items = item_names.count(heart_item_name)
if all_villagers_by_name[villager_name].bachelor:
self.assertEqual(number_heart_items, 3)
else:
self.assertEqual(number_heart_items, 2)
self.assertEqual(item_names.count("Pet <3"), 1)
def test_friendsanity_all_with_marriage_locations(self):
prefix = "Friendsanity: "
suffix = " <3"
for location_name in get_real_location_names(self, self.multiworld):
if not location_name.startswith(prefix):
continue
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 = int(parts[1])
self.assertTrue(name in all_villagers_by_mod_by_name[ModNames.vanilla] or name == "Pet")
if name == "Pet":
self.assertTrue(hearts == 5)
elif all_villagers_by_name[name].bachelor:
self.assertTrue(hearts == 5 or hearts == 10 or hearts == 14)
else:
self.assertTrue(hearts == 5 or hearts == 10)

View File

@ -1,14 +1,17 @@
import itertools
import math
import sys
import unittest
import random
from typing import Set
from BaseClasses import ItemClassification, MultiWorld
from . import setup_solo_multiworld, SVTestBase
from .. import ItemData, StardewValleyWorld
from ..items import Group, ResourcePackData, item_table
from ..items import Group, item_table
class TestItems(unittest.TestCase):
class TestItems(SVTestBase):
def test_can_create_item_of_resource_pack(self):
item_name = "Resource Pack: 500 Money"
@ -20,77 +23,30 @@ class TestItems(unittest.TestCase):
assert item.name == item_name
def test_items_table_footprint_is_between_717000_and_727000(self):
def test_items_table_footprint_is_between_717000_and_737000(self):
item_with_lowest_id = min((item for item in item_table.values() if item.code is not None), key=lambda x: x.code)
item_with_highest_id = max((item for item in item_table.values() if item.code is not None),
key=lambda x: x.code)
assert item_with_lowest_id.code >= 717000
assert item_with_highest_id.code < 727000
assert item_with_highest_id.code < 737000
def test_babies_come_in_all_shapes_and_sizes(self):
baby_permutations = set()
for attempt_number in range(50):
if len(baby_permutations) >= 4:
print(f"Already got all 4 baby permutations, breaking early [{attempt_number} generations]")
break
seed = random.randrange(sys.maxsize)
multiworld = setup_solo_multiworld(seed=seed)
baby_items = [item for item in multiworld.get_items() if "Baby" in item.name]
self.assertEqual(len(baby_items), 2)
baby_permutations.add(f"{baby_items[0]} - {baby_items[1]}")
self.assertEqual(len(baby_permutations), 4)
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"
def test_correct_number_of_stardrops(self):
seed = random.randrange(sys.maxsize)
allsanity_options = self.allsanity_options_without_mods()
multiworld = setup_solo_multiworld(allsanity_options, seed=seed)
stardrop_items = [item for item in multiworld.get_items() if "Stardrop" in item.name]
self.assertEqual(len(stardrop_items), 5)

View File

@ -22,67 +22,81 @@ class TestLogic(unittest.TestCase):
def test_given_bundle_item_then_is_available_in_logic(self):
for bundle_item in all_bundle_items_except_money:
with self.subTest(msg=bundle_item.item.name):
assert bundle_item.item.name in logic.item_rules
self.assertIn(bundle_item.item.name, logic.item_rules)
def test_given_item_rule_then_can_be_resolved(self):
for item in logic.item_rules.keys():
with self.subTest(msg=item):
rule = logic.item_rules[item]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve item rule for {item} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve item rule for {item} {rule}")
def test_given_building_rule_then_can_be_resolved(self):
for building in logic.building_rules.keys():
with self.subTest(msg=building):
rule = logic.building_rules[building]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve building rule for {building} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve building rule for {building} {rule}")
def test_given_quest_rule_then_can_be_resolved(self):
for quest in logic.quest_rules.keys():
with self.subTest(msg=quest):
rule = logic.quest_rules[quest]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve quest rule for {quest} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve quest rule for {quest} {rule}")
def test_given_special_order_rule_then_can_be_resolved(self):
for special_order in logic.special_order_rules.keys():
with self.subTest(msg=special_order):
rule = logic.special_order_rules[special_order]
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve special order rule for {special_order} {rule}")
def test_given_tree_fruit_rule_then_can_be_resolved(self):
for tree_fruit in logic.tree_fruit_rules.keys():
with self.subTest(msg=tree_fruit):
rule = logic.tree_fruit_rules[tree_fruit]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve tree fruit rule for {tree_fruit} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve tree fruit rule for {tree_fruit} {rule}")
def test_given_seed_rule_then_can_be_resolved(self):
for seed in logic.seed_rules.keys():
with self.subTest(msg=seed):
rule = logic.seed_rules[seed]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve seed rule for {seed} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve seed rule for {seed} {rule}")
def test_given_crop_rule_then_can_be_resolved(self):
for crop in logic.crop_rules.keys():
with self.subTest(msg=crop):
rule = logic.crop_rules[crop]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve crop rule for {crop} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve crop rule for {crop} {rule}")
def test_given_fish_rule_then_can_be_resolved(self):
for fish in logic.fish_rules.keys():
with self.subTest(msg=fish):
rule = logic.fish_rules[fish]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve fish rule for {fish} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve fish rule for {fish} {rule}")
def test_given_museum_rule_then_can_be_resolved(self):
for donation in logic.museum_rules.keys():
with self.subTest(msg=donation):
rule = logic.museum_rules[donation]
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve museum rule for {donation} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve museum rule for {donation} {rule}")
def test_given_cooking_rule_then_can_be_resolved(self):
for cooking_rule in logic.cooking_rules.keys():
with self.subTest(msg=cooking_rule):
rule = logic.cooking_rules[cooking_rule]
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve cooking rule for {cooking_rule} {rule}")
def test_given_location_rule_then_can_be_resolved(self):
for location in multi_world.get_locations(1):
with self.subTest(msg=location.name):
rule = location.access_rule
assert MISSING_ITEM not in repr(rule)
assert rule == False_() or rule(multi_world.state), f"Could not resolve location rule for {location} {rule}"
self.assertNotIn(MISSING_ITEM, repr(rule))
self.assertTrue(rule == False_() or rule(multi_world.state), f"Could not resolve location rule for {location} {rule}")

View File

@ -55,11 +55,3 @@ def test_simplify_true_in_or():
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

@ -0,0 +1,192 @@
from typing import List, Union
import unittest
import random
import sys
from BaseClasses import MultiWorld
from . import setup_solo_multiworld
from .TestOptions import basic_checks, SVTestBase
from .. import options, locations, items, Group, ItemClassification, StardewOptions
from ..regions import RandomizationFlag, create_final_connections, randomize_connections, create_final_regions
from ..items import item_table, items_by_group
from ..locations import location_table, LocationTags
from ..options import stardew_valley_option_classes, Mods, EntranceRandomization
mod_list = ["DeepWoods", "Tractor Mod", "Bigger Backpack",
"Luck Skill", "Magic", "Socializing Skill", "Archaeology",
"Cooking Skill", "Binning Skill", "Juna - Roommate NPC",
"Professor Jasper Thomas", "Alec Revisited", "Custom NPC - Yoba", "Custom NPC Eugene",
"'Prophet' Wellwick", "Mister Ginger (cat npc)", "Shiko - New Custom NPC", "Delores - Custom NPC",
"Ayeisha - The Postal Worker (Custom NPC)", "Custom NPC - Riley", "Skull Cavern Elevator"]
def check_stray_mod_items(chosen_mods: Union[List[str], str], tester: SVTestBase, multiworld: MultiWorld):
if isinstance(chosen_mods, str):
chosen_mods = [chosen_mods]
for multiworld_item in multiworld.get_items():
item = item_table[multiworld_item.name]
tester.assertTrue(item.mod_name is None or item.mod_name in chosen_mods)
for multiworld_location in multiworld.get_locations():
if multiworld_location.event:
continue
location = location_table[multiworld_location.name]
tester.assertTrue(location.mod_name is None or location.mod_name in chosen_mods)
class TestGenerateModsOptions(SVTestBase):
def test_given_single_mods_when_generate_then_basic_checks(self):
for mod in mod_list:
with self.subTest(f"Mod: {mod}"):
multi_world = setup_solo_multiworld({Mods: mod})
basic_checks(self, multi_world)
check_stray_mod_items(mod, self, multi_world)
def test_given_mod_pairs_when_generate_then_basic_checks(self):
if self.skip_long_tests:
return
num_mods = len(mod_list)
for mod1_index in range(0, num_mods):
for mod2_index in range(mod1_index + 1, num_mods):
mod1 = mod_list[mod1_index]
mod2 = mod_list[mod2_index]
mods = (mod1, mod2)
with self.subTest(f"Mods: {mods}"):
multiworld = setup_solo_multiworld({Mods: mods})
basic_checks(self, multiworld)
check_stray_mod_items(list(mods), self, multiworld)
def test_given_mod_names_when_generate_paired_with_entrance_randomizer_then_basic_checks(self):
for option in EntranceRandomization.options:
for mod in mod_list:
with self.subTest(f"entrance_randomization: {option}, Mod: {mod}"):
multiworld = setup_solo_multiworld({EntranceRandomization.internal_name: option, Mods: mod})
basic_checks(self, multiworld)
check_stray_mod_items(mod, self, multiworld)
def test_given_mod_names_when_generate_paired_with_other_options_then_basic_checks(self):
if self.skip_long_tests:
return
for option in stardew_valley_option_classes:
if not option.options:
continue
for value in option.options:
for mod in mod_list:
with self.subTest(f"{option.internal_name}: {value}, Mod: {mod}"):
multiworld = setup_solo_multiworld({option.internal_name: option.options[value], Mods: mod})
basic_checks(self, multiworld)
check_stray_mod_items(mod, self, multiworld)
class TestGivenModdedProgressiveBackpack(SVTestBase):
options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive,
options.Mods.internal_name: "Bigger Backpack"}
def test_when_generate_world_then_three_progressive_backpack_are_added(self):
self.assertEqual(self.multiworld.itempool.count(self.world.create_item("Progressive Backpack")), 3)
def test_when_generate_world_then_all_backpack_locations_are_added(self):
created_locations = {location.name for location in self.multiworld.get_locations(1)}
backpacks_exist = [location.name in created_locations
for location in locations.locations_by_tag[LocationTags.BACKPACK]]
all_exist = all(backpacks_exist)
self.assertTrue(all_exist)
class TestBaseItemGeneration(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.Mods.internal_name: mod_list
}
def test_all_progression_items_are_added_to_the_pool(self):
all_created_items = [item.name for item in self.multiworld.itempool]
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(weapon.name for weapon in items.items_by_group[Group.WEAPON])
items_to_ignore.extend(footwear.name for footwear in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
items_to_ignore.extend(resource_pack.name for resource_pack in items.items_by_group[Group.RESOURCE_PACK])
progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
and item.name not in items_to_ignore]
for progression_item in progression_items:
with self.subTest(f"{progression_item.name}"):
self.assertIn(progression_item.name, all_created_items)
class TestNoGingerIslandModItemGeneration(SVTestBase):
options = {
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
options.Mods.internal_name: mod_list
}
def test_all_progression_items_except_island_are_added_to_the_pool(self):
all_created_items = [item.name for item in self.multiworld.itempool]
# Ignore all the stuff that the algorithm chooses one of, instead of all, to fulfill logical progression
items_to_ignore = [event.name for event in items.events]
items_to_ignore.extend(season.name for season in items.items_by_group[Group.SEASON])
items_to_ignore.extend(weapon.name for weapon in items.items_by_group[Group.WEAPON])
items_to_ignore.extend(footwear.name for footwear in items.items_by_group[Group.FOOTWEAR])
items_to_ignore.extend(baby.name for baby in items.items_by_group[Group.BABY])
items_to_ignore.extend(resource_pack.name for resource_pack in items.items_by_group[Group.RESOURCE_PACK])
progression_items = [item for item in items.all_items if item.classification is ItemClassification.progression
and item.name not in items_to_ignore]
for progression_item in progression_items:
with self.subTest(f"{progression_item.name}"):
if Group.GINGER_ISLAND in progression_item.groups:
self.assertNotIn(progression_item.name, all_created_items)
else:
self.assertIn(progression_item.name, all_created_items)
class TestModEntranceRando(unittest.TestCase):
def test_mod_entrance_randomization(self):
for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
(options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
(options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
with self.subTest(option=option, flag=flag):
seed = random.randrange(sys.maxsize)
rand = random.Random(seed)
world_options = StardewOptions({options.EntranceRandomization.internal_name: option,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
options.Mods.internal_name: mod_list})
final_regions = create_final_regions(world_options)
final_connections = create_final_connections(world_options)
regions_by_name = {region.name: region for region in final_regions}
_, randomized_connections = randomize_connections(rand, world_options, regions_by_name)
for connection in final_connections:
if flag in connection.flag:
connection_in_randomized = connection.name in randomized_connections
reverse_in_randomized = connection.reverse in randomized_connections
self.assertTrue(connection_in_randomized,
f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}")
self.assertTrue(reverse_in_randomized,
f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
f"Connections are duplicated in randomization. Seed = {seed}")
class TestModTraps(SVTestBase):
def test_given_traps_when_generate_then_all_traps_in_pool(self):
trap_option = options.TrapItems
for value in trap_option.options:
if value == "no_traps":
continue
world_options = self.allsanity_options_without_mods()
world_options.update({options.TrapItems.internal_name: trap_option.options[value], Mods: "Magic"})
multi_world = setup_solo_multiworld(world_options)
trap_items = [item_data.name for item_data in items_by_group[Group.TRAP] if Group.DEPRECATED not in item_data.groups]
multiworld_items = [item.name for item in multi_world.get_items()]
for item in trap_items:
with self.subTest(f"Option: {value}, Item: {item}"):
self.assertIn(item, multiworld_items)

View File

@ -1,28 +1,52 @@
import itertools
import unittest
from random import random
from typing import Dict
from BaseClasses import ItemClassification, MultiWorld
from Options import SpecialRange
from Options import SpecialRange, OptionSet
from . import setup_solo_multiworld, SVTestBase
from .. import StardewItem, options
from ..options import StardewOption, stardew_valley_option_classes
from .. import StardewItem, options, items_by_group, Group
from ..locations import locations_by_tag, LocationTags, location_table
from ..options import StardewOption, stardew_valley_option_classes, Mods
from ..strings.goal_names import Goal
from ..strings.season_names import Season
from ..strings.special_order_names import SpecialOrder
from ..strings.tool_names import ToolMaterial, Tool
SEASONS = {"Spring", "Summer", "Fall", "Winter"}
SEASONS = {Season.spring, Season.summer, Season.fall, Season.winter}
TOOLS = {"Hoe", "Pickaxe", "Axe", "Watering Can", "Trash Can", "Fishing Rod"}
def assert_can_win(multiworld: MultiWorld):
def assert_can_win(tester: SVTestBase, multiworld: MultiWorld):
for item in multiworld.get_items():
multiworld.state.collect(item)
assert multiworld.find_item("Victory", 1).can_reach(multiworld.state)
tester.assertTrue(multiworld.find_item("Victory", 1).can_reach(multiworld.state))
def basic_checks(multiworld: MultiWorld):
assert StardewItem("Victory", ItemClassification.progression, None, 1) in multiworld.get_items()
assert_can_win(multiworld)
assert len(multiworld.itempool) == len(
[location for location in multiworld.get_locations() if not location.event])
def basic_checks(tester: SVTestBase, multiworld: MultiWorld):
tester.assertIn(StardewItem("Victory", ItemClassification.progression, None, 1), multiworld.get_items())
assert_can_win(tester, multiworld)
non_event_locations = [location for location in multiworld.get_locations() if not location.event]
tester.assertEqual(len(multiworld.itempool), len(non_event_locations))
def check_no_ginger_island(tester: SVTestBase, multiworld: MultiWorld):
ginger_island_items = [item_data.name for item_data in items_by_group[Group.GINGER_ISLAND]]
ginger_island_locations = [location_data.name for location_data in locations_by_tag[LocationTags.GINGER_ISLAND]]
for item in multiworld.get_items():
tester.assertNotIn(item.name, ginger_island_items)
for location in multiworld.get_locations():
tester.assertNotIn(location.name, ginger_island_locations)
def get_option_choices(option) -> Dict[str, int]:
if issubclass(option, SpecialRange):
return option.special_range_names
elif option.options:
return option.options
return {}
class TestGenerateDynamicOptions(SVTestBase):
@ -30,45 +54,39 @@ class TestGenerateDynamicOptions(SVTestBase):
for option in stardew_valley_option_classes:
if not issubclass(option, SpecialRange):
continue
with self.subTest(msg=option.internal_name):
for value in option.special_range_names:
multiworld = setup_solo_multiworld({option.internal_name: option.special_range_names[value]})
basic_checks(multiworld)
for value in option.special_range_names:
with self.subTest(f"{option.internal_name}: {value}"):
choices = {option.internal_name: option.special_range_names[value]}
multiworld = setup_solo_multiworld(choices)
basic_checks(self, multiworld)
def test_given_choice_when_generate_then_basic_checks(self):
seed = int(random() * pow(10, 18) - 1)
for option in stardew_valley_option_classes:
if not option.options:
continue
with self.subTest(msg=option.internal_name):
for value in option.options:
multiworld = setup_solo_multiworld({option.internal_name: option.options[value]})
basic_checks(multiworld)
def test_given_option_combination_when_generate_then_basic_checks(self):
option_combinations = [{options.Goal.internal_name: options.Goal.option_master_angler,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla}]
ids = ["Master Angler + Vanilla tools"]
for i in range(0, len(option_combinations)):
option_combination = option_combinations[i]
id = ids[i]
with self.subTest(msg=f"{id}"):
multi_world = setup_solo_multiworld(option_combination)
basic_checks(multi_world)
for value in option.options:
with self.subTest(f"{option.internal_name}: {value} [Seed: {seed}]"):
choices = {option.internal_name: option.options[value]}
multiworld = setup_solo_multiworld(choices, seed)
basic_checks(self, multiworld)
class TestGoal(SVTestBase):
def test_given_goal_when_generate_then_victory_is_in_correct_location(self):
for goal, location in [("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")]:
for goal, location in [("community_center", Goal.community_center),
("grandpa_evaluation", Goal.grandpa_evaluation),
("bottom_of_the_mines", Goal.bottom_of_the_mines),
("cryptic_note", Goal.cryptic_note),
("master_angler", Goal.master_angler),
("complete_collection", Goal.complete_museum),
("full_house", Goal.full_house),
("perfection", Goal.perfection)]:
with self.subTest(msg=f"Goal: {goal}, Location: {location}"):
world_options = {options.Goal.internal_name: options.Goal.options[goal]}
multi_world = setup_solo_multiworld(world_options)
victory = multi_world.find_item("Victory", 1)
assert victory.name == location
self.assertEqual(victory.name, location)
class TestSeasonRandomization(SVTestBase):
@ -77,22 +95,22 @@ class TestSeasonRandomization(SVTestBase):
multi_world = setup_solo_multiworld(world_options)
precollected_items = {item.name for item in multi_world.precollected_items[1]}
assert all([season in precollected_items for season in SEASONS])
self.assertTrue(all([season in precollected_items for season in SEASONS]))
def test_given_randomized_when_generate_then_all_seasons_are_in_the_pool_or_precollected(self):
world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized}
multi_world = setup_solo_multiworld(world_options)
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
self.assertTrue(all([season in items for season in SEASONS]))
self.assertEqual(len(SEASONS.intersection(precollected_items)), 1)
def test_given_progressive_when_generate_then_3_progressive_seasons_are_in_the_pool(self):
world_options = {options.SeasonRandomization.internal_name: options.SeasonRandomization.option_progressive}
multi_world = setup_solo_multiworld(world_options)
items = [item.name for item in multi_world.get_items()]
assert items.count("Progressive Season") == 3
self.assertEqual(items.count(Season.progressive), 3)
class TestBackpackProgression(SVTestBase):
@ -106,21 +124,22 @@ class TestBackpackProgression(SVTestBase):
world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive}
multi_world = setup_solo_multiworld(world_options)
items = [item.name for item in multi_world.get_items()]
assert items.count("Progressive Backpack") == 2
self.assertEqual(items.count("Progressive Backpack"), 2)
def test_given_progressive_when_generate_then_backpack_upgrades_are_locations(self):
world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive}
multi_world = setup_solo_multiworld(world_options)
locations = {locations.name for locations in multi_world.get_locations(1)}
assert "Large Pack" in locations
assert "Deluxe Pack" in locations
self.assertIn("Large Pack", locations)
self.assertIn("Deluxe Pack", locations)
def test_given_early_progressive_when_generate_then_progressive_backpack_is_in_early_pool(self):
world_options = {options.BackpackProgression.internal_name: options.BackpackProgression.option_early_progressive}
world_options = {
options.BackpackProgression.internal_name: options.BackpackProgression.option_early_progressive}
multi_world = setup_solo_multiworld(world_options)
assert "Progressive Backpack" in multi_world.early_items[1]
self.assertIn("Progressive Backpack", multi_world.early_items[1])
class TestToolProgression(SVTestBase):
@ -130,25 +149,147 @@ class TestToolProgression(SVTestBase):
items = {item.name for item in multi_world.get_items()}
for tool in TOOLS:
assert tool not in items
self.assertNotIn(tool, items)
def test_given_progressive_when_generate_then_progressive_tool_of_each_is_in_pool_four_times(self):
world_options = {options.ToolProgression.internal_name:options.ToolProgression.option_progressive}
world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive}
multi_world = setup_solo_multiworld(world_options)
items = [item.name for item in multi_world.get_items()]
for tool in TOOLS:
assert items.count("Progressive " + tool) == 4
self.assertEqual(items.count("Progressive " + tool), 4)
def test_given_progressive_when_generate_then_tool_upgrades_are_locations(self):
world_options = {options.ToolProgression.internal_name: options.ToolProgression.option_progressive}
multi_world = setup_solo_multiworld(world_options)
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
for material, tool in itertools.product(ToolMaterial.tiers.values(),
[Tool.hoe, Tool.pickaxe, Tool.axe, Tool.watering_can, Tool.trash_can]):
if material == ToolMaterial.basic:
continue
self.assertIn(f"{material} {tool} Upgrade", locations)
self.assertIn("Purchase Training Rod", locations)
self.assertIn("Bamboo Pole Cutscene", locations)
self.assertIn("Purchase Fiberglass Rod", locations)
self.assertIn("Purchase Iridium Rod", locations)
class TestGenerateAllOptionsWithExcludeGingerIsland(SVTestBase):
def test_given_special_range_when_generate_exclude_ginger_island(self):
for option in stardew_valley_option_classes:
if not issubclass(option,
SpecialRange) or option.internal_name == options.ExcludeGingerIsland.internal_name:
continue
for value in option.special_range_names:
with self.subTest(f"{option.internal_name}: {value}"):
multiworld = setup_solo_multiworld(
{options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
option.internal_name: option.special_range_names[value]})
check_no_ginger_island(self, multiworld)
def test_given_choice_when_generate_exclude_ginger_island(self):
seed = int(random() * pow(10, 18) - 1)
island_option = options.ExcludeGingerIsland
for option in stardew_valley_option_classes:
if not option.options or option.internal_name == island_option.internal_name:
continue
for value in option.options:
with self.subTest(f"{option.internal_name}: {value} [Seed: {seed}]"):
multiworld = setup_solo_multiworld(
{island_option.internal_name: island_option.option_true,
option.internal_name: option.options[value]}, seed)
if multiworld.worlds[self.player].options[island_option.internal_name] != island_option.option_true:
continue
basic_checks(self, multiworld)
check_no_ginger_island(self, multiworld)
def test_given_island_related_goal_then_override_exclude_ginger_island(self):
island_goals = [value for value in options.Goal.options if value in ["walnut_hunter", "perfection"]]
island_option = options.ExcludeGingerIsland
for goal in island_goals:
for value in island_option.options:
with self.subTest(f"Goal: {goal}, {island_option.internal_name}: {value}"):
multiworld = setup_solo_multiworld(
{options.Goal.internal_name: options.Goal.options[goal],
island_option.internal_name: island_option.options[value]})
self.assertEqual(multiworld.worlds[self.player].options[island_option.internal_name], island_option.option_false)
basic_checks(self, multiworld)
class TestTraps(SVTestBase):
def test_given_no_traps_when_generate_then_no_trap_in_pool(self):
world_options = self.allsanity_options_without_mods()
world_options.update({options.TrapItems.internal_name: options.TrapItems.option_no_traps})
multi_world = setup_solo_multiworld(world_options)
trap_items = [item_data.name for item_data in items_by_group[Group.TRAP]]
multiworld_items = [item.name for item in multi_world.get_items()]
for item in trap_items:
with self.subTest(f"{item}"):
self.assertNotIn(item, multiworld_items)
def test_given_traps_when_generate_then_all_traps_in_pool(self):
trap_option = options.TrapItems
for value in trap_option.options:
if value == "no_traps":
continue
world_options = self.allsanity_options_with_mods()
world_options.update({options.TrapItems.internal_name: trap_option.options[value]})
multi_world = setup_solo_multiworld(world_options)
trap_items = [item_data.name for item_data in items_by_group[Group.TRAP] if Group.DEPRECATED not in item_data.groups and item_data.mod_name is None]
multiworld_items = [item.name for item in multi_world.get_items()]
for item in trap_items:
with self.subTest(f"Option: {value}, Item: {item}"):
self.assertIn(item, multiworld_items)
class TestSpecialOrders(SVTestBase):
def test_given_disabled_then_no_order_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_disabled}
multi_world = setup_solo_multiworld(world_options)
locations_in_pool = {location.name for location in multi_world.get_locations() if location.name in location_table}
for location_name in locations_in_pool:
location = location_table[location_name]
self.assertNotIn(LocationTags.SPECIAL_ORDER_BOARD, location.tags)
self.assertNotIn(LocationTags.SPECIAL_ORDER_QI, location.tags)
def test_given_board_only_then_no_qi_order_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_only}
multi_world = setup_solo_multiworld(world_options)
locations_in_pool = {location.name for location in multi_world.get_locations() if location.name in location_table}
for location_name in locations_in_pool:
location = location_table[location_name]
self.assertNotIn(LocationTags.SPECIAL_ORDER_QI, location.tags)
for board_location in locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]:
if board_location.mod_name:
continue
self.assertIn(board_location.name, locations_in_pool)
def test_given_board_and_qi_then_all_orders_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_victories}
multi_world = setup_solo_multiworld(world_options)
locations_in_pool = {location.name for location in multi_world.get_locations()}
for qi_location in locations_by_tag[LocationTags.SPECIAL_ORDER_QI]:
if qi_location.mod_name:
continue
self.assertIn(qi_location.name, locations_in_pool)
for board_location in locations_by_tag[LocationTags.SPECIAL_ORDER_BOARD]:
if board_location.mod_name:
continue
self.assertIn(board_location.name, locations_in_pool)
def test_given_board_and_qi_without_arcade_machines_then_lets_play_a_game_not_in_pool(self):
world_options = {options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled}
multi_world = setup_solo_multiworld(world_options)
locations_in_pool = {location.name for location in multi_world.get_locations()}
self.assertNotIn(SpecialOrder.lets_play_a_game, locations_in_pool)

View File

@ -3,44 +3,82 @@ import sys
import unittest
from .. import StardewOptions, options
from ..regions import stardew_valley_regions, mandatory_connections, randomize_connections, RandomizationFlag
from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag
connections_by_name = {connection.name for connection in mandatory_connections}
regions_by_name = {region.name for region in stardew_valley_regions}
connections_by_name = {connection.name for connection in vanilla_connections}
regions_by_name = {region.name for region in vanilla_regions}
class TestRegions(unittest.TestCase):
def test_region_exits_lead_somewhere(self):
for region in stardew_valley_regions:
for region in vanilla_regions:
with self.subTest(region=region):
for exit in region.exits:
assert exit in connections_by_name, f"{region.name} is leading to {exit} but it does not exist."
self.assertIn(exit, connections_by_name,
f"{region.name} is leading to {exit} but it does not exist.")
def test_connection_lead_somewhere(self):
for connection in mandatory_connections:
for connection in vanilla_connections:
with self.subTest(connection=connection):
assert connection.destination in regions_by_name, \
f"{connection.name} is leading to {connection.destination} but it does not exist."
self.assertIn(connection.destination, regions_by_name,
f"{connection.name} is leading to {connection.destination} but it does not exist.")
class TestEntranceRando(unittest.TestCase):
def test_pelican_town_entrance_randomization(self):
def test_entrance_randomization(self):
for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
(options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION)]:
(options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
(options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
# option = options.EntranceRandomization.option_buildings
# flag = RandomizationFlag.BUILDINGS
# for i in range(0, 100000):
seed = random.randrange(sys.maxsize)
with self.subTest(flag=flag, msg=f"Seed: {seed}"):
rand = random.Random(seed)
world_options = StardewOptions({options.EntranceRandomization.internal_name: option,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false})
regions_by_name = {region.name: region for region in vanilla_regions}
_, randomized_connections = randomize_connections(rand, world_options, regions_by_name)
for connection in vanilla_connections:
if flag in connection.flag:
connection_in_randomized = connection.name in randomized_connections
reverse_in_randomized = connection.reverse in randomized_connections
self.assertTrue(connection_in_randomized,
f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}")
self.assertTrue(reverse_in_randomized,
f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
f"Connections are duplicated in randomization. Seed = {seed}")
def test_entrance_randomization_without_island(self):
for option, flag in [(options.EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
(options.EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
(options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
with self.subTest(option=option, flag=flag):
seed = random.randrange(sys.maxsize)
rand = random.Random(seed)
world_options = StardewOptions({options.EntranceRandomization.internal_name: option})
world_options = StardewOptions({options.EntranceRandomization.internal_name: option,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true})
regions_by_name = {region.name: region for region in vanilla_regions}
_, randomized_connections = randomize_connections(rand, world_options)
_, randomized_connections = randomize_connections(rand, world_options, regions_by_name)
for connection in mandatory_connections:
for connection in vanilla_connections:
if flag in connection.flag:
assert connection.name in randomized_connections, \
f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}"
assert connection.reverse in randomized_connections, \
f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}"
if RandomizationFlag.GINGER_ISLAND in connection.flag:
self.assertNotIn(connection.name, randomized_connections,
f"Connection {connection.name} should not be randomized but it is in the output. Seed = {seed}")
self.assertNotIn(connection.reverse, randomized_connections,
f"Connection {connection.reverse} should not be randomized but it is in the output. Seed = {seed}")
else:
self.assertIn(connection.name, randomized_connections,
f"Connection {connection.name} should be randomized but it is not in the output. Seed = {seed}")
self.assertIn(connection.reverse, randomized_connections,
f"Connection {connection.reverse} should be randomized but it is not in the output. Seed = {seed}")
assert len(set(randomized_connections.values())) == len(
randomized_connections.values()), f"Connections are duplicated in randomization. Seed = {seed}"
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
f"Connections are duplicated in randomization. Seed = {seed}")

View File

@ -2,6 +2,16 @@ from collections import Counter
from . import SVTestBase
from .. import options
from ..strings.animal_names import Animal
from ..strings.animal_product_names import AnimalProduct
from ..strings.artisan_good_names import ArtisanGood
from ..strings.crop_names import Vegetable
from ..strings.food_names import Meal
from ..strings.ingredient_names import Ingredient
from ..strings.machine_names import Machine
from ..strings.region_names import Region
from ..strings.season_names import Season
from ..strings.seed_names import Seed
class TestProgressiveToolsLogic(SVTestBase):
@ -15,67 +25,71 @@ class TestProgressiveToolsLogic(SVTestBase):
self.multiworld.state.prog_items = Counter()
def test_sturgeon(self):
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
self.assertFalse(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)
self.assertFalse(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)
self.assertFalse(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.assertFalse(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.assertTrue(self.world.logic.has("Sturgeon")(self.multiworld.state))
self.remove(summer)
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
self.assertFalse(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.assertTrue(self.world.logic.has("Sturgeon")(self.multiworld.state))
self.remove(fishing_rod)
assert not self.world.logic.has("Sturgeon")(self.multiworld.state)
self.assertFalse(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)
self.assertFalse(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)
self.assertFalse(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.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
rare_seed = self.world.create_item("Rare Seed")
self.multiworld.state.collect(rare_seed, event=True)
self.assertTrue(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.assertFalse(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)
self.assertFalse(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.assertTrue(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.assertFalse(self.world.logic.can_reach_location("Old Master Cannoli")(self.multiworld.state))
self.remove(friday)
@ -84,7 +98,7 @@ class TestBundlesLogic(SVTestBase):
}
def test_vault_2500g_bundle(self):
assert self.world.logic.can_reach_location("2,500g Bundle")(self.multiworld.state)
self.assertTrue(self.world.logic.can_reach_location("2,500g Bundle")(self.multiworld.state))
class TestBuildingLogic(SVTestBase):
@ -93,27 +107,27 @@ class TestBuildingLogic(SVTestBase):
}
def test_coop_blueprint(self):
assert not self.world.logic.can_reach_location("Coop Blueprint")(self.multiworld.state)
self.assertFalse(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)
self.assertTrue(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.assertFalse(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.assertFalse(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)}"
self.assertTrue(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.assertFalse(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)
@ -122,17 +136,17 @@ class TestBuildingLogic(SVTestBase):
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.assertFalse(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.assertFalse(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)
self.assertTrue(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.assertFalse(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)
@ -140,12 +154,12 @@ class TestBuildingLogic(SVTestBase):
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.assertFalse(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)}"
self.assertTrue(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):
@ -154,10 +168,10 @@ class TestArcadeMachinesLogic(SVTestBase):
}
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)
self.assertFalse(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
self.assertFalse(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")
@ -167,19 +181,19 @@ class TestArcadeMachinesLogic(SVTestBase):
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.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
self.assertFalse(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.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(boots)
@ -187,10 +201,10 @@ class TestArcadeMachinesLogic(SVTestBase):
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.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
self.assertTrue(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(gun)
self.remove(ammo)
@ -203,10 +217,10 @@ class TestArcadeMachinesLogic(SVTestBase):
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.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
self.assertTrue(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
self.assertTrue(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
self.assertFalse(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(gun)
self.remove(gun)
@ -226,10 +240,10 @@ class TestArcadeMachinesLogic(SVTestBase):
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.assertTrue(self.world.logic.can_reach_region("JotPK World 1")(self.multiworld.state))
self.assertTrue(self.world.logic.can_reach_region("JotPK World 2")(self.multiworld.state))
self.assertTrue(self.world.logic.can_reach_region("JotPK World 3")(self.multiworld.state))
self.assertTrue(self.world.logic.can_reach_location("Journey of the Prairie King Victory")(self.multiworld.state))
self.remove(boots)
self.remove(boots)
self.remove(gun)
@ -282,28 +296,76 @@ class TestWeaponsLogic(SVTestBase):
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)
self.assertTrue(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)
self.assertFalse(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)
self.assertTrue(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)
self.assertFalse(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)
self.assertTrue(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)
self.assertFalse(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)
self.assertTrue(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)
self.assertFalse(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)
self.assertTrue(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.assertFalse(self.world.logic.can_mine_perfectly_in_the_skull_cavern()(self.multiworld.state))
self.remove(item)
class TestRecipeLogic(SVTestBase):
options = {
options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive,
options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
options.Cropsanity.internal_name: options.Cropsanity.option_shuffled,
}
# I wanted to make a test for different ways to obtain a pizza, but I'm stuck not knowing how to block the immediate purchase from Gus
# def test_pizza(self):
# world = self.world
# logic = world.logic
# multiworld = self.multiworld
#
# self.assertTrue(logic.has(Ingredient.wheat_flour)(multiworld.state))
# self.assertTrue(logic.can_spend_money_at(Region.saloon, 150)(multiworld.state))
# self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
#
# self.assertFalse(logic.can_cook()(multiworld.state))
# self.collect(world.create_item("Progressive House"))
# self.assertTrue(logic.can_cook()(multiworld.state))
# self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
#
# self.assertFalse(logic.has(Seed.tomato)(multiworld.state))
# self.collect(world.create_item(Seed.tomato))
# self.assertTrue(logic.has(Seed.tomato)(multiworld.state))
# self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
#
# self.assertFalse(logic.has(Vegetable.tomato)(multiworld.state))
# self.collect(world.create_item(Season.summer))
# self.assertTrue(logic.has(Vegetable.tomato)(multiworld.state))
# self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
#
# self.assertFalse(logic.has(Animal.cow)(multiworld.state))
# self.assertFalse(logic.has(AnimalProduct.cow_milk)(multiworld.state))
# self.collect(world.create_item("Progressive Barn"))
# self.assertTrue(logic.has(Animal.cow)(multiworld.state))
# self.assertTrue(logic.has(AnimalProduct.cow_milk)(multiworld.state))
# self.assertFalse(logic.has(Meal.pizza)(multiworld.state))
#
# self.assertFalse(logic.has(Machine.cheese_press)(self.multiworld.state))
# self.assertFalse(logic.has(ArtisanGood.cheese)(self.multiworld.state))
# self.collect(world.create_item(item) for item in ["Farming Level"] * 6)
# self.collect(world.create_item(item) for item in ["Progressive Axe"] * 2)
# self.assertTrue(logic.has(Machine.cheese_press)(self.multiworld.state))
# self.assertTrue(logic.has(ArtisanGood.cheese)(self.multiworld.state))
# self.assertTrue(logic.has(Meal.pizza)(self.multiworld.state))

View File

@ -1,10 +1,12 @@
import os
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 .. import StardewValleyWorld, options
from ..mods.mod_data import ModNames
from ...AutoWorld import call_all
@ -12,36 +14,103 @@ class SVTestBase(WorldTestBase):
game = "Stardew Valley"
world: StardewValleyWorld
player: ClassVar[int] = 1
skip_long_tests: bool = True
def world_setup(self, *args, **kwargs):
super().world_setup(*args, **kwargs)
long_tests_key = "long"
if long_tests_key in os.environ:
self.skip_long_tests = not bool(os.environ[long_tests_key])
if self.constructed:
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
is_not_stardew_test = type(self) is not SVTestBase
should_run_default_tests = is_not_stardew_test and super().run_default_tests
return should_run_default_tests
def minimal_locations_maximal_items(self):
min_max_options = {
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
options.Cropsanity.internal_name: options.Cropsanity.option_shuffled,
options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.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.Friendsanity.option_none,
options.NumberOfMovementBuffs.internal_name: 12,
options.NumberOfLuckBuffs.internal_name: 12,
}
return min_max_options
def allsanity_options_without_mods(self):
allsanity = {
options.Goal.internal_name: options.Goal.option_perfection,
options.BundleRandomization.internal_name: options.BundleRandomization.option_shuffled,
options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
options.Cropsanity.internal_name: options.Cropsanity.option_shuffled,
options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive,
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
options.SkillProgression.internal_name: options.SkillProgression.option_progressive,
options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive,
options.FestivalLocations.internal_name: options.FestivalLocations.option_hard,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive,
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.HelpWantedLocations.internal_name: 56,
options.Fishsanity.internal_name: options.Fishsanity.option_all,
options.Museumsanity.internal_name: options.Museumsanity.option_all,
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 1,
options.NumberOfMovementBuffs.internal_name: 12,
options.NumberOfLuckBuffs.internal_name: 12,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
options.TrapItems.internal_name: options.TrapItems.option_nightmare,
}
return allsanity
def allsanity_options_with_mods(self):
allsanity = {}
allsanity.update(self.allsanity_options_without_mods())
all_mods = (
ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack,
ModNames.luck_skill, ModNames.magic, ModNames.socializing_skill, ModNames.archaeology,
ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna,
ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene,
ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores,
ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator
)
allsanity.update({options.Mods.internal_name: all_mods})
return allsanity
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,
def setup_solo_multiworld(test_options=None, seed=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())
frozen_options = frozenset(test_options.items()).union({seed})
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()
multiworld.set_seed(seed)
# print(f"Seed: {multiworld.seed}") # Uncomment to print the seed for every test
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)

View File

@ -0,0 +1,55 @@
from BaseClasses import MultiWorld
from .option_checks import is_setting, assert_is_setting
from ... import options
from .. import SVTestBase
def is_goal(multiworld: MultiWorld, goal: int) -> bool:
return is_setting(multiworld, options.Goal.internal_name, goal)
def is_bottom_mines(multiworld: MultiWorld) -> bool:
return is_goal(multiworld, options.Goal.option_bottom_of_the_mines)
def is_not_bottom_mines(multiworld: MultiWorld) -> bool:
return not is_bottom_mines(multiworld)
def is_walnut_hunter(multiworld: MultiWorld) -> bool:
return is_goal(multiworld, options.Goal.option_greatest_walnut_hunter)
def is_not_walnut_hunter(multiworld: MultiWorld) -> bool:
return not is_walnut_hunter(multiworld)
def is_perfection(multiworld: MultiWorld) -> bool:
return is_goal(multiworld, options.Goal.option_perfection)
def is_not_perfection(multiworld: MultiWorld) -> bool:
return not is_perfection(multiworld)
def assert_ginger_island_is_included(tester: SVTestBase, multiworld: MultiWorld):
assert_is_setting(tester, multiworld, options.ExcludeGingerIsland.internal_name, options.ExcludeGingerIsland.option_false)
def assert_walnut_hunter_world_is_valid(tester: SVTestBase, multiworld: MultiWorld):
if is_not_walnut_hunter(multiworld):
return
assert_ginger_island_is_included(tester, multiworld)
def assert_perfection_world_is_valid(tester: SVTestBase, multiworld: MultiWorld):
if is_not_perfection(multiworld):
return
assert_ginger_island_is_included(tester, multiworld)
def assert_goal_world_is_valid(tester: SVTestBase, multiworld: MultiWorld):
assert_walnut_hunter_world_is_valid(tester, multiworld)
assert_perfection_world_is_valid(tester, multiworld)

View File

@ -0,0 +1,90 @@
from typing import Union
from BaseClasses import MultiWorld
from .world_checks import get_all_item_names, get_all_location_names
from .. import SVTestBase
from ... import StardewValleyWorld, options, item_table, Group, location_table
from ...locations import LocationTags
from ...strings.ap_names.transport_names import Transportation
def get_stardew_world(multiworld: MultiWorld) -> Union[StardewValleyWorld, None]:
for world_key in multiworld.worlds:
world = multiworld.worlds[world_key]
if isinstance(world, StardewValleyWorld):
return world
return None
def is_setting(multiworld: MultiWorld, setting_name: str, setting_value: int) -> bool:
stardew_world = get_stardew_world(multiworld)
if not stardew_world:
return False
current_value = stardew_world.options[setting_name]
return current_value == setting_value
def is_not_setting(multiworld: MultiWorld, setting_name: str, setting_value: int) -> bool:
return not is_setting(multiworld, setting_name, setting_value)
def assert_is_setting(tester: SVTestBase, multiworld: MultiWorld, setting_name: str, setting_value: int) -> bool:
stardew_world = get_stardew_world(multiworld)
if not stardew_world:
return False
current_value = stardew_world.options[setting_name]
tester.assertEqual(current_value, setting_value)
def assert_can_reach_island(tester: SVTestBase, multiworld: MultiWorld):
all_item_names = get_all_item_names(multiworld)
tester.assertIn(Transportation.boat_repair, all_item_names)
tester.assertIn(Transportation.island_obelisk, all_item_names)
def assert_cannot_reach_island(tester: SVTestBase, multiworld: MultiWorld):
all_item_names = get_all_item_names(multiworld)
tester.assertNotIn(Transportation.boat_repair, all_item_names)
tester.assertNotIn(Transportation.island_obelisk, all_item_names)
def assert_can_reach_island_if_should(tester: SVTestBase, multiworld: MultiWorld):
include_island = is_setting(multiworld, options.ExcludeGingerIsland.internal_name, options.ExcludeGingerIsland.option_false)
if include_island:
assert_can_reach_island(tester, multiworld)
else:
assert_cannot_reach_island(tester, multiworld)
def assert_cropsanity_same_number_items_and_locations(tester: SVTestBase, multiworld: MultiWorld):
is_cropsanity = is_setting(multiworld, options.Cropsanity.internal_name, options.Cropsanity.option_shuffled)
if not is_cropsanity:
return
all_item_names = set(get_all_item_names(multiworld))
all_location_names = set(get_all_location_names(multiworld))
all_cropsanity_item_names = {item_name for item_name in all_item_names if Group.CROPSANITY in item_table[item_name].groups}
all_cropsanity_location_names = {location_name for location_name in all_location_names if LocationTags.CROPSANITY in location_table[location_name].tags}
tester.assertEqual(len(all_cropsanity_item_names), len(all_cropsanity_location_names))
def assert_all_rarecrows_exist(tester: SVTestBase, multiworld: MultiWorld):
all_item_names = set(get_all_item_names(multiworld))
for rarecrow_number in range(1, 9):
tester.assertIn(f"Rarecrow #{rarecrow_number}", all_item_names)
def assert_has_deluxe_scarecrow_recipe(tester: SVTestBase, multiworld: MultiWorld):
all_item_names = set(get_all_item_names(multiworld))
tester.assertIn(f"Deluxe Scarecrow Recipe", all_item_names)
def assert_festivals_give_access_to_deluxe_scarecrow(tester: SVTestBase, multiworld: MultiWorld):
has_festivals = is_not_setting(multiworld, options.FestivalLocations.internal_name, options.FestivalLocations.option_disabled)
if not has_festivals:
return
assert_all_rarecrows_exist(tester, multiworld)
assert_has_deluxe_scarecrow_recipe(tester, multiworld)

View File

@ -0,0 +1,33 @@
from typing import List
from BaseClasses import MultiWorld, ItemClassification
from ... import StardewItem
from .. import SVTestBase
def get_all_item_names(multiworld: MultiWorld) -> List[str]:
return [item.name for item in multiworld.itempool]
def get_all_location_names(multiworld: MultiWorld) -> List[str]:
return [location.name for location in multiworld.get_locations() if not location.event]
def assert_victory_exists(tester: SVTestBase, multiworld: MultiWorld):
tester.assertIn(StardewItem("Victory", ItemClassification.progression, None, 1), multiworld.get_items())
def collect_all_then_assert_can_win(tester: SVTestBase, multiworld: MultiWorld):
for item in multiworld.get_items():
multiworld.state.collect(item)
tester.assertTrue(multiworld.find_item("Victory", 1).can_reach(multiworld.state))
def assert_can_win(tester: SVTestBase, multiworld: MultiWorld):
assert_victory_exists(tester, multiworld)
collect_all_then_assert_can_win(tester, multiworld)
def assert_same_number_items_locations(tester: SVTestBase, multiworld: MultiWorld):
non_event_locations = [location for location in multiworld.get_locations() if not location.event]
tester.assertEqual(len(multiworld.itempool), len(non_event_locations))

View File

@ -0,0 +1,41 @@
from typing import Dict
from BaseClasses import MultiWorld
from Options import SpecialRange
from .option_names import options_to_include
from worlds.stardew_valley.test.checks.world_checks import assert_can_win, assert_same_number_items_locations
from .. import setup_solo_multiworld, SVTestBase
def basic_checks(tester: SVTestBase, multiworld: MultiWorld):
assert_can_win(tester, multiworld)
assert_same_number_items_locations(tester, multiworld)
def get_option_choices(option) -> Dict[str, int]:
if issubclass(option, SpecialRange):
return option.special_range_names
elif option.options:
return option.options
return {}
class TestGenerateDynamicOptions(SVTestBase):
def test_given_option_pair_when_generate_then_basic_checks(self):
if self.skip_long_tests:
return
num_options = len(options_to_include)
for option1_index in range(0, num_options):
for option2_index in range(option1_index + 1, num_options):
option1 = options_to_include[option1_index]
option2 = options_to_include[option2_index]
option1_choices = get_option_choices(option1)
option2_choices = get_option_choices(option2)
for key1 in option1_choices:
for key2 in option2_choices:
with self.subTest(f"{option1.internal_name}: {key1}, {option2.internal_name}: {key2}"):
choices = {option1.internal_name: option1_choices[key1],
option2.internal_name: option2_choices[key2]}
multiworld = setup_solo_multiworld(choices)
basic_checks(self, multiworld)

View File

@ -0,0 +1,99 @@
from typing import Dict, List
import random
from BaseClasses import MultiWorld
from Options import SpecialRange, Range
from .option_names import options_to_include
from .. import setup_solo_multiworld, SVTestBase
from ..checks.goal_checks import assert_perfection_world_is_valid, assert_goal_world_is_valid
from ..checks.option_checks import assert_can_reach_island_if_should, assert_cropsanity_same_number_items_and_locations, \
assert_festivals_give_access_to_deluxe_scarecrow
from ..checks.world_checks import assert_same_number_items_locations, assert_victory_exists
from ... import options
def get_option_choices(option) -> Dict[str, int]:
if issubclass(option, SpecialRange):
return option.special_range_names
if issubclass(option, Range):
return {f"{val}": val for val in range(option.range_start, option.range_end + 1)}
elif option.options:
return option.options
return {}
def generate_random_multiworld(world_id: int):
world_options = generate_random_world_options(world_id)
multiworld = setup_solo_multiworld(world_options, seed=world_id)
return multiworld
def generate_random_world_options(world_id: int) -> Dict[str, int]:
num_options = len(options_to_include)
world_options = dict()
rng = random.Random(world_id)
for option_index in range(0, num_options):
option = options_to_include[option_index]
option_choices = get_option_choices(option)
if not option_choices:
continue
chosen_option_value = rng.choice(list(option_choices.values()))
world_options[option.internal_name] = chosen_option_value
return world_options
def get_number_log_steps(number_worlds: int) -> int:
if number_worlds <= 10:
return 2
if number_worlds <= 100:
return 5
if number_worlds <= 500:
return 10
if number_worlds <= 1000:
return 20
if number_worlds <= 5000:
return 25
if number_worlds <= 10000:
return 50
return 100
def generate_many_worlds(number_worlds: int, start_index: int) -> Dict[int, MultiWorld]:
num_steps = get_number_log_steps(number_worlds)
log_step = number_worlds / num_steps
multiworlds = dict()
print(f"Generating {number_worlds} Solo Multiworlds [Start Seed: {start_index}] for Stardew Valley...")
for world_number in range(0, number_worlds + 1):
world_id = world_number + start_index
multiworld = generate_random_multiworld(world_id)
multiworlds[world_id] = multiworld
if world_number > 0 and world_number % log_step == 0:
print(f"Generated {world_number}/{number_worlds} worlds [{(world_number * 100) // number_worlds}%]")
print(f"Finished generating {number_worlds} Solo Multiworlds for Stardew Valley")
return multiworlds
def check_every_multiworld_is_valid(tester: SVTestBase, multiworlds: Dict[int, MultiWorld]):
for multiworld_id in multiworlds:
multiworld = multiworlds[multiworld_id]
with tester.subTest(f"Checking validity of world {multiworld_id}"):
check_multiworld_is_valid(tester, multiworld_id, multiworld)
def check_multiworld_is_valid(tester: SVTestBase, multiworld_id: int, multiworld: MultiWorld):
assert_victory_exists(tester, multiworld)
assert_same_number_items_locations(tester, multiworld)
assert_goal_world_is_valid(tester, multiworld)
assert_can_reach_island_if_should(tester, multiworld)
assert_cropsanity_same_number_items_and_locations(tester, multiworld)
assert_festivals_give_access_to_deluxe_scarecrow(tester, multiworld)
class TestGenerateManyWorlds(SVTestBase):
def test_generate_many_worlds_then_check_results(self):
if self.skip_long_tests:
return
number_worlds = 1000
start_index = random.Random().randint(0, 9999999999)
multiworlds = generate_many_worlds(number_worlds, start_index)
check_every_multiworld_is_valid(self, multiworlds)

View File

@ -0,0 +1,7 @@
from worlds.stardew_valley.options import stardew_valley_option_classes
options_to_exclude = ["profit_margin", "starting_money", "multiple_day_sleep_enabled", "multiple_day_sleep_cost",
"experience_multiplier", "friendship_multiplier", "debris_multiplier",
"quick_start", "gifting", "gift_tax"]
options_to_include = [option_to_include for option_to_include in stardew_valley_option_classes
if option_to_include.internal_name not in options_to_exclude]