Stardew Valley: Replace current naive entrance rando with GER (#4624)

This commit is contained in:
Jérémie Bolduc
2025-05-24 01:15:41 -04:00
committed by GitHub
parent e82d50a3c5
commit c64791e3a8
21 changed files with 1351 additions and 1224 deletions

View File

@@ -1,9 +1,10 @@
import logging import logging
import typing import typing
from random import Random from random import Random
from typing import Dict, Any, Iterable, Optional, List, TextIO from typing import Dict, Any, Optional, List, TextIO
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState import entrance_rando
from BaseClasses import Region, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState
from Options import PerGameCommonOptions from Options import PerGameCommonOptions
from worlds.AutoWorld import World, WebWorld from worlds.AutoWorld import World, WebWorld
from .bundles.bundle_room import BundleRoom from .bundles.bundle_room import BundleRoom
@@ -21,7 +22,7 @@ from .options.forced_options import force_change_options_if_incompatible
from .options.option_groups import sv_option_groups from .options.option_groups import sv_option_groups
from .options.presets import sv_options_presets from .options.presets import sv_options_presets
from .options.worlds_group import apply_most_restrictive_options from .options.worlds_group import apply_most_restrictive_options
from .regions import create_regions from .regions import create_regions, prepare_mod_data
from .rules import set_rules from .rules import set_rules
from .stardew_rule import True_, StardewRule, HasProgressionPercent from .stardew_rule import True_, StardewRule, HasProgressionPercent
from .strings.ap_names.event_names import Event from .strings.ap_names.event_names import Event
@@ -124,18 +125,13 @@ class StardewValleyWorld(World):
self.content = create_content(self.options) self.content = create_content(self.options)
def create_regions(self): def create_regions(self):
def create_region(name: str, exits: Iterable[str]) -> Region: def create_region(name: str) -> Region:
region = Region(name, self.player, self.multiworld) return Region(name, self.player, self.multiworld)
region.exits = [Entrance(self.player, exit_name, region) for exit_name in exits]
return region
world_regions, world_entrances, self.randomized_entrances = create_regions(create_region, self.random, self.options, self.content) world_regions = create_regions(create_region, self.options, self.content)
self.logic = StardewLogic(self.player, self.options, self.content, world_regions.keys()) self.logic = StardewLogic(self.player, self.options, self.content, world_regions.keys())
self.modified_bundles = get_all_bundles(self.random, self.modified_bundles = get_all_bundles(self.random, self.logic, self.content, self.options)
self.logic,
self.content,
self.options)
def add_location(name: str, code: Optional[int], region: str): def add_location(name: str, code: Optional[int], region: str):
region: Region = world_regions[region] region: Region = world_regions[region]
@@ -308,6 +304,11 @@ class StardewValleyWorld(World):
def set_rules(self): def set_rules(self):
set_rules(self) set_rules(self)
def connect_entrances(self) -> None:
no_target_groups = {0: [0]}
placement = entrance_rando.randomize_entrances(self, coupled=True, target_group_lookup=no_target_groups)
self.randomized_entrances = prepare_mod_data(placement)
def generate_basic(self): def generate_basic(self):
pass pass

View File

@@ -24,6 +24,9 @@ from ...strings.skill_names import Skill
from ...strings.tool_names import Tool, ToolMaterial from ...strings.tool_names import Tool, ToolMaterial
from ...strings.villager_names import ModNPC from ...strings.villager_names import ModNPC
# Used to adapt content not yet moved to content packs to easily detect when SVE and Ginger Island are both enabled.
SVE_GINGER_ISLAND_PACK = ModNames.sve + "+" + ginger_island_content_pack.name
class SVEContentPack(ContentPack): class SVEContentPack(ContentPack):
@@ -67,6 +70,10 @@ class SVEContentPack(ContentPack):
content.game_items.pop(SVESeed.slime) content.game_items.pop(SVESeed.slime)
content.game_items.pop(SVEFruit.slime_berry) content.game_items.pop(SVEFruit.slime_berry)
def finalize_hook(self, content: StardewContent):
if ginger_island_content_pack.name in content.registered_packs:
content.registered_packs.add(SVE_GINGER_ISLAND_PACK)
register_mod_content_pack(SVEContentPack( register_mod_content_pack(SVEContentPack(
ModNames.sve, ModNames.sve,
@@ -80,8 +87,9 @@ register_mod_content_pack(SVEContentPack(
ModEdible.lightning_elixir: (ShopSource(money_price=12000, shop_region=SVERegion.galmoran_outpost),), ModEdible.lightning_elixir: (ShopSource(money_price=12000, shop_region=SVERegion.galmoran_outpost),),
ModEdible.barbarian_elixir: (ShopSource(money_price=22000, shop_region=SVERegion.galmoran_outpost),), ModEdible.barbarian_elixir: (ShopSource(money_price=22000, shop_region=SVERegion.galmoran_outpost),),
ModEdible.gravity_elixir: (ShopSource(money_price=4000, shop_region=SVERegion.galmoran_outpost),), ModEdible.gravity_elixir: (ShopSource(money_price=4000, shop_region=SVERegion.galmoran_outpost),),
SVEMeal.grampleton_orange_chicken: ( SVEMeal.grampleton_orange_chicken: (ShopSource(money_price=650,
ShopSource(money_price=650, shop_region=Region.saloon, other_requirements=(RelationshipRequirement(ModNPC.sophia, 6),)),), shop_region=Region.saloon,
other_requirements=(RelationshipRequirement(ModNPC.sophia, 6),)),),
ModEdible.hero_elixir: (ShopSource(money_price=8000, shop_region=SVERegion.isaac_shop),), ModEdible.hero_elixir: (ShopSource(money_price=8000, shop_region=SVERegion.isaac_shop),),
ModEdible.aegis_elixir: (ShopSource(money_price=28000, shop_region=SVERegion.galmoran_outpost),), ModEdible.aegis_elixir: (ShopSource(money_price=28000, shop_region=SVERegion.galmoran_outpost),),
SVEBeverage.sports_drink: (ShopSource(money_price=750, shop_region=Region.hospital),), SVEBeverage.sports_drink: (ShopSource(money_price=750, shop_region=Region.hospital),),
@@ -118,8 +126,8 @@ register_mod_content_pack(SVEContentPack(
ModLoot.green_mushroom: (ForagingSource(regions=(SVERegion.highlands_pond,), seasons=Season.not_winter),), ModLoot.green_mushroom: (ForagingSource(regions=(SVERegion.highlands_pond,), seasons=Season.not_winter),),
ModLoot.ornate_treasure_chest: (ForagingSource(regions=(SVERegion.highlands_outside,), ModLoot.ornate_treasure_chest: (ForagingSource(regions=(SVERegion.highlands_outside,),
other_requirements=( other_requirements=(CombatRequirement(Performance.galaxy),
CombatRequirement(Performance.galaxy), ToolRequirement(Tool.axe, ToolMaterial.iron))),), ToolRequirement(Tool.axe, ToolMaterial.iron))),),
ModLoot.swirl_stone: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.galaxy),)),), ModLoot.swirl_stone: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.galaxy),)),),
ModLoot.void_soul: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.good),)),), ModLoot.void_soul: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.good),)),),
SVEForage.winter_star_rose: (ForagingSource(regions=(SVERegion.summit,), seasons=(Season.winter,)),), SVEForage.winter_star_rose: (ForagingSource(regions=(SVERegion.summit,), seasons=(Season.winter,)),),
@@ -139,8 +147,9 @@ register_mod_content_pack(SVEContentPack(
SVEForage.thistle: (ForagingSource(regions=(SVERegion.summit,)),), SVEForage.thistle: (ForagingSource(regions=(SVERegion.summit,)),),
ModLoot.void_pebble: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.great),)),), ModLoot.void_pebble: (ForagingSource(regions=(SVERegion.crimson_badlands,), other_requirements=(CombatRequirement(Performance.great),)),),
ModLoot.void_shard: (ForagingSource(regions=(SVERegion.crimson_badlands,), ModLoot.void_shard: (ForagingSource(regions=(SVERegion.crimson_badlands,),
other_requirements=( other_requirements=(CombatRequirement(Performance.galaxy),
CombatRequirement(Performance.galaxy), SkillRequirement(Skill.combat, 10), YearRequirement(3),)),), SkillRequirement(Skill.combat, 10),
YearRequirement(3),)),),
SVEWaterItem.dulse_seaweed: (ForagingSource(regions=(Region.beach,), other_requirements=(FishingRequirement(Region.beach),)),), SVEWaterItem.dulse_seaweed: (ForagingSource(regions=(Region.beach,), other_requirements=(FishingRequirement(Region.beach),)),),
# Fable Reef # Fable Reef

View File

@@ -1,8 +1,7 @@
from ..mod_regions import SVERegion
from ...logic.base_logic import BaseLogicMixin, BaseLogic from ...logic.base_logic import BaseLogicMixin, BaseLogic
from ...strings.ap_names.mods.mod_items import SVELocation, SVERunes, SVEQuestItem from ...strings.ap_names.mods.mod_items import SVELocation, SVERunes, SVEQuestItem
from ...strings.quest_names import Quest, ModQuest from ...strings.quest_names import Quest, ModQuest
from ...strings.region_names import Region from ...strings.region_names import Region, SVERegion
from ...strings.tool_names import Tool, ToolMaterial from ...strings.tool_names import Tool, ToolMaterial
from ...strings.wallet_item_names import Wallet from ...strings.wallet_item_names import Wallet

View File

@@ -1,15 +1,14 @@
from typing import Dict, List
from .mod_data import ModNames from .mod_data import ModNames
from ..region_classes import RegionData, ConnectionData, ModificationFlag, RandomizationFlag, ModRegionData from ..content.mods.sve import SVE_GINGER_ISLAND_PACK
from ..regions.model import RegionData, ConnectionData, MergeFlag, RandomizationFlag, ModRegionsData
from ..strings.entrance_names import Entrance, DeepWoodsEntrance, EugeneEntrance, LaceyEntrance, BoardingHouseEntrance, \ from ..strings.entrance_names import Entrance, DeepWoodsEntrance, EugeneEntrance, LaceyEntrance, BoardingHouseEntrance, \
JasperEntrance, AlecEntrance, YobaEntrance, JunaEntrance, MagicEntrance, AyeishaEntrance, RileyEntrance, SVEEntrance, AlectoEntrance JasperEntrance, AlecEntrance, YobaEntrance, JunaEntrance, MagicEntrance, AyeishaEntrance, RileyEntrance, SVEEntrance, AlectoEntrance
from ..strings.region_names import Region, DeepWoodsRegion, EugeneRegion, JasperRegion, BoardingHouseRegion, \ from ..strings.region_names import Region, DeepWoodsRegion, EugeneRegion, JasperRegion, BoardingHouseRegion, \
AlecRegion, YobaRegion, JunaRegion, MagicRegion, AyeishaRegion, RileyRegion, SVERegion, AlectoRegion, LaceyRegion AlecRegion, YobaRegion, JunaRegion, MagicRegion, AyeishaRegion, RileyRegion, SVERegion, AlectoRegion, LaceyRegion
deep_woods_regions = [ deep_woods_regions = [
RegionData(Region.farm, [DeepWoodsEntrance.use_woods_obelisk]), RegionData(Region.farm, (DeepWoodsEntrance.use_woods_obelisk,)),
RegionData(DeepWoodsRegion.woods_obelisk_menu, [DeepWoodsEntrance.deep_woods_depth_1, RegionData(DeepWoodsRegion.woods_obelisk_menu, (DeepWoodsEntrance.deep_woods_depth_1,
DeepWoodsEntrance.deep_woods_depth_10, DeepWoodsEntrance.deep_woods_depth_10,
DeepWoodsEntrance.deep_woods_depth_20, DeepWoodsEntrance.deep_woods_depth_20,
DeepWoodsEntrance.deep_woods_depth_30, DeepWoodsEntrance.deep_woods_depth_30,
@@ -19,9 +18,9 @@ deep_woods_regions = [
DeepWoodsEntrance.deep_woods_depth_70, DeepWoodsEntrance.deep_woods_depth_70,
DeepWoodsEntrance.deep_woods_depth_80, DeepWoodsEntrance.deep_woods_depth_80,
DeepWoodsEntrance.deep_woods_depth_90, DeepWoodsEntrance.deep_woods_depth_90,
DeepWoodsEntrance.deep_woods_depth_100]), DeepWoodsEntrance.deep_woods_depth_100)),
RegionData(Region.secret_woods, [DeepWoodsEntrance.secret_woods_to_deep_woods]), RegionData(Region.secret_woods, (DeepWoodsEntrance.secret_woods_to_deep_woods,)),
RegionData(DeepWoodsRegion.main_lichtung, [DeepWoodsEntrance.deep_woods_house]), RegionData(DeepWoodsRegion.main_lichtung, (DeepWoodsEntrance.deep_woods_house,)),
RegionData(DeepWoodsRegion.abandoned_home), RegionData(DeepWoodsRegion.abandoned_home),
RegionData(DeepWoodsRegion.floor_10), RegionData(DeepWoodsRegion.floor_10),
RegionData(DeepWoodsRegion.floor_20), RegionData(DeepWoodsRegion.floor_20),
@@ -32,14 +31,13 @@ deep_woods_regions = [
RegionData(DeepWoodsRegion.floor_70), RegionData(DeepWoodsRegion.floor_70),
RegionData(DeepWoodsRegion.floor_80), RegionData(DeepWoodsRegion.floor_80),
RegionData(DeepWoodsRegion.floor_90), RegionData(DeepWoodsRegion.floor_90),
RegionData(DeepWoodsRegion.floor_100) RegionData(DeepWoodsRegion.floor_100),
] ]
deep_woods_entrances = [ deep_woods_entrances = [
ConnectionData(DeepWoodsEntrance.use_woods_obelisk, DeepWoodsRegion.woods_obelisk_menu), ConnectionData(DeepWoodsEntrance.use_woods_obelisk, DeepWoodsRegion.woods_obelisk_menu),
ConnectionData(DeepWoodsEntrance.secret_woods_to_deep_woods, DeepWoodsRegion.main_lichtung), ConnectionData(DeepWoodsEntrance.secret_woods_to_deep_woods, DeepWoodsRegion.main_lichtung),
ConnectionData(DeepWoodsEntrance.deep_woods_house, DeepWoodsRegion.abandoned_home, ConnectionData(DeepWoodsEntrance.deep_woods_house, DeepWoodsRegion.abandoned_home, flag=RandomizationFlag.BUILDINGS),
flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_1, DeepWoodsRegion.main_lichtung), ConnectionData(DeepWoodsEntrance.deep_woods_depth_1, DeepWoodsRegion.main_lichtung),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_10, DeepWoodsRegion.floor_10), ConnectionData(DeepWoodsEntrance.deep_woods_depth_10, DeepWoodsRegion.floor_10),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_20, DeepWoodsRegion.floor_20), ConnectionData(DeepWoodsEntrance.deep_woods_depth_20, DeepWoodsRegion.floor_20),
@@ -50,165 +48,166 @@ deep_woods_entrances = [
ConnectionData(DeepWoodsEntrance.deep_woods_depth_70, DeepWoodsRegion.floor_70), ConnectionData(DeepWoodsEntrance.deep_woods_depth_70, DeepWoodsRegion.floor_70),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_80, DeepWoodsRegion.floor_80), ConnectionData(DeepWoodsEntrance.deep_woods_depth_80, DeepWoodsRegion.floor_80),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_90, DeepWoodsRegion.floor_90), ConnectionData(DeepWoodsEntrance.deep_woods_depth_90, DeepWoodsRegion.floor_90),
ConnectionData(DeepWoodsEntrance.deep_woods_depth_100, DeepWoodsRegion.floor_100) ConnectionData(DeepWoodsEntrance.deep_woods_depth_100, DeepWoodsRegion.floor_100),
] ]
eugene_regions = [ eugene_regions = [
RegionData(Region.forest, [EugeneEntrance.forest_to_garden]), RegionData(Region.forest, (EugeneEntrance.forest_to_garden,)),
RegionData(EugeneRegion.eugene_garden, [EugeneEntrance.garden_to_bedroom]), RegionData(EugeneRegion.eugene_garden, (EugeneEntrance.garden_to_bedroom,)),
RegionData(EugeneRegion.eugene_bedroom) RegionData(EugeneRegion.eugene_bedroom),
] ]
eugene_entrances = [ eugene_entrances = [
ConnectionData(EugeneEntrance.forest_to_garden, EugeneRegion.eugene_garden, ConnectionData(EugeneEntrance.forest_to_garden, EugeneRegion.eugene_garden,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA), flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(EugeneEntrance.garden_to_bedroom, EugeneRegion.eugene_bedroom, flag=RandomizationFlag.BUILDINGS) ConnectionData(EugeneEntrance.garden_to_bedroom, EugeneRegion.eugene_bedroom, flag=RandomizationFlag.BUILDINGS),
] ]
magic_regions = [ magic_regions = [
RegionData(Region.pierre_store, [MagicEntrance.store_to_altar]), RegionData(Region.pierre_store, (MagicEntrance.store_to_altar,)),
RegionData(MagicRegion.altar) RegionData(MagicRegion.altar),
] ]
magic_entrances = [ magic_entrances = [
ConnectionData(MagicEntrance.store_to_altar, MagicRegion.altar, flag=RandomizationFlag.NOT_RANDOMIZED) ConnectionData(MagicEntrance.store_to_altar, MagicRegion.altar, flag=RandomizationFlag.NOT_RANDOMIZED),
] ]
jasper_regions = [ jasper_regions = [
RegionData(Region.museum, [JasperEntrance.museum_to_bedroom]), RegionData(Region.museum, (JasperEntrance.museum_to_bedroom,)),
RegionData(JasperRegion.jasper_bedroom) RegionData(JasperRegion.jasper_bedroom),
] ]
jasper_entrances = [ jasper_entrances = [
ConnectionData(JasperEntrance.museum_to_bedroom, JasperRegion.jasper_bedroom, flag=RandomizationFlag.BUILDINGS) ConnectionData(JasperEntrance.museum_to_bedroom, JasperRegion.jasper_bedroom, flag=RandomizationFlag.BUILDINGS),
] ]
alec_regions = [ alec_regions = [
RegionData(Region.forest, [AlecEntrance.forest_to_petshop]), RegionData(Region.forest, (AlecEntrance.forest_to_petshop,)),
RegionData(AlecRegion.pet_store, [AlecEntrance.petshop_to_bedroom]), RegionData(AlecRegion.pet_store, (AlecEntrance.petshop_to_bedroom,)),
RegionData(AlecRegion.alec_bedroom) RegionData(AlecRegion.alec_bedroom),
] ]
alec_entrances = [ alec_entrances = [
ConnectionData(AlecEntrance.forest_to_petshop, AlecRegion.pet_store, ConnectionData(AlecEntrance.forest_to_petshop, AlecRegion.pet_store,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA), flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(AlecEntrance.petshop_to_bedroom, AlecRegion.alec_bedroom, flag=RandomizationFlag.BUILDINGS) ConnectionData(AlecEntrance.petshop_to_bedroom, AlecRegion.alec_bedroom, flag=RandomizationFlag.BUILDINGS),
] ]
yoba_regions = [ yoba_regions = [
RegionData(Region.secret_woods, [YobaEntrance.secret_woods_to_clearing]), RegionData(Region.secret_woods, (YobaEntrance.secret_woods_to_clearing,)),
RegionData(YobaRegion.yoba_clearing) RegionData(YobaRegion.yoba_clearing),
] ]
yoba_entrances = [ yoba_entrances = [
ConnectionData(YobaEntrance.secret_woods_to_clearing, YobaRegion.yoba_clearing, flag=RandomizationFlag.BUILDINGS) ConnectionData(YobaEntrance.secret_woods_to_clearing, YobaRegion.yoba_clearing, flag=RandomizationFlag.BUILDINGS),
] ]
juna_regions = [ juna_regions = [
RegionData(Region.forest, [JunaEntrance.forest_to_juna_cave]), RegionData(Region.forest, (JunaEntrance.forest_to_juna_cave,)),
RegionData(JunaRegion.juna_cave) RegionData(JunaRegion.juna_cave),
] ]
juna_entrances = [ juna_entrances = [
ConnectionData(JunaEntrance.forest_to_juna_cave, JunaRegion.juna_cave, ConnectionData(JunaEntrance.forest_to_juna_cave, JunaRegion.juna_cave,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA) flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
] ]
ayeisha_regions = [ ayeisha_regions = [
RegionData(Region.bus_stop, [AyeishaEntrance.bus_stop_to_mail_van]), RegionData(Region.bus_stop, (AyeishaEntrance.bus_stop_to_mail_van,)),
RegionData(AyeishaRegion.mail_van) RegionData(AyeishaRegion.mail_van),
] ]
ayeisha_entrances = [ ayeisha_entrances = [
ConnectionData(AyeishaEntrance.bus_stop_to_mail_van, AyeishaRegion.mail_van, ConnectionData(AyeishaEntrance.bus_stop_to_mail_van, AyeishaRegion.mail_van,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA) flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
] ]
riley_regions = [ riley_regions = [
RegionData(Region.town, [RileyEntrance.town_to_riley]), RegionData(Region.town, (RileyEntrance.town_to_riley,)),
RegionData(RileyRegion.riley_house) RegionData(RileyRegion.riley_house),
] ]
riley_entrances = [ riley_entrances = [
ConnectionData(RileyEntrance.town_to_riley, RileyRegion.riley_house, ConnectionData(RileyEntrance.town_to_riley, RileyRegion.riley_house,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA) flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
] ]
stardew_valley_expanded_regions = [ sve_main_land_regions = [
RegionData(Region.backwoods, [SVEEntrance.backwoods_to_grove]), RegionData(Region.backwoods, (SVEEntrance.backwoods_to_grove,)),
RegionData(SVERegion.enchanted_grove, [SVEEntrance.grove_to_outpost_warp, SVEEntrance.grove_to_wizard_warp, RegionData(SVERegion.enchanted_grove, (SVEEntrance.grove_to_outpost_warp, SVEEntrance.grove_to_wizard_warp,
SVEEntrance.grove_to_farm_warp, SVEEntrance.grove_to_guild_warp, SVEEntrance.grove_to_junimo_warp, SVEEntrance.grove_to_farm_warp, SVEEntrance.grove_to_guild_warp, SVEEntrance.grove_to_junimo_warp,
SVEEntrance.grove_to_spring_warp, SVEEntrance.grove_to_aurora_warp]), SVEEntrance.grove_to_spring_warp, SVEEntrance.grove_to_aurora_warp)),
RegionData(SVERegion.grove_farm_warp, [SVEEntrance.farm_warp_to_farm]), RegionData(SVERegion.grove_farm_warp, (SVEEntrance.farm_warp_to_farm,)),
RegionData(SVERegion.grove_aurora_warp, [SVEEntrance.aurora_warp_to_aurora]), RegionData(SVERegion.grove_aurora_warp, (SVEEntrance.aurora_warp_to_aurora,)),
RegionData(SVERegion.grove_guild_warp, [SVEEntrance.guild_warp_to_guild]), RegionData(SVERegion.grove_guild_warp, (SVEEntrance.guild_warp_to_guild,)),
RegionData(SVERegion.grove_junimo_warp, [SVEEntrance.junimo_warp_to_junimo]), RegionData(SVERegion.grove_junimo_warp, (SVEEntrance.junimo_warp_to_junimo,)),
RegionData(SVERegion.grove_spring_warp, [SVEEntrance.spring_warp_to_spring]), RegionData(SVERegion.grove_spring_warp, (SVEEntrance.spring_warp_to_spring,)),
RegionData(SVERegion.grove_outpost_warp, [SVEEntrance.outpost_warp_to_outpost]), RegionData(SVERegion.grove_outpost_warp, (SVEEntrance.outpost_warp_to_outpost,)),
RegionData(SVERegion.grove_wizard_warp, [SVEEntrance.wizard_warp_to_wizard]), RegionData(SVERegion.grove_wizard_warp, (SVEEntrance.wizard_warp_to_wizard,)),
RegionData(SVERegion.galmoran_outpost, [SVEEntrance.outpost_to_badlands_entrance, SVEEntrance.use_alesia_shop, RegionData(SVERegion.galmoran_outpost, (SVEEntrance.outpost_to_badlands_entrance, SVEEntrance.use_alesia_shop, SVEEntrance.use_isaac_shop)),
SVEEntrance.use_isaac_shop]), RegionData(SVERegion.badlands_entrance, (SVEEntrance.badlands_entrance_to_badlands,)),
RegionData(SVERegion.badlands_entrance, [SVEEntrance.badlands_entrance_to_badlands]), RegionData(SVERegion.crimson_badlands, (SVEEntrance.badlands_to_cave,)),
RegionData(SVERegion.crimson_badlands, [SVEEntrance.badlands_to_cave]),
RegionData(SVERegion.badlands_cave), RegionData(SVERegion.badlands_cave),
RegionData(Region.bus_stop, [SVEEntrance.bus_stop_to_shed]), RegionData(Region.bus_stop, (SVEEntrance.bus_stop_to_shed,)),
RegionData(SVERegion.grandpas_shed, [SVEEntrance.grandpa_shed_to_interior, SVEEntrance.grandpa_shed_to_town]), RegionData(SVERegion.grandpas_shed, (SVEEntrance.grandpa_shed_to_interior, SVEEntrance.grandpa_shed_to_town)),
RegionData(SVERegion.grandpas_shed_interior, [SVEEntrance.grandpa_interior_to_upstairs]), RegionData(SVERegion.grandpas_shed_interior, (SVEEntrance.grandpa_interior_to_upstairs,)),
RegionData(SVERegion.grandpas_shed_upstairs), RegionData(SVERegion.grandpas_shed_upstairs),
RegionData(Region.forest, RegionData(Region.forest,
[SVEEntrance.forest_to_fairhaven, SVEEntrance.forest_to_west, SVEEntrance.forest_to_lost_woods, (SVEEntrance.forest_to_fairhaven, SVEEntrance.forest_to_west, SVEEntrance.forest_to_lost_woods,
SVEEntrance.forest_to_bmv, SVEEntrance.forest_to_marnie_shed]), SVEEntrance.forest_to_bmv, SVEEntrance.forest_to_marnie_shed)),
RegionData(SVERegion.marnies_shed), RegionData(SVERegion.marnies_shed),
RegionData(SVERegion.fairhaven_farm), RegionData(SVERegion.fairhaven_farm),
RegionData(Region.town, [SVEEntrance.town_to_bmv, SVEEntrance.town_to_jenkins, RegionData(Region.town, (SVEEntrance.town_to_bmv, SVEEntrance.town_to_jenkins, SVEEntrance.town_to_bridge, SVEEntrance.town_to_plot)),
SVEEntrance.town_to_bridge, SVEEntrance.town_to_plot]), RegionData(SVERegion.blue_moon_vineyard, (SVEEntrance.bmv_to_sophia, SVEEntrance.bmv_to_beach)),
RegionData(SVERegion.blue_moon_vineyard, [SVEEntrance.bmv_to_sophia, SVEEntrance.bmv_to_beach]),
RegionData(SVERegion.sophias_house), RegionData(SVERegion.sophias_house),
RegionData(SVERegion.jenkins_residence, [SVEEntrance.jenkins_to_cellar]), RegionData(SVERegion.jenkins_residence, (SVEEntrance.jenkins_to_cellar,)),
RegionData(SVERegion.jenkins_cellar), RegionData(SVERegion.jenkins_cellar),
RegionData(SVERegion.unclaimed_plot, [SVEEntrance.plot_to_bridge]), RegionData(SVERegion.unclaimed_plot, (SVEEntrance.plot_to_bridge,)),
RegionData(SVERegion.shearwater), RegionData(SVERegion.shearwater),
RegionData(Region.museum, [SVEEntrance.museum_to_gunther_bedroom]), RegionData(Region.museum, (SVEEntrance.museum_to_gunther_bedroom,)),
RegionData(SVERegion.gunther_bedroom), RegionData(SVERegion.gunther_bedroom),
RegionData(Region.fish_shop, [SVEEntrance.fish_shop_to_willy_bedroom]), RegionData(Region.fish_shop, (SVEEntrance.fish_shop_to_willy_bedroom,)),
RegionData(SVERegion.willy_bedroom), RegionData(SVERegion.willy_bedroom),
RegionData(Region.mountain, [SVEEntrance.mountain_to_guild_summit]), RegionData(Region.mountain, (SVEEntrance.mountain_to_guild_summit,)),
RegionData(SVERegion.guild_summit, [SVEEntrance.guild_to_interior, SVEEntrance.guild_to_mines, # These entrances are removed from the mountain region when SVE is enabled
SVEEntrance.summit_to_highlands]), RegionData(Region.mountain, (Entrance.mountain_to_adventurer_guild, Entrance.mountain_to_the_mines), flag=MergeFlag.REMOVE_EXITS),
RegionData(Region.railroad, [SVEEntrance.to_susan_house, SVEEntrance.enter_summit, SVEEntrance.railroad_to_grampleton_station]), RegionData(SVERegion.guild_summit, (SVEEntrance.guild_to_interior, SVEEntrance.guild_to_mines)),
RegionData(SVERegion.grampleton_station, [SVEEntrance.grampleton_station_to_grampleton_suburbs]), RegionData(Region.railroad, (SVEEntrance.to_susan_house, SVEEntrance.enter_summit, SVEEntrance.railroad_to_grampleton_station)),
RegionData(SVERegion.grampleton_suburbs, [SVEEntrance.grampleton_suburbs_to_scarlett_house]), RegionData(SVERegion.grampleton_station, (SVEEntrance.grampleton_station_to_grampleton_suburbs,)),
RegionData(SVERegion.grampleton_suburbs, (SVEEntrance.grampleton_suburbs_to_scarlett_house,)),
RegionData(SVERegion.scarlett_house), RegionData(SVERegion.scarlett_house),
RegionData(Region.wizard_basement, [SVEEntrance.wizard_to_fable_reef]), RegionData(SVERegion.forest_west, (SVEEntrance.forest_west_to_spring, SVEEntrance.west_to_aurora, SVEEntrance.use_bear_shop,)),
RegionData(SVERegion.fable_reef, [SVEEntrance.fable_reef_to_guild], is_ginger_island=True), RegionData(SVERegion.aurora_vineyard, (SVEEntrance.to_aurora_basement,)),
RegionData(SVERegion.first_slash_guild, [SVEEntrance.first_slash_guild_to_hallway], is_ginger_island=True),
RegionData(SVERegion.first_slash_hallway, [SVEEntrance.first_slash_hallway_to_room], is_ginger_island=True),
RegionData(SVERegion.first_slash_spare_room, is_ginger_island=True),
RegionData(SVERegion.highlands_outside, [SVEEntrance.highlands_to_lance, SVEEntrance.highlands_to_cave, SVEEntrance.highlands_to_pond], is_ginger_island=True),
RegionData(SVERegion.highlands_pond, is_ginger_island=True),
RegionData(SVERegion.highlands_cavern, [SVEEntrance.to_dwarf_prison], is_ginger_island=True),
RegionData(SVERegion.dwarf_prison, is_ginger_island=True),
RegionData(SVERegion.lances_house, [SVEEntrance.lance_to_ladder], is_ginger_island=True),
RegionData(SVERegion.lances_ladder, [SVEEntrance.lance_ladder_to_highlands], is_ginger_island=True),
RegionData(SVERegion.forest_west, [SVEEntrance.forest_west_to_spring, SVEEntrance.west_to_aurora,
SVEEntrance.use_bear_shop]),
RegionData(SVERegion.aurora_vineyard, [SVEEntrance.to_aurora_basement]),
RegionData(SVERegion.aurora_vineyard_basement), RegionData(SVERegion.aurora_vineyard_basement),
RegionData(Region.secret_woods, [SVEEntrance.secret_woods_to_west]), RegionData(Region.secret_woods, (SVEEntrance.secret_woods_to_west,)),
RegionData(SVERegion.bear_shop), RegionData(SVERegion.bear_shop),
RegionData(SVERegion.sprite_spring, [SVEEntrance.sprite_spring_to_cave]), RegionData(SVERegion.sprite_spring, (SVEEntrance.sprite_spring_to_cave,)),
RegionData(SVERegion.sprite_spring_cave), RegionData(SVERegion.sprite_spring_cave),
RegionData(SVERegion.lost_woods, [SVEEntrance.lost_woods_to_junimo_woods]), RegionData(SVERegion.lost_woods, (SVEEntrance.lost_woods_to_junimo_woods,)),
RegionData(SVERegion.junimo_woods, [SVEEntrance.use_purple_junimo]), RegionData(SVERegion.junimo_woods, (SVEEntrance.use_purple_junimo,)),
RegionData(SVERegion.purple_junimo_shop), RegionData(SVERegion.purple_junimo_shop),
RegionData(SVERegion.alesia_shop), RegionData(SVERegion.alesia_shop),
RegionData(SVERegion.isaac_shop), RegionData(SVERegion.isaac_shop),
RegionData(SVERegion.summit), RegionData(SVERegion.summit),
RegionData(SVERegion.susans_house), RegionData(SVERegion.susans_house),
RegionData(Region.mountain, [Entrance.mountain_to_adventurer_guild, Entrance.mountain_to_the_mines], ModificationFlag.MODIFIED)
] ]
mandatory_sve_connections = [ sve_ginger_island_regions = [
RegionData(Region.wizard_basement, (SVEEntrance.wizard_to_fable_reef,)),
RegionData(SVERegion.fable_reef, (SVEEntrance.fable_reef_to_guild,)),
RegionData(SVERegion.first_slash_guild, (SVEEntrance.first_slash_guild_to_hallway,)),
RegionData(SVERegion.first_slash_hallway, (SVEEntrance.first_slash_hallway_to_room,)),
RegionData(SVERegion.first_slash_spare_room),
RegionData(SVERegion.guild_summit, (SVEEntrance.summit_to_highlands,)),
RegionData(SVERegion.highlands_outside, (SVEEntrance.highlands_to_lance, SVEEntrance.highlands_to_cave, SVEEntrance.highlands_to_pond), ),
RegionData(SVERegion.highlands_pond),
RegionData(SVERegion.highlands_cavern, (SVEEntrance.to_dwarf_prison,)),
RegionData(SVERegion.dwarf_prison),
RegionData(SVERegion.lances_house, (SVEEntrance.lance_to_ladder,)),
RegionData(SVERegion.lances_ladder, (SVEEntrance.lance_ladder_to_highlands,)),
]
sve_main_land_connections = [
ConnectionData(SVEEntrance.town_to_jenkins, SVERegion.jenkins_residence, flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA), ConnectionData(SVEEntrance.town_to_jenkins, SVERegion.jenkins_residence, flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(SVEEntrance.jenkins_to_cellar, SVERegion.jenkins_cellar, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.jenkins_to_cellar, SVERegion.jenkins_cellar, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.forest_to_bmv, SVERegion.blue_moon_vineyard), ConnectionData(SVEEntrance.forest_to_bmv, SVERegion.blue_moon_vineyard),
@@ -223,7 +222,7 @@ mandatory_sve_connections = [
ConnectionData(SVEEntrance.grandpa_interior_to_upstairs, SVERegion.grandpas_shed_upstairs, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.grandpa_interior_to_upstairs, SVERegion.grandpas_shed_upstairs, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.grandpa_shed_to_town, Region.town), ConnectionData(SVEEntrance.grandpa_shed_to_town, Region.town),
ConnectionData(SVEEntrance.bmv_to_sophia, SVERegion.sophias_house, flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA), ConnectionData(SVEEntrance.bmv_to_sophia, SVERegion.sophias_house, flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(SVEEntrance.summit_to_highlands, SVERegion.highlands_outside, flag=RandomizationFlag.GINGER_ISLAND), ConnectionData(SVEEntrance.summit_to_highlands, SVERegion.highlands_outside),
ConnectionData(SVEEntrance.guild_to_interior, Region.adventurer_guild, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.guild_to_interior, Region.adventurer_guild, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.backwoods_to_grove, SVERegion.enchanted_grove, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA), ConnectionData(SVEEntrance.backwoods_to_grove, SVERegion.enchanted_grove, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(SVEEntrance.grove_to_outpost_warp, SVERegion.grove_outpost_warp), ConnectionData(SVEEntrance.grove_to_outpost_warp, SVERegion.grove_outpost_warp),
@@ -242,8 +241,6 @@ mandatory_sve_connections = [
ConnectionData(SVEEntrance.use_purple_junimo, SVERegion.purple_junimo_shop), ConnectionData(SVEEntrance.use_purple_junimo, SVERegion.purple_junimo_shop),
ConnectionData(SVEEntrance.grove_to_spring_warp, SVERegion.grove_spring_warp), ConnectionData(SVEEntrance.grove_to_spring_warp, SVERegion.grove_spring_warp),
ConnectionData(SVEEntrance.spring_warp_to_spring, SVERegion.sprite_spring, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.spring_warp_to_spring, SVERegion.sprite_spring, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.wizard_to_fable_reef, SVERegion.fable_reef, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.fable_reef_to_guild, SVERegion.first_slash_guild, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.outpost_to_badlands_entrance, SVERegion.badlands_entrance, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.outpost_to_badlands_entrance, SVERegion.badlands_entrance, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.badlands_entrance_to_badlands, SVERegion.crimson_badlands, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.badlands_entrance_to_badlands, SVERegion.crimson_badlands, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.badlands_to_cave, SVERegion.badlands_cave, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.badlands_to_cave, SVERegion.badlands_cave, flag=RandomizationFlag.BUILDINGS),
@@ -259,71 +256,75 @@ mandatory_sve_connections = [
ConnectionData(SVEEntrance.to_susan_house, SVERegion.susans_house, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.to_susan_house, SVERegion.susans_house, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.enter_summit, SVERegion.summit, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.enter_summit, SVERegion.summit, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.forest_to_fairhaven, SVERegion.fairhaven_farm, flag=RandomizationFlag.NON_PROGRESSION), ConnectionData(SVEEntrance.forest_to_fairhaven, SVERegion.fairhaven_farm, flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(SVEEntrance.highlands_to_lance, SVERegion.lances_house, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.lance_to_ladder, SVERegion.lances_ladder, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.lance_ladder_to_highlands, SVERegion.highlands_outside, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.highlands_to_cave, SVERegion.highlands_cavern, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.use_bear_shop, SVERegion.bear_shop), ConnectionData(SVEEntrance.use_bear_shop, SVERegion.bear_shop),
ConnectionData(SVEEntrance.use_purple_junimo, SVERegion.purple_junimo_shop), ConnectionData(SVEEntrance.use_purple_junimo, SVERegion.purple_junimo_shop),
ConnectionData(SVEEntrance.use_alesia_shop, SVERegion.alesia_shop), ConnectionData(SVEEntrance.use_alesia_shop, SVERegion.alesia_shop),
ConnectionData(SVEEntrance.use_isaac_shop, SVERegion.isaac_shop), ConnectionData(SVEEntrance.use_isaac_shop, SVERegion.isaac_shop),
ConnectionData(SVEEntrance.to_dwarf_prison, SVERegion.dwarf_prison, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.railroad_to_grampleton_station, SVERegion.grampleton_station), ConnectionData(SVEEntrance.railroad_to_grampleton_station, SVERegion.grampleton_station),
ConnectionData(SVEEntrance.grampleton_station_to_grampleton_suburbs, SVERegion.grampleton_suburbs), ConnectionData(SVEEntrance.grampleton_station_to_grampleton_suburbs, SVERegion.grampleton_suburbs),
ConnectionData(SVEEntrance.grampleton_suburbs_to_scarlett_house, SVERegion.scarlett_house, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.grampleton_suburbs_to_scarlett_house, SVERegion.scarlett_house, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.first_slash_guild_to_hallway, SVERegion.first_slash_hallway, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.first_slash_hallway_to_room, SVERegion.first_slash_spare_room,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(SVEEntrance.sprite_spring_to_cave, SVERegion.sprite_spring_cave, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.sprite_spring_to_cave, SVERegion.sprite_spring_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.fish_shop_to_willy_bedroom, SVERegion.willy_bedroom, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.fish_shop_to_willy_bedroom, SVERegion.willy_bedroom, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.museum_to_gunther_bedroom, SVERegion.gunther_bedroom, flag=RandomizationFlag.BUILDINGS), ConnectionData(SVEEntrance.museum_to_gunther_bedroom, SVERegion.gunther_bedroom, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.highlands_to_pond, SVERegion.highlands_pond), ConnectionData(SVEEntrance.highlands_to_pond, SVERegion.highlands_pond),
] ]
sve_ginger_island_connections = [
ConnectionData(SVEEntrance.wizard_to_fable_reef, SVERegion.fable_reef, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.fable_reef_to_guild, SVERegion.first_slash_guild, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.highlands_to_lance, SVERegion.lances_house, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.lance_to_ladder, SVERegion.lances_ladder),
ConnectionData(SVEEntrance.lance_ladder_to_highlands, SVERegion.highlands_outside, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.highlands_to_cave, SVERegion.highlands_cavern, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.to_dwarf_prison, SVERegion.dwarf_prison, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.first_slash_guild_to_hallway, SVERegion.first_slash_hallway, flag=RandomizationFlag.BUILDINGS),
ConnectionData(SVEEntrance.first_slash_hallway_to_room, SVERegion.first_slash_spare_room, flag=RandomizationFlag.BUILDINGS),
]
alecto_regions = [ alecto_regions = [
RegionData(Region.witch_hut, [AlectoEntrance.witch_hut_to_witch_attic]), RegionData(Region.witch_hut, (AlectoEntrance.witch_hut_to_witch_attic,)),
RegionData(AlectoRegion.witch_attic) RegionData(AlectoRegion.witch_attic),
] ]
alecto_entrances = [ alecto_entrances = [
ConnectionData(AlectoEntrance.witch_hut_to_witch_attic, AlectoRegion.witch_attic, flag=RandomizationFlag.BUILDINGS) ConnectionData(AlectoEntrance.witch_hut_to_witch_attic, AlectoRegion.witch_attic, flag=RandomizationFlag.BUILDINGS),
] ]
lacey_regions = [ lacey_regions = [
RegionData(Region.forest, [LaceyEntrance.forest_to_hat_house]), RegionData(Region.forest, (LaceyEntrance.forest_to_hat_house,)),
RegionData(LaceyRegion.hat_house) RegionData(LaceyRegion.hat_house),
] ]
lacey_entrances = [ lacey_entrances = [
ConnectionData(LaceyEntrance.forest_to_hat_house, LaceyRegion.hat_house, flag=RandomizationFlag.BUILDINGS) ConnectionData(LaceyEntrance.forest_to_hat_house, LaceyRegion.hat_house, flag=RandomizationFlag.BUILDINGS),
] ]
boarding_house_regions = [ boarding_house_regions = [
RegionData(Region.bus_stop, [BoardingHouseEntrance.bus_stop_to_boarding_house_plateau]), RegionData(Region.bus_stop, (BoardingHouseEntrance.bus_stop_to_boarding_house_plateau,)),
RegionData(BoardingHouseRegion.boarding_house_plateau, [BoardingHouseEntrance.boarding_house_plateau_to_boarding_house_first, RegionData(BoardingHouseRegion.boarding_house_plateau, (BoardingHouseEntrance.boarding_house_plateau_to_boarding_house_first,
BoardingHouseEntrance.boarding_house_plateau_to_buffalo_ranch, BoardingHouseEntrance.boarding_house_plateau_to_buffalo_ranch,
BoardingHouseEntrance.boarding_house_plateau_to_abandoned_mines_entrance]), BoardingHouseEntrance.boarding_house_plateau_to_abandoned_mines_entrance)),
RegionData(BoardingHouseRegion.boarding_house_first, [BoardingHouseEntrance.boarding_house_first_to_boarding_house_second]), RegionData(BoardingHouseRegion.boarding_house_first, (BoardingHouseEntrance.boarding_house_first_to_boarding_house_second,)),
RegionData(BoardingHouseRegion.boarding_house_second), RegionData(BoardingHouseRegion.boarding_house_second),
RegionData(BoardingHouseRegion.buffalo_ranch), RegionData(BoardingHouseRegion.buffalo_ranch),
RegionData(BoardingHouseRegion.abandoned_mines_entrance, [BoardingHouseEntrance.abandoned_mines_entrance_to_abandoned_mines_1a, RegionData(BoardingHouseRegion.abandoned_mines_entrance, (BoardingHouseEntrance.abandoned_mines_entrance_to_abandoned_mines_1a,
BoardingHouseEntrance.abandoned_mines_entrance_to_the_lost_valley]), BoardingHouseEntrance.abandoned_mines_entrance_to_the_lost_valley)),
RegionData(BoardingHouseRegion.abandoned_mines_1a, [BoardingHouseEntrance.abandoned_mines_1a_to_abandoned_mines_1b]), RegionData(BoardingHouseRegion.abandoned_mines_1a, (BoardingHouseEntrance.abandoned_mines_1a_to_abandoned_mines_1b,)),
RegionData(BoardingHouseRegion.abandoned_mines_1b, [BoardingHouseEntrance.abandoned_mines_1b_to_abandoned_mines_2a]), RegionData(BoardingHouseRegion.abandoned_mines_1b, (BoardingHouseEntrance.abandoned_mines_1b_to_abandoned_mines_2a,)),
RegionData(BoardingHouseRegion.abandoned_mines_2a, [BoardingHouseEntrance.abandoned_mines_2a_to_abandoned_mines_2b]), RegionData(BoardingHouseRegion.abandoned_mines_2a, (BoardingHouseEntrance.abandoned_mines_2a_to_abandoned_mines_2b,)),
RegionData(BoardingHouseRegion.abandoned_mines_2b, [BoardingHouseEntrance.abandoned_mines_2b_to_abandoned_mines_3]), RegionData(BoardingHouseRegion.abandoned_mines_2b, (BoardingHouseEntrance.abandoned_mines_2b_to_abandoned_mines_3,)),
RegionData(BoardingHouseRegion.abandoned_mines_3, [BoardingHouseEntrance.abandoned_mines_3_to_abandoned_mines_4]), RegionData(BoardingHouseRegion.abandoned_mines_3, (BoardingHouseEntrance.abandoned_mines_3_to_abandoned_mines_4,)),
RegionData(BoardingHouseRegion.abandoned_mines_4, [BoardingHouseEntrance.abandoned_mines_4_to_abandoned_mines_5]), RegionData(BoardingHouseRegion.abandoned_mines_4, (BoardingHouseEntrance.abandoned_mines_4_to_abandoned_mines_5,)),
RegionData(BoardingHouseRegion.abandoned_mines_5, [BoardingHouseEntrance.abandoned_mines_5_to_the_lost_valley]), RegionData(BoardingHouseRegion.abandoned_mines_5, (BoardingHouseEntrance.abandoned_mines_5_to_the_lost_valley,)),
RegionData(BoardingHouseRegion.the_lost_valley, [BoardingHouseEntrance.the_lost_valley_to_gregory_tent, RegionData(BoardingHouseRegion.the_lost_valley, (BoardingHouseEntrance.the_lost_valley_to_gregory_tent,
BoardingHouseEntrance.lost_valley_to_lost_valley_minecart, BoardingHouseEntrance.lost_valley_to_lost_valley_minecart,
BoardingHouseEntrance.the_lost_valley_to_lost_valley_ruins]), BoardingHouseEntrance.the_lost_valley_to_lost_valley_ruins)),
RegionData(BoardingHouseRegion.gregory_tent), RegionData(BoardingHouseRegion.gregory_tent),
RegionData(BoardingHouseRegion.lost_valley_ruins, [BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_1, RegionData(BoardingHouseRegion.lost_valley_ruins, (BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_1,
BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_2]), BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_2)),
RegionData(BoardingHouseRegion.lost_valley_minecart), RegionData(BoardingHouseRegion.lost_valley_minecart),
RegionData(BoardingHouseRegion.lost_valley_house_1), RegionData(BoardingHouseRegion.lost_valley_house_1),
RegionData(BoardingHouseRegion.lost_valley_house_2) RegionData(BoardingHouseRegion.lost_valley_house_2),
] ]
boarding_house_entrances = [ boarding_house_entrances = [
@@ -351,30 +352,29 @@ boarding_house_entrances = [
ConnectionData(BoardingHouseEntrance.lost_valley_to_lost_valley_minecart, BoardingHouseRegion.lost_valley_minecart), ConnectionData(BoardingHouseEntrance.lost_valley_to_lost_valley_minecart, BoardingHouseRegion.lost_valley_minecart),
ConnectionData(BoardingHouseEntrance.the_lost_valley_to_lost_valley_ruins, BoardingHouseRegion.lost_valley_ruins, flag=RandomizationFlag.BUILDINGS), ConnectionData(BoardingHouseEntrance.the_lost_valley_to_lost_valley_ruins, BoardingHouseRegion.lost_valley_ruins, flag=RandomizationFlag.BUILDINGS),
ConnectionData(BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_1, BoardingHouseRegion.lost_valley_house_1, flag=RandomizationFlag.BUILDINGS), ConnectionData(BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_1, BoardingHouseRegion.lost_valley_house_1, flag=RandomizationFlag.BUILDINGS),
ConnectionData(BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_2, BoardingHouseRegion.lost_valley_house_2, flag=RandomizationFlag.BUILDINGS) ConnectionData(BoardingHouseEntrance.lost_valley_ruins_to_lost_valley_house_2, BoardingHouseRegion.lost_valley_house_2, flag=RandomizationFlag.BUILDINGS),
] ]
vanilla_connections_to_remove_by_mod: Dict[str, List[ConnectionData]] = { vanilla_connections_to_remove_by_content_pack: dict[str, tuple[str, ...]] = {
ModNames.sve: [ ModNames.sve: (
ConnectionData(Entrance.mountain_to_the_mines, Region.mines, Entrance.mountain_to_the_mines,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA), Entrance.mountain_to_adventurer_guild,
ConnectionData(Entrance.mountain_to_adventurer_guild, Region.adventurer_guild, )
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
]
} }
ModDataList = { region_data_by_content_pack = {
ModNames.deepwoods: ModRegionData(ModNames.deepwoods, deep_woods_regions, deep_woods_entrances), ModNames.deepwoods: ModRegionsData(ModNames.deepwoods, deep_woods_regions, deep_woods_entrances),
ModNames.eugene: ModRegionData(ModNames.eugene, eugene_regions, eugene_entrances), ModNames.eugene: ModRegionsData(ModNames.eugene, eugene_regions, eugene_entrances),
ModNames.jasper: ModRegionData(ModNames.jasper, jasper_regions, jasper_entrances), ModNames.jasper: ModRegionsData(ModNames.jasper, jasper_regions, jasper_entrances),
ModNames.alec: ModRegionData(ModNames.alec, alec_regions, alec_entrances), ModNames.alec: ModRegionsData(ModNames.alec, alec_regions, alec_entrances),
ModNames.yoba: ModRegionData(ModNames.yoba, yoba_regions, yoba_entrances), ModNames.yoba: ModRegionsData(ModNames.yoba, yoba_regions, yoba_entrances),
ModNames.juna: ModRegionData(ModNames.juna, juna_regions, juna_entrances), ModNames.juna: ModRegionsData(ModNames.juna, juna_regions, juna_entrances),
ModNames.magic: ModRegionData(ModNames.magic, magic_regions, magic_entrances), ModNames.magic: ModRegionsData(ModNames.magic, magic_regions, magic_entrances),
ModNames.ayeisha: ModRegionData(ModNames.ayeisha, ayeisha_regions, ayeisha_entrances), ModNames.ayeisha: ModRegionsData(ModNames.ayeisha, ayeisha_regions, ayeisha_entrances),
ModNames.riley: ModRegionData(ModNames.riley, riley_regions, riley_entrances), ModNames.riley: ModRegionsData(ModNames.riley, riley_regions, riley_entrances),
ModNames.sve: ModRegionData(ModNames.sve, stardew_valley_expanded_regions, mandatory_sve_connections), ModNames.sve: ModRegionsData(ModNames.sve, sve_main_land_regions, sve_main_land_connections),
ModNames.alecto: ModRegionData(ModNames.alecto, alecto_regions, alecto_entrances), SVE_GINGER_ISLAND_PACK: ModRegionsData(SVE_GINGER_ISLAND_PACK, sve_ginger_island_regions, sve_ginger_island_connections),
ModNames.lacey: ModRegionData(ModNames.lacey, lacey_regions, lacey_entrances), ModNames.alecto: ModRegionsData(ModNames.alecto, alecto_regions, alecto_entrances),
ModNames.boarding_house: ModRegionData(ModNames.boarding_house, boarding_house_regions, boarding_house_entrances), ModNames.lacey: ModRegionsData(ModNames.lacey, lacey_regions, lacey_entrances),
ModNames.boarding_house: ModRegionsData(ModNames.boarding_house, boarding_house_regions, boarding_house_entrances),
} }

View File

@@ -1,67 +0,0 @@
from copy import deepcopy
from dataclasses import dataclass, field
from enum import IntFlag
from typing import Optional, List, Set
connector_keyword = " to "
class ModificationFlag(IntFlag):
NOT_MODIFIED = 0
MODIFIED = 1
class RandomizationFlag(IntFlag):
NOT_RANDOMIZED = 0b0
PELICAN_TOWN = 0b00011111
NON_PROGRESSION = 0b00011110
BUILDINGS = 0b00011100
EVERYTHING = 0b00011000
GINGER_ISLAND = 0b00100000
LEAD_TO_OPEN_AREA = 0b01000000
MASTERIES = 0b10000000
@dataclass(frozen=True)
class RegionData:
name: str
exits: List[str] = field(default_factory=list)
flag: ModificationFlag = ModificationFlag.NOT_MODIFIED
is_ginger_island: bool = False
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 = sorted(set(merged_exits))
return RegionData(self.name, merged_exits, is_ginger_island=self.is_ginger_island)
def get_without_exits(self, exits_to_remove: Set[str]):
exits = [exit_ for exit_ in self.exits if exit_ not in exits_to_remove]
return RegionData(self.name, exits, is_ginger_island=self.is_ginger_island)
def get_clone(self):
return deepcopy(self)
@dataclass(frozen=True)
class ConnectionData:
name: str
destination: str
origin: Optional[str] = None
reverse: Optional[str] = None
flag: RandomizationFlag = RandomizationFlag.NOT_RANDOMIZED
def __post_init__(self):
if connector_keyword in self.name:
origin, destination = self.name.split(connector_keyword)
if self.reverse is None:
super().__setattr__("reverse", f"{destination}{connector_keyword}{origin}")
@dataclass(frozen=True)
class ModRegionData:
mod_name: str
regions: List[RegionData]
connections: List[ConnectionData]

View File

@@ -1,775 +0,0 @@
from random import Random
from typing import Iterable, Dict, Protocol, List, Tuple, Set
from BaseClasses import Region, Entrance
from .content import content_packs, StardewContent
from .mods.mod_regions import ModDataList, vanilla_connections_to_remove_by_mod
from .options import EntranceRandomization, ExcludeGingerIsland, StardewValleyOptions
from .region_classes import RegionData, ConnectionData, RandomizationFlag, ModificationFlag
from .strings.entrance_names import Entrance, LogicEntrance
from .strings.region_names import Region as RegionName, LogicRegion
class RegionFactory(Protocol):
def __call__(self, name: str, regions: Iterable[str]) -> Region:
raise NotImplementedError
vanilla_regions = [
RegionData(RegionName.menu, [Entrance.to_stardew_valley]),
RegionData(RegionName.stardew_valley, [Entrance.to_farmhouse]),
RegionData(RegionName.farm_house,
[Entrance.farmhouse_to_farm, Entrance.downstairs_to_cellar, LogicEntrance.farmhouse_cooking, LogicEntrance.watch_queen_of_sauce]),
RegionData(RegionName.cellar),
RegionData(RegionName.farm,
[Entrance.farm_to_backwoods, Entrance.farm_to_bus_stop, Entrance.farm_to_forest, Entrance.farm_to_farmcave, Entrance.enter_greenhouse,
Entrance.enter_coop, Entrance.enter_barn, Entrance.enter_shed, Entrance.enter_slime_hutch, LogicEntrance.grow_spring_crops,
LogicEntrance.grow_summer_crops, LogicEntrance.grow_fall_crops, LogicEntrance.grow_winter_crops, LogicEntrance.shipping,
LogicEntrance.fishing, ]),
RegionData(RegionName.backwoods, [Entrance.backwoods_to_mountain]),
RegionData(RegionName.bus_stop,
[Entrance.bus_stop_to_town, Entrance.take_bus_to_desert, Entrance.bus_stop_to_tunnel_entrance]),
RegionData(RegionName.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.forest_to_mastery_cave, LogicEntrance.buy_from_traveling_merchant,
LogicEntrance.complete_raccoon_requests, LogicEntrance.fish_in_waterfall, LogicEntrance.attend_flower_dance, LogicEntrance.attend_trout_derby,
LogicEntrance.attend_festival_of_ice]),
RegionData(LogicRegion.forest_waterfall),
RegionData(RegionName.farm_cave),
RegionData(RegionName.greenhouse,
[LogicEntrance.grow_spring_crops_in_greenhouse, LogicEntrance.grow_summer_crops_in_greenhouse, LogicEntrance.grow_fall_crops_in_greenhouse,
LogicEntrance.grow_winter_crops_in_greenhouse, LogicEntrance.grow_indoor_crops_in_greenhouse]),
RegionData(RegionName.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(RegionName.leo_treehouse, is_ginger_island=True),
RegionData(RegionName.maru_room),
RegionData(RegionName.tunnel_entrance, [Entrance.tunnel_entrance_to_bus_tunnel]),
RegionData(RegionName.bus_tunnel),
RegionData(RegionName.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,
Entrance.purchase_movie_ticket, LogicEntrance.buy_experience_books, LogicEntrance.attend_egg_festival, LogicEntrance.attend_fair,
LogicEntrance.attend_spirit_eve, LogicEntrance.attend_winter_star]),
RegionData(RegionName.beach,
[Entrance.beach_to_willy_fish_shop, Entrance.enter_elliott_house, Entrance.enter_tide_pools, LogicEntrance.attend_luau,
LogicEntrance.attend_moonlight_jellies, LogicEntrance.attend_night_market, LogicEntrance.attend_squidfest]),
RegionData(RegionName.railroad, [Entrance.enter_bathhouse_entrance, Entrance.enter_witch_warp_cave]),
RegionData(RegionName.ranch),
RegionData(RegionName.leah_house),
RegionData(RegionName.mastery_cave),
RegionData(RegionName.sewer, [Entrance.enter_mutant_bug_lair]),
RegionData(RegionName.mutant_bug_lair),
RegionData(RegionName.wizard_tower, [Entrance.enter_wizard_basement, Entrance.use_desert_obelisk, Entrance.use_island_obelisk]),
RegionData(RegionName.wizard_basement),
RegionData(RegionName.tent),
RegionData(RegionName.carpenter, [Entrance.enter_sebastian_room]),
RegionData(RegionName.sebastian_room),
RegionData(RegionName.adventurer_guild, [Entrance.adventurer_guild_to_bedroom]),
RegionData(RegionName.adventurer_guild_bedroom),
RegionData(RegionName.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(RegionName.crafts_room),
RegionData(RegionName.pantry),
RegionData(RegionName.fish_tank),
RegionData(RegionName.boiler_room),
RegionData(RegionName.bulletin_board),
RegionData(RegionName.vault),
RegionData(RegionName.hospital, [Entrance.enter_harvey_room]),
RegionData(RegionName.harvey_room),
RegionData(RegionName.pierre_store, [Entrance.enter_sunroom]),
RegionData(RegionName.sunroom),
RegionData(RegionName.saloon, [Entrance.play_journey_of_the_prairie_king, Entrance.play_junimo_kart]),
RegionData(RegionName.jotpk_world_1, [Entrance.reach_jotpk_world_2]),
RegionData(RegionName.jotpk_world_2, [Entrance.reach_jotpk_world_3]),
RegionData(RegionName.jotpk_world_3),
RegionData(RegionName.junimo_kart_1, [Entrance.reach_junimo_kart_2]),
RegionData(RegionName.junimo_kart_2, [Entrance.reach_junimo_kart_3]),
RegionData(RegionName.junimo_kart_3, [Entrance.reach_junimo_kart_4]),
RegionData(RegionName.junimo_kart_4),
RegionData(RegionName.alex_house),
RegionData(RegionName.trailer),
RegionData(RegionName.mayor_house),
RegionData(RegionName.sam_house),
RegionData(RegionName.haley_house),
RegionData(RegionName.blacksmith, [LogicEntrance.blacksmith_copper]),
RegionData(RegionName.museum),
RegionData(RegionName.jojamart, [Entrance.enter_abandoned_jojamart]),
RegionData(RegionName.abandoned_jojamart, [Entrance.enter_movie_theater]),
RegionData(RegionName.movie_ticket_stand),
RegionData(RegionName.movie_theater),
RegionData(RegionName.fish_shop, [Entrance.fish_shop_to_boat_tunnel]),
RegionData(RegionName.boat_tunnel, [Entrance.boat_to_ginger_island], is_ginger_island=True),
RegionData(RegionName.elliott_house),
RegionData(RegionName.tide_pools),
RegionData(RegionName.bathhouse_entrance, [Entrance.enter_locker_room]),
RegionData(RegionName.locker_room, [Entrance.enter_public_bath]),
RegionData(RegionName.public_bath),
RegionData(RegionName.witch_warp_cave, [Entrance.enter_witch_swamp]),
RegionData(RegionName.witch_swamp, [Entrance.enter_witch_hut]),
RegionData(RegionName.witch_hut, [Entrance.witch_warp_to_wizard_basement]),
RegionData(RegionName.quarry, [Entrance.enter_quarry_mine_entrance]),
RegionData(RegionName.quarry_mine_entrance, [Entrance.enter_quarry_mine]),
RegionData(RegionName.quarry_mine),
RegionData(RegionName.secret_woods),
RegionData(RegionName.desert, [Entrance.enter_skull_cavern_entrance, Entrance.enter_oasis, LogicEntrance.attend_desert_festival]),
RegionData(RegionName.oasis, [Entrance.enter_casino]),
RegionData(RegionName.casino),
RegionData(RegionName.skull_cavern_entrance, [Entrance.enter_skull_cavern]),
RegionData(RegionName.skull_cavern, [Entrance.mine_to_skull_cavern_floor_25]),
RegionData(RegionName.skull_cavern_25, [Entrance.mine_to_skull_cavern_floor_50]),
RegionData(RegionName.skull_cavern_50, [Entrance.mine_to_skull_cavern_floor_75]),
RegionData(RegionName.skull_cavern_75, [Entrance.mine_to_skull_cavern_floor_100]),
RegionData(RegionName.skull_cavern_100, [Entrance.mine_to_skull_cavern_floor_125]),
RegionData(RegionName.skull_cavern_125, [Entrance.mine_to_skull_cavern_floor_150]),
RegionData(RegionName.skull_cavern_150, [Entrance.mine_to_skull_cavern_floor_175]),
RegionData(RegionName.skull_cavern_175, [Entrance.mine_to_skull_cavern_floor_200]),
RegionData(RegionName.skull_cavern_200, [Entrance.enter_dangerous_skull_cavern]),
RegionData(RegionName.dangerous_skull_cavern, is_ginger_island=True),
RegionData(RegionName.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],
is_ginger_island=True),
RegionData(RegionName.island_resort, is_ginger_island=True),
RegionData(RegionName.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, LogicEntrance.grow_spring_crops_on_island,
LogicEntrance.grow_summer_crops_on_island, LogicEntrance.grow_fall_crops_on_island, LogicEntrance.grow_winter_crops_on_island,
LogicEntrance.grow_indoor_crops_on_island],
is_ginger_island=True),
RegionData(RegionName.island_east, [Entrance.island_east_to_leo_hut, Entrance.island_east_to_island_shrine], is_ginger_island=True),
RegionData(RegionName.island_shrine, is_ginger_island=True),
RegionData(RegionName.island_south_east, [Entrance.island_southeast_to_pirate_cove], is_ginger_island=True),
RegionData(RegionName.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],
is_ginger_island=True),
RegionData(RegionName.volcano, [Entrance.climb_to_volcano_5, Entrance.volcano_to_secret_beach], is_ginger_island=True),
RegionData(RegionName.volcano_secret_beach, is_ginger_island=True),
RegionData(RegionName.volcano_floor_5, [Entrance.talk_to_volcano_dwarf, Entrance.climb_to_volcano_10], is_ginger_island=True),
RegionData(RegionName.volcano_dwarf_shop, is_ginger_island=True),
RegionData(RegionName.volcano_floor_10, is_ginger_island=True),
RegionData(RegionName.island_trader, is_ginger_island=True),
RegionData(RegionName.island_farmhouse, [LogicEntrance.island_cooking], is_ginger_island=True),
RegionData(RegionName.gourmand_frog_cave, is_ginger_island=True),
RegionData(RegionName.colored_crystals_cave, is_ginger_island=True),
RegionData(RegionName.shipwreck, is_ginger_island=True),
RegionData(RegionName.qi_walnut_room, is_ginger_island=True),
RegionData(RegionName.leo_hut, is_ginger_island=True),
RegionData(RegionName.pirate_cove, is_ginger_island=True),
RegionData(RegionName.field_office, is_ginger_island=True),
RegionData(RegionName.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],
is_ginger_island=True),
RegionData(RegionName.professor_snail_cave, is_ginger_island=True),
RegionData(RegionName.coop),
RegionData(RegionName.barn),
RegionData(RegionName.shed),
RegionData(RegionName.slime_hutch),
RegionData(RegionName.mines, [LogicEntrance.talk_to_mines_dwarf,
Entrance.dig_to_mines_floor_5]),
RegionData(RegionName.mines_floor_5, [Entrance.dig_to_mines_floor_10]),
RegionData(RegionName.mines_floor_10, [Entrance.dig_to_mines_floor_15]),
RegionData(RegionName.mines_floor_15, [Entrance.dig_to_mines_floor_20]),
RegionData(RegionName.mines_floor_20, [Entrance.dig_to_mines_floor_25]),
RegionData(RegionName.mines_floor_25, [Entrance.dig_to_mines_floor_30]),
RegionData(RegionName.mines_floor_30, [Entrance.dig_to_mines_floor_35]),
RegionData(RegionName.mines_floor_35, [Entrance.dig_to_mines_floor_40]),
RegionData(RegionName.mines_floor_40, [Entrance.dig_to_mines_floor_45]),
RegionData(RegionName.mines_floor_45, [Entrance.dig_to_mines_floor_50]),
RegionData(RegionName.mines_floor_50, [Entrance.dig_to_mines_floor_55]),
RegionData(RegionName.mines_floor_55, [Entrance.dig_to_mines_floor_60]),
RegionData(RegionName.mines_floor_60, [Entrance.dig_to_mines_floor_65]),
RegionData(RegionName.mines_floor_65, [Entrance.dig_to_mines_floor_70]),
RegionData(RegionName.mines_floor_70, [Entrance.dig_to_mines_floor_75]),
RegionData(RegionName.mines_floor_75, [Entrance.dig_to_mines_floor_80]),
RegionData(RegionName.mines_floor_80, [Entrance.dig_to_mines_floor_85]),
RegionData(RegionName.mines_floor_85, [Entrance.dig_to_mines_floor_90]),
RegionData(RegionName.mines_floor_90, [Entrance.dig_to_mines_floor_95]),
RegionData(RegionName.mines_floor_95, [Entrance.dig_to_mines_floor_100]),
RegionData(RegionName.mines_floor_100, [Entrance.dig_to_mines_floor_105]),
RegionData(RegionName.mines_floor_105, [Entrance.dig_to_mines_floor_110]),
RegionData(RegionName.mines_floor_110, [Entrance.dig_to_mines_floor_115]),
RegionData(RegionName.mines_floor_115, [Entrance.dig_to_mines_floor_120]),
RegionData(RegionName.mines_floor_120, [Entrance.dig_to_dangerous_mines_20, Entrance.dig_to_dangerous_mines_60, Entrance.dig_to_dangerous_mines_100]),
RegionData(RegionName.dangerous_mines_20, is_ginger_island=True),
RegionData(RegionName.dangerous_mines_60, is_ginger_island=True),
RegionData(RegionName.dangerous_mines_100, is_ginger_island=True),
RegionData(LogicRegion.mines_dwarf_shop),
RegionData(LogicRegion.blacksmith_copper, [LogicEntrance.blacksmith_iron]),
RegionData(LogicRegion.blacksmith_iron, [LogicEntrance.blacksmith_gold]),
RegionData(LogicRegion.blacksmith_gold, [LogicEntrance.blacksmith_iridium]),
RegionData(LogicRegion.blacksmith_iridium),
RegionData(LogicRegion.kitchen),
RegionData(LogicRegion.queen_of_sauce),
RegionData(LogicRegion.fishing),
RegionData(LogicRegion.spring_farming),
RegionData(LogicRegion.summer_farming, [LogicEntrance.grow_summer_fall_crops_in_summer]),
RegionData(LogicRegion.fall_farming, [LogicEntrance.grow_summer_fall_crops_in_fall]),
RegionData(LogicRegion.winter_farming),
RegionData(LogicRegion.summer_or_fall_farming),
RegionData(LogicRegion.indoor_farming),
RegionData(LogicRegion.shipping),
RegionData(LogicRegion.traveling_cart, [LogicEntrance.buy_from_traveling_merchant_sunday,
LogicEntrance.buy_from_traveling_merchant_monday,
LogicEntrance.buy_from_traveling_merchant_tuesday,
LogicEntrance.buy_from_traveling_merchant_wednesday,
LogicEntrance.buy_from_traveling_merchant_thursday,
LogicEntrance.buy_from_traveling_merchant_friday,
LogicEntrance.buy_from_traveling_merchant_saturday]),
RegionData(LogicRegion.traveling_cart_sunday),
RegionData(LogicRegion.traveling_cart_monday),
RegionData(LogicRegion.traveling_cart_tuesday),
RegionData(LogicRegion.traveling_cart_wednesday),
RegionData(LogicRegion.traveling_cart_thursday),
RegionData(LogicRegion.traveling_cart_friday),
RegionData(LogicRegion.traveling_cart_saturday),
RegionData(LogicRegion.raccoon_daddy, [LogicEntrance.buy_from_raccoon]),
RegionData(LogicRegion.raccoon_shop),
RegionData(LogicRegion.egg_festival),
RegionData(LogicRegion.desert_festival),
RegionData(LogicRegion.flower_dance),
RegionData(LogicRegion.luau),
RegionData(LogicRegion.trout_derby),
RegionData(LogicRegion.moonlight_jellies),
RegionData(LogicRegion.fair),
RegionData(LogicRegion.spirit_eve),
RegionData(LogicRegion.festival_of_ice),
RegionData(LogicRegion.night_market),
RegionData(LogicRegion.winter_star),
RegionData(LogicRegion.squidfest),
RegionData(LogicRegion.bookseller_1, [LogicEntrance.buy_year1_books]),
RegionData(LogicRegion.bookseller_2, [LogicEntrance.buy_year3_books]),
RegionData(LogicRegion.bookseller_3),
]
# Exists and where they lead
vanilla_connections = [
ConnectionData(Entrance.to_stardew_valley, RegionName.stardew_valley),
ConnectionData(Entrance.to_farmhouse, RegionName.farm_house),
ConnectionData(Entrance.farmhouse_to_farm, RegionName.farm),
ConnectionData(Entrance.downstairs_to_cellar, RegionName.cellar),
ConnectionData(Entrance.farm_to_backwoods, RegionName.backwoods),
ConnectionData(Entrance.farm_to_bus_stop, RegionName.bus_stop),
ConnectionData(Entrance.farm_to_forest, RegionName.forest),
ConnectionData(Entrance.farm_to_farmcave, RegionName.farm_cave, flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(Entrance.enter_greenhouse, RegionName.greenhouse),
ConnectionData(Entrance.enter_coop, RegionName.coop),
ConnectionData(Entrance.enter_barn, RegionName.barn),
ConnectionData(Entrance.enter_shed, RegionName.shed),
ConnectionData(Entrance.enter_slime_hutch, RegionName.slime_hutch),
ConnectionData(Entrance.use_desert_obelisk, RegionName.desert),
ConnectionData(Entrance.use_island_obelisk, RegionName.island_south, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.use_farm_obelisk, RegionName.farm),
ConnectionData(Entrance.backwoods_to_mountain, RegionName.mountain),
ConnectionData(Entrance.bus_stop_to_town, RegionName.town),
ConnectionData(Entrance.bus_stop_to_tunnel_entrance, RegionName.tunnel_entrance),
ConnectionData(Entrance.tunnel_entrance_to_bus_tunnel, RegionName.bus_tunnel, flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(Entrance.take_bus_to_desert, RegionName.desert),
ConnectionData(Entrance.forest_to_town, RegionName.town),
ConnectionData(Entrance.forest_to_wizard_tower, RegionName.wizard_tower,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_wizard_basement, RegionName.wizard_basement, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.forest_to_marnie_ranch, RegionName.ranch,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.forest_to_leah_cottage, RegionName.leah_house,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_secret_woods, RegionName.secret_woods),
ConnectionData(Entrance.forest_to_sewer, RegionName.sewer, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.forest_to_mastery_cave, RegionName.mastery_cave, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.MASTERIES),
ConnectionData(Entrance.town_to_sewer, RegionName.sewer, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_mutant_bug_lair, RegionName.mutant_bug_lair, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.mountain_to_railroad, RegionName.railroad),
ConnectionData(Entrance.mountain_to_tent, RegionName.tent,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.mountain_to_leo_treehouse, RegionName.leo_treehouse,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.mountain_to_carpenter_shop, RegionName.carpenter,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.mountain_to_maru_room, RegionName.maru_room,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_sebastian_room, RegionName.sebastian_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.mountain_to_adventurer_guild, RegionName.adventurer_guild,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.adventurer_guild_to_bedroom, RegionName.adventurer_guild_bedroom),
ConnectionData(Entrance.enter_quarry, RegionName.quarry),
ConnectionData(Entrance.enter_quarry_mine_entrance, RegionName.quarry_mine_entrance,
flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_quarry_mine, RegionName.quarry_mine),
ConnectionData(Entrance.mountain_to_town, RegionName.town),
ConnectionData(Entrance.town_to_community_center, RegionName.community_center,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.access_crafts_room, RegionName.crafts_room),
ConnectionData(Entrance.access_pantry, RegionName.pantry),
ConnectionData(Entrance.access_fish_tank, RegionName.fish_tank),
ConnectionData(Entrance.access_boiler_room, RegionName.boiler_room),
ConnectionData(Entrance.access_bulletin_board, RegionName.bulletin_board),
ConnectionData(Entrance.access_vault, RegionName.vault),
ConnectionData(Entrance.town_to_hospital, RegionName.hospital,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_harvey_room, RegionName.harvey_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.town_to_pierre_general_store, RegionName.pierre_store,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_sunroom, RegionName.sunroom, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.town_to_clint_blacksmith, RegionName.blacksmith,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_saloon, RegionName.saloon,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.play_journey_of_the_prairie_king, RegionName.jotpk_world_1),
ConnectionData(Entrance.reach_jotpk_world_2, RegionName.jotpk_world_2),
ConnectionData(Entrance.reach_jotpk_world_3, RegionName.jotpk_world_3),
ConnectionData(Entrance.play_junimo_kart, RegionName.junimo_kart_1),
ConnectionData(Entrance.reach_junimo_kart_2, RegionName.junimo_kart_2),
ConnectionData(Entrance.reach_junimo_kart_3, RegionName.junimo_kart_3),
ConnectionData(Entrance.reach_junimo_kart_4, RegionName.junimo_kart_4),
ConnectionData(Entrance.town_to_sam_house, RegionName.sam_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_haley_house, RegionName.haley_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_mayor_manor, RegionName.mayor_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_alex_house, RegionName.alex_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_trailer, RegionName.trailer,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_museum, RegionName.museum,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_jojamart, RegionName.jojamart,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.purchase_movie_ticket, RegionName.movie_ticket_stand),
ConnectionData(Entrance.enter_abandoned_jojamart, RegionName.abandoned_jojamart),
ConnectionData(Entrance.enter_movie_theater, RegionName.movie_theater),
ConnectionData(Entrance.town_to_beach, RegionName.beach),
ConnectionData(Entrance.enter_elliott_house, RegionName.elliott_house,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.beach_to_willy_fish_shop, RegionName.fish_shop,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.fish_shop_to_boat_tunnel, RegionName.boat_tunnel,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.boat_to_ginger_island, RegionName.island_south, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.enter_tide_pools, RegionName.tide_pools),
ConnectionData(Entrance.mountain_to_the_mines, RegionName.mines,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.dig_to_mines_floor_5, RegionName.mines_floor_5),
ConnectionData(Entrance.dig_to_mines_floor_10, RegionName.mines_floor_10),
ConnectionData(Entrance.dig_to_mines_floor_15, RegionName.mines_floor_15),
ConnectionData(Entrance.dig_to_mines_floor_20, RegionName.mines_floor_20),
ConnectionData(Entrance.dig_to_mines_floor_25, RegionName.mines_floor_25),
ConnectionData(Entrance.dig_to_mines_floor_30, RegionName.mines_floor_30),
ConnectionData(Entrance.dig_to_mines_floor_35, RegionName.mines_floor_35),
ConnectionData(Entrance.dig_to_mines_floor_40, RegionName.mines_floor_40),
ConnectionData(Entrance.dig_to_mines_floor_45, RegionName.mines_floor_45),
ConnectionData(Entrance.dig_to_mines_floor_50, RegionName.mines_floor_50),
ConnectionData(Entrance.dig_to_mines_floor_55, RegionName.mines_floor_55),
ConnectionData(Entrance.dig_to_mines_floor_60, RegionName.mines_floor_60),
ConnectionData(Entrance.dig_to_mines_floor_65, RegionName.mines_floor_65),
ConnectionData(Entrance.dig_to_mines_floor_70, RegionName.mines_floor_70),
ConnectionData(Entrance.dig_to_mines_floor_75, RegionName.mines_floor_75),
ConnectionData(Entrance.dig_to_mines_floor_80, RegionName.mines_floor_80),
ConnectionData(Entrance.dig_to_mines_floor_85, RegionName.mines_floor_85),
ConnectionData(Entrance.dig_to_mines_floor_90, RegionName.mines_floor_90),
ConnectionData(Entrance.dig_to_mines_floor_95, RegionName.mines_floor_95),
ConnectionData(Entrance.dig_to_mines_floor_100, RegionName.mines_floor_100),
ConnectionData(Entrance.dig_to_mines_floor_105, RegionName.mines_floor_105),
ConnectionData(Entrance.dig_to_mines_floor_110, RegionName.mines_floor_110),
ConnectionData(Entrance.dig_to_mines_floor_115, RegionName.mines_floor_115),
ConnectionData(Entrance.dig_to_mines_floor_120, RegionName.mines_floor_120),
ConnectionData(Entrance.dig_to_dangerous_mines_20, RegionName.dangerous_mines_20, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.dig_to_dangerous_mines_60, RegionName.dangerous_mines_60, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.dig_to_dangerous_mines_100, RegionName.dangerous_mines_100, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.enter_skull_cavern_entrance, RegionName.skull_cavern_entrance,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_oasis, RegionName.oasis,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_casino, RegionName.casino, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_skull_cavern, RegionName.skull_cavern),
ConnectionData(Entrance.mine_to_skull_cavern_floor_25, RegionName.skull_cavern_25),
ConnectionData(Entrance.mine_to_skull_cavern_floor_50, RegionName.skull_cavern_50),
ConnectionData(Entrance.mine_to_skull_cavern_floor_75, RegionName.skull_cavern_75),
ConnectionData(Entrance.mine_to_skull_cavern_floor_100, RegionName.skull_cavern_100),
ConnectionData(Entrance.mine_to_skull_cavern_floor_125, RegionName.skull_cavern_125),
ConnectionData(Entrance.mine_to_skull_cavern_floor_150, RegionName.skull_cavern_150),
ConnectionData(Entrance.mine_to_skull_cavern_floor_175, RegionName.skull_cavern_175),
ConnectionData(Entrance.mine_to_skull_cavern_floor_200, RegionName.skull_cavern_200),
ConnectionData(Entrance.enter_dangerous_skull_cavern, RegionName.dangerous_skull_cavern, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.enter_witch_warp_cave, RegionName.witch_warp_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_swamp, RegionName.witch_swamp, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_hut, RegionName.witch_hut, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.witch_warp_to_wizard_basement, RegionName.wizard_basement, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_bathhouse_entrance, RegionName.bathhouse_entrance,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_locker_room, RegionName.locker_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_public_bath, RegionName.public_bath, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_south_to_west, RegionName.island_west, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_south_to_north, RegionName.island_north, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_south_to_east, RegionName.island_east, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_south_to_southeast, RegionName.island_south_east,
flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.use_island_resort, RegionName.island_resort, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_islandfarmhouse, RegionName.island_farmhouse,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_gourmand_cave, RegionName.gourmand_frog_cave,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_crystals_cave, RegionName.colored_crystals_cave,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_shipwreck, RegionName.shipwreck,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_west_to_qi_walnut_room, RegionName.qi_walnut_room, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_east_to_leo_hut, RegionName.leo_hut,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_east_to_island_shrine, RegionName.island_shrine,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_southeast_to_pirate_cove, RegionName.pirate_cove,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_north_to_field_office, RegionName.field_office,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_north_to_dig_site, RegionName.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.dig_site_to_professor_snail_cave, RegionName.professor_snail_cave,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.island_north_to_volcano, RegionName.volcano,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.volcano_to_secret_beach, RegionName.volcano_secret_beach,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.talk_to_island_trader, RegionName.island_trader, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.climb_to_volcano_5, RegionName.volcano_floor_5, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.talk_to_volcano_dwarf, RegionName.volcano_dwarf_shop, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.climb_to_volcano_10, RegionName.volcano_floor_10, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_jungle_to_docks, RegionName.island_south, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_dig_site_to_docks, RegionName.island_south, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_volcano_to_docks, RegionName.island_south, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_volcano_to_jungle, RegionName.island_west, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_docks_to_jungle, RegionName.island_west, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_dig_site_to_jungle, RegionName.island_west, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_docks_to_dig_site, RegionName.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_volcano_to_dig_site, RegionName.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_jungle_to_dig_site, RegionName.dig_site, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_dig_site_to_volcano, RegionName.island_north, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_docks_to_volcano, RegionName.island_north, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(Entrance.parrot_express_jungle_to_volcano, RegionName.island_north, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(LogicEntrance.talk_to_mines_dwarf, LogicRegion.mines_dwarf_shop),
ConnectionData(LogicEntrance.buy_from_traveling_merchant, LogicRegion.traveling_cart),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_sunday, LogicRegion.traveling_cart_sunday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_monday, LogicRegion.traveling_cart_monday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_tuesday, LogicRegion.traveling_cart_tuesday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_wednesday, LogicRegion.traveling_cart_wednesday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_thursday, LogicRegion.traveling_cart_thursday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_friday, LogicRegion.traveling_cart_friday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_saturday, LogicRegion.traveling_cart_saturday),
ConnectionData(LogicEntrance.complete_raccoon_requests, LogicRegion.raccoon_daddy),
ConnectionData(LogicEntrance.fish_in_waterfall, LogicRegion.forest_waterfall),
ConnectionData(LogicEntrance.buy_from_raccoon, LogicRegion.raccoon_shop),
ConnectionData(LogicEntrance.farmhouse_cooking, LogicRegion.kitchen),
ConnectionData(LogicEntrance.watch_queen_of_sauce, LogicRegion.queen_of_sauce),
ConnectionData(LogicEntrance.grow_spring_crops, LogicRegion.spring_farming),
ConnectionData(LogicEntrance.grow_summer_crops, LogicRegion.summer_farming),
ConnectionData(LogicEntrance.grow_fall_crops, LogicRegion.fall_farming),
ConnectionData(LogicEntrance.grow_winter_crops, LogicRegion.winter_farming),
ConnectionData(LogicEntrance.grow_spring_crops_in_greenhouse, LogicRegion.spring_farming),
ConnectionData(LogicEntrance.grow_summer_crops_in_greenhouse, LogicRegion.summer_farming),
ConnectionData(LogicEntrance.grow_fall_crops_in_greenhouse, LogicRegion.fall_farming),
ConnectionData(LogicEntrance.grow_winter_crops_in_greenhouse, LogicRegion.winter_farming),
ConnectionData(LogicEntrance.grow_indoor_crops_in_greenhouse, LogicRegion.indoor_farming),
ConnectionData(LogicEntrance.grow_spring_crops_on_island, LogicRegion.spring_farming, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(LogicEntrance.grow_summer_crops_on_island, LogicRegion.summer_farming, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(LogicEntrance.grow_fall_crops_on_island, LogicRegion.fall_farming, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(LogicEntrance.grow_winter_crops_on_island, LogicRegion.winter_farming, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(LogicEntrance.grow_indoor_crops_on_island, LogicRegion.indoor_farming, flag=RandomizationFlag.GINGER_ISLAND),
ConnectionData(LogicEntrance.grow_summer_fall_crops_in_summer, LogicRegion.summer_or_fall_farming),
ConnectionData(LogicEntrance.grow_summer_fall_crops_in_fall, LogicRegion.summer_or_fall_farming),
ConnectionData(LogicEntrance.shipping, LogicRegion.shipping),
ConnectionData(LogicEntrance.blacksmith_copper, LogicRegion.blacksmith_copper),
ConnectionData(LogicEntrance.blacksmith_iron, LogicRegion.blacksmith_iron),
ConnectionData(LogicEntrance.blacksmith_gold, LogicRegion.blacksmith_gold),
ConnectionData(LogicEntrance.blacksmith_iridium, LogicRegion.blacksmith_iridium),
ConnectionData(LogicEntrance.fishing, LogicRegion.fishing),
ConnectionData(LogicEntrance.island_cooking, LogicRegion.kitchen),
ConnectionData(LogicEntrance.attend_egg_festival, LogicRegion.egg_festival),
ConnectionData(LogicEntrance.attend_desert_festival, LogicRegion.desert_festival),
ConnectionData(LogicEntrance.attend_flower_dance, LogicRegion.flower_dance),
ConnectionData(LogicEntrance.attend_luau, LogicRegion.luau),
ConnectionData(LogicEntrance.attend_trout_derby, LogicRegion.trout_derby),
ConnectionData(LogicEntrance.attend_moonlight_jellies, LogicRegion.moonlight_jellies),
ConnectionData(LogicEntrance.attend_fair, LogicRegion.fair),
ConnectionData(LogicEntrance.attend_spirit_eve, LogicRegion.spirit_eve),
ConnectionData(LogicEntrance.attend_festival_of_ice, LogicRegion.festival_of_ice),
ConnectionData(LogicEntrance.attend_night_market, LogicRegion.night_market),
ConnectionData(LogicEntrance.attend_winter_star, LogicRegion.winter_star),
ConnectionData(LogicEntrance.attend_squidfest, LogicRegion.squidfest),
ConnectionData(LogicEntrance.buy_experience_books, LogicRegion.bookseller_1),
ConnectionData(LogicEntrance.buy_year1_books, LogicRegion.bookseller_2),
ConnectionData(LogicEntrance.buy_year3_books, LogicRegion.bookseller_3),
]
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 sorted(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)
if ModificationFlag.MODIFIED in mod_region.flag:
mod_region = modify_vanilla_regions(existing_region, mod_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_and_regions(world_options) -> Tuple[Dict[str, ConnectionData], Dict[str, RegionData]]:
regions_data: Dict[str, RegionData] = {region.name: region for region in create_final_regions(world_options)}
connections = {connection.name: connection for connection in vanilla_connections}
connections = modify_connections_for_mods(connections, sorted(world_options.mods.value))
include_island = world_options.exclude_ginger_island == ExcludeGingerIsland.option_false
return remove_ginger_island_regions_and_connections(regions_data, connections, include_island)
def remove_ginger_island_regions_and_connections(regions_by_name: Dict[str, RegionData], connections: Dict[str, ConnectionData], include_island: bool):
if include_island:
return connections, regions_by_name
removed_connections = set()
for connection_name in tuple(connections):
connection = connections[connection_name]
if connection.flag & RandomizationFlag.GINGER_ISLAND:
connections.pop(connection_name)
removed_connections.add(connection_name)
for region_name in tuple(regions_by_name):
region = regions_by_name[region_name]
if region.is_ginger_island:
regions_by_name.pop(region_name)
else:
regions_by_name[region_name] = region.get_without_exits(removed_connections)
return connections, regions_by_name
def modify_connections_for_mods(connections: Dict[str, ConnectionData], mods: Iterable) -> Dict[str, ConnectionData]:
for mod in mods:
if mod not in ModDataList:
continue
if mod in vanilla_connections_to_remove_by_mod:
for connection_data in vanilla_connections_to_remove_by_mod[mod]:
connections.pop(connection_data.name)
connections.update({connection.name: connection for connection in ModDataList[mod].connections})
return connections
def modify_vanilla_regions(existing_region: RegionData, modified_region: RegionData) -> RegionData:
updated_region = existing_region
region_exits = updated_region.exits
modified_exits = modified_region.exits
for exits in modified_exits:
region_exits.remove(exits)
return updated_region
def create_regions(region_factory: RegionFactory, random: Random, world_options: StardewValleyOptions, content: StardewContent) \
-> Tuple[Dict[str, Region], Dict[str, Entrance], Dict[str, str]]:
entrances_data, regions_data = create_final_connections_and_regions(world_options)
regions_by_name: Dict[str: Region] = {region_name: region_factory(region_name, regions_data[region_name].exits) for region_name in regions_data}
entrances_by_name: Dict[str: Entrance] = {
entrance.name: entrance
for region in regions_by_name.values()
for entrance in region.exits
if entrance.name in entrances_data
}
connections, randomized_data = randomize_connections(random, world_options, content, regions_data, entrances_data)
for connection in connections:
if connection.name in entrances_by_name:
entrances_by_name[connection.name].connect(regions_by_name[connection.destination])
return regions_by_name, entrances_by_name, randomized_data
def randomize_connections(random: Random, world_options: StardewValleyOptions, content: StardewContent, regions_by_name: Dict[str, RegionData],
connections_by_name: Dict[str, ConnectionData]) -> Tuple[List[ConnectionData], Dict[str, str]]:
connections_to_randomize: List[ConnectionData] = []
if world_options.entrance_randomization == EntranceRandomization.option_pelican_town:
connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
RandomizationFlag.PELICAN_TOWN in connections_by_name[connection].flag]
elif world_options.entrance_randomization == EntranceRandomization.option_non_progression:
connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
RandomizationFlag.NON_PROGRESSION in connections_by_name[connection].flag]
elif world_options.entrance_randomization == EntranceRandomization.option_buildings or world_options.entrance_randomization == EntranceRandomization.option_buildings_without_house:
connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
RandomizationFlag.BUILDINGS in connections_by_name[connection].flag]
elif world_options.entrance_randomization == EntranceRandomization.option_chaos:
connections_to_randomize = [connections_by_name[connection] for connection in connections_by_name if
RandomizationFlag.BUILDINGS in connections_by_name[connection].flag]
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, content)
# 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 list(connections_by_name.values()), randomized_data_for_mod
connections_to_randomize = remove_excluded_entrances(connections_to_randomize, content)
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(list(connections_by_name.values()), 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: List[ConnectionData], content: StardewContent) -> List[ConnectionData]:
# FIXME remove when regions are handled in content packs
if content_packs.ginger_island_content_pack.name not in content.registered_packs:
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.GINGER_ISLAND not in connection.flag]
if not content.features.skill_progression.are_masteries_shuffled:
connections_to_randomize = [connection for connection in connections_to_randomize if RandomizationFlag.MASTERIES 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(all_connections: List[ConnectionData], connections_to_randomize: List[ConnectionData],
randomized_connections: Dict[ConnectionData, ConnectionData]):
for connection in all_connections:
if connection in connections_to_randomize:
continue
randomized_connections[connection] = connection
def swap_connections_until_valid(regions_by_name, connections_by_name: Dict[str, ConnectionData], 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_random_connection(regions_by_name, connections_by_name, randomized_connections, reachable_regions,
unreachable_regions, connections_to_randomize, random)
def region_should_be_reachable(region_name: str, connections_in_slot: Iterable[ConnectionData]) -> bool:
if region_name == RegionName.menu:
return True
for connection in connections_in_slot:
if region_name == connection.destination:
return True
return False
def find_reachable_regions(regions_by_name, connections_by_name,
randomized_connections: Dict[ConnectionData, ConnectionData]):
reachable_regions = {RegionName.menu}
unreachable_regions = {region for region in regions_by_name.keys()}
# unreachable_regions = {region for region in regions_by_name.keys() if region_should_be_reachable(region, connections_by_name.values())}
unreachable_regions.remove(RegionName.menu)
exits_to_explore = list(regions_by_name[RegionName.menu].exits)
while exits_to_explore:
exit_name = exits_to_explore.pop()
# if exit_name not in connections_by_name:
# continue
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_random_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 = [region for region in sorted(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]
swap_two_connections(chosen_reachable_entrance, chosen_unreachable_entrance, randomized_connections)
def swap_two_connections(entrance_1, entrance_2, randomized_connections):
reachable_destination = randomized_connections[entrance_1]
unreachable_destination = randomized_connections[entrance_2]
randomized_connections[entrance_1] = unreachable_destination
randomized_connections[entrance_2] = reachable_destination

View File

@@ -0,0 +1,2 @@
from .entrance_rando import prepare_mod_data
from .regions import create_regions, RegionFactory

View File

@@ -0,0 +1,73 @@
from BaseClasses import Region
from entrance_rando import ERPlacementState
from .model import ConnectionData, RandomizationFlag, reverse_connection_name, RegionData
from ..content import StardewContent
from ..options import EntranceRandomization
def create_player_randomization_flag(entrance_randomization_choice: EntranceRandomization, content: StardewContent):
"""Return the flag that a connection is expected to have to be randomized. Only the bit corresponding to the player randomization choice will be enabled.
Other bits for content exclusion might also be enabled, tho the preferred solution to exclude content should be to not create those regions at alls, when possible.
"""
flag = RandomizationFlag.NOT_RANDOMIZED
if entrance_randomization_choice.value == EntranceRandomization.option_disabled:
return flag
if entrance_randomization_choice == EntranceRandomization.option_pelican_town:
flag |= RandomizationFlag.BIT_PELICAN_TOWN
elif entrance_randomization_choice == EntranceRandomization.option_non_progression:
flag |= RandomizationFlag.BIT_NON_PROGRESSION
elif entrance_randomization_choice in (
EntranceRandomization.option_buildings,
EntranceRandomization.option_buildings_without_house,
EntranceRandomization.option_chaos
):
flag |= RandomizationFlag.BIT_BUILDINGS
if not content.features.skill_progression.are_masteries_shuffled:
flag |= RandomizationFlag.EXCLUDE_MASTERIES
return flag
def connect_regions(region_data_by_name: dict[str, RegionData], connection_data_by_name: dict[str, ConnectionData], regions_by_name: dict[str, Region],
player_randomization_flag: RandomizationFlag) -> None:
for region_name, region_data in region_data_by_name.items():
origin_region = regions_by_name[region_name]
for exit_name in region_data.exits:
connection_data = connection_data_by_name[exit_name]
destination_region = regions_by_name[connection_data.destination]
if connection_data.is_eligible_for_randomization(player_randomization_flag):
create_entrance_rando_target(origin_region, destination_region, connection_data)
else:
origin_region.connect(destination_region, connection_data.name)
def create_entrance_rando_target(origin: Region, destination: Region, connection_data: ConnectionData) -> None:
"""We need our own function to create the GER targets, because the Stardew Mod have very specific expectations for the name of the entrances.
We need to know exactly which entrances to swap in both directions."""
origin.create_exit(connection_data.name)
destination.create_er_target(connection_data.reverse)
def prepare_mod_data(placements: ERPlacementState) -> dict[str, str]:
"""Take the placements from GER and prepare the data for the mod.
The mod require a dictionary detailing which connections need to be swapped. It acts as if the connections are decoupled, so both directions are required.
For instance, GER will provide placements like (Town to Community Center, Hospital to Town), meaning that the door of the Community Center will instead lead
to the Hospital, and that the exit of the Hospital will lead to the Town by the Community Center door. The StardewAP mod need to know both swaps, being the
original destination of the "Town to Community Center" connection is to be replaced by the original destination of "Town to Hospital", and the original
destination of "Hospital to Town" is to be replaced by the original destination of "Community Center to Town".
"""
swapped_connections = {}
for entrance, exit_ in placements.pairings:
swapped_connections[entrance] = reverse_connection_name(exit_)
swapped_connections[exit_] = reverse_connection_name(entrance)
return swapped_connections

View File

@@ -0,0 +1,94 @@
from __future__ import annotations
from collections.abc import Container
from dataclasses import dataclass, field
from enum import IntFlag
connector_keyword = " to "
def reverse_connection_name(name: str) -> str | None:
try:
origin, destination = name.split(connector_keyword)
except ValueError:
return None
return f"{destination}{connector_keyword}{origin}"
class MergeFlag(IntFlag):
ADD_EXITS = 0
REMOVE_EXITS = 1
class RandomizationFlag(IntFlag):
NOT_RANDOMIZED = 0
# Randomization options
# The first 4 bits are used to mark if an entrance is eligible for randomization according to the entrance randomization options.
BIT_PELICAN_TOWN = 1 # 0b0001
BIT_NON_PROGRESSION = 1 << 1 # 0b0010
BIT_BUILDINGS = 1 << 2 # 0b0100
BIT_EVERYTHING = 1 << 3 # 0b1000
# Content flag for entrances exclusions
# The next 2 bits are used to mark if an entrance is to be excluded from randomization according to the content options.
# Those bits must be removed from an entrance flags when then entrance must be excluded.
__UNUSED = 1 << 4 # 0b010000
EXCLUDE_MASTERIES = 1 << 5 # 0b100000
# Entrance groups
# The last bit is used to add additional qualifiers on entrances to group them
# Those bits should be added when an entrance need additional qualifiers.
LEAD_TO_OPEN_AREA = 1 << 6
# Tags to apply on connections
EVERYTHING = EXCLUDE_MASTERIES | BIT_EVERYTHING
BUILDINGS = EVERYTHING | BIT_BUILDINGS
NON_PROGRESSION = BUILDINGS | BIT_NON_PROGRESSION
PELICAN_TOWN = NON_PROGRESSION | BIT_PELICAN_TOWN
@dataclass(frozen=True)
class RegionData:
name: str
exits: tuple[str, ...] = field(default_factory=tuple)
flag: MergeFlag = MergeFlag.ADD_EXITS
def __post_init__(self):
assert not isinstance(self.exits, str), "Exits must be a tuple of strings, you probably forgot a trailing comma."
def merge_with(self, other: RegionData) -> RegionData:
assert self.name == other.name, "Regions must have the same name to be merged"
if other.flag == MergeFlag.REMOVE_EXITS:
return self.get_without_exits(other.exits)
merged_exits = self.exits + other.exits
assert len(merged_exits) == len(set(merged_exits)), "Two regions getting merged have duplicated exists..."
return RegionData(self.name, merged_exits)
def get_without_exits(self, exits_to_remove: Container[str]) -> RegionData:
exits = tuple(exit_ for exit_ in self.exits if exit_ not in exits_to_remove)
return RegionData(self.name, exits)
@dataclass(frozen=True)
class ConnectionData:
name: str
destination: str
flag: RandomizationFlag = RandomizationFlag.NOT_RANDOMIZED
@property
def reverse(self) -> str | None:
return reverse_connection_name(self.name)
def is_eligible_for_randomization(self, chosen_randomization_flag: RandomizationFlag) -> bool:
return chosen_randomization_flag and chosen_randomization_flag in self.flag
@dataclass(frozen=True)
class ModRegionsData:
mod_name: str
regions: list[RegionData]
connections: list[ConnectionData]

View File

@@ -0,0 +1,46 @@
from collections.abc import Iterable
from .model import ConnectionData, RegionData, ModRegionsData
from ..mods.region_data import region_data_by_content_pack, vanilla_connections_to_remove_by_content_pack
def modify_regions_for_mods(current_regions_by_name: dict[str, RegionData], active_content_packs: Iterable[str]) -> None:
for content_pack in active_content_packs:
try:
region_data = region_data_by_content_pack[content_pack]
except KeyError:
continue
merge_mod_regions(current_regions_by_name, region_data)
def merge_mod_regions(current_regions_by_name: dict[str, RegionData], mod_region_data: ModRegionsData) -> None:
for new_region in mod_region_data.regions:
region_name = new_region.name
try:
current_region = current_regions_by_name[region_name]
except KeyError:
current_regions_by_name[region_name] = new_region
continue
current_regions_by_name[region_name] = current_region.merge_with(new_region)
def modify_connections_for_mods(connections: dict[str, ConnectionData], active_mods: Iterable[str]) -> None:
for active_mod in active_mods:
try:
region_data = region_data_by_content_pack[active_mod]
except KeyError:
continue
try:
vanilla_connections_to_remove = vanilla_connections_to_remove_by_content_pack[active_mod]
for connection_name in vanilla_connections_to_remove:
connections.pop(connection_name)
except KeyError:
pass
connections.update({
connection.name: connection
for connection in region_data.connections
})

View File

@@ -0,0 +1,61 @@
from typing import Protocol
from BaseClasses import Region
from . import vanilla_data, mods
from .entrance_rando import create_player_randomization_flag, connect_regions
from .model import ConnectionData, RegionData
from ..content import StardewContent
from ..content.vanilla.ginger_island import ginger_island_content_pack
from ..options import StardewValleyOptions
class RegionFactory(Protocol):
def __call__(self, name: str) -> Region:
raise NotImplementedError
def create_regions(region_factory: RegionFactory, world_options: StardewValleyOptions, content: StardewContent) -> dict[str, Region]:
connection_data_by_name, region_data_by_name = create_connections_and_regions(content.registered_packs)
regions_by_name: dict[str: Region] = {
region_name: region_factory(region_name)
for region_name in region_data_by_name
}
randomization_flag = create_player_randomization_flag(world_options.entrance_randomization, content)
connect_regions(region_data_by_name, connection_data_by_name, regions_by_name, randomization_flag)
return regions_by_name
def create_connections_and_regions(active_content_packs: set[str]) -> tuple[dict[str, ConnectionData], dict[str, RegionData]]:
regions_by_name = create_all_regions(active_content_packs)
connections_by_name = create_all_connections(active_content_packs)
return connections_by_name, regions_by_name
def create_all_regions(active_content_packs: set[str]) -> dict[str, RegionData]:
current_regions_by_name = create_vanilla_regions(active_content_packs)
mods.modify_regions_for_mods(current_regions_by_name, sorted(active_content_packs))
return current_regions_by_name
def create_vanilla_regions(active_content_packs: set[str]) -> dict[str, RegionData]:
if ginger_island_content_pack.name in active_content_packs:
return {**vanilla_data.regions_with_ginger_island_by_name}
else:
return {**vanilla_data.regions_without_ginger_island_by_name}
def create_all_connections(active_content_packs: set[str]) -> dict[str, ConnectionData]:
connections = create_vanilla_connections(active_content_packs)
mods.modify_connections_for_mods(connections, sorted(active_content_packs))
return connections
def create_vanilla_connections(active_content_packs: set[str]) -> dict[str, ConnectionData]:
if ginger_island_content_pack.name in active_content_packs:
return {**vanilla_data.connections_with_ginger_island_by_name}
else:
return {**vanilla_data.connections_without_ginger_island_by_name}

View File

@@ -0,0 +1,522 @@
from collections.abc import Mapping
from types import MappingProxyType
from .model import ConnectionData, RandomizationFlag, RegionData
from ..strings.entrance_names import LogicEntrance, Entrance
from ..strings.region_names import LogicRegion, Region as RegionName
vanilla_regions: tuple[RegionData, ...] = (
RegionData(RegionName.menu, (Entrance.to_stardew_valley,)),
RegionData(RegionName.stardew_valley, (Entrance.to_farmhouse,)),
RegionData(RegionName.farm_house,
(Entrance.farmhouse_to_farm, Entrance.downstairs_to_cellar, LogicEntrance.farmhouse_cooking, LogicEntrance.watch_queen_of_sauce)),
RegionData(RegionName.cellar),
RegionData(RegionName.farm,
(Entrance.farm_to_backwoods, Entrance.farm_to_bus_stop, Entrance.farm_to_forest, Entrance.farm_to_farmcave, Entrance.enter_greenhouse,
Entrance.enter_coop, Entrance.enter_barn, Entrance.enter_shed, Entrance.enter_slime_hutch, LogicEntrance.grow_spring_crops,
LogicEntrance.grow_summer_crops, LogicEntrance.grow_fall_crops, LogicEntrance.grow_winter_crops, LogicEntrance.shipping,
LogicEntrance.fishing,)),
RegionData(RegionName.backwoods, (Entrance.backwoods_to_mountain,)),
RegionData(RegionName.bus_stop,
(Entrance.bus_stop_to_town, Entrance.take_bus_to_desert, Entrance.bus_stop_to_tunnel_entrance)),
RegionData(RegionName.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.forest_to_mastery_cave, LogicEntrance.buy_from_traveling_merchant,
LogicEntrance.complete_raccoon_requests, LogicEntrance.fish_in_waterfall, LogicEntrance.attend_flower_dance, LogicEntrance.attend_trout_derby,
LogicEntrance.attend_festival_of_ice)),
RegionData(LogicRegion.forest_waterfall),
RegionData(RegionName.farm_cave),
RegionData(RegionName.greenhouse,
(LogicEntrance.grow_spring_crops_in_greenhouse, LogicEntrance.grow_summer_crops_in_greenhouse, LogicEntrance.grow_fall_crops_in_greenhouse,
LogicEntrance.grow_winter_crops_in_greenhouse, LogicEntrance.grow_indoor_crops_in_greenhouse)),
RegionData(RegionName.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)),
RegionData(RegionName.maru_room),
RegionData(RegionName.tunnel_entrance, (Entrance.tunnel_entrance_to_bus_tunnel,)),
RegionData(RegionName.bus_tunnel),
RegionData(RegionName.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,
Entrance.purchase_movie_ticket, LogicEntrance.buy_experience_books, LogicEntrance.attend_egg_festival, LogicEntrance.attend_fair,
LogicEntrance.attend_spirit_eve, LogicEntrance.attend_winter_star)),
RegionData(RegionName.beach,
(Entrance.beach_to_willy_fish_shop, Entrance.enter_elliott_house, Entrance.enter_tide_pools, LogicEntrance.attend_luau,
LogicEntrance.attend_moonlight_jellies, LogicEntrance.attend_night_market, LogicEntrance.attend_squidfest)),
RegionData(RegionName.railroad, (Entrance.enter_bathhouse_entrance, Entrance.enter_witch_warp_cave)),
RegionData(RegionName.ranch),
RegionData(RegionName.leah_house),
RegionData(RegionName.mastery_cave),
RegionData(RegionName.sewer, (Entrance.enter_mutant_bug_lair,)),
RegionData(RegionName.mutant_bug_lair),
RegionData(RegionName.wizard_tower, (Entrance.enter_wizard_basement, Entrance.use_desert_obelisk)),
RegionData(RegionName.wizard_basement),
RegionData(RegionName.tent),
RegionData(RegionName.carpenter, (Entrance.enter_sebastian_room,)),
RegionData(RegionName.sebastian_room),
RegionData(RegionName.adventurer_guild, (Entrance.adventurer_guild_to_bedroom,)),
RegionData(RegionName.adventurer_guild_bedroom),
RegionData(RegionName.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(RegionName.crafts_room),
RegionData(RegionName.pantry),
RegionData(RegionName.fish_tank),
RegionData(RegionName.boiler_room),
RegionData(RegionName.bulletin_board),
RegionData(RegionName.vault),
RegionData(RegionName.hospital, (Entrance.enter_harvey_room,)),
RegionData(RegionName.harvey_room),
RegionData(RegionName.pierre_store, (Entrance.enter_sunroom,)),
RegionData(RegionName.sunroom),
RegionData(RegionName.saloon, (Entrance.play_journey_of_the_prairie_king, Entrance.play_junimo_kart)),
RegionData(RegionName.jotpk_world_1, (Entrance.reach_jotpk_world_2,)),
RegionData(RegionName.jotpk_world_2, (Entrance.reach_jotpk_world_3,)),
RegionData(RegionName.jotpk_world_3),
RegionData(RegionName.junimo_kart_1, (Entrance.reach_junimo_kart_2,)),
RegionData(RegionName.junimo_kart_2, (Entrance.reach_junimo_kart_3,)),
RegionData(RegionName.junimo_kart_3, (Entrance.reach_junimo_kart_4,)),
RegionData(RegionName.junimo_kart_4),
RegionData(RegionName.alex_house),
RegionData(RegionName.trailer),
RegionData(RegionName.mayor_house),
RegionData(RegionName.sam_house),
RegionData(RegionName.haley_house),
RegionData(RegionName.blacksmith, (LogicEntrance.blacksmith_copper,)),
RegionData(RegionName.museum),
RegionData(RegionName.jojamart, (Entrance.enter_abandoned_jojamart,)),
RegionData(RegionName.abandoned_jojamart, (Entrance.enter_movie_theater,)),
RegionData(RegionName.movie_ticket_stand),
RegionData(RegionName.movie_theater),
RegionData(RegionName.fish_shop),
RegionData(RegionName.elliott_house),
RegionData(RegionName.tide_pools),
RegionData(RegionName.bathhouse_entrance, (Entrance.enter_locker_room,)),
RegionData(RegionName.locker_room, (Entrance.enter_public_bath,)),
RegionData(RegionName.public_bath),
RegionData(RegionName.witch_warp_cave, (Entrance.enter_witch_swamp,)),
RegionData(RegionName.witch_swamp, (Entrance.enter_witch_hut,)),
RegionData(RegionName.witch_hut, (Entrance.witch_warp_to_wizard_basement,)),
RegionData(RegionName.quarry, (Entrance.enter_quarry_mine_entrance,)),
RegionData(RegionName.quarry_mine_entrance, (Entrance.enter_quarry_mine,)),
RegionData(RegionName.quarry_mine),
RegionData(RegionName.secret_woods),
RegionData(RegionName.desert, (Entrance.enter_skull_cavern_entrance, Entrance.enter_oasis, LogicEntrance.attend_desert_festival)),
RegionData(RegionName.oasis, (Entrance.enter_casino,)),
RegionData(RegionName.casino),
RegionData(RegionName.skull_cavern_entrance, (Entrance.enter_skull_cavern,)),
RegionData(RegionName.skull_cavern, (Entrance.mine_to_skull_cavern_floor_25,)),
RegionData(RegionName.skull_cavern_25, (Entrance.mine_to_skull_cavern_floor_50,)),
RegionData(RegionName.skull_cavern_50, (Entrance.mine_to_skull_cavern_floor_75,)),
RegionData(RegionName.skull_cavern_75, (Entrance.mine_to_skull_cavern_floor_100,)),
RegionData(RegionName.skull_cavern_100, (Entrance.mine_to_skull_cavern_floor_125,)),
RegionData(RegionName.skull_cavern_125, (Entrance.mine_to_skull_cavern_floor_150,)),
RegionData(RegionName.skull_cavern_150, (Entrance.mine_to_skull_cavern_floor_175,)),
RegionData(RegionName.skull_cavern_175, (Entrance.mine_to_skull_cavern_floor_200,)),
RegionData(RegionName.skull_cavern_200),
RegionData(RegionName.coop),
RegionData(RegionName.barn),
RegionData(RegionName.shed),
RegionData(RegionName.slime_hutch),
RegionData(RegionName.mines, (LogicEntrance.talk_to_mines_dwarf, Entrance.dig_to_mines_floor_5)),
RegionData(RegionName.mines_floor_5, (Entrance.dig_to_mines_floor_10,)),
RegionData(RegionName.mines_floor_10, (Entrance.dig_to_mines_floor_15,)),
RegionData(RegionName.mines_floor_15, (Entrance.dig_to_mines_floor_20,)),
RegionData(RegionName.mines_floor_20, (Entrance.dig_to_mines_floor_25,)),
RegionData(RegionName.mines_floor_25, (Entrance.dig_to_mines_floor_30,)),
RegionData(RegionName.mines_floor_30, (Entrance.dig_to_mines_floor_35,)),
RegionData(RegionName.mines_floor_35, (Entrance.dig_to_mines_floor_40,)),
RegionData(RegionName.mines_floor_40, (Entrance.dig_to_mines_floor_45,)),
RegionData(RegionName.mines_floor_45, (Entrance.dig_to_mines_floor_50,)),
RegionData(RegionName.mines_floor_50, (Entrance.dig_to_mines_floor_55,)),
RegionData(RegionName.mines_floor_55, (Entrance.dig_to_mines_floor_60,)),
RegionData(RegionName.mines_floor_60, (Entrance.dig_to_mines_floor_65,)),
RegionData(RegionName.mines_floor_65, (Entrance.dig_to_mines_floor_70,)),
RegionData(RegionName.mines_floor_70, (Entrance.dig_to_mines_floor_75,)),
RegionData(RegionName.mines_floor_75, (Entrance.dig_to_mines_floor_80,)),
RegionData(RegionName.mines_floor_80, (Entrance.dig_to_mines_floor_85,)),
RegionData(RegionName.mines_floor_85, (Entrance.dig_to_mines_floor_90,)),
RegionData(RegionName.mines_floor_90, (Entrance.dig_to_mines_floor_95,)),
RegionData(RegionName.mines_floor_95, (Entrance.dig_to_mines_floor_100,)),
RegionData(RegionName.mines_floor_100, (Entrance.dig_to_mines_floor_105,)),
RegionData(RegionName.mines_floor_105, (Entrance.dig_to_mines_floor_110,)),
RegionData(RegionName.mines_floor_110, (Entrance.dig_to_mines_floor_115,)),
RegionData(RegionName.mines_floor_115, (Entrance.dig_to_mines_floor_120,)),
RegionData(RegionName.mines_floor_120),
RegionData(LogicRegion.mines_dwarf_shop),
RegionData(LogicRegion.blacksmith_copper, (LogicEntrance.blacksmith_iron,)),
RegionData(LogicRegion.blacksmith_iron, (LogicEntrance.blacksmith_gold,)),
RegionData(LogicRegion.blacksmith_gold, (LogicEntrance.blacksmith_iridium,)),
RegionData(LogicRegion.blacksmith_iridium),
RegionData(LogicRegion.kitchen),
RegionData(LogicRegion.queen_of_sauce),
RegionData(LogicRegion.fishing),
RegionData(LogicRegion.spring_farming),
RegionData(LogicRegion.summer_farming, (LogicEntrance.grow_summer_fall_crops_in_summer,)),
RegionData(LogicRegion.fall_farming, (LogicEntrance.grow_summer_fall_crops_in_fall,)),
RegionData(LogicRegion.winter_farming),
RegionData(LogicRegion.summer_or_fall_farming),
RegionData(LogicRegion.indoor_farming),
RegionData(LogicRegion.shipping),
RegionData(LogicRegion.traveling_cart, (LogicEntrance.buy_from_traveling_merchant_sunday,
LogicEntrance.buy_from_traveling_merchant_monday,
LogicEntrance.buy_from_traveling_merchant_tuesday,
LogicEntrance.buy_from_traveling_merchant_wednesday,
LogicEntrance.buy_from_traveling_merchant_thursday,
LogicEntrance.buy_from_traveling_merchant_friday,
LogicEntrance.buy_from_traveling_merchant_saturday)),
RegionData(LogicRegion.traveling_cart_sunday),
RegionData(LogicRegion.traveling_cart_monday),
RegionData(LogicRegion.traveling_cart_tuesday),
RegionData(LogicRegion.traveling_cart_wednesday),
RegionData(LogicRegion.traveling_cart_thursday),
RegionData(LogicRegion.traveling_cart_friday),
RegionData(LogicRegion.traveling_cart_saturday),
RegionData(LogicRegion.raccoon_daddy, (LogicEntrance.buy_from_raccoon,)),
RegionData(LogicRegion.raccoon_shop),
RegionData(LogicRegion.egg_festival),
RegionData(LogicRegion.desert_festival),
RegionData(LogicRegion.flower_dance),
RegionData(LogicRegion.luau),
RegionData(LogicRegion.trout_derby),
RegionData(LogicRegion.moonlight_jellies),
RegionData(LogicRegion.fair),
RegionData(LogicRegion.spirit_eve),
RegionData(LogicRegion.festival_of_ice),
RegionData(LogicRegion.night_market),
RegionData(LogicRegion.winter_star),
RegionData(LogicRegion.squidfest),
RegionData(LogicRegion.bookseller_1, (LogicEntrance.buy_year1_books,)),
RegionData(LogicRegion.bookseller_2, (LogicEntrance.buy_year3_books,)),
RegionData(LogicRegion.bookseller_3),
)
ginger_island_regions = (
# This overrides the regions from vanilla... When regions are moved to content packs, overriding existing entrances should no longer be necessary.
RegionData(RegionName.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(RegionName.wizard_tower, (Entrance.enter_wizard_basement, Entrance.use_desert_obelisk, Entrance.use_island_obelisk,)),
RegionData(RegionName.fish_shop, (Entrance.fish_shop_to_boat_tunnel,)),
RegionData(RegionName.mines_floor_120, (Entrance.dig_to_dangerous_mines_20, Entrance.dig_to_dangerous_mines_60, Entrance.dig_to_dangerous_mines_100)),
RegionData(RegionName.skull_cavern_200, (Entrance.enter_dangerous_skull_cavern,)),
RegionData(RegionName.leo_treehouse),
RegionData(RegionName.boat_tunnel, (Entrance.boat_to_ginger_island,)),
RegionData(RegionName.dangerous_skull_cavern),
RegionData(RegionName.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(RegionName.island_resort),
RegionData(RegionName.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, LogicEntrance.grow_spring_crops_on_island,
LogicEntrance.grow_summer_crops_on_island, LogicEntrance.grow_fall_crops_on_island, LogicEntrance.grow_winter_crops_on_island,
LogicEntrance.grow_indoor_crops_on_island), ),
RegionData(RegionName.island_east, (Entrance.island_east_to_leo_hut, Entrance.island_east_to_island_shrine)),
RegionData(RegionName.island_shrine),
RegionData(RegionName.island_south_east, (Entrance.island_southeast_to_pirate_cove,)),
RegionData(RegionName.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(RegionName.volcano, (Entrance.climb_to_volcano_5, Entrance.volcano_to_secret_beach)),
RegionData(RegionName.volcano_secret_beach),
RegionData(RegionName.volcano_floor_5, (Entrance.talk_to_volcano_dwarf, Entrance.climb_to_volcano_10)),
RegionData(RegionName.volcano_dwarf_shop),
RegionData(RegionName.volcano_floor_10),
RegionData(RegionName.island_trader),
RegionData(RegionName.island_farmhouse, (LogicEntrance.island_cooking,)),
RegionData(RegionName.gourmand_frog_cave),
RegionData(RegionName.colored_crystals_cave),
RegionData(RegionName.shipwreck),
RegionData(RegionName.qi_walnut_room),
RegionData(RegionName.leo_hut),
RegionData(RegionName.pirate_cove),
RegionData(RegionName.field_office),
RegionData(RegionName.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(RegionName.professor_snail_cave),
RegionData(RegionName.dangerous_mines_20),
RegionData(RegionName.dangerous_mines_60),
RegionData(RegionName.dangerous_mines_100),
)
# Exists and where they lead
vanilla_connections: tuple[ConnectionData, ...] = (
ConnectionData(Entrance.to_stardew_valley, RegionName.stardew_valley),
ConnectionData(Entrance.to_farmhouse, RegionName.farm_house),
ConnectionData(Entrance.farmhouse_to_farm, RegionName.farm),
ConnectionData(Entrance.downstairs_to_cellar, RegionName.cellar),
ConnectionData(Entrance.farm_to_backwoods, RegionName.backwoods),
ConnectionData(Entrance.farm_to_bus_stop, RegionName.bus_stop),
ConnectionData(Entrance.farm_to_forest, RegionName.forest),
ConnectionData(Entrance.farm_to_farmcave, RegionName.farm_cave, flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(Entrance.enter_greenhouse, RegionName.greenhouse),
ConnectionData(Entrance.enter_coop, RegionName.coop),
ConnectionData(Entrance.enter_barn, RegionName.barn),
ConnectionData(Entrance.enter_shed, RegionName.shed),
ConnectionData(Entrance.enter_slime_hutch, RegionName.slime_hutch),
ConnectionData(Entrance.use_desert_obelisk, RegionName.desert),
ConnectionData(Entrance.backwoods_to_mountain, RegionName.mountain),
ConnectionData(Entrance.bus_stop_to_town, RegionName.town),
ConnectionData(Entrance.bus_stop_to_tunnel_entrance, RegionName.tunnel_entrance),
ConnectionData(Entrance.tunnel_entrance_to_bus_tunnel, RegionName.bus_tunnel, flag=RandomizationFlag.NON_PROGRESSION),
ConnectionData(Entrance.take_bus_to_desert, RegionName.desert),
ConnectionData(Entrance.forest_to_town, RegionName.town),
ConnectionData(Entrance.forest_to_wizard_tower, RegionName.wizard_tower,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_wizard_basement, RegionName.wizard_basement, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.forest_to_marnie_ranch, RegionName.ranch,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.forest_to_leah_cottage, RegionName.leah_house,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_secret_woods, RegionName.secret_woods),
ConnectionData(Entrance.forest_to_sewer, RegionName.sewer, flag=RandomizationFlag.BUILDINGS),
# We remove the bit for masteries, because the mastery cave is to be excluded from the randomization if masteries are not shuffled.
ConnectionData(Entrance.forest_to_mastery_cave, RegionName.mastery_cave, flag=RandomizationFlag.BUILDINGS ^ RandomizationFlag.EXCLUDE_MASTERIES),
ConnectionData(Entrance.town_to_sewer, RegionName.sewer, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_mutant_bug_lair, RegionName.mutant_bug_lair, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.mountain_to_railroad, RegionName.railroad),
ConnectionData(Entrance.mountain_to_tent, RegionName.tent,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.mountain_to_carpenter_shop, RegionName.carpenter,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.mountain_to_maru_room, RegionName.maru_room,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_sebastian_room, RegionName.sebastian_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.mountain_to_adventurer_guild, RegionName.adventurer_guild,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.adventurer_guild_to_bedroom, RegionName.adventurer_guild_bedroom),
ConnectionData(Entrance.enter_quarry, RegionName.quarry),
ConnectionData(Entrance.enter_quarry_mine_entrance, RegionName.quarry_mine_entrance,
flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_quarry_mine, RegionName.quarry_mine),
ConnectionData(Entrance.mountain_to_town, RegionName.town),
ConnectionData(Entrance.town_to_community_center, RegionName.community_center,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.access_crafts_room, RegionName.crafts_room),
ConnectionData(Entrance.access_pantry, RegionName.pantry),
ConnectionData(Entrance.access_fish_tank, RegionName.fish_tank),
ConnectionData(Entrance.access_boiler_room, RegionName.boiler_room),
ConnectionData(Entrance.access_bulletin_board, RegionName.bulletin_board),
ConnectionData(Entrance.access_vault, RegionName.vault),
ConnectionData(Entrance.town_to_hospital, RegionName.hospital,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_harvey_room, RegionName.harvey_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.town_to_pierre_general_store, RegionName.pierre_store,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_sunroom, RegionName.sunroom, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.town_to_clint_blacksmith, RegionName.blacksmith,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_saloon, RegionName.saloon,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.play_journey_of_the_prairie_king, RegionName.jotpk_world_1),
ConnectionData(Entrance.reach_jotpk_world_2, RegionName.jotpk_world_2),
ConnectionData(Entrance.reach_jotpk_world_3, RegionName.jotpk_world_3),
ConnectionData(Entrance.play_junimo_kart, RegionName.junimo_kart_1),
ConnectionData(Entrance.reach_junimo_kart_2, RegionName.junimo_kart_2),
ConnectionData(Entrance.reach_junimo_kart_3, RegionName.junimo_kart_3),
ConnectionData(Entrance.reach_junimo_kart_4, RegionName.junimo_kart_4),
ConnectionData(Entrance.town_to_sam_house, RegionName.sam_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_haley_house, RegionName.haley_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_mayor_manor, RegionName.mayor_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_alex_house, RegionName.alex_house,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_trailer, RegionName.trailer,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_museum, RegionName.museum,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.town_to_jojamart, RegionName.jojamart,
flag=RandomizationFlag.PELICAN_TOWN | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.purchase_movie_ticket, RegionName.movie_ticket_stand),
ConnectionData(Entrance.enter_abandoned_jojamart, RegionName.abandoned_jojamart),
ConnectionData(Entrance.enter_movie_theater, RegionName.movie_theater),
ConnectionData(Entrance.town_to_beach, RegionName.beach),
ConnectionData(Entrance.enter_elliott_house, RegionName.elliott_house,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.beach_to_willy_fish_shop, RegionName.fish_shop,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_tide_pools, RegionName.tide_pools),
ConnectionData(Entrance.mountain_to_the_mines, RegionName.mines,
flag=RandomizationFlag.NON_PROGRESSION | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.dig_to_mines_floor_5, RegionName.mines_floor_5),
ConnectionData(Entrance.dig_to_mines_floor_10, RegionName.mines_floor_10),
ConnectionData(Entrance.dig_to_mines_floor_15, RegionName.mines_floor_15),
ConnectionData(Entrance.dig_to_mines_floor_20, RegionName.mines_floor_20),
ConnectionData(Entrance.dig_to_mines_floor_25, RegionName.mines_floor_25),
ConnectionData(Entrance.dig_to_mines_floor_30, RegionName.mines_floor_30),
ConnectionData(Entrance.dig_to_mines_floor_35, RegionName.mines_floor_35),
ConnectionData(Entrance.dig_to_mines_floor_40, RegionName.mines_floor_40),
ConnectionData(Entrance.dig_to_mines_floor_45, RegionName.mines_floor_45),
ConnectionData(Entrance.dig_to_mines_floor_50, RegionName.mines_floor_50),
ConnectionData(Entrance.dig_to_mines_floor_55, RegionName.mines_floor_55),
ConnectionData(Entrance.dig_to_mines_floor_60, RegionName.mines_floor_60),
ConnectionData(Entrance.dig_to_mines_floor_65, RegionName.mines_floor_65),
ConnectionData(Entrance.dig_to_mines_floor_70, RegionName.mines_floor_70),
ConnectionData(Entrance.dig_to_mines_floor_75, RegionName.mines_floor_75),
ConnectionData(Entrance.dig_to_mines_floor_80, RegionName.mines_floor_80),
ConnectionData(Entrance.dig_to_mines_floor_85, RegionName.mines_floor_85),
ConnectionData(Entrance.dig_to_mines_floor_90, RegionName.mines_floor_90),
ConnectionData(Entrance.dig_to_mines_floor_95, RegionName.mines_floor_95),
ConnectionData(Entrance.dig_to_mines_floor_100, RegionName.mines_floor_100),
ConnectionData(Entrance.dig_to_mines_floor_105, RegionName.mines_floor_105),
ConnectionData(Entrance.dig_to_mines_floor_110, RegionName.mines_floor_110),
ConnectionData(Entrance.dig_to_mines_floor_115, RegionName.mines_floor_115),
ConnectionData(Entrance.dig_to_mines_floor_120, RegionName.mines_floor_120),
ConnectionData(Entrance.enter_skull_cavern_entrance, RegionName.skull_cavern_entrance,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_oasis, RegionName.oasis,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_casino, RegionName.casino, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_skull_cavern, RegionName.skull_cavern),
ConnectionData(Entrance.mine_to_skull_cavern_floor_25, RegionName.skull_cavern_25),
ConnectionData(Entrance.mine_to_skull_cavern_floor_50, RegionName.skull_cavern_50),
ConnectionData(Entrance.mine_to_skull_cavern_floor_75, RegionName.skull_cavern_75),
ConnectionData(Entrance.mine_to_skull_cavern_floor_100, RegionName.skull_cavern_100),
ConnectionData(Entrance.mine_to_skull_cavern_floor_125, RegionName.skull_cavern_125),
ConnectionData(Entrance.mine_to_skull_cavern_floor_150, RegionName.skull_cavern_150),
ConnectionData(Entrance.mine_to_skull_cavern_floor_175, RegionName.skull_cavern_175),
ConnectionData(Entrance.mine_to_skull_cavern_floor_200, RegionName.skull_cavern_200),
ConnectionData(Entrance.enter_witch_warp_cave, RegionName.witch_warp_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_swamp, RegionName.witch_swamp, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_witch_hut, RegionName.witch_hut, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.witch_warp_to_wizard_basement, RegionName.wizard_basement, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_bathhouse_entrance, RegionName.bathhouse_entrance,
flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.enter_locker_room, RegionName.locker_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.enter_public_bath, RegionName.public_bath, flag=RandomizationFlag.BUILDINGS),
ConnectionData(LogicEntrance.talk_to_mines_dwarf, LogicRegion.mines_dwarf_shop),
ConnectionData(LogicEntrance.buy_from_traveling_merchant, LogicRegion.traveling_cart),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_sunday, LogicRegion.traveling_cart_sunday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_monday, LogicRegion.traveling_cart_monday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_tuesday, LogicRegion.traveling_cart_tuesday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_wednesday, LogicRegion.traveling_cart_wednesday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_thursday, LogicRegion.traveling_cart_thursday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_friday, LogicRegion.traveling_cart_friday),
ConnectionData(LogicEntrance.buy_from_traveling_merchant_saturday, LogicRegion.traveling_cart_saturday),
ConnectionData(LogicEntrance.complete_raccoon_requests, LogicRegion.raccoon_daddy),
ConnectionData(LogicEntrance.fish_in_waterfall, LogicRegion.forest_waterfall),
ConnectionData(LogicEntrance.buy_from_raccoon, LogicRegion.raccoon_shop),
ConnectionData(LogicEntrance.farmhouse_cooking, LogicRegion.kitchen),
ConnectionData(LogicEntrance.watch_queen_of_sauce, LogicRegion.queen_of_sauce),
ConnectionData(LogicEntrance.grow_spring_crops, LogicRegion.spring_farming),
ConnectionData(LogicEntrance.grow_summer_crops, LogicRegion.summer_farming),
ConnectionData(LogicEntrance.grow_fall_crops, LogicRegion.fall_farming),
ConnectionData(LogicEntrance.grow_winter_crops, LogicRegion.winter_farming),
ConnectionData(LogicEntrance.grow_spring_crops_in_greenhouse, LogicRegion.spring_farming),
ConnectionData(LogicEntrance.grow_summer_crops_in_greenhouse, LogicRegion.summer_farming),
ConnectionData(LogicEntrance.grow_fall_crops_in_greenhouse, LogicRegion.fall_farming),
ConnectionData(LogicEntrance.grow_winter_crops_in_greenhouse, LogicRegion.winter_farming),
ConnectionData(LogicEntrance.grow_indoor_crops_in_greenhouse, LogicRegion.indoor_farming),
ConnectionData(LogicEntrance.grow_summer_fall_crops_in_summer, LogicRegion.summer_or_fall_farming),
ConnectionData(LogicEntrance.grow_summer_fall_crops_in_fall, LogicRegion.summer_or_fall_farming),
ConnectionData(LogicEntrance.shipping, LogicRegion.shipping),
ConnectionData(LogicEntrance.blacksmith_copper, LogicRegion.blacksmith_copper),
ConnectionData(LogicEntrance.blacksmith_iron, LogicRegion.blacksmith_iron),
ConnectionData(LogicEntrance.blacksmith_gold, LogicRegion.blacksmith_gold),
ConnectionData(LogicEntrance.blacksmith_iridium, LogicRegion.blacksmith_iridium),
ConnectionData(LogicEntrance.fishing, LogicRegion.fishing),
ConnectionData(LogicEntrance.attend_egg_festival, LogicRegion.egg_festival),
ConnectionData(LogicEntrance.attend_desert_festival, LogicRegion.desert_festival),
ConnectionData(LogicEntrance.attend_flower_dance, LogicRegion.flower_dance),
ConnectionData(LogicEntrance.attend_luau, LogicRegion.luau),
ConnectionData(LogicEntrance.attend_trout_derby, LogicRegion.trout_derby),
ConnectionData(LogicEntrance.attend_moonlight_jellies, LogicRegion.moonlight_jellies),
ConnectionData(LogicEntrance.attend_fair, LogicRegion.fair),
ConnectionData(LogicEntrance.attend_spirit_eve, LogicRegion.spirit_eve),
ConnectionData(LogicEntrance.attend_festival_of_ice, LogicRegion.festival_of_ice),
ConnectionData(LogicEntrance.attend_night_market, LogicRegion.night_market),
ConnectionData(LogicEntrance.attend_winter_star, LogicRegion.winter_star),
ConnectionData(LogicEntrance.attend_squidfest, LogicRegion.squidfest),
ConnectionData(LogicEntrance.buy_experience_books, LogicRegion.bookseller_1),
ConnectionData(LogicEntrance.buy_year1_books, LogicRegion.bookseller_2),
ConnectionData(LogicEntrance.buy_year3_books, LogicRegion.bookseller_3),
)
ginger_island_connections = (
ConnectionData(Entrance.use_island_obelisk, RegionName.island_south),
ConnectionData(Entrance.use_farm_obelisk, RegionName.farm),
ConnectionData(Entrance.mountain_to_leo_treehouse, RegionName.leo_treehouse, flag=RandomizationFlag.BUILDINGS | RandomizationFlag.LEAD_TO_OPEN_AREA),
ConnectionData(Entrance.fish_shop_to_boat_tunnel, RegionName.boat_tunnel, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.boat_to_ginger_island, RegionName.island_south),
ConnectionData(Entrance.enter_dangerous_skull_cavern, RegionName.dangerous_skull_cavern),
ConnectionData(Entrance.dig_to_dangerous_mines_20, RegionName.dangerous_mines_20),
ConnectionData(Entrance.dig_to_dangerous_mines_60, RegionName.dangerous_mines_60),
ConnectionData(Entrance.dig_to_dangerous_mines_100, RegionName.dangerous_mines_100),
ConnectionData(Entrance.island_south_to_west, RegionName.island_west),
ConnectionData(Entrance.island_south_to_north, RegionName.island_north),
ConnectionData(Entrance.island_south_to_east, RegionName.island_east),
ConnectionData(Entrance.island_south_to_southeast, RegionName.island_south_east),
ConnectionData(Entrance.use_island_resort, RegionName.island_resort),
ConnectionData(Entrance.island_west_to_islandfarmhouse, RegionName.island_farmhouse, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_west_to_gourmand_cave, RegionName.gourmand_frog_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_west_to_crystals_cave, RegionName.colored_crystals_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_west_to_shipwreck, RegionName.shipwreck, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_west_to_qi_walnut_room, RegionName.qi_walnut_room, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_east_to_leo_hut, RegionName.leo_hut, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_east_to_island_shrine, RegionName.island_shrine, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_southeast_to_pirate_cove, RegionName.pirate_cove, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_north_to_field_office, RegionName.field_office, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_north_to_dig_site, RegionName.dig_site),
ConnectionData(Entrance.dig_site_to_professor_snail_cave, RegionName.professor_snail_cave, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.island_north_to_volcano, RegionName.volcano, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.volcano_to_secret_beach, RegionName.volcano_secret_beach, flag=RandomizationFlag.BUILDINGS),
ConnectionData(Entrance.talk_to_island_trader, RegionName.island_trader),
ConnectionData(Entrance.climb_to_volcano_5, RegionName.volcano_floor_5),
ConnectionData(Entrance.talk_to_volcano_dwarf, RegionName.volcano_dwarf_shop),
ConnectionData(Entrance.climb_to_volcano_10, RegionName.volcano_floor_10),
ConnectionData(Entrance.parrot_express_jungle_to_docks, RegionName.island_south),
ConnectionData(Entrance.parrot_express_dig_site_to_docks, RegionName.island_south),
ConnectionData(Entrance.parrot_express_volcano_to_docks, RegionName.island_south),
ConnectionData(Entrance.parrot_express_volcano_to_jungle, RegionName.island_west),
ConnectionData(Entrance.parrot_express_docks_to_jungle, RegionName.island_west),
ConnectionData(Entrance.parrot_express_dig_site_to_jungle, RegionName.island_west),
ConnectionData(Entrance.parrot_express_docks_to_dig_site, RegionName.dig_site),
ConnectionData(Entrance.parrot_express_volcano_to_dig_site, RegionName.dig_site),
ConnectionData(Entrance.parrot_express_jungle_to_dig_site, RegionName.dig_site),
ConnectionData(Entrance.parrot_express_dig_site_to_volcano, RegionName.island_north),
ConnectionData(Entrance.parrot_express_docks_to_volcano, RegionName.island_north),
ConnectionData(Entrance.parrot_express_jungle_to_volcano, RegionName.island_north),
ConnectionData(LogicEntrance.grow_spring_crops_on_island, LogicRegion.spring_farming),
ConnectionData(LogicEntrance.grow_summer_crops_on_island, LogicRegion.summer_farming),
ConnectionData(LogicEntrance.grow_fall_crops_on_island, LogicRegion.fall_farming),
ConnectionData(LogicEntrance.grow_winter_crops_on_island, LogicRegion.winter_farming),
ConnectionData(LogicEntrance.grow_indoor_crops_on_island, LogicRegion.indoor_farming),
ConnectionData(LogicEntrance.island_cooking, LogicRegion.kitchen),
)
connections_without_ginger_island_by_name: Mapping[str, ConnectionData] = MappingProxyType({
connection.name: connection
for connection in vanilla_connections
})
regions_without_ginger_island_by_name: Mapping[str, RegionData] = MappingProxyType({
region.name: region
for region in vanilla_regions
})
connections_with_ginger_island_by_name: Mapping[str, ConnectionData] = MappingProxyType({
connection.name: connection
for connection in vanilla_connections + ginger_island_connections
})
regions_with_ginger_island_by_name: Mapping[str, RegionData] = MappingProxyType({
region.name: region
for region in vanilla_regions + ginger_island_regions
})

View File

@@ -1,173 +0,0 @@
import random
import unittest
from typing import Set
from BaseClasses import get_seed
from .bases import SVTestCase
from .options.utils import fill_dataclass_with_default
from .. import create_content
from ..options import EntranceRandomization, ExcludeGingerIsland, SkillProgression
from ..regions import vanilla_regions, vanilla_connections, randomize_connections, RandomizationFlag, create_final_connections_and_regions
from ..strings.entrance_names import Entrance as EntranceName
from ..strings.region_names import Region as RegionName
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 vanilla_regions:
with self.subTest(region=region):
for exit in region.exits:
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 vanilla_connections:
with self.subTest(connection=connection):
self.assertIn(connection.destination, regions_by_name,
f"{connection.name} is leading to {connection.destination} but it does not exist.")
def explore_connections_tree_up_to_blockers(blocked_entrances: Set[str], connections_by_name, regions_by_name):
explored_entrances = set()
explored_regions = set()
entrances_to_explore = set()
current_node_name = "Menu"
current_node = regions_by_name[current_node_name]
entrances_to_explore.update(current_node.exits)
while entrances_to_explore:
current_entrance_name = entrances_to_explore.pop()
current_entrance = connections_by_name[current_entrance_name]
current_node_name = current_entrance.destination
explored_entrances.add(current_entrance_name)
explored_regions.add(current_node_name)
if current_entrance_name in blocked_entrances:
continue
current_node = regions_by_name[current_node_name]
entrances_to_explore.update({entrance for entrance in current_node.exits if entrance not in explored_entrances})
return explored_regions
class TestEntranceRando(SVTestCase):
def test_entrance_randomization(self):
for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
(EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
(EntranceRandomization.option_buildings_without_house, RandomizationFlag.BUILDINGS),
(EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
sv_options = fill_dataclass_with_default({
EntranceRandomization.internal_name: option,
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
})
content = create_content(sv_options)
seed = get_seed()
rand = random.Random(seed)
with self.subTest(flag=flag, msg=f"Seed: {seed}"):
entrances, regions = create_final_connections_and_regions(sv_options)
_, randomized_connections = randomize_connections(rand, sv_options, content, regions, entrances)
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.")
self.assertTrue(reverse_in_randomized, f"Connection {connection.reverse} should be randomized but it is not in the output.")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
f"Connections are duplicated in randomization.")
def test_entrance_randomization_without_island(self):
for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
(EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION),
(EntranceRandomization.option_buildings_without_house, RandomizationFlag.BUILDINGS),
(EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
sv_options = fill_dataclass_with_default({
EntranceRandomization.internal_name: option,
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true,
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
})
content = create_content(sv_options)
seed = get_seed()
rand = random.Random(seed)
with self.subTest(option=option, flag=flag, seed=seed):
entrances, regions = create_final_connections_and_regions(sv_options)
_, randomized_connections = randomize_connections(rand, sv_options, content, regions, entrances)
for connection in vanilla_connections:
if flag in connection.flag:
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.")
self.assertNotIn(connection.reverse, randomized_connections,
f"Connection {connection.reverse} should not be randomized but it is in the output.")
else:
self.assertIn(connection.name, randomized_connections,
f"Connection {connection.name} should be randomized but it is not in the output.")
self.assertIn(connection.reverse, randomized_connections,
f"Connection {connection.reverse} should be randomized but it is not in the output.")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
f"Connections are duplicated in randomization.")
def test_cannot_put_island_access_on_island(self):
sv_options = fill_dataclass_with_default({
EntranceRandomization.internal_name: EntranceRandomization.option_buildings,
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_false,
SkillProgression.internal_name: SkillProgression.option_progressive_with_masteries,
})
content = create_content(sv_options)
for i in range(0, 100 if self.skip_long_tests else 10000):
seed = get_seed()
rand = random.Random(seed)
with self.subTest(msg=f"Seed: {seed}"):
entrances, regions = create_final_connections_and_regions(sv_options)
randomized_connections, randomized_data = randomize_connections(rand, sv_options, content, regions, entrances)
connections_by_name = {connection.name: connection for connection in randomized_connections}
blocked_entrances = {EntranceName.use_island_obelisk, EntranceName.boat_to_ginger_island}
required_regions = {RegionName.wizard_tower, RegionName.boat_tunnel}
self.assert_can_reach_any_region_before_blockers(required_regions, blocked_entrances, connections_by_name, regions)
def assert_can_reach_any_region_before_blockers(self, required_regions, blocked_entrances, connections_by_name, regions_by_name):
explored_regions = explore_connections_tree_up_to_blockers(blocked_entrances, connections_by_name, regions_by_name)
self.assertTrue(any(region in explored_regions for region in required_regions))
class TestEntranceClassifications(SVTestCase):
def test_non_progression_are_all_accessible_with_empty_inventory(self):
for option, flag in [(EntranceRandomization.option_pelican_town, RandomizationFlag.PELICAN_TOWN),
(EntranceRandomization.option_non_progression, RandomizationFlag.NON_PROGRESSION)]:
world_options = {
EntranceRandomization.internal_name: option
}
with self.solo_world_sub_test(world_options=world_options, flag=flag) as (multiworld, sv_world):
ap_entrances = {entrance.name: entrance for entrance in multiworld.get_entrances()}
for randomized_entrance in sv_world.randomized_entrances:
if randomized_entrance in ap_entrances:
ap_entrance_origin = ap_entrances[randomized_entrance]
self.assertTrue(ap_entrance_origin.access_rule(multiworld.state))
if sv_world.randomized_entrances[randomized_entrance] in ap_entrances:
ap_entrance_destination = multiworld.get_entrance(sv_world.randomized_entrances[randomized_entrance], 1)
self.assertTrue(ap_entrance_destination.access_rule(multiworld.state))
def test_no_ginger_island_entrances_when_excluded(self):
world_options = {
EntranceRandomization.internal_name: EntranceRandomization.option_disabled,
ExcludeGingerIsland.internal_name: ExcludeGingerIsland.option_true
}
with self.solo_world_sub_test(world_options=world_options) as (multiworld, _):
ap_entrances = {entrance.name: entrance for entrance in multiworld.get_entrances()}
entrance_data_by_name = {entrance.name: entrance for entrance in vanilla_connections}
for entrance_name in ap_entrances:
entrance_data = entrance_data_by_name[entrance_name]
with self.subTest(f"{entrance_name}: {entrance_data.flag}"):
self.assertFalse(entrance_data.flag & RandomizationFlag.GINGER_ISLAND)

View File

@@ -1,7 +1,7 @@
from typing import List from typing import List
from unittest import TestCase from unittest import TestCase
from BaseClasses import CollectionState, Location, Region from BaseClasses import CollectionState, Location, Region, Entrance
from ...stardew_rule import StardewRule, false_, MISSING_ITEM, Reach from ...stardew_rule import StardewRule, false_, MISSING_ITEM, Reach
from ...stardew_rule.rule_explain import explain from ...stardew_rule.rule_explain import explain
@@ -79,3 +79,13 @@ class RuleAssertMixin(TestCase):
except KeyError as e: except KeyError as e:
raise AssertionError(f"Error while checking region {region_name}: {e}" raise AssertionError(f"Error while checking region {region_name}: {e}"
f"\nExplanation: {expl}") f"\nExplanation: {expl}")
def assert_can_reach_entrance(self, entrance: Entrance | str, state: CollectionState) -> None:
entrance_name = entrance.name if isinstance(entrance, Entrance) else entrance
expl = explain(Reach(entrance_name, "Entrance", 1), state)
try:
can_reach = state.can_reach_entrance(entrance_name, 1)
self.assertTrue(can_reach, expl)
except KeyError as e:
raise AssertionError(f"Error while checking entrance {entrance_name}: {e}"
f"\nExplanation: {expl}")

View File

@@ -7,7 +7,7 @@ import unittest
from contextlib import contextmanager from contextlib import contextmanager
from typing import Optional, Dict, Union, Any, List, Iterable from typing import Optional, Dict, Union, Any, List, Iterable
from BaseClasses import get_seed, MultiWorld, Location, Item, CollectionState from BaseClasses import get_seed, MultiWorld, Location, Item, CollectionState, Entrance
from test.bases import WorldTestBase from test.bases import WorldTestBase
from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_multiworld
from worlds.AutoWorld import call_all from worlds.AutoWorld import call_all
@@ -179,6 +179,11 @@ class SVTestBase(RuleAssertMixin, WorldTestBase, SVTestCase):
state = self.multiworld.state state = self.multiworld.state
super().assert_cannot_reach_location(location, state) super().assert_cannot_reach_location(location, state)
def assert_can_reach_entrance(self, entrance: Entrance | str, state: CollectionState | None = None) -> None:
if state is None:
state = self.multiworld.state
super().assert_can_reach_entrance(entrance, state)
pre_generated_worlds = {} pre_generated_worlds = {}

View File

@@ -1,17 +1,13 @@
import random
from typing import ClassVar from typing import ClassVar
from BaseClasses import get_seed
from test.param import classvar_matrix from test.param import classvar_matrix
from ..TestGeneration import get_all_permanent_progression_items from ..TestGeneration import get_all_permanent_progression_items
from ..assertion import ModAssertMixin, WorldAssertMixin from ..assertion import ModAssertMixin, WorldAssertMixin
from ..bases import SVTestCase, SVTestBase, solo_multiworld from ..bases import SVTestCase, SVTestBase, solo_multiworld
from ..options.presets import allsanity_mods_6_x_x from ..options.presets import allsanity_mods_6_x_x
from ..options.utils import fill_dataclass_with_default from ... import options, Group
from ... import options, Group, create_content
from ...mods.mod_data import ModNames from ...mods.mod_data import ModNames
from ...options.options import all_mods from ...options.options import all_mods
from ...regions import RandomizationFlag, randomize_connections, create_final_connections_and_regions
class TestCanGenerateAllsanityWithMods(WorldAssertMixin, ModAssertMixin, SVTestCase): class TestCanGenerateAllsanityWithMods(WorldAssertMixin, ModAssertMixin, SVTestCase):
@@ -117,39 +113,6 @@ class TestNoGingerIslandModItemGeneration(SVTestBase):
self.assertIn(progression_item.name, all_created_items) self.assertIn(progression_item.name, all_created_items)
class TestModEntranceRando(SVTestCase):
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_without_house, RandomizationFlag.BUILDINGS),
(options.EntranceRandomization.option_buildings, RandomizationFlag.BUILDINGS)]:
sv_options = fill_dataclass_with_default({
options.EntranceRandomization.internal_name: option,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
options.SkillProgression.internal_name: options.SkillProgression.option_progressive_with_masteries,
options.Mods.internal_name: frozenset(options.Mods.valid_keys)
})
content = create_content(sv_options)
seed = get_seed()
rand = random.Random(seed)
with self.subTest(option=option, flag=flag, seed=seed):
final_connections, final_regions = create_final_connections_and_regions(sv_options)
_, randomized_connections = randomize_connections(rand, sv_options, content, final_regions, final_connections)
for connection_name in final_connections:
connection = final_connections[connection_name]
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")
self.assertTrue(reverse_in_randomized, f"Connection {connection.reverse} should be randomized but it is not in the output.")
self.assertEqual(len(set(randomized_connections.values())), len(randomized_connections.values()),
f"Connections are duplicated in randomization.")
class TestVanillaLogicAlternativeWhenQuestsAreNotRandomized(WorldAssertMixin, SVTestBase): class TestVanillaLogicAlternativeWhenQuestsAreNotRandomized(WorldAssertMixin, SVTestBase):
"""We often forget to add an alternative rule that works when quests are not randomized. When this happens, some """We often forget to add an alternative rule that works when quests are not randomized. When this happens, some
Location are not reachable because they depend on items that are only added to the pool when quests are randomized. Location are not reachable because they depend on items that are only added to the pool when quests are randomized.

View File

@@ -0,0 +1,36 @@
from ..bases import SVTestBase
from ... import options
from ...regions.model import RandomizationFlag
from ...regions.regions import create_all_connections
class EntranceRandomizationAssertMixin:
def assert_non_progression_are_all_accessible_with_empty_inventory(self: SVTestBase):
all_connections = create_all_connections(self.world.content.registered_packs)
non_progression_connections = [connection for connection in all_connections.values() if RandomizationFlag.BIT_NON_PROGRESSION in connection.flag]
for non_progression_connections in non_progression_connections:
with self.subTest(connection=non_progression_connections):
self.assert_can_reach_entrance(non_progression_connections.name)
# This test does not actually need to generate with entrance randomization. Entrances rules are the same regardless of the randomization.
class TestVanillaEntranceClassifications(EntranceRandomizationAssertMixin, SVTestBase):
options = {
options.ExcludeGingerIsland: options.ExcludeGingerIsland.option_false,
options.Mods: frozenset()
}
def test_non_progression_are_all_accessible_with_empty_inventory(self):
self.assert_non_progression_are_all_accessible_with_empty_inventory()
class TestModdedEntranceClassifications(EntranceRandomizationAssertMixin, SVTestBase):
options = {
options.ExcludeGingerIsland: options.ExcludeGingerIsland.option_false,
options.Mods: frozenset(options.Mods.valid_keys)
}
def test_non_progression_are_all_accessible_with_empty_inventory(self):
self.assert_non_progression_are_all_accessible_with_empty_inventory()

View File

@@ -0,0 +1,167 @@
from collections import deque
from collections.abc import Collection
from unittest.mock import patch, Mock
from BaseClasses import get_seed, MultiWorld, Entrance
from ..assertion import WorldAssertMixin
from ..bases import SVTestCase, solo_multiworld
from ... import options
from ...mods.mod_data import ModNames
from ...options import EntranceRandomization, ExcludeGingerIsland, SkillProgression
from ...options.options import all_mods
from ...regions.entrance_rando import create_entrance_rando_target, prepare_mod_data, connect_regions
from ...regions.model import RegionData, ConnectionData, RandomizationFlag
from ...strings.entrance_names import Entrance as EntranceName
from ...strings.region_names import Region as RegionName
class TestEntranceRando(SVTestCase):
def test_given_connection_matching_randomization_when_connect_regions_then_make_connection_entrance_rando_target(self):
region_data_by_name = {
"Region1": RegionData("Region1", ("randomized_connection", "not_randomized")),
"Region2": RegionData("Region2"),
"Region3": RegionData("Region3"),
}
connection_data_by_name = {
"randomized_connection": ConnectionData("randomized_connection", "Region2", flag=RandomizationFlag.PELICAN_TOWN),
"not_randomized": ConnectionData("not_randomized", "Region2", flag=RandomizationFlag.BUILDINGS),
}
regions_by_name = {
"Region1": Mock(),
"Region2": Mock(),
"Region3": Mock(),
}
player_randomization_flag = RandomizationFlag.BIT_PELICAN_TOWN
with patch("worlds.stardew_valley.regions.entrance_rando.create_entrance_rando_target") as mock_create_entrance_rando_target:
connect_regions(region_data_by_name, connection_data_by_name, regions_by_name, player_randomization_flag)
expected_origin, expected_destination = regions_by_name["Region1"], regions_by_name["Region2"]
expected_connection = connection_data_by_name["randomized_connection"]
mock_create_entrance_rando_target.assert_called_once_with(expected_origin, expected_destination, expected_connection)
def test_when_create_entrance_rando_target_then_create_exit_and_er_target(self):
origin = Mock()
destination = Mock()
connection_data = ConnectionData("origin to destination", "destination")
create_entrance_rando_target(origin, destination, connection_data)
origin.create_exit.assert_called_once_with("origin to destination")
destination.create_er_target.assert_called_once_with("destination to origin")
def test_when_prepare_mod_data_then_swapped_connections_contains_both_directions(self):
placements = Mock(pairings=[("A to B", "C to A"), ("C to D", "A to C")])
swapped_connections = prepare_mod_data(placements)
self.assertEqual({"A to B": "A to C", "C to A": "B to A", "C to D": "C to A", "A to C": "D to C"}, swapped_connections)
class TestEntranceRandoCreatesValidWorlds(WorldAssertMixin, SVTestCase):
# The following tests validate that ER still generates winnable and logically-sane games with given mods.
# Mods that do not interact with entrances are skipped
# Not all ER settings are tested, because 'buildings' is, essentially, a superset of all others
def test_ginger_island_excluded_buildings(self):
world_options = {
options.EntranceRandomization: options.EntranceRandomization.option_buildings,
options.ExcludeGingerIsland: options.ExcludeGingerIsland.option_true
}
with solo_multiworld(world_options) as (multi_world, _):
self.assert_basic_checks(multi_world)
def test_deepwoods_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.deepwoods, options.EntranceRandomization.option_buildings)
def test_juna_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.juna, options.EntranceRandomization.option_buildings)
def test_jasper_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.jasper, options.EntranceRandomization.option_buildings)
def test_alec_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.alec, options.EntranceRandomization.option_buildings)
def test_yoba_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.yoba, options.EntranceRandomization.option_buildings)
def test_eugene_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.eugene, options.EntranceRandomization.option_buildings)
def test_ayeisha_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.ayeisha, options.EntranceRandomization.option_buildings)
def test_riley_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.riley, options.EntranceRandomization.option_buildings)
def test_sve_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.sve, options.EntranceRandomization.option_buildings)
def test_alecto_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.alecto, options.EntranceRandomization.option_buildings)
def test_lacey_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.lacey, options.EntranceRandomization.option_buildings)
def test_boarding_house_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(ModNames.boarding_house, options.EntranceRandomization.option_buildings)
def test_all_mods_entrance_randomization_buildings(self):
self.perform_basic_checks_on_mod_with_er(all_mods, options.EntranceRandomization.option_buildings)
def perform_basic_checks_on_mod_with_er(self, mods: str | set[str], er_option: int) -> None:
if isinstance(mods, str):
mods = {mods}
world_options = {
options.EntranceRandomization: er_option,
options.Mods: frozenset(mods),
options.ExcludeGingerIsland: options.ExcludeGingerIsland.option_false
}
with solo_multiworld(world_options) as (multi_world, _):
self.assert_basic_checks(multi_world)
# GER should have this covered, but it's good to have a backup
class TestGingerIslandEntranceRando(SVTestCase):
def test_cannot_put_island_access_on_island(self):
test_options = {
options.EntranceRandomization: EntranceRandomization.option_buildings,
options.ExcludeGingerIsland: ExcludeGingerIsland.option_false,
options.SkillProgression: SkillProgression.option_progressive_with_masteries,
}
blocked_entrances = {EntranceName.use_island_obelisk, EntranceName.boat_to_ginger_island}
required_regions = {RegionName.wizard_tower, RegionName.boat_tunnel}
for i in range(0, 10 if self.skip_long_tests else 1000):
seed = get_seed()
with self.solo_world_sub_test(f"Seed: {seed}", world_options=test_options, world_caching=False, seed=seed) as (multiworld, world):
self.assert_can_reach_any_region_before_blockers(required_regions, blocked_entrances, multiworld)
def assert_can_reach_any_region_before_blockers(self, required_regions: Collection[str], blocked_entrances: Collection[str], multiworld: MultiWorld):
explored_regions = explore_regions_up_to_blockers(blocked_entrances, multiworld)
self.assertTrue(any(region in explored_regions for region in required_regions))
def explore_regions_up_to_blockers(blocked_entrances: Collection[str], multiworld: MultiWorld) -> set[str]:
explored_regions: set[str] = set()
regions_by_name = multiworld.regions.region_cache[1]
regions_to_explore = deque([regions_by_name["Menu"]])
while regions_to_explore:
region = regions_to_explore.pop()
if region.name in explored_regions:
continue
explored_regions.add(region.name)
for exit_ in region.exits:
exit_: Entrance
if exit_.name in blocked_entrances:
continue
regions_to_explore.append(exit_.connected_region)
return explored_regions

View File

@@ -0,0 +1,88 @@
import unittest
from ..options.utils import fill_dataclass_with_default
from ... import create_content, options
from ...regions.entrance_rando import create_player_randomization_flag
from ...regions.model import RandomizationFlag, ConnectionData
class TestConnectionData(unittest.TestCase):
def test_given_entrances_not_randomized_when_is_eligible_for_randomization_then_not_eligible(self):
player_flag = RandomizationFlag.NOT_RANDOMIZED
connection = ConnectionData("Go to Somewhere", "Somewhere", RandomizationFlag.PELICAN_TOWN)
is_eligible = connection.is_eligible_for_randomization(player_flag)
self.assertFalse(is_eligible)
def test_given_pelican_town_connection_when_is_eligible_for_pelican_town_randomization_then_eligible(self):
player_flag = RandomizationFlag.BIT_PELICAN_TOWN
connection = ConnectionData("Go to Somewhere", "Somewhere", RandomizationFlag.PELICAN_TOWN)
is_eligible = connection.is_eligible_for_randomization(player_flag)
self.assertTrue(is_eligible)
def test_given_pelican_town_connection_when_is_eligible_for_buildings_randomization_then_eligible(self):
player_flag = RandomizationFlag.BIT_BUILDINGS
connection = ConnectionData("Go to Somewhere", "Somewhere", RandomizationFlag.PELICAN_TOWN)
is_eligible = connection.is_eligible_for_randomization(player_flag)
self.assertTrue(is_eligible)
def test_given_non_progression_connection_when_is_eligible_for_pelican_town_randomization_then_not_eligible(self):
player_flag = RandomizationFlag.BIT_PELICAN_TOWN
connection = ConnectionData("Go to Somewhere", "Somewhere", RandomizationFlag.NON_PROGRESSION)
is_eligible = connection.is_eligible_for_randomization(player_flag)
self.assertFalse(is_eligible)
def test_given_non_progression_masteries_connection_when_is_eligible_for_non_progression_randomization_then_eligible(self):
player_flag = RandomizationFlag.BIT_NON_PROGRESSION
connection = ConnectionData("Go to Somewhere", "Somewhere", RandomizationFlag.NON_PROGRESSION ^ RandomizationFlag.EXCLUDE_MASTERIES)
is_eligible = connection.is_eligible_for_randomization(player_flag)
self.assertTrue(is_eligible)
def test_given_non_progression_masteries_connection_when_is_eligible_for_non_progression_without_masteries_randomization_then_not_eligible(self):
player_flag = RandomizationFlag.BIT_NON_PROGRESSION | RandomizationFlag.EXCLUDE_MASTERIES
connection = ConnectionData("Go to Somewhere", "Somewhere", RandomizationFlag.NON_PROGRESSION ^ RandomizationFlag.EXCLUDE_MASTERIES)
is_eligible = connection.is_eligible_for_randomization(player_flag)
self.assertFalse(is_eligible)
class TestRandomizationFlag(unittest.TestCase):
def test_given_entrance_randomization_choice_when_create_player_randomization_flag_then_only_relevant_bit_is_enabled(self):
for entrance_randomization_choice, expected_bit in (
(options.EntranceRandomization.option_disabled, RandomizationFlag.NOT_RANDOMIZED),
(options.EntranceRandomization.option_pelican_town, RandomizationFlag.BIT_PELICAN_TOWN),
(options.EntranceRandomization.option_non_progression, RandomizationFlag.BIT_NON_PROGRESSION),
(options.EntranceRandomization.option_buildings_without_house, RandomizationFlag.BIT_BUILDINGS),
(options.EntranceRandomization.option_buildings, RandomizationFlag.BIT_BUILDINGS),
(options.EntranceRandomization.option_chaos, RandomizationFlag.BIT_BUILDINGS),
):
player_options = fill_dataclass_with_default({options.EntranceRandomization: entrance_randomization_choice})
content = create_content(player_options)
flag = create_player_randomization_flag(player_options.entrance_randomization, content)
self.assertEqual(flag, expected_bit)
def test_given_masteries_not_randomized_when_create_player_randomization_flag_then_exclude_masteries_bit_enabled(self):
for entrance_randomization_choice in set(options.EntranceRandomization.options.values()) ^ {options.EntranceRandomization.option_disabled}:
player_options = fill_dataclass_with_default({
options.EntranceRandomization: entrance_randomization_choice,
options.SkillProgression: options.SkillProgression.option_progressive
})
content = create_content(player_options)
flag = create_player_randomization_flag(player_options.entrance_randomization, content)
self.assertIn(RandomizationFlag.EXCLUDE_MASTERIES, flag)

View File

@@ -0,0 +1,66 @@
import unittest
from ..options.utils import fill_dataclass_with_default
from ... import options
from ...content import create_content
from ...mods.region_data import region_data_by_content_pack
from ...regions import vanilla_data
from ...regions.model import MergeFlag
from ...regions.regions import create_all_regions, create_all_connections
class TestVanillaRegionsConnectionsWithGingerIsland(unittest.TestCase):
def test_region_exits_lead_somewhere(self):
for region in vanilla_data.regions_with_ginger_island_by_name.values():
with self.subTest(region=region):
for exit_ in region.exits:
self.assertIn(exit_, vanilla_data.connections_with_ginger_island_by_name,
f"{region.name} is leading to {exit_} but it does not exist.")
def test_connection_lead_somewhere(self):
for connection in vanilla_data.connections_with_ginger_island_by_name.values():
with self.subTest(connection=connection):
self.assertIn(connection.destination, vanilla_data.regions_with_ginger_island_by_name,
f"{connection.name} is leading to {connection.destination} but it does not exist.")
class TestVanillaRegionsConnectionsWithoutGingerIsland(unittest.TestCase):
def test_region_exits_lead_somewhere(self):
for region in vanilla_data.regions_without_ginger_island_by_name.values():
with self.subTest(region=region):
for exit_ in region.exits:
self.assertIn(exit_, vanilla_data.connections_without_ginger_island_by_name,
f"{region.name} is leading to {exit_} but it does not exist.")
def test_connection_lead_somewhere(self):
for connection in vanilla_data.connections_without_ginger_island_by_name.values():
with self.subTest(connection=connection):
self.assertIn(connection.destination, vanilla_data.regions_without_ginger_island_by_name,
f"{connection.name} is leading to {connection.destination} but it does not exist.")
class TestModsConnections(unittest.TestCase):
options = {
options.ExcludeGingerIsland: options.ExcludeGingerIsland.option_false,
options.Mods: frozenset(options.Mods.valid_keys)
}
content = create_content(fill_dataclass_with_default(options))
all_regions_by_name = create_all_regions(content.registered_packs)
all_connections_by_name = create_all_connections(content.registered_packs)
def test_region_exits_lead_somewhere(self):
for mod_region_data in region_data_by_content_pack.values():
for region in mod_region_data.regions:
if MergeFlag.REMOVE_EXITS in region.flag:
continue
with self.subTest(mod=mod_region_data.mod_name, region=region.name):
for exit_ in region.exits:
self.assertIn(exit_, self.all_connections_by_name, f"{region.name} is leading to {exit_} but it does not exist.")
def test_connection_lead_somewhere(self):
for mod_region_data in region_data_by_content_pack.values():
for connection in mod_region_data.connections:
with self.subTest(mod=mod_region_data.mod_name, connection=connection.name):
self.assertIn(connection.destination, self.all_regions_by_name,
f"{connection.name} is leading to {connection.destination} but it does not exist.")