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:
parent
1f6db12797
commit
62657df3fb
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
|
@ -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}
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
class Entrance:
|
||||
to_stardew_valley = "To Stardew Valley"
|
|
@ -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}
|
||||
|
|
|
@ -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
|
||||
|
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
||||
|
|
|
@ -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})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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,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}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||

|
||||
|
||||
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.
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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))
|
|
@ -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)
|
|
@ -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
|
|
@ -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()))
|
|
@ -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_()
|
|
@ -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
|
|
@ -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",
|
||||
}
|
|
@ -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),
|
||||
}
|
|
@ -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}
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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",
|
||||
]
|
|
@ -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"]
|
|
@ -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"
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
class Buff:
|
||||
movement = "Movement Speed Bonus"
|
||||
luck = "Luck Bonus"
|
|
@ -0,0 +1,2 @@
|
|||
class ModSkillLevel:
|
||||
magic_level = "Magic Level"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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]
|
||||
|
||||
|
|
@ -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"
|
||||
|
||||
|
||||
|
||||
|
|
@ -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")
|
|
@ -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"
|
||||
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
class Flower:
|
||||
sunflower = "Sunflower"
|
||||
poppy = "Poppy"
|
|
@ -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"
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
@ -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"
|
|
@ -0,0 +1,4 @@
|
|||
class Generic:
|
||||
any = "Any"
|
||||
all = "All"
|
||||
bachelor = "Bachelor"
|
|
@ -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"
|
|
@ -0,0 +1,6 @@
|
|||
class Gift:
|
||||
bouquet = "Bouquet"
|
||||
wilted_bouquet = "Wilted Bouquet"
|
||||
pearl = "Pearl"
|
||||
golden_pumpkin = "Golden Pumpkin"
|
||||
mermaid_pendant = "Mermaid's Pendant"
|
|
@ -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"
|
|
@ -0,0 +1,6 @@
|
|||
class Ingredient:
|
||||
wheat_flour = "Wheat Flour"
|
||||
sugar = "Sugar"
|
||||
oil = "Oil"
|
||||
rice = "Rice"
|
||||
vinegar = "Vinegar"
|
|
@ -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"
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
class Material:
|
||||
coal = "Coal"
|
||||
fiber = "Fiber"
|
||||
hardwood = "Hardwood"
|
||||
sap = "Sap"
|
||||
stone = "Stone"
|
||||
wood = "Wood"
|
||||
clay = "Clay"
|
||||
cinder_shard = "Cinder Shard"
|
|
@ -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"
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
class Loot:
|
||||
slime = "Slime"
|
||||
bug_meat = "Bug Meat"
|
||||
bat_wing = "Bat Wing"
|
||||
solar_essence = "Solar Essence"
|
||||
void_essence = "Void Essence"
|
|
@ -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}
|
|
@ -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"
|
|
@ -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"
|
|
@ -0,0 +1,6 @@
|
|||
class Season:
|
||||
spring = "Spring"
|
||||
summer = "Summer"
|
||||
fall = "Fall"
|
||||
winter = "Winter"
|
||||
progressive = "Progressive Season"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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"
|
|
@ -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}"
|
|
@ -0,0 +1,2 @@
|
|||
class Channel:
|
||||
queen_of_sauce = "The Queen of Sauce"
|
|
@ -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"
|
|
@ -0,0 +1,5 @@
|
|||
class Wallet:
|
||||
magnifying_glass = "Magnifying Glass"
|
||||
rusty_key = "Rusty Key"
|
||||
skull_key = "Skull Key"
|
||||
dark_talisman = "Dark Talisman"
|
|
@ -0,0 +1,4 @@
|
|||
class Weapon:
|
||||
slingshot = "Slingshot"
|
||||
master_slingshot = "Master Slingshot"
|
||||
any_slingshot = "Any Slingshot"
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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_()
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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}")
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
||||
|
|
@ -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))
|
|
@ -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)
|
|
@ -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)
|
|
@ -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]
|
Loading…
Reference in New Issue