mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-29 21:53:21 -07:00
775 lines
36 KiB
Python
775 lines
36 KiB
Python
from worlds.AutoWorld import World
|
|
from BaseClasses import Region, Entrance, ItemClassification, Location
|
|
from .Locations import HatInTimeLocation, location_table, storybook_pages, event_locs, is_location_valid, shop_locations
|
|
from .Items import HatInTimeItem
|
|
from .Types import ChapterIndex
|
|
import typing
|
|
from .Rules import set_rift_rules
|
|
|
|
|
|
# ChapterIndex: region
|
|
chapter_regions = {
|
|
ChapterIndex.SPACESHIP: "Spaceship",
|
|
ChapterIndex.MAFIA: "Mafia Town",
|
|
ChapterIndex.BIRDS: "Battle of the Birds",
|
|
ChapterIndex.SUBCON: "Subcon Forest",
|
|
ChapterIndex.ALPINE: "Alpine Skyline",
|
|
ChapterIndex.FINALE: "Time's End",
|
|
ChapterIndex.CRUISE: "The Arctic Cruise",
|
|
ChapterIndex.METRO: "Nyakuza Metro",
|
|
}
|
|
|
|
# entrance: region
|
|
act_entrances = {
|
|
"Welcome to Mafia Town": "Mafia Town - Act 1",
|
|
"Barrel Battle": "Mafia Town - Act 2",
|
|
"She Came from Outer Space": "Mafia Town - Act 3",
|
|
"Down with the Mafia!": "Mafia Town - Act 4",
|
|
"Cheating the Race": "Mafia Town - Act 5",
|
|
"Heating Up Mafia Town": "Mafia Town - Act 6",
|
|
"The Golden Vault": "Mafia Town - Act 7",
|
|
|
|
"Dead Bird Studio": "Battle of the Birds - Act 1",
|
|
"Murder on the Owl Express": "Battle of the Birds - Act 2",
|
|
"Picture Perfect": "Battle of the Birds - Act 3",
|
|
"Train Rush": "Battle of the Birds - Act 4",
|
|
"The Big Parade": "Battle of the Birds - Act 5",
|
|
"Award Ceremony": "Battle of the Birds - Finale A",
|
|
"Dead Bird Studio Basement": "Battle of the Birds - Finale B",
|
|
|
|
"Contractual Obligations": "Subcon Forest - Act 1",
|
|
"The Subcon Well": "Subcon Forest - Act 2",
|
|
"Toilet of Doom": "Subcon Forest - Act 3",
|
|
"Queen Vanessa's Manor": "Subcon Forest - Act 4",
|
|
"Mail Delivery Service": "Subcon Forest - Act 5",
|
|
"Your Contract has Expired": "Subcon Forest - Finale",
|
|
|
|
"Alpine Free Roam": "Alpine Skyline - Free Roam",
|
|
"The Illness has Spread": "Alpine Skyline - Finale",
|
|
|
|
"The Finale": "Time's End - Act 1",
|
|
|
|
"Bon Voyage!": "The Arctic Cruise - Act 1",
|
|
"Ship Shape": "The Arctic Cruise - Act 2",
|
|
"Rock the Boat": "The Arctic Cruise - Finale",
|
|
|
|
"Nyakuza Free Roam": "Nyakuza Metro - Free Roam",
|
|
"Rush Hour": "Nyakuza Metro - Finale",
|
|
}
|
|
|
|
act_chapters = {
|
|
"Time Rift - Gallery": "Spaceship",
|
|
"Time Rift - The Lab": "Spaceship",
|
|
|
|
"Welcome to Mafia Town": "Mafia Town",
|
|
"Barrel Battle": "Mafia Town",
|
|
"She Came from Outer Space": "Mafia Town",
|
|
"Down with the Mafia!": "Mafia Town",
|
|
"Cheating the Race": "Mafia Town",
|
|
"Heating Up Mafia Town": "Mafia Town",
|
|
"The Golden Vault": "Mafia Town",
|
|
"Time Rift - Mafia of Cooks": "Mafia Town",
|
|
"Time Rift - Sewers": "Mafia Town",
|
|
"Time Rift - Bazaar": "Mafia Town",
|
|
|
|
"Dead Bird Studio": "Battle of the Birds",
|
|
"Murder on the Owl Express": "Battle of the Birds",
|
|
"Picture Perfect": "Battle of the Birds",
|
|
"Train Rush": "Battle of the Birds",
|
|
"The Big Parade": "Battle of the Birds",
|
|
"Award Ceremony": "Battle of the Birds",
|
|
"Dead Bird Studio Basement": "Battle of the Birds",
|
|
"Time Rift - Dead Bird Studio": "Battle of the Birds",
|
|
"Time Rift - The Owl Express": "Battle of the Birds",
|
|
"Time Rift - The Moon": "Battle of the Birds",
|
|
|
|
"Contractual Obligations": "Subcon Forest",
|
|
"The Subcon Well": "Subcon Forest",
|
|
"Toilet of Doom": "Subcon Forest",
|
|
"Queen Vanessa's Manor": "Subcon Forest",
|
|
"Mail Delivery Service": "Subcon Forest",
|
|
"Your Contract has Expired": "Subcon Forest",
|
|
"Time Rift - Sleepy Subcon": "Subcon Forest",
|
|
"Time Rift - Pipe": "Subcon Forest",
|
|
"Time Rift - Village": "Subcon Forest",
|
|
|
|
"Alpine Free Roam": "Alpine Skyline",
|
|
"The Illness has Spread": "Alpine Skyline",
|
|
"Time Rift - Alpine Skyline": "Alpine Skyline",
|
|
"Time Rift - The Twilight Bell": "Alpine Skyline",
|
|
"Time Rift - Curly Tail Trail": "Alpine Skyline",
|
|
|
|
"The Finale": "Time's End",
|
|
"Time Rift - Tour": "Time's End",
|
|
|
|
"Bon Voyage!": "The Arctic Cruise",
|
|
"Ship Shape": "The Arctic Cruise",
|
|
"Rock the Boat": "The Arctic Cruise",
|
|
"Time Rift - Balcony": "The Arctic Cruise",
|
|
"Time Rift - Deep Sea": "The Arctic Cruise",
|
|
|
|
"Nyakuza Free Roam": "Nyakuza Metro",
|
|
"Rush Hour": "Nyakuza Metro",
|
|
"Time Rift - Rumbi Factory": "Nyakuza Metro",
|
|
}
|
|
|
|
# region: list[Region]
|
|
rift_access_regions = {
|
|
"Time Rift - Gallery": ["Spaceship"],
|
|
"Time Rift - The Lab": ["Spaceship"],
|
|
|
|
"Time Rift - Sewers": ["Welcome to Mafia Town", "Barrel Battle", "She Came from Outer Space",
|
|
"Down with the Mafia!", "Cheating the Race", "Heating Up Mafia Town",
|
|
"The Golden Vault"],
|
|
|
|
"Time Rift - Bazaar": ["Welcome to Mafia Town", "Barrel Battle", "She Came from Outer Space",
|
|
"Down with the Mafia!", "Cheating the Race", "Heating Up Mafia Town",
|
|
"The Golden Vault"],
|
|
|
|
"Time Rift - Mafia of Cooks": ["Welcome to Mafia Town", "Barrel Battle", "She Came from Outer Space",
|
|
"Down with the Mafia!", "Cheating the Race", "The Golden Vault"],
|
|
|
|
"Time Rift - The Owl Express": ["Murder on the Owl Express"],
|
|
"Time Rift - The Moon": ["Picture Perfect", "The Big Parade"],
|
|
"Time Rift - Dead Bird Studio": ["Dead Bird Studio", "Dead Bird Studio Basement"],
|
|
|
|
"Time Rift - Pipe": ["Contractual Obligations", "The Subcon Well",
|
|
"Toilet of Doom", "Queen Vanessa's Manor",
|
|
"Mail Delivery Service"],
|
|
|
|
"Time Rift - Village": ["Contractual Obligations", "The Subcon Well",
|
|
"Toilet of Doom", "Queen Vanessa's Manor",
|
|
"Mail Delivery Service"],
|
|
|
|
"Time Rift - Sleepy Subcon": ["Contractual Obligations", "The Subcon Well",
|
|
"Toilet of Doom", "Queen Vanessa's Manor",
|
|
"Mail Delivery Service"],
|
|
|
|
"Time Rift - The Twilight Bell": ["Alpine Free Roam"],
|
|
"Time Rift - Curly Tail Trail": ["Alpine Free Roam"],
|
|
"Time Rift - Alpine Skyline": ["Alpine Free Roam", "The Illness has Spread"],
|
|
|
|
"Time Rift - Tour": ["Time's End"],
|
|
|
|
"Time Rift - Balcony": ["Cruise Ship"],
|
|
"Time Rift - Deep Sea": ["Bon Voyage!"],
|
|
|
|
"Time Rift - Rumbi Factory": ["Nyakuza Free Roam"],
|
|
}
|
|
|
|
# Hat_ChapterActInfo, from the game files to be used in act shuffle
|
|
chapter_act_info = {
|
|
"Time Rift - Gallery": "hatintime_chapterinfo.spaceship.Spaceship_WaterRift_Gallery",
|
|
"Time Rift - The Lab": "hatintime_chapterinfo.spaceship.Spaceship_WaterRift_MailRoom",
|
|
|
|
"Welcome to Mafia Town": "hatintime_chapterinfo.MafiaTown.MafiaTown_Welcome",
|
|
"Barrel Battle": "hatintime_chapterinfo.MafiaTown.MafiaTown_BarrelBattle",
|
|
"She Came from Outer Space": "hatintime_chapterinfo.MafiaTown.MafiaTown_AlienChase",
|
|
"Down with the Mafia!": "hatintime_chapterinfo.MafiaTown.MafiaTown_MafiaBoss",
|
|
"Cheating the Race": "hatintime_chapterinfo.MafiaTown.MafiaTown_Race",
|
|
"Heating Up Mafia Town": "hatintime_chapterinfo.MafiaTown.MafiaTown_Lava",
|
|
"The Golden Vault": "hatintime_chapterinfo.MafiaTown.MafiaTown_GoldenVault",
|
|
"Time Rift - Mafia of Cooks": "hatintime_chapterinfo.MafiaTown.MafiaTown_CaveRift_Mafia",
|
|
"Time Rift - Sewers": "hatintime_chapterinfo.MafiaTown.MafiaTown_WaterRift_Easy",
|
|
"Time Rift - Bazaar": "hatintime_chapterinfo.MafiaTown.MafiaTown_WaterRift_Hard",
|
|
|
|
"Dead Bird Studio": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_DeadBirdStudio",
|
|
"Murder on the Owl Express": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_Murder",
|
|
"Picture Perfect": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_PicturePerfect",
|
|
"Train Rush": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_TrainRush",
|
|
"The Big Parade": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_Parade",
|
|
"Award Ceremony": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_AwardCeremony",
|
|
"Dead Bird Studio Basement": "DeadBirdBasement", # Dead Bird Studio Basement has no ChapterActInfo
|
|
"Time Rift - Dead Bird Studio": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_CaveRift_Basement",
|
|
"Time Rift - The Owl Express": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_WaterRift_Panels",
|
|
"Time Rift - The Moon": "hatintime_chapterinfo.BattleOfTheBirds.BattleOfTheBirds_WaterRift_Parade",
|
|
|
|
"Contractual Obligations": "hatintime_chapterinfo.subconforest.SubconForest_IceWall",
|
|
"The Subcon Well": "hatintime_chapterinfo.subconforest.SubconForest_Cave",
|
|
"Toilet of Doom": "hatintime_chapterinfo.subconforest.SubconForest_Toilet",
|
|
"Queen Vanessa's Manor": "hatintime_chapterinfo.subconforest.SubconForest_Manor",
|
|
"Mail Delivery Service": "hatintime_chapterinfo.subconforest.SubconForest_MailDelivery",
|
|
"Your Contract has Expired": "hatintime_chapterinfo.subconforest.SubconForest_SnatcherBoss",
|
|
"Time Rift - Sleepy Subcon": "hatintime_chapterinfo.subconforest.SubconForest_CaveRift_Raccoon",
|
|
"Time Rift - Pipe": "hatintime_chapterinfo.subconforest.SubconForest_WaterRift_Hookshot",
|
|
"Time Rift - Village": "hatintime_chapterinfo.subconforest.SubconForest_WaterRift_Dwellers",
|
|
|
|
"Alpine Free Roam": "hatintime_chapterinfo.AlpineSkyline.AlpineSkyline_IntroMountain",
|
|
"The Illness has Spread": "hatintime_chapterinfo.AlpineSkyline.AlpineSkyline_Finale",
|
|
"Time Rift - Alpine Skyline": "hatintime_chapterinfo.AlpineSkyline.AlpineSkyline_CaveRift_Alpine",
|
|
"Time Rift - The Twilight Bell": "hatintime_chapterinfo.AlpineSkyline.AlpineSkyline_WaterRift_Goats",
|
|
"Time Rift - Curly Tail Trail": "hatintime_chapterinfo.AlpineSkyline.AlpineSkyline_WaterRift_Cats",
|
|
|
|
"The Finale": "hatintime_chapterinfo.TheFinale.TheFinale_FinalBoss",
|
|
"Time Rift - Tour": "hatintime_chapterinfo_dlc1.spaceship.CaveRift_Tour",
|
|
|
|
"Bon Voyage!": "hatintime_chapterinfo_dlc1.Cruise.Cruise_Boarding",
|
|
"Ship Shape": "hatintime_chapterinfo_dlc1.Cruise.Cruise_Working",
|
|
"Rock the Boat": "hatintime_chapterinfo_dlc1.Cruise.Cruise_Sinking",
|
|
"Time Rift - Balcony": "hatintime_chapterinfo_dlc1.Cruise.Cruise_WaterRift_Slide",
|
|
"Time Rift - Deep Sea": "hatintime_chapterinfo_dlc1.Cruise.Cruise_CaveRift",
|
|
|
|
"Nyakuza Free Roam": "hatintime_chapterinfo_dlc2.metro.Metro_FreeRoam",
|
|
"Rush Hour": "hatintime_chapterinfo_dlc2.metro.Metro_Escape",
|
|
"Time Rift - Rumbi Factory": "hatintime_chapterinfo_dlc2.metro.Metro_RumbiFactory"
|
|
}
|
|
|
|
# Guarantee that the first level a player can access is a location dense area beatable with no items
|
|
guaranteed_first_acts = [
|
|
"Welcome to Mafia Town",
|
|
"Barrel Battle",
|
|
"She Came from Outer Space",
|
|
"Down with the Mafia!",
|
|
"Heating Up Mafia Town", # Removed in umbrella logic
|
|
"The Golden Vault",
|
|
|
|
"Contractual Obligations", # Removed in painting logic
|
|
"Queen Vanessa's Manor", # Removed in umbrella/painting logic
|
|
]
|
|
|
|
purple_time_rifts = [
|
|
"Time Rift - Mafia of Cooks",
|
|
"Time Rift - Dead Bird Studio",
|
|
"Time Rift - Sleepy Subcon",
|
|
"Time Rift - Alpine Skyline",
|
|
"Time Rift - Deep Sea",
|
|
"Time Rift - Tour",
|
|
"Time Rift - Rumbi Factory",
|
|
]
|
|
|
|
chapter_finales = [
|
|
"Dead Bird Studio Basement",
|
|
"Your Contract has Expired",
|
|
"The Illness has Spread",
|
|
"Rock the Boat",
|
|
"Rush Hour",
|
|
]
|
|
|
|
# Acts blacklisted in act shuffle
|
|
# entrance: region
|
|
blacklisted_acts = {
|
|
"Battle of the Birds - Finale A": "Award Ceremony",
|
|
"Time's End - Act 1": "The Finale",
|
|
}
|
|
|
|
|
|
def create_regions(world: World):
|
|
w = world
|
|
mw = world.multiworld
|
|
p = world.player
|
|
|
|
# ------------------------------------------- HUB -------------------------------------------------- #
|
|
menu = create_region(w, "Menu")
|
|
spaceship = create_region_and_connect(w, "Spaceship", "Save File -> Spaceship", menu)
|
|
create_rift_connections(w, create_region(w, "Time Rift - Gallery"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - The Lab"))
|
|
|
|
# ------------------------------------------- MAFIA TOWN ------------------------------------------- #
|
|
mafia_town = create_region_and_connect(w, "Mafia Town", "Telescope -> Mafia Town", spaceship)
|
|
mt_act1 = create_region_and_connect(w, "Welcome to Mafia Town", "Mafia Town - Act 1", mafia_town)
|
|
mt_act2 = create_region_and_connect(w, "Barrel Battle", "Mafia Town - Act 2", mafia_town)
|
|
mt_act3 = create_region_and_connect(w, "She Came from Outer Space", "Mafia Town - Act 3", mafia_town)
|
|
mt_act4 = create_region_and_connect(w, "Down with the Mafia!", "Mafia Town - Act 4", mafia_town)
|
|
mt_act6 = create_region_and_connect(w, "Heating Up Mafia Town", "Mafia Town - Act 6", mafia_town)
|
|
mt_act5 = create_region_and_connect(w, "Cheating the Race", "Mafia Town - Act 5", mafia_town)
|
|
mt_act7 = create_region_and_connect(w, "The Golden Vault", "Mafia Town - Act 7", mafia_town)
|
|
|
|
# ------------------------------------------- BOTB ------------------------------------------------- #
|
|
botb = create_region_and_connect(w, "Battle of the Birds", "Telescope -> Battle of the Birds", spaceship)
|
|
dbs = create_region_and_connect(w, "Dead Bird Studio", "Battle of the Birds - Act 1", botb)
|
|
create_region_and_connect(w, "Murder on the Owl Express", "Battle of the Birds - Act 2", botb)
|
|
create_region_and_connect(w, "Picture Perfect", "Battle of the Birds - Act 3", botb)
|
|
create_region_and_connect(w, "Train Rush", "Battle of the Birds - Act 4", botb)
|
|
create_region_and_connect(w, "The Big Parade", "Battle of the Birds - Act 5", botb)
|
|
create_region_and_connect(w, "Award Ceremony", "Battle of the Birds - Finale A", botb)
|
|
create_region_and_connect(w, "Dead Bird Studio Basement", "Battle of the Birds - Finale B", botb)
|
|
create_rift_connections(w, create_region(w, "Time Rift - Dead Bird Studio"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - The Owl Express"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - The Moon"))
|
|
|
|
# Items near the Dead Bird Studio elevator can be reached from the basement act
|
|
ev_area = create_region_and_connect(w, "Dead Bird Studio - Elevator Area", "DBS -> Elevator Area", dbs)
|
|
connect_regions(mw.get_region("Dead Bird Studio Basement", p), ev_area, "DBS Basement -> Elevator Area", p)
|
|
|
|
# ------------------------------------------- SUBCON FOREST --------------------------------------- #
|
|
subcon_forest = create_region_and_connect(w, "Subcon Forest", "Telescope -> Subcon Forest", spaceship)
|
|
sf_act1 = create_region_and_connect(w, "Contractual Obligations", "Subcon Forest - Act 1", subcon_forest)
|
|
sf_act2 = create_region_and_connect(w, "The Subcon Well", "Subcon Forest - Act 2", subcon_forest)
|
|
sf_act3 = create_region_and_connect(w, "Toilet of Doom", "Subcon Forest - Act 3", subcon_forest)
|
|
sf_act4 = create_region_and_connect(w, "Queen Vanessa's Manor", "Subcon Forest - Act 4", subcon_forest)
|
|
sf_act5 = create_region_and_connect(w, "Mail Delivery Service", "Subcon Forest - Act 5", subcon_forest)
|
|
create_region_and_connect(w, "Your Contract has Expired", "Subcon Forest - Finale", subcon_forest)
|
|
|
|
# ------------------------------------------- ALPINE SKYLINE ------------------------------------------ #
|
|
alpine_skyline = create_region_and_connect(w, "Alpine Skyline", "Telescope -> Alpine Skyline", spaceship)
|
|
alpine_freeroam = create_region_and_connect(w, "Alpine Free Roam", "Alpine Skyline - Free Roam", alpine_skyline)
|
|
alpine_area = create_region_and_connect(w, "Alpine Skyline Area", "AFR -> Alpine Skyline Area", alpine_freeroam)
|
|
goat_village = create_region_and_connect(w, "Goat Village", "ASA -> Goat Village", alpine_area)
|
|
|
|
create_region_and_connect(w, "The Birdhouse", "-> The Birdhouse", alpine_area)
|
|
create_region_and_connect(w, "The Lava Cake", "-> The Lava Cake", alpine_area)
|
|
create_region_and_connect(w, "The Windmill", "-> The Windmill", alpine_area)
|
|
create_region_and_connect(w, "The Twilight Bell", "-> The Twilight Bell", alpine_area)
|
|
|
|
illness = create_region_and_connect(w, "The Illness has Spread", "Alpine Skyline - Finale", alpine_skyline)
|
|
connect_regions(illness, alpine_area, "TIHS -> Alpine Skyline Area", p)
|
|
connect_regions(illness, goat_village, "TIHS -> Goat Village", p)
|
|
create_rift_connections(w, create_region(w, "Time Rift - Alpine Skyline"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - The Twilight Bell"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - Curly Tail Trail"))
|
|
|
|
# ------------------------------------------- OTHER -------------------------------------------------- #
|
|
mt_area: Region = create_region(w, "Mafia Town Area")
|
|
mt_area_humt: Region = create_region(w, "Mafia Town Area (HUMT)")
|
|
connect_regions(mt_area, mt_area_humt, "MT Area -> MT Area (HUMT)", p)
|
|
connect_regions(mt_act1, mt_area, "Mafia Town Entrance WTMT", p)
|
|
connect_regions(mt_act2, mt_area, "Mafia Town Entrance BB", p)
|
|
connect_regions(mt_act3, mt_area, "Mafia Town Entrance SCFOS", p)
|
|
connect_regions(mt_act4, mt_area, "Mafia Town Entrance DWTM", p)
|
|
connect_regions(mt_act5, mt_area, "Mafia Town Entrance CTR", p)
|
|
connect_regions(mt_act6, mt_area_humt, "Mafia Town Entrance HUMT", p)
|
|
connect_regions(mt_act7, mt_area, "Mafia Town Entrance TGV", p)
|
|
|
|
create_rift_connections(w, create_region(w, "Time Rift - Mafia of Cooks"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - Sewers"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - Bazaar"))
|
|
|
|
sf_area: Region = create_region(w, "Subcon Forest Area")
|
|
connect_regions(sf_act1, sf_area, "Subcon Forest Entrance CO", p)
|
|
connect_regions(sf_act2, sf_area, "Subcon Forest Entrance SW", p)
|
|
connect_regions(sf_act3, sf_area, "Subcon Forest Entrance TOD", p)
|
|
connect_regions(sf_act4, sf_area, "Subcon Forest Entrance QVM", p)
|
|
connect_regions(sf_act5, sf_area, "Subcon Forest Entrance MDS", p)
|
|
|
|
create_rift_connections(w, create_region(w, "Time Rift - Sleepy Subcon"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - Pipe"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - Village"))
|
|
|
|
badge_seller = create_badge_seller(w)
|
|
connect_regions(mt_area, badge_seller, "MT Area -> Badge Seller", p)
|
|
connect_regions(mt_area_humt, badge_seller, "MT Area (HUMT) -> Badge Seller", p)
|
|
connect_regions(sf_area, badge_seller, "SF Area -> Badge Seller", p)
|
|
connect_regions(mw.get_region("Dead Bird Studio", p), badge_seller, "DBS -> Badge Seller", p)
|
|
connect_regions(mw.get_region("Picture Perfect", p), badge_seller, "PP -> Badge Seller", p)
|
|
connect_regions(mw.get_region("Train Rush", p), badge_seller, "TR -> Badge Seller", p)
|
|
connect_regions(mw.get_region("Goat Village", p), badge_seller, "GV -> Badge Seller", p)
|
|
|
|
times_end = create_region_and_connect(w, "Time's End", "Telescope -> Time's End", spaceship)
|
|
create_region_and_connect(w, "The Finale", "Time's End - Act 1", times_end)
|
|
|
|
# ------------------------------------------- DLC1 ------------------------------------------------- #
|
|
if mw.EnableDLC1[p].value > 0:
|
|
arctic_cruise = create_region_and_connect(w, "The Arctic Cruise", "Telescope -> The Arctic Cruise", spaceship)
|
|
cruise_ship = create_region(w, "Cruise Ship")
|
|
|
|
ac_act1 = create_region_and_connect(w, "Bon Voyage!", "The Arctic Cruise - Act 1", arctic_cruise)
|
|
ac_act2 = create_region_and_connect(w, "Ship Shape", "The Arctic Cruise - Act 2", arctic_cruise)
|
|
ac_act3 = create_region_and_connect(w, "Rock the Boat", "The Arctic Cruise - Finale", arctic_cruise)
|
|
|
|
connect_regions(ac_act1, cruise_ship, "Cruise Ship Entrance BV", p)
|
|
connect_regions(ac_act2, cruise_ship, "Cruise Ship Entrance SS", p)
|
|
connect_regions(ac_act3, cruise_ship, "Cruise Ship Entrance RTB", p)
|
|
create_rift_connections(w, create_region(w, "Time Rift - Balcony"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - Deep Sea"))
|
|
create_rift_connections(w, create_region(w, "Time Rift - Tour"))
|
|
|
|
if mw.Tasksanity[p].value > 0:
|
|
create_tasksanity_locations(w)
|
|
|
|
# force recache
|
|
mw.get_region("Time Rift - Deep Sea", p)
|
|
|
|
connect_regions(mw.get_region("Cruise Ship", p), badge_seller, "CS -> Badge Seller", p)
|
|
|
|
if mw.EnableDLC2[p].value > 0:
|
|
nyakuza_metro = create_region_and_connect(w, "Nyakuza Metro", "Telescope -> Nyakuza Metro", spaceship)
|
|
metro_freeroam = create_region_and_connect(w, "Nyakuza Free Roam", "Nyakuza Metro - Free Roam", nyakuza_metro)
|
|
create_region_and_connect(w, "Rush Hour", "Nyakuza Metro - Finale", nyakuza_metro)
|
|
|
|
yellow = create_region_and_connect(w, "Yellow Overpass Station", "-> Yellow Overpass Station", metro_freeroam)
|
|
green = create_region_and_connect(w, "Green Clean Station", "-> Green Clean Station", metro_freeroam)
|
|
pink = create_region_and_connect(w, "Pink Paw Station", "-> Pink Paw Station", metro_freeroam)
|
|
create_region_and_connect(w, "Bluefin Tunnel", "-> Bluefin Tunnel", metro_freeroam) # No manhole
|
|
|
|
create_region_and_connect(w, "Yellow Overpass Manhole", "-> Yellow Overpass Manhole", yellow)
|
|
create_region_and_connect(w, "Green Clean Manhole", "-> Green Clean Manhole", green)
|
|
create_region_and_connect(w, "Pink Paw Manhole", "-> Pink Paw Manhole", pink)
|
|
|
|
create_rift_connections(w, create_region(w, "Time Rift - Rumbi Factory"))
|
|
create_thug_shops(w)
|
|
|
|
# force recache
|
|
mw.get_region("Time Rift - Sewers", p)
|
|
|
|
|
|
def create_rift_connections(world: World, region: Region):
|
|
i = 1
|
|
for name in rift_access_regions[region.name]:
|
|
act_region = world.multiworld.get_region(name, world.player)
|
|
entrance_name = "{name} Portal - Entrance {num}"
|
|
connect_regions(act_region, region, entrance_name.format(name=region.name, num=i), world.player)
|
|
i += 1
|
|
|
|
|
|
def create_tasksanity_locations(world: World):
|
|
ship_shape: Region = world.multiworld.get_region("Ship Shape", world.player)
|
|
id_start: int = 300204
|
|
for i in range(world.multiworld.TasksanityCheckCount[world.player].value):
|
|
location = HatInTimeLocation(world.player, format("Tasksanity Check %i" % (i+1)), id_start+i, ship_shape)
|
|
ship_shape.locations.append(location)
|
|
# world.location_name_to_id.setdefault(location.name, location.address)
|
|
|
|
|
|
def randomize_act_entrances(world: World):
|
|
region_list: typing.List[Region] = get_act_regions(world)
|
|
world.multiworld.random.shuffle(region_list)
|
|
|
|
separate_rifts: bool = bool(world.multiworld.ActRandomizer[world.player].value == 1)
|
|
|
|
for region in region_list.copy():
|
|
if (act_chapters[region.name] == "Alpine Skyline" or act_chapters[region.name] == "Nyakuza Metro") \
|
|
and "Time Rift" not in region.name:
|
|
region_list.remove(region)
|
|
region_list.append(region)
|
|
|
|
for region in region_list.copy():
|
|
if "Time Rift" in region.name:
|
|
region_list.remove(region)
|
|
region_list.append(region)
|
|
|
|
# Reverse the list, so we can do what we want to do first
|
|
region_list.reverse()
|
|
|
|
shuffled_list: typing.List[Region] = []
|
|
mapped_list: typing.List[Region] = []
|
|
rift_dict: typing.Dict[str, Region] = {}
|
|
first_chapter: Region = get_first_chapter_region(world)
|
|
has_guaranteed: bool = False
|
|
|
|
i: int = 0
|
|
while i < len(region_list):
|
|
region = region_list[i]
|
|
i += 1
|
|
|
|
# Get the first accessible act, so we can map that to something first
|
|
if not has_guaranteed:
|
|
if act_chapters[region.name] != first_chapter.name:
|
|
continue
|
|
|
|
if region.name not in act_entrances.keys() or "Act 1" not in act_entrances[region.name] \
|
|
and "Free Roam" not in act_entrances[region.name]:
|
|
continue
|
|
|
|
i = 0
|
|
|
|
# Already mapped to something else
|
|
if region in mapped_list:
|
|
continue
|
|
|
|
mapped_list.append(region)
|
|
|
|
# Look for candidates to map this act to
|
|
candidate_list: typing.List[Region] = []
|
|
for candidate in region_list:
|
|
|
|
if world.multiworld.VanillaAlpine[world.player].value > 0 and region.name == "Alpine Free Roam" \
|
|
or world.multiworld.VanillaAlpine[world.player].value == 2 and region.name == "The Illness has Spread":
|
|
candidate_list.append(region)
|
|
break
|
|
|
|
if world.multiworld.VanillaMetro[world.player].value > 0 and region.name == "Nyakuza Free Roam":
|
|
candidate_list.append(region)
|
|
break
|
|
|
|
if region.name == "Rush Hour":
|
|
if world.multiworld.EndGoal[world.player].value == 2 or \
|
|
world.multiworld.VanillaMetro[world.player].value == 2:
|
|
candidate_list.append(region)
|
|
break
|
|
|
|
# We're mapping something to the first act, make sure it is valid
|
|
if not has_guaranteed:
|
|
if candidate.name not in guaranteed_first_acts:
|
|
continue
|
|
|
|
# Not completable without Umbrella
|
|
if world.multiworld.UmbrellaLogic[world.player].value > 0 \
|
|
and (candidate.name == "Heating Up Mafia Town" or candidate.name == "Queen Vanessa's Manor"):
|
|
continue
|
|
|
|
# Subcon sphere 1 is too small without painting unlocks, and no acts are completable either
|
|
if world.multiworld.ShuffleSubconPaintings[world.player].value > 0 \
|
|
and "Subcon Forest" in act_entrances[candidate.name]:
|
|
continue
|
|
|
|
candidate_list.append(candidate)
|
|
has_guaranteed = True
|
|
break
|
|
|
|
# Already mapped onto something else
|
|
if candidate in shuffled_list:
|
|
continue
|
|
|
|
if separate_rifts:
|
|
# Don't map Time Rifts to normal acts
|
|
if "Time Rift" in region.name and "Time Rift" not in candidate.name:
|
|
continue
|
|
|
|
# Don't map normal acts to Time Rifts
|
|
if "Time Rift" not in region.name and "Time Rift" in candidate.name:
|
|
continue
|
|
|
|
# Separate purple rifts
|
|
if region.name in purple_time_rifts and candidate.name not in purple_time_rifts \
|
|
or region.name not in purple_time_rifts and candidate.name in purple_time_rifts:
|
|
continue
|
|
|
|
# Don't map Alpine to its own finale
|
|
if region.name == "The Illness has Spread" and candidate.name == "Alpine Free Roam":
|
|
continue
|
|
|
|
# Ditto for Metro
|
|
if region.name == "Rush Hour" and candidate.name == "Nyakuza Free Roam":
|
|
continue
|
|
|
|
# CTR entrance and Tour aren't a finale, but have a fuck ton of unlock requirements
|
|
if world.multiworld.NoFreeRoamFinale[world.player].value > 0 and "Free Roam" in candidate.name:
|
|
if region.name in chapter_finales or region.name == "Cheating the Race" \
|
|
or world.multiworld.EndGoal[world.player].value == 1 and region.name == "Time Rift - Tour":
|
|
continue
|
|
|
|
if region.name in rift_access_regions and candidate.name in rift_access_regions[region.name]:
|
|
continue
|
|
|
|
candidate_list.append(candidate)
|
|
|
|
candidate: Region = candidate_list[world.multiworld.random.randint(0, len(candidate_list)-1)]
|
|
shuffled_list.append(candidate)
|
|
|
|
# Vanilla
|
|
if candidate.name == region.name:
|
|
if region.name in rift_access_regions.keys():
|
|
rift_dict.setdefault(region.name, candidate)
|
|
|
|
world.update_chapter_act_info(region, candidate)
|
|
continue
|
|
|
|
if region.name in rift_access_regions.keys():
|
|
connect_time_rift(world, region, candidate)
|
|
rift_dict.setdefault(region.name, candidate)
|
|
else:
|
|
if candidate.name in rift_access_regions.keys():
|
|
for e in candidate.entrances.copy():
|
|
e.parent_region.exits.remove(e)
|
|
e.connected_region.entrances.remove(e)
|
|
del e.parent_region
|
|
del e.connected_region
|
|
|
|
entrance = world.multiworld.get_entrance(act_entrances[region.name], world.player)
|
|
reconnect_regions(entrance, world.multiworld.get_region(act_chapters[region.name], world.player), candidate)
|
|
|
|
world.update_chapter_act_info(region, candidate)
|
|
|
|
for name in blacklisted_acts.values():
|
|
if not is_act_blacklisted(world, name):
|
|
continue
|
|
|
|
region: Region = world.multiworld.get_region(name, world.player)
|
|
world.update_chapter_act_info(region, region)
|
|
|
|
set_rift_rules(world, rift_dict)
|
|
|
|
|
|
def connect_time_rift(world: World, time_rift: Region, exit_region: Region):
|
|
count: int = len(rift_access_regions[time_rift.name])
|
|
i: int = 1
|
|
while i <= count:
|
|
name = format("%s Portal - Entrance %i" % (time_rift.name, i))
|
|
entrance: Entrance = world.multiworld.get_entrance(name, world.player)
|
|
reconnect_regions(entrance, entrance.parent_region, exit_region)
|
|
i += 1
|
|
|
|
|
|
def get_act_regions(world: World) -> typing.List[Region]:
|
|
act_list: typing.List[Region] = []
|
|
for region in world.multiworld.get_regions(world.player):
|
|
if region.name in chapter_act_info.keys():
|
|
if not is_act_blacklisted(world, region.name):
|
|
act_list.append(region)
|
|
|
|
return act_list
|
|
|
|
|
|
def is_act_blacklisted(world: World, name: str) -> bool:
|
|
if name == "The Finale":
|
|
return world.multiworld.EndGoal[world.player].value == 1
|
|
|
|
return name in blacklisted_acts.values()
|
|
|
|
|
|
def create_region(world: World, name: str) -> Region:
|
|
reg = Region(name, world.player, world.multiworld)
|
|
|
|
for (key, data) in location_table.items():
|
|
if data.nyakuza_thug != "":
|
|
continue
|
|
|
|
if data.region == name:
|
|
if key in storybook_pages.keys() \
|
|
and world.multiworld.ShuffleStorybookPages[world.player].value == 0:
|
|
continue
|
|
|
|
location = HatInTimeLocation(world.player, key, data.id, reg)
|
|
reg.locations.append(location)
|
|
if location.name in shop_locations:
|
|
world.shop_locs.append(location.name)
|
|
|
|
world.multiworld.regions.append(reg)
|
|
return reg
|
|
|
|
|
|
def create_badge_seller(world: World) -> Region:
|
|
badge_seller = Region("Badge Seller", world.player, world.multiworld)
|
|
world.multiworld.regions.append(badge_seller)
|
|
count: int = 0
|
|
max_items: int = 0
|
|
|
|
if world.multiworld.BadgeSellerMaxItems[world.player].value > 0:
|
|
max_items = world.multiworld.random.randint(world.multiworld.BadgeSellerMinItems[world.player].value,
|
|
world.multiworld.BadgeSellerMaxItems[world.player].value)
|
|
|
|
if max_items <= 0:
|
|
world.badge_seller_count = 0
|
|
return badge_seller
|
|
|
|
for (key, data) in shop_locations.items():
|
|
if "Badge Seller" not in key:
|
|
continue
|
|
|
|
location = HatInTimeLocation(world.player, key, data.id, badge_seller)
|
|
badge_seller.locations.append(location)
|
|
world.shop_locs.append(location.name)
|
|
|
|
count += 1
|
|
if count >= max_items:
|
|
break
|
|
|
|
world.badge_seller_count = max_items
|
|
return badge_seller
|
|
|
|
|
|
def connect_regions(start_region: Region, exit_region: Region, entrancename: str, player: int):
|
|
entrance = Entrance(player, entrancename, start_region)
|
|
start_region.exits.append(entrance)
|
|
entrance.connect(exit_region)
|
|
|
|
|
|
# Takes an entrance, removes its old connections, and reconnects it between the two regions specified.
|
|
def reconnect_regions(entrance: Entrance, start_region: Region, exit_region: Region):
|
|
if entrance in entrance.connected_region.entrances:
|
|
entrance.connected_region.entrances.remove(entrance)
|
|
|
|
if entrance in entrance.parent_region.exits:
|
|
entrance.parent_region.exits.remove(entrance)
|
|
|
|
if entrance in start_region.exits:
|
|
start_region.exits.remove(entrance)
|
|
|
|
if entrance in exit_region.entrances:
|
|
exit_region.entrances.remove(entrance)
|
|
|
|
entrance.parent_region = start_region
|
|
start_region.exits.append(entrance)
|
|
entrance.connect(exit_region)
|
|
|
|
|
|
def create_region_and_connect(world: World,
|
|
name: str, entrancename: str, connected_region: Region, is_exit: bool = True) -> Region:
|
|
|
|
reg: Region = create_region(world, name)
|
|
entrance_region: Region
|
|
exit_region: Region
|
|
|
|
if is_exit:
|
|
entrance_region = connected_region
|
|
exit_region = reg
|
|
else:
|
|
entrance_region = reg
|
|
exit_region = connected_region
|
|
|
|
connect_regions(entrance_region, exit_region, entrancename, world.player)
|
|
return reg
|
|
|
|
|
|
def get_first_chapter_region(world: World) -> Region:
|
|
start_chapter: ChapterIndex = world.multiworld.StartingChapter[world.player]
|
|
return world.multiworld.get_region(chapter_regions.get(start_chapter), world.player)
|
|
|
|
|
|
def get_act_original_chapter(world: World, act_name: str) -> Region:
|
|
return world.multiworld.get_region(act_chapters[act_name], world.player)
|
|
|
|
|
|
def create_thug_shops(world: World):
|
|
min_items: int = world.multiworld.NyakuzaThugMinShopItems[world.player].value
|
|
max_items: int = world.multiworld.NyakuzaThugMaxShopItems[world.player].value
|
|
count: int = -1
|
|
step: int = 0
|
|
old_name: str = ""
|
|
|
|
for key, data in shop_locations.items():
|
|
if data.nyakuza_thug == "":
|
|
continue
|
|
|
|
if old_name != "" and old_name == data.nyakuza_thug:
|
|
continue
|
|
|
|
try:
|
|
if world.nyakuza_thug_items[data.nyakuza_thug] <= 0:
|
|
continue
|
|
except KeyError:
|
|
pass
|
|
|
|
if count == -1:
|
|
count = world.multiworld.random.randint(min_items, max_items)
|
|
world.nyakuza_thug_items.setdefault(data.nyakuza_thug, count)
|
|
if count <= 0:
|
|
continue
|
|
|
|
if count >= 1:
|
|
region = world.multiworld.get_region(data.region, world.player)
|
|
loc = HatInTimeLocation(world.player, key, data.id, region)
|
|
region.locations.append(loc)
|
|
world.shop_locs.append(loc.name)
|
|
|
|
step += 1
|
|
if step >= count:
|
|
old_name = data.nyakuza_thug
|
|
step = 0
|
|
count = -1
|
|
|
|
|
|
def create_events(world: World) -> int:
|
|
count: int = 0
|
|
|
|
for (name, data) in event_locs.items():
|
|
if not is_location_valid(world, name):
|
|
continue
|
|
|
|
event: Location = create_event(name, world.multiworld.get_region(data.region, world.player), world)
|
|
|
|
if data.act_complete_event:
|
|
act_completion: str = format("Act Completion (%s)" % data.region)
|
|
event.access_rule = world.multiworld.get_location(act_completion, world.player).access_rule
|
|
|
|
count += 1
|
|
|
|
return count
|
|
|
|
|
|
def create_event(name: str, region: Region, world: World) -> Location:
|
|
event = HatInTimeLocation(world.player, name, None, region)
|
|
region.locations.append(event)
|
|
event.place_locked_item(HatInTimeItem(name, ItemClassification.progression, None, world.player))
|
|
return event
|