forked from mirror/Archipelago
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
159 lines
8.9 KiB
Python
159 lines
8.9 KiB
Python
"""Shuffles the locations of shops."""
|
|
|
|
import randomizer.Lists.Exceptions as Ex
|
|
|
|
from randomizer.Enums.Levels import Levels
|
|
from randomizer.Enums.Regions import Regions
|
|
from randomizer.Enums.Settings import ShuffleLoadingZones
|
|
from randomizer.Enums.Maps import Maps
|
|
from randomizer.LogicClasses import TransitionFront
|
|
|
|
|
|
class ShopLocation:
|
|
"""Class which stores data for a shop location."""
|
|
|
|
def __init__(self, shop, map, containing_region, shop_exit, locked=False):
|
|
"""Initialize with given parameters."""
|
|
self.shop = shop
|
|
self.map = map
|
|
self.locked = locked
|
|
self.new_shop = shop
|
|
self.containing_region = containing_region
|
|
self.shop_exit = shop_exit
|
|
self.new_shop_exit = shop_exit
|
|
|
|
def setShop(self, shop):
|
|
"""Assign new shop to shop location object."""
|
|
self.new_shop = shop.shop
|
|
self.new_shop_exit = shop.shop_exit
|
|
|
|
|
|
available_shops = {
|
|
Levels.DKIsles: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.TrainingGrounds, Regions.TrainingGrounds, Regions.CrankyIsles),
|
|
ShopLocation(Regions.Snide, Maps.IslesSnideRoom, Regions.IslesSnideRoom, Regions.Snide),
|
|
],
|
|
Levels.JungleJapes: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.JungleJapes, Regions.JapesBeyondCoconutGate2, Regions.CrankyJapes),
|
|
ShopLocation(Regions.Snide, Maps.JungleJapes, Regions.JapesHillTop, Regions.Snide),
|
|
ShopLocation(Regions.FunkyGeneric, Maps.JungleJapes, Regions.JapesHill, Regions.FunkyJapes),
|
|
],
|
|
Levels.AngryAztec: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.AngryAztec, Regions.AngryAztecConnectorTunnel, Regions.CrankyAztec),
|
|
ShopLocation(Regions.CandyGeneric, Maps.AngryAztec, Regions.AngryAztecOasis, Regions.CandyAztec),
|
|
ShopLocation(Regions.FunkyGeneric, Maps.AngryAztec, Regions.AngryAztecMain, Regions.FunkyAztec),
|
|
ShopLocation(Regions.Snide, Maps.AngryAztec, Regions.AngryAztecMain, Regions.Snide),
|
|
],
|
|
Levels.FranticFactory: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.FranticFactory, Regions.BeyondHatch, Regions.CrankyFactory),
|
|
ShopLocation(Regions.CandyGeneric, Maps.FranticFactory, Regions.BeyondHatch, Regions.CandyFactory),
|
|
ShopLocation(Regions.FunkyGeneric, Maps.FranticFactory, Regions.Testing, Regions.FunkyFactory),
|
|
ShopLocation(Regions.Snide, Maps.FranticFactory, Regions.Testing, Regions.Snide),
|
|
],
|
|
Levels.GloomyGalleon: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.GloomyGalleon, Regions.GloomyGalleonStart, Regions.CrankyGalleon),
|
|
ShopLocation(Regions.CandyGeneric, Maps.GloomyGalleon, Regions.Shipyard, Regions.CandyGalleon),
|
|
ShopLocation(Regions.FunkyGeneric, Maps.GloomyGalleon, Regions.Shipyard, Regions.FunkyGalleon),
|
|
ShopLocation(Regions.Snide, Maps.GloomyGalleon, Regions.LighthouseSnideAlcove, Regions.Snide),
|
|
],
|
|
Levels.FungiForest: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.FungiForest, Regions.GiantMushroomArea, Regions.CrankyForest),
|
|
ShopLocation(Regions.FunkyGeneric, Maps.FungiForest, Regions.WormArea, Regions.FunkyForest),
|
|
ShopLocation(Regions.Snide, Maps.FungiForest, Regions.MillArea, Regions.Snide),
|
|
],
|
|
Levels.CrystalCaves: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.CrystalCaves, Regions.CrystalCavesMain, Regions.CrankyCaves),
|
|
ShopLocation(Regions.CandyGeneric, Maps.CrystalCaves, Regions.CabinArea, Regions.CandyCaves),
|
|
ShopLocation(Regions.FunkyGeneric, Maps.CrystalCaves, Regions.CrystalCavesMain, Regions.FunkyCaves),
|
|
ShopLocation(Regions.Snide, Maps.CrystalCaves, Regions.CavesSnideArea, Regions.Snide),
|
|
],
|
|
Levels.CreepyCastle: [
|
|
ShopLocation(Regions.CrankyGeneric, Maps.CreepyCastle, Regions.CreepyCastleMain, Regions.CrankyCastle),
|
|
ShopLocation(Regions.CandyGeneric, Maps.CastleUpperCave, Regions.UpperCave, Regions.CandyCastle),
|
|
ShopLocation(Regions.FunkyGeneric, Maps.CastleLowerCave, Regions.LowerCave, Regions.FunkyCastle),
|
|
ShopLocation(Regions.Snide, Maps.CreepyCastle, Regions.CreepyCastleMain, Regions.Snide),
|
|
],
|
|
}
|
|
|
|
|
|
def ShuffleShopLocations(spoiler):
|
|
"""Shuffle Shop locations within their own pool inside the level."""
|
|
# Reset
|
|
for level in available_shops:
|
|
shop_array = available_shops[level]
|
|
for shop in shop_array:
|
|
shop.setShop(shop)
|
|
# Shuffle
|
|
assortment = {}
|
|
for level in available_shops:
|
|
# Don't shuffle Isles shops in entrance rando.
|
|
# This prevents having the one-entrance-locked Isles Snide room from being progression.
|
|
# Also ban it with fast start beginning of game off. Introduces a lot of oddities about things
|
|
if level == Levels.DKIsles and (spoiler.settings.shuffle_loading_zones == ShuffleLoadingZones.all or not spoiler.settings.fast_start_beginning_of_game):
|
|
continue
|
|
shop_array = available_shops[level]
|
|
# Get list of shops in level
|
|
shops_in_levels = []
|
|
if spoiler.settings.enable_plandomizer and spoiler.settings.plandomizer_dict["plando_shop_location_rando"] != -1:
|
|
if spoiler.settings.plandomizer_dict["plando_shop_location_rando"][str(level.value)] != -1:
|
|
available_dict = {}
|
|
filled_dict = {}
|
|
unused_locations = []
|
|
unused_vendors = []
|
|
for shop in shop_array:
|
|
if not shop.locked:
|
|
# This is a valid shop to shuffle, so we need to remove all preexisting logical access, wherever it is
|
|
possible_containing_region_ids = [shop_location.containing_region for shop_location in shop_array]
|
|
for region_id in possible_containing_region_ids:
|
|
old_region = spoiler.RegionList[region_id]
|
|
old_region.exits = [exit for exit in old_region.exits if exit.dest != shop.shop_exit]
|
|
available_dict[shop.shop] = shop
|
|
unused_locations.append(shop.shop)
|
|
unused_vendors.append(shop.shop)
|
|
for planned_location in spoiler.settings.plandomizer_dict["plando_shop_location_rando"][str(level.value)].keys():
|
|
planned_region = int(planned_location)
|
|
if planned_region in available_dict.keys():
|
|
planned_shop = spoiler.settings.plandomizer_dict["plando_shop_location_rando"][str(level.value)][planned_location]
|
|
filled_dict[planned_region] = available_dict[planned_shop]
|
|
unused_locations.remove(planned_region)
|
|
unused_vendors.remove(planned_shop)
|
|
if len(unused_locations) > 0:
|
|
spoiler.settings.random.shuffle(unused_vendors)
|
|
for real_estate in unused_locations:
|
|
vendor_region = unused_vendors.pop()
|
|
filled_dict[real_estate] = available_dict[vendor_region]
|
|
for shop_again in shop_array:
|
|
if not shop_again.locked:
|
|
shops_in_levels.append(filled_dict[shop_again.shop])
|
|
else:
|
|
for shop in shop_array:
|
|
if not shop.locked:
|
|
# This is a valid shop to shuffle, so we need to remove all preexisting logical access, wherever it is
|
|
possible_containing_region_ids = [shop_location.containing_region for shop_location in shop_array]
|
|
for region_id in possible_containing_region_ids:
|
|
old_region = spoiler.RegionList[region_id]
|
|
old_region.exits = [exit for exit in old_region.exits if exit.dest != shop.shop_exit]
|
|
shops_in_levels.append(shop)
|
|
spoiler.settings.random.shuffle(shops_in_levels)
|
|
# Assign shuffle to data
|
|
assortment_in_level = {}
|
|
placement_index = 0
|
|
for shop_index, shop in enumerate(shop_array):
|
|
if not shop.locked:
|
|
shop.setShop(shops_in_levels[placement_index])
|
|
assortment_in_level[shop.shop] = shop.new_shop
|
|
placement_index += 1
|
|
# Add exit to new containing region for logical access
|
|
region = spoiler.RegionList[shop.containing_region]
|
|
if shop.new_shop == Regions.CrankyGeneric:
|
|
region.exits.append(TransitionFront(shop.new_shop_exit, lambda l: l.crankyAccess))
|
|
elif shop.new_shop == Regions.FunkyGeneric:
|
|
region.exits.append(TransitionFront(shop.new_shop_exit, lambda l: l.funkyAccess))
|
|
elif shop.new_shop == Regions.CandyGeneric:
|
|
region.exits.append(TransitionFront(shop.new_shop_exit, lambda l: l.candyAccess))
|
|
elif shop.new_shop == Regions.Snide:
|
|
region.exits.append(TransitionFront(shop.new_shop_exit, lambda l: l.snideAccess))
|
|
assortment[level] = assortment_in_level
|
|
# Write Assortment to spoiler
|
|
spoiler.shuffled_shop_locations = assortment
|