313 lines
17 KiB
Python
313 lines
17 KiB
Python
from dataclasses import dataclass, field
|
|
from enum import IntFlag
|
|
from random import Random
|
|
from typing import Iterable, Dict, Protocol, Optional, List, Tuple
|
|
|
|
from BaseClasses import Region, Entrance
|
|
from . import options
|
|
from .data.region_data import SVRegion
|
|
from .options import StardewOptions
|
|
|
|
|
|
class RegionFactory(Protocol):
|
|
def __call__(self, name: str, regions: Iterable[str]) -> Region:
|
|
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),
|
|
]
|
|
|
|
# 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),
|
|
]
|
|
|
|
|
|
def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewOptions) -> Tuple[
|
|
Iterable[Region], Dict[str, str]]:
|
|
regions: Dict[str: Region] = {region.name: region_factory(region.name, region.exits) for region in
|
|
stardew_valley_regions}
|
|
entrances: Dict[str: Entrance] = {entrance.name: entrance
|
|
for region in regions.values()
|
|
for entrance in region.exits}
|
|
|
|
connections, randomized_data = randomize_connections(random, world_options)
|
|
|
|
for connection in connections:
|
|
if connection.name not in entrances:
|
|
continue
|
|
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]]:
|
|
connections_to_randomize = []
|
|
if world_options[options.EntranceRandomization] == options.EntranceRandomization.option_pelican_town:
|
|
connections_to_randomize = [connection for connection in mandatory_connections if
|
|
RandomizationFlag.PELICAN_TOWN in connection.flag]
|
|
elif world_options[options.EntranceRandomization] == options.EntranceRandomization.option_non_progression:
|
|
connections_to_randomize = [connection for connection in mandatory_connections if
|
|
RandomizationFlag.NON_PROGRESSION in connection.flag]
|
|
random.shuffle(connections_to_randomize)
|
|
|
|
destination_pool = list(connections_to_randomize)
|
|
random.shuffle(destination_pool)
|
|
|
|
randomized_connections = []
|
|
randomized_data = {}
|
|
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
|
|
|
|
return mandatory_connections, randomized_data
|