Archipelago/worlds/stardew_valley/regions.py

611 lines
38 KiB
Python

from random import Random
from typing import Iterable, Dict, Protocol, List, Tuple, Set
from BaseClasses import Region, Entrance
from .options import EntranceRandomization, ExcludeGingerIsland, Museumsanity
from .strings.entrance_names import Entrance
from .strings.region_names import Region
from .region_classes import RegionData, ConnectionData, RandomizationFlag
from .mods.mod_regions import ModDataList
class RegionFactory(Protocol):
def __call__(self, name: str, regions: Iterable[str]) -> Region:
raise NotImplementedError
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
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.BUILDINGS | 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) -> List[RegionData]:
final_regions = []
final_regions.extend(vanilla_regions)
if world_options.mods is None:
return final_regions
for mod in world_options.mods.value:
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) -> List[ConnectionData]:
final_connections = []
final_connections.extend(vanilla_connections)
if world_options.mods is None:
return final_connections
for mod in world_options.mods.value:
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) -> 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
final_regions}
entrances: Dict[str: Entrance] = {entrance.name: entrance
for region in regions.values()
for entrance in region.exits}
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 in entrances:
entrances[connection.name].connect(regions[connection.destination])
return regions.values(), randomized_data
def randomize_connections(random: Random, world_options, 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.entrance_randomization == EntranceRandomization.option_pelican_town:
connections_to_randomize = [connection for connection in final_connections if
RandomizationFlag.PELICAN_TOWN in connection.flag]
elif world_options.entrance_randomization == EntranceRandomization.option_non_progression:
connections_to_randomize = [connection for connection in final_connections if
RandomizationFlag.NON_PROGRESSION in connection.flag]
elif world_options.entrance_randomization == EntranceRandomization.option_buildings:
connections_to_randomize = [connection for connection in final_connections if
RandomizationFlag.BUILDINGS in connection.flag]
elif world_options.entrance_randomization == 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 = 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.exclude_ginger_island == ExcludeGingerIsland.option_true
exclude_sewers = world_options.museumsanity == 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.exclude_ginger_island == 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[connection] = destination
return randomized_connections
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