292 lines
16 KiB
Python
292 lines
16 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 .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("Menu", ["To Stardew Valley"]),
|
|
RegionData("Stardew Valley", ["To Farmhouse"]),
|
|
RegionData("Farmhouse", ["Outside to Farm", "Downstairs to Cellar"]),
|
|
RegionData("Cellar"),
|
|
RegionData("Farm", ["Farm to Backwoods", "Farm to Bus Stop", "Farm to Forest", "Farm to Farmcave", "Enter Greenhouse",
|
|
"Use Desert Obelisk", "Use Island Obelisk"]),
|
|
RegionData("Backwoods", ["Backwoods to Mountain"]),
|
|
RegionData("Bus Stop", ["Bus Stop to Town", "Take Bus to Desert", "Bus Stop to Tunnel Entrance"]),
|
|
RegionData("Forest", ["Forest to Town", "Enter Secret Woods", "Forest to Wizard Tower", "Forest to Marnie's Ranch",
|
|
"Forest to Leah's Cottage", "Forest to Sewers"]),
|
|
RegionData("Farmcave"),
|
|
RegionData("Greenhouse"),
|
|
RegionData("Mountain", ["Mountain to Railroad", "Mountain to Tent", "Mountain to Carpenter Shop", "Mountain to The Mines",
|
|
"Enter Quarry", "Mountain to Adventurer's Guild", "Mountain to Town"]),
|
|
RegionData("Tunnel Entrance", ["Enter Tunnel"]),
|
|
RegionData("Tunnel"),
|
|
RegionData("Town", ["Town to Community Center", "Town to Beach", "Town to Hospital",
|
|
"Town to Pierre's General Store", "Town to Saloon", "Town to Alex's House", "Town to Trailer", "Town to Mayor's Manor",
|
|
"Town to Sam's House", "Town to Haley's House", "Town to Sewers", "Town to Clint's Blacksmith", "Town to Museum",
|
|
"Town to JojaMart"]),
|
|
RegionData("Beach", ["Beach to Willy's Fish Shop", "Enter Elliott's House", "Enter Tide Pools"]),
|
|
RegionData("Railroad", ["Enter Bathhouse Entrance", "Enter Witch Warp Cave"]), # "Enter Perfection Cutscene Area"
|
|
RegionData("Marnie's Ranch"),
|
|
RegionData("Leah's Cottage"),
|
|
RegionData("Sewers", ["Enter Mutant Bug Lair"]),
|
|
RegionData("Mutant Bug Lair"),
|
|
RegionData("Wizard Tower", ["Enter Wizard Basement"]),
|
|
RegionData("Wizard Basement"),
|
|
RegionData("Tent"),
|
|
RegionData("Carpenter Shop", ["Enter Sebastian's Room"]),
|
|
RegionData("Sebastian's Room"),
|
|
RegionData("Adventurer's Guild"),
|
|
RegionData("Community Center",
|
|
["Access Crafts Room", "Access Pantry", "Access Fish Tank", "Access Boiler Room", "Access Bulletin Board",
|
|
"Access Vault"]),
|
|
RegionData("Crafts Room"),
|
|
RegionData("Pantry"),
|
|
RegionData("Fish Tank"),
|
|
RegionData("Boiler Room"),
|
|
RegionData("Bulletin Board"),
|
|
RegionData("Vault"),
|
|
RegionData("Hospital", ["Enter Harvey's Room"]),
|
|
RegionData("Harvey's Room"),
|
|
RegionData("Pierre's General Store", ["Enter Sunroom"]),
|
|
RegionData("Sunroom"),
|
|
RegionData("Saloon", ["Play Journey of the Prairie King", "Play Junimo Kart"]),
|
|
RegionData("Alex's House"),
|
|
RegionData("Trailer"),
|
|
RegionData("Mayor's Manor"),
|
|
RegionData("Sam's House"),
|
|
RegionData("Haley's House"),
|
|
RegionData("Clint's Blacksmith"),
|
|
RegionData("Museum"),
|
|
RegionData("JojaMart"),
|
|
RegionData("Willy's Fish Shop"),
|
|
RegionData("Elliott's House"),
|
|
RegionData("Tide Pools"),
|
|
RegionData("Bathhouse Entrance", ["Enter Locker Room"]),
|
|
RegionData("Locker Room", ["Enter Public Bath"]),
|
|
RegionData("Public Bath"),
|
|
RegionData("Witch Warp Cave", ["Enter Witch's Swamp"]),
|
|
RegionData("Witch's Swamp"),
|
|
RegionData("Quarry", ["Enter Quarry Mine Entrance"]),
|
|
RegionData("Quarry Mine Entrance", ["Enter Quarry Mine"]),
|
|
RegionData("Quarry Mine"),
|
|
RegionData("Secret Woods"),
|
|
RegionData("The Desert", ["Enter Skull Cavern Entrance"]),
|
|
RegionData("Skull Cavern Entrance", ["Enter Skull Cavern"]),
|
|
RegionData("Skull Cavern"),
|
|
RegionData("Ginger Island"),
|
|
RegionData("JotPK World 1", ["Reach JotPK World 2"]),
|
|
RegionData("JotPK World 2", ["Reach JotPK World 3"]),
|
|
RegionData("JotPK World 3"),
|
|
RegionData("Junimo Kart 1", ["Reach Junimo Kart 2"]),
|
|
RegionData("Junimo Kart 2", ["Reach Junimo Kart 3"]),
|
|
RegionData("Junimo Kart 3"),
|
|
RegionData("The Mines", ["Dig to The Mines - Floor 5", "Dig to The Mines - Floor 10", "Dig to The Mines - Floor 15",
|
|
"Dig to The Mines - Floor 20", "Dig to The Mines - Floor 25", "Dig to The Mines - Floor 30",
|
|
"Dig to The Mines - Floor 35", "Dig to The Mines - Floor 40", "Dig to The Mines - Floor 45",
|
|
"Dig to The Mines - Floor 50", "Dig to The Mines - Floor 55", "Dig to The Mines - Floor 60",
|
|
"Dig to The Mines - Floor 65", "Dig to The Mines - Floor 70", "Dig to The Mines - Floor 75",
|
|
"Dig to The Mines - Floor 80", "Dig to The Mines - Floor 85", "Dig to The Mines - Floor 90",
|
|
"Dig to The Mines - Floor 95", "Dig to The Mines - Floor 100", "Dig to The Mines - Floor 105",
|
|
"Dig to The Mines - Floor 110", "Dig to The Mines - Floor 115", "Dig to The Mines - Floor 120"]),
|
|
RegionData("The Mines - Floor 5"),
|
|
RegionData("The Mines - Floor 10"),
|
|
RegionData("The Mines - Floor 15"),
|
|
RegionData("The Mines - Floor 20"),
|
|
RegionData("The Mines - Floor 25"),
|
|
RegionData("The Mines - Floor 30"),
|
|
RegionData("The Mines - Floor 35"),
|
|
RegionData("The Mines - Floor 40"),
|
|
RegionData("The Mines - Floor 45"),
|
|
RegionData("The Mines - Floor 50"),
|
|
RegionData("The Mines - Floor 55"),
|
|
RegionData("The Mines - Floor 60"),
|
|
RegionData("The Mines - Floor 65"),
|
|
RegionData("The Mines - Floor 70"),
|
|
RegionData("The Mines - Floor 75"),
|
|
RegionData("The Mines - Floor 80"),
|
|
RegionData("The Mines - Floor 85"),
|
|
RegionData("The Mines - Floor 90"),
|
|
RegionData("The Mines - Floor 95"),
|
|
RegionData("The Mines - Floor 100"),
|
|
RegionData("The Mines - Floor 105"),
|
|
RegionData("The Mines - Floor 110"),
|
|
RegionData("The Mines - Floor 115"),
|
|
RegionData("The Mines - Floor 120"),
|
|
]
|
|
|
|
# Exists and where they lead
|
|
mandatory_connections = [
|
|
ConnectionData("To Stardew Valley", "Stardew Valley"),
|
|
ConnectionData("To Farmhouse", "Farmhouse"),
|
|
ConnectionData("Outside to Farm", "Farm"),
|
|
ConnectionData("Downstairs to Cellar", "Cellar"),
|
|
ConnectionData("Farm to Backwoods", "Backwoods"),
|
|
ConnectionData("Farm to Bus Stop", "Bus Stop"),
|
|
ConnectionData("Farm to Forest", "Forest"),
|
|
ConnectionData("Farm to Farmcave", "Farmcave", flag=RandomizationFlag.NON_PROGRESSION),
|
|
ConnectionData("Enter Greenhouse", "Greenhouse"),
|
|
ConnectionData("Use Desert Obelisk", "The Desert"),
|
|
ConnectionData("Use Island Obelisk", "Ginger Island"),
|
|
ConnectionData("Backwoods to Mountain", "Mountain"),
|
|
ConnectionData("Bus Stop to Town", "Town"),
|
|
ConnectionData("Bus Stop to Tunnel Entrance", "Tunnel Entrance"),
|
|
ConnectionData("Take Bus to Desert", "The Desert"),
|
|
ConnectionData("Enter Tunnel", "Tunnel"),
|
|
ConnectionData("Forest to Town", "Town"),
|
|
ConnectionData("Forest to Wizard Tower", "Wizard Tower", flag=RandomizationFlag.NON_PROGRESSION),
|
|
ConnectionData("Enter Wizard Basement", "Wizard Basement"),
|
|
ConnectionData("Forest to Marnie's Ranch", "Marnie's Ranch", flag=RandomizationFlag.NON_PROGRESSION),
|
|
ConnectionData("Forest to Leah's Cottage", "Leah's Cottage"),
|
|
ConnectionData("Enter Secret Woods", "Secret Woods"),
|
|
ConnectionData("Forest to Sewers", "Sewers"),
|
|
ConnectionData("Town to Sewers", "Sewers"),
|
|
ConnectionData("Enter Mutant Bug Lair", "Mutant Bug Lair"),
|
|
ConnectionData("Mountain to Railroad", "Railroad"),
|
|
ConnectionData("Mountain to Tent", "Tent", flag=RandomizationFlag.NON_PROGRESSION),
|
|
ConnectionData("Mountain to Carpenter Shop", "Carpenter Shop", flag=RandomizationFlag.NON_PROGRESSION),
|
|
ConnectionData("Enter Sebastian's Room", "Sebastian's Room"),
|
|
ConnectionData("Mountain to Adventurer's Guild", "Adventurer's Guild"),
|
|
ConnectionData("Enter Quarry", "Quarry"),
|
|
ConnectionData("Enter Quarry Mine Entrance", "Quarry Mine Entrance"),
|
|
ConnectionData("Enter Quarry Mine", "Quarry Mine"),
|
|
ConnectionData("Mountain to Town", "Town"),
|
|
ConnectionData("Town to Community Center", "Community Center", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Access Crafts Room", "Crafts Room"),
|
|
ConnectionData("Access Pantry", "Pantry"),
|
|
ConnectionData("Access Fish Tank", "Fish Tank"),
|
|
ConnectionData("Access Boiler Room", "Boiler Room"),
|
|
ConnectionData("Access Bulletin Board", "Bulletin Board"),
|
|
ConnectionData("Access Vault", "Vault"),
|
|
ConnectionData("Town to Hospital", "Hospital", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Enter Harvey's Room", "Harvey's Room"),
|
|
ConnectionData("Town to Pierre's General Store", "Pierre's General Store", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Enter Sunroom", "Sunroom"),
|
|
ConnectionData("Town to Clint's Blacksmith", "Clint's Blacksmith", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to Saloon", "Saloon", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Play Journey of the Prairie King", "JotPK World 1"),
|
|
ConnectionData("Reach JotPK World 2", "JotPK World 2"),
|
|
ConnectionData("Reach JotPK World 3", "JotPK World 3"),
|
|
ConnectionData("Play Junimo Kart", "Junimo Kart 1"),
|
|
ConnectionData("Reach Junimo Kart 2", "Junimo Kart 2"),
|
|
ConnectionData("Reach Junimo Kart 3", "Junimo Kart 3"),
|
|
ConnectionData("Town to Sam's House", "Sam's House", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to Haley's House", "Haley's House", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to Mayor's Manor", "Mayor's Manor", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to Alex's House", "Alex's House", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to Trailer", "Trailer", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to Museum", "Museum", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to JojaMart", "JojaMart", flag=RandomizationFlag.PELICAN_TOWN),
|
|
ConnectionData("Town to Beach", "Beach"),
|
|
ConnectionData("Enter Elliott's House", "Elliott's House"),
|
|
ConnectionData("Beach to Willy's Fish Shop", "Willy's Fish Shop", flag=RandomizationFlag.NON_PROGRESSION),
|
|
ConnectionData("Enter Tide Pools", "Tide Pools"),
|
|
ConnectionData("Mountain to The Mines", "The Mines", flag=RandomizationFlag.NON_PROGRESSION),
|
|
ConnectionData("Dig to The Mines - Floor 5", "The Mines - Floor 5"),
|
|
ConnectionData("Dig to The Mines - Floor 10", "The Mines - Floor 10"),
|
|
ConnectionData("Dig to The Mines - Floor 15", "The Mines - Floor 15"),
|
|
ConnectionData("Dig to The Mines - Floor 20", "The Mines - Floor 20"),
|
|
ConnectionData("Dig to The Mines - Floor 25", "The Mines - Floor 25"),
|
|
ConnectionData("Dig to The Mines - Floor 30", "The Mines - Floor 30"),
|
|
ConnectionData("Dig to The Mines - Floor 35", "The Mines - Floor 35"),
|
|
ConnectionData("Dig to The Mines - Floor 40", "The Mines - Floor 40"),
|
|
ConnectionData("Dig to The Mines - Floor 45", "The Mines - Floor 45"),
|
|
ConnectionData("Dig to The Mines - Floor 50", "The Mines - Floor 50"),
|
|
ConnectionData("Dig to The Mines - Floor 55", "The Mines - Floor 55"),
|
|
ConnectionData("Dig to The Mines - Floor 60", "The Mines - Floor 60"),
|
|
ConnectionData("Dig to The Mines - Floor 65", "The Mines - Floor 65"),
|
|
ConnectionData("Dig to The Mines - Floor 70", "The Mines - Floor 70"),
|
|
ConnectionData("Dig to The Mines - Floor 75", "The Mines - Floor 75"),
|
|
ConnectionData("Dig to The Mines - Floor 80", "The Mines - Floor 80"),
|
|
ConnectionData("Dig to The Mines - Floor 85", "The Mines - Floor 85"),
|
|
ConnectionData("Dig to The Mines - Floor 90", "The Mines - Floor 90"),
|
|
ConnectionData("Dig to The Mines - Floor 95", "The Mines - Floor 95"),
|
|
ConnectionData("Dig to The Mines - Floor 100", "The Mines - Floor 100"),
|
|
ConnectionData("Dig to The Mines - Floor 105", "The Mines - Floor 105"),
|
|
ConnectionData("Dig to The Mines - Floor 110", "The Mines - Floor 110"),
|
|
ConnectionData("Dig to The Mines - Floor 115", "The Mines - Floor 115"),
|
|
ConnectionData("Dig to The Mines - Floor 120", "The Mines - Floor 120"),
|
|
ConnectionData("Enter Skull Cavern Entrance", "Skull Cavern Entrance"),
|
|
ConnectionData("Enter Skull Cavern", "Skull Cavern"),
|
|
ConnectionData("Enter Witch Warp Cave", "Witch Warp Cave"),
|
|
ConnectionData("Enter Witch's Swamp", "Witch's Swamp"),
|
|
ConnectionData("Enter Bathhouse Entrance", "Bathhouse Entrance"),
|
|
ConnectionData("Enter Locker Room", "Locker Room"),
|
|
ConnectionData("Enter Public Bath", "Public Bath"),
|
|
]
|
|
|
|
|
|
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
|