Files
dockipelago/worlds/apeescape3/Regions.py
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

277 lines
12 KiB
Python

from typing import TYPE_CHECKING
from BaseClasses import Entrance, Location, Region
from .data.Stages import STAGES_BREAK_ROOMS, STAGES_DIRECTORY, STAGES_MASTER, ENTRANCES_MASTER, STAGES_DIRECTORY_LABEL, \
STAGES_SHOP_PROGRESSION, STAGES_FARMABLE, AE3EntranceMeta, STAGES_FARMABLE_SNEAKY_BORG
from .data.Locations import CAMERAS_INDEX, CELLPHONES_INDEX, MONKEYS_PASSWORDS, MONKEYS_INDEX, EVENTS_INDEX, \
SHOP_PROGRESSION_MASTER, SHOP_PROGRESSION_MORPH, SHOP_COLLECTION_INDEX, CameraLocation, CellphoneLocation, \
EventMeta, MonkeyLocation, ShopItemLocation, SHOP_PROGRESSION_DIRECTORY, SHOP_EVENT_ACCESS_DIRECTORY, \
SHOP_PROGRESSION_75COMPLETION
from .data.Logic import Rulesets
from .data.Strings import Stage
if TYPE_CHECKING:
from . import AE3World
### [< --- HELPERS --- >]
def establish_entrance(player : int, name : str, parent_region : Region, destination : Region,
ruleset : Rulesets = None):
"""Connects the parent region to its destinations and assigns access rules where present."""
entrance : Entrance = Entrance(player, name, parent_region)
if ruleset is not None and ruleset:
entrance.access_rule = ruleset.condense(player)
parent_region.exits.append(entrance)
entrance.connect(destination)
def create_regions(world : "AE3World"):
entrance_rules : dict[str, Rulesets] = {**world.logic_preference.entrance_rules,
**world.shop_rules.entrance_rules,
**world.progression.generate_rules(world),}
# world.logic_preference.entrance_rules.update(world.progression.generate_rules(world))
add_cameras : bool = bool(world.options.camerasanity)
add_cellphones : bool = bool(world.options.cellphonesanity.value)
add_break_rooms : bool = bool(world.options.monkeysanity_break_rooms.value)
# Initialize Regions
stages : dict[str, Region] = {name : Region(name, world.player, world.multiworld) for name in STAGES_MASTER
if name not in world.shop_rules.blacklisted_stages}
entrances : list[AE3EntranceMeta] = [*ENTRANCES_MASTER,
*world.shop_rules.entrances,
*world.progression.level_select_entrances]
blacklisted_entrances : list[Entrance] = [*world.logic_preference.blacklisted_entrances,
*world.shop_rules.blacklisted_entrances]
# Connect Regions, building a name->Entrance lookup for indirect condition registration
entrance_objects : dict[str, Entrance] = {}
for entrance in entrances:
if entrance.name in blacklisted_entrances:
continue
ruleset : Rulesets = Rulesets()
if entrance.parent in stages:
parent = stages[entrance.parent]
else:
continue
if entrance.destination in stages:
destination = stages[entrance.destination]
else:
continue
if entrance.name in entrance_rules:
ruleset = entrance_rules[entrance.name]
establish_entrance(world.player, entrance.name, parent, destination, ruleset)
entrance_objects[entrance.name] = destination.entrances[-1]
# Register Indirect Connections
farm_entrances : set[str] = set()
pgc_entrances : set[str] = set()
if world.options.shoppingsanity.value >= 1:
farm_entrances.add(Stage.entrance_shop_expensive.value)
if world.options.cheap_items_minimum_requirement:
farm_entrances.add(Stage.entrance_travel_ab.value)
if world.options.post_game_condition_cameras:
pgc_entrances.update(entrance_rules.keys() & world.progression.pgc_entrance_names)
if world.options.shoppingsanity.value >= 1:
pgc_entrances.update(world.shop_rules.post_game_entrances)
if world.options.cheap_items_minimum_requirement.value >= 100:
pgc_entrances.add(Stage.entrance_travel_ab.value)
if farm_entrances:
farmable_stages : list[str] = [*STAGES_FARMABLE]
if world.options.farm_logic_sneaky_borgs.value:
farmable_stages.extend(STAGES_FARMABLE_SNEAKY_BORG)
farmable_regions = [region for name, region in stages.items() if name in farmable_stages]
for ent_name in farm_entrances:
if ent_name in entrance_objects:
for region in farmable_regions:
world.multiworld.register_indirect_condition(region, entrance_objects[ent_name])
if pgc_entrances:
camera_regions = [region for name, region in stages.items() if name in CAMERAS_INDEX]
for ent_name in pgc_entrances:
if ent_name in entrance_objects:
for region in camera_regions:
world.multiworld.register_indirect_condition(region, entrance_objects[ent_name])
# Define Regions
blacklist : list[str] = [stage for channel in world.options.blacklist_channel.value
for stage in STAGES_DIRECTORY_LABEL[channel]
if channel in STAGES_DIRECTORY_LABEL]
for stage in stages.values():
# Skip Blacklisted Stages
if stage.name in blacklist:
continue
# Skip stage if Monkeysanity Break Rooms is enabled and the stage is a break room.
# It should be safe to skip outright since there are no break rooms with Cameras or Cellphones in them.
if not add_break_rooms and stage.name in STAGES_BREAK_ROOMS:
continue
# Define Locations
## Monkeys
if stage.name in MONKEYS_INDEX:
for monkeys in MONKEYS_INDEX[stage.name]:
if not world.options.monkeysanity_passwords and monkeys in MONKEYS_PASSWORDS:
continue
meta : MonkeyLocation = MonkeyLocation(monkeys)
loc : Location = meta.to_location(world.player, stage)
# Initialize Ruleset for Location
ruleset : Rulesets = Rulesets()
if monkeys in world.logic_preference.monkey_rules.keys():
ruleset = world.logic_preference.monkey_rules[monkeys]
ruleset.critical.update(world.logic_preference.default_critical_rule)
loc.access_rule = ruleset.condense(world.player)
stage.locations.append(loc)
## Cameras
if add_cameras and stage.name in CAMERAS_INDEX:
camera : str = CAMERAS_INDEX[stage.name]
meta : CameraLocation = CameraLocation(camera)
loc : Location = meta.to_location(world.player, stage)
# Add Access Rule for completing the stage to ensure maximum accessibility,
# if the player chooses to require the monkey actors for the Cameras,
# and they did not choose to have early Freeplay
if world.options.camerasanity == 1 and not world.options.early_free_play:
ruleset : Rulesets = Rulesets()
parent_channel : str = ""
for channel, regions in STAGES_DIRECTORY.items():
if not stage.name in regions:
continue
parent_channel = channel
break
if parent_channel:
ruleset = world.logic_preference.get_channel_clear_rules(parent_channel)
loc.access_rule = ruleset.condense(world.player)
stage.locations.append(loc)
## Cellphones
if add_cellphones:
if stage.name in CELLPHONES_INDEX:
for cellphone in CELLPHONES_INDEX[stage.name]:
meta : CellphoneLocation = CellphoneLocation(cellphone)
loc : Location = meta.to_location(world.player, stage)
stage.locations.append(loc)
## Events
if stage.name in EVENTS_INDEX:
for event in EVENTS_INDEX[stage.name]:
meta : EventMeta = EventMeta(event)
loc : Location = meta.to_event_location(world.player, stage)
if event in world.logic_preference.event_rules:
loc.access_rule = world.logic_preference.event_rules[event].condense(world.player)
stage.locations.append(loc)
# Handle Shop Regions
shopping_area: Region = stages[Stage.travel_station_b.value]
expensive_area : Region = stages[Stage.region_shop_expensive.value]
if world.options.shoppingsanity != 0:
## Handle Shop Items that require specific events
for region, items in SHOP_EVENT_ACCESS_DIRECTORY.items():
if region in blacklist:
continue
for item in items:
meta: ShopItemLocation = ShopItemLocation(item)
loc: Location = meta.to_location(world.player, shopping_area)
if item in world.shop_rules.item_rules:
loc.access_rule = world.shop_rules.item_rules[item].condense(world.player)
shopping_area.locations.append(loc)
if not world.options.blacklist_channel.value:
meta: ShopItemLocation = ShopItemLocation(SHOP_PROGRESSION_75COMPLETION[0])
loc: Location = meta.to_location(world.player, shopping_area)
if loc.name in world.shop_rules.item_rules:
loc.access_rule = world.shop_rules.item_rules[loc.name].condense(world.player)
shopping_area.locations.append(loc)
## Handle Morph Stocks
if world.options.shoppingsanity != 2:
stocks_region : Region = stages[Stage.region_shop_morph.value]
for i, item in enumerate(SHOP_PROGRESSION_MORPH):
meta : ShopItemLocation = ShopItemLocation(item, 1, i)
loc : Location = meta.to_location(world.player, stocks_region)
if item in world.shop_rules.item_rules:
loc.access_rule = world.shop_rules.item_rules[item].condense(world.player)
stocks_region.locations.append(loc)
## Handle Shoppingsanity options Enabled/Collection
if 0 < world.options.shoppingsanity.value < 3:
shop_locations_meta : list[ShopItemLocation] = []
if world.options.shoppingsanity.value == 1:
for item in [*SHOP_PROGRESSION_MASTER]:
meta : ShopItemLocation = ShopItemLocation(item)
shop_locations_meta.append(meta)
else:
for category_index, category in enumerate(SHOP_COLLECTION_INDEX):
for offset, item in enumerate(category):
meta : ShopItemLocation = ShopItemLocation(item, category_index, offset)
shop_locations_meta.append(meta)
if shop_locations_meta:
for item in shop_locations_meta:
parent = shopping_area if item.name in world.shop_rules.cheap_early_items else expensive_area
loc : Location = item.to_location(world.player, parent)
if item.name in world.shop_rules.item_rules:
loc.access_rule = world.shop_rules.item_rules[item.name].condense(world.player)
parent.locations.append(loc)
## Handle Shoppingsanity Options Progressive/Restock
elif 2 < world.options.shoppingsanity.value < 5:
shop_progression_regions : list[Region] = [region for name, region in stages.items()
if name in STAGES_SHOP_PROGRESSION]
for region in shop_progression_regions:
for location in SHOP_PROGRESSION_DIRECTORY[region.name]:
meta : ShopItemLocation = ShopItemLocation(location)
loc : Location = meta.to_location(world.player, region)
if location in world.shop_rules.item_rules:
loc.access_rule = world.shop_rules.item_rules[location].condense(world.player)
region.locations.append(loc)
# Send Regions to Archipelago
world.multiworld.regions.extend(list(stages.values()))
# # <!> DEBUG
# # Connection Diagrams
# from Utils import visualize_regions
# visualize_regions(world.multiworld.get_region("Menu", world.player), "_region_diagram.puml")