| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  | from typing import Dict, List, Set, Tuple, TYPE_CHECKING | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | from BaseClasses import Region, ItemClassification, Item, Location | 
					
						
							| 
									
										
										
										
											2025-01-15 18:17:07 -05:00
										 |  |  | from .locations import all_locations | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  | from .er_data import Portal, portal_mapping, traversal_requirements, DeadEnd, RegionInfo | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | from .er_rules import set_er_region_rules | 
					
						
							| 
									
										
										
										
											2025-03-08 11:25:47 -05:00
										 |  |  | from .breakables import create_breakable_exclusive_regions, set_breakable_location_rules | 
					
						
							| 
									
										
										
										
											2024-06-01 06:34:41 -05:00
										 |  |  | from Options import PlandoConnection | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  | from .options import EntranceRando | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  | from random import Random | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  | from copy import deepcopy | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     from . import TunicWorld | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TunicERItem(Item): | 
					
						
							| 
									
										
										
										
											2024-01-17 19:56:34 -05:00
										 |  |  |     game: str = "TUNIC" | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class TunicERLocation(Location): | 
					
						
							| 
									
										
										
										
											2024-01-17 19:56:34 -05:00
										 |  |  |     game: str = "TUNIC" | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  | def create_er_regions(world: "TunicWorld") -> Dict[Portal, Portal]: | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     regions: Dict[str, Region] = {} | 
					
						
							| 
									
										
										
										
											2025-03-08 11:25:47 -05:00
										 |  |  |     for region_name, region_data in world.er_regions.items(): | 
					
						
							|  |  |  |         if world.options.entrance_rando and region_name == "Zig Skip Exit": | 
					
						
							|  |  |  |             # need to check if there's a seed group for this first | 
					
						
							|  |  |  |             if world.options.entrance_rando.value not in EntranceRando.options.values(): | 
					
						
							|  |  |  |                 if not world.seed_groups[world.options.entrance_rando.value]["fixed_shop"]: | 
					
						
							| 
									
										
										
										
											2024-12-15 16:40:36 -05:00
										 |  |  |                     continue | 
					
						
							| 
									
										
										
										
											2025-03-08 11:25:47 -05:00
										 |  |  |             elif not world.options.fixed_shop: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |         if not world.options.entrance_rando and region_name in ("Zig Skip Exit", "Purgatory"): | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         region = Region(region_name, world.player, world.multiworld) | 
					
						
							|  |  |  |         regions[region_name] = region | 
					
						
							|  |  |  |         world.multiworld.regions.append(region) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if world.options.breakable_shuffle: | 
					
						
							|  |  |  |         breakable_regions = create_breakable_exclusive_regions(world) | 
					
						
							|  |  |  |         regions.update({region.name: region for region in breakable_regions}) | 
					
						
							| 
									
										
										
										
											2024-12-15 16:40:36 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-08 11:25:47 -05:00
										 |  |  |     if world.options.entrance_rando: | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         portal_pairs = pair_portals(world, regions) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |         # output the entrances to the spoiler log here for convenience | 
					
						
							| 
									
										
										
										
											2024-08-07 12:18:50 -04:00
										 |  |  |         sorted_portal_pairs = sort_portals(portal_pairs) | 
					
						
							|  |  |  |         for portal1, portal2 in sorted_portal_pairs.items(): | 
					
						
							|  |  |  |             world.multiworld.spoiler.set_entrance(portal1, portal2, "both", world.player) | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         portal_pairs = vanilla_portals(world, regions) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-12-15 16:40:36 -05:00
										 |  |  |     create_randomized_entrances(portal_pairs, regions) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-05 16:50:12 -04:00
										 |  |  |     set_er_region_rules(world, regions, portal_pairs) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-15 18:17:07 -05:00
										 |  |  |     for location_name, location_id in world.player_location_table.items(): | 
					
						
							|  |  |  |         region = regions[all_locations[location_name].er_region] | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         location = TunicERLocation(world.player, location_name, location_id, region) | 
					
						
							|  |  |  |         region.locations.append(location) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-08 11:25:47 -05:00
										 |  |  |     if world.options.breakable_shuffle: | 
					
						
							|  |  |  |         set_breakable_location_rules(world) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     place_event_items(world, regions) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     victory_region = regions["Spirit Arena Victory"] | 
					
						
							|  |  |  |     victory_location = TunicERLocation(world.player, "The Heir", None, victory_region) | 
					
						
							|  |  |  |     victory_location.place_locked_item(TunicERItem("Victory", ItemClassification.progression, None, world.player)) | 
					
						
							|  |  |  |     world.multiworld.completion_condition[world.player] = lambda state: state.has("Victory", world.player) | 
					
						
							|  |  |  |     victory_region.locations.append(victory_location) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |     return portal_pairs | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | tunic_events: Dict[str, str] = { | 
					
						
							|  |  |  |     "Eastern Bell": "Forest Belltower Upper", | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |     "Western Bell": "Overworld Belltower at Bell", | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     "Furnace Fuse": "Furnace Fuse", | 
					
						
							|  |  |  |     "South and West Fortress Exterior Fuses": "Fortress Exterior from Overworld", | 
					
						
							|  |  |  |     "Upper and Central Fortress Exterior Fuses": "Fortress Courtyard Upper", | 
					
						
							|  |  |  |     "Beneath the Vault Fuse": "Beneath the Vault Back", | 
					
						
							|  |  |  |     "Eastern Vault West Fuses": "Eastern Vault Fortress", | 
					
						
							|  |  |  |     "Eastern Vault East Fuse": "Eastern Vault Fortress", | 
					
						
							|  |  |  |     "Quarry Connector Fuse": "Quarry Connector", | 
					
						
							| 
									
										
										
										
											2024-07-11 16:44:29 -04:00
										 |  |  |     "Quarry Fuse": "Quarry Entry", | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     "Ziggurat Fuse": "Rooted Ziggurat Lower Back", | 
					
						
							| 
									
										
										
										
											2024-12-15 16:40:36 -05:00
										 |  |  |     "West Garden Fuse": "West Garden South Checkpoint", | 
					
						
							| 
									
										
										
										
											2024-04-21 11:00:16 -04:00
										 |  |  |     "Library Fuse": "Library Lab", | 
					
						
							|  |  |  |     "Place Questagons": "Sealed Temple", | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def place_event_items(world: "TunicWorld", regions: Dict[str, Region]) -> None: | 
					
						
							|  |  |  |     for event_name, region_name in tunic_events.items(): | 
					
						
							|  |  |  |         region = regions[region_name] | 
					
						
							|  |  |  |         location = TunicERLocation(world.player, event_name, None, region) | 
					
						
							| 
									
										
										
										
											2024-04-21 11:00:16 -04:00
										 |  |  |         if event_name == "Place Questagons": | 
					
						
							|  |  |  |             if world.options.hexagon_quest: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             location.place_locked_item( | 
					
						
							|  |  |  |                 TunicERItem("Unseal the Heir", ItemClassification.progression, None, world.player)) | 
					
						
							|  |  |  |         elif event_name.endswith("Bell"): | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |             location.place_locked_item( | 
					
						
							|  |  |  |                 TunicERItem("Ring " + event_name, ItemClassification.progression, None, world.player)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             location.place_locked_item( | 
					
						
							|  |  |  |                 TunicERItem("Activate " + event_name, ItemClassification.progression, None, world.player)) | 
					
						
							|  |  |  |         region.locations.append(location) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  | # all shops are the same shop. however, you cannot get to all shops from the same shop entrance. | 
					
						
							|  |  |  | # so, we need a bunch of shop regions that connect to the actual shop, but the actual shop cannot connect back | 
					
						
							|  |  |  | def create_shop_region(world: "TunicWorld", regions: Dict[str, Region]) -> None: | 
					
						
							|  |  |  |     new_shop_name = f"Shop {world.shop_num}" | 
					
						
							|  |  |  |     world.er_regions[new_shop_name] = RegionInfo("Shop", dead_end=DeadEnd.all_cats) | 
					
						
							|  |  |  |     new_shop_region = Region(new_shop_name, world.player, world.multiworld) | 
					
						
							|  |  |  |     new_shop_region.connect(regions["Shop"]) | 
					
						
							|  |  |  |     regions[new_shop_name] = new_shop_region | 
					
						
							|  |  |  |     world.shop_num += 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def vanilla_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]: | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |     portal_pairs: Dict[Portal, Portal] = {} | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     # we don't want the zig skip exit for vanilla portals, since it shouldn't be considered for logic here | 
					
						
							| 
									
										
										
										
											2024-12-15 16:40:36 -05:00
										 |  |  |     portal_map = [portal for portal in portal_mapping if portal.name not in | 
					
						
							|  |  |  |                   ["Ziggurat Lower Falling Entrance", "Purgatory Bottom Exit", "Purgatory Top Exit"]] | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     while portal_map: | 
					
						
							|  |  |  |         portal1 = portal_map[0] | 
					
						
							|  |  |  |         portal2 = None | 
					
						
							|  |  |  |         # portal2 scene destination tag is portal1's destination scene tag | 
					
						
							|  |  |  |         portal2_sdt = portal1.destination_scene() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if portal2_sdt.startswith("Shop,"): | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |             portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |                              destination="Previous Region", tag="_") | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |             create_shop_region(world, regions) | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         for portal in portal_map: | 
					
						
							|  |  |  |             if portal.scene_destination() == portal2_sdt: | 
					
						
							|  |  |  |                 portal2 = portal | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         portal_pairs[portal1] = portal2 | 
					
						
							|  |  |  |         portal_map.remove(portal1) | 
					
						
							|  |  |  |         if not portal2_sdt.startswith("Shop,"): | 
					
						
							|  |  |  |             portal_map.remove(portal2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return portal_pairs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | # pairing off portals, starting with dead ends | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  | def pair_portals(world: "TunicWorld", regions: Dict[str, Region]) -> Dict[Portal, Portal]: | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     portal_pairs: Dict[Portal, Portal] = {} | 
					
						
							|  |  |  |     dead_ends: List[Portal] = [] | 
					
						
							|  |  |  |     two_plus: List[Portal] = [] | 
					
						
							| 
									
										
										
										
											2024-08-13 20:35:08 -04:00
										 |  |  |     player_name = world.player_name | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     portal_map = portal_mapping.copy() | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |     laurels_zips = world.options.laurels_zips.value | 
					
						
							|  |  |  |     ice_grappling = world.options.ice_grappling.value | 
					
						
							|  |  |  |     ladder_storage = world.options.ladder_storage.value | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |     fixed_shop = world.options.fixed_shop | 
					
						
							|  |  |  |     laurels_location = world.options.laurels_location | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     traversal_reqs = deepcopy(traversal_requirements) | 
					
						
							|  |  |  |     has_laurels = True | 
					
						
							|  |  |  |     waterfall_plando = False | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # if it's not one of the EntranceRando options, it's a custom seed | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  |     if world.options.entrance_rando.value not in EntranceRando.options.values(): | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |         seed_group = world.seed_groups[world.options.entrance_rando.value] | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         laurels_zips = seed_group["laurels_zips"] | 
					
						
							|  |  |  |         ice_grappling = seed_group["ice_grappling"] | 
					
						
							|  |  |  |         ladder_storage = seed_group["ladder_storage"] | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |         fixed_shop = seed_group["fixed_shop"] | 
					
						
							|  |  |  |         laurels_location = "10_fairies" if seed_group["laurels_at_10_fairies"] is True else False | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |     logic_tricks: Tuple[bool, int, int] = (laurels_zips, ice_grappling, ladder_storage) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     # marking that you don't immediately have laurels | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |     if laurels_location == "10_fairies" and not world.using_ut: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         has_laurels = False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |     shop_count = 6 | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |     if fixed_shop: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         shop_count = 0 | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         # if fixed shop is off, remove this portal | 
					
						
							|  |  |  |         for portal in portal_map: | 
					
						
							|  |  |  |             if portal.region == "Zig Skip Exit": | 
					
						
							|  |  |  |                 portal_map.remove(portal) | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  |     # If using Universal Tracker, restore portal_map. Could be cleaner, but it does not matter for UT even a little bit | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |     if world.using_ut: | 
					
						
							|  |  |  |         portal_map = portal_mapping.copy() | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     # create separate lists for dead ends and non-dead ends | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     for portal in portal_map: | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         dead_end_status = world.er_regions[portal.region].dead_end | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         if dead_end_status == DeadEnd.free: | 
					
						
							|  |  |  |             two_plus.append(portal) | 
					
						
							|  |  |  |         elif dead_end_status == DeadEnd.all_cats: | 
					
						
							|  |  |  |             dead_ends.append(portal) | 
					
						
							|  |  |  |         elif dead_end_status == DeadEnd.restricted: | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |             if ice_grappling: | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |                 two_plus.append(portal) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                 dead_ends.append(portal) | 
					
						
							|  |  |  |         # these two get special handling | 
					
						
							|  |  |  |         elif dead_end_status == DeadEnd.special: | 
					
						
							|  |  |  |             if portal.region == "Secret Gathering Place": | 
					
						
							|  |  |  |                 if laurels_location == "10_fairies": | 
					
						
							|  |  |  |                     two_plus.append(portal) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     dead_ends.append(portal) | 
					
						
							|  |  |  |             if portal.region == "Zig Skip Exit": | 
					
						
							|  |  |  |                 if fixed_shop: | 
					
						
							|  |  |  |                     two_plus.append(portal) | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     dead_ends.append(portal) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     connected_regions: Set[str] = set() | 
					
						
							|  |  |  |     # make better start region stuff when/if implementing random start | 
					
						
							|  |  |  |     start_region = "Overworld" | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     connected_regions.add(start_region) | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |     connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  |     if world.options.entrance_rando.value in EntranceRando.options.values(): | 
					
						
							| 
									
										
										
										
											2024-06-01 06:34:41 -05:00
										 |  |  |         plando_connections = world.options.plando_connections.value | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |     else: | 
					
						
							|  |  |  |         plando_connections = world.seed_groups[world.options.entrance_rando.value]["plando"] | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  |     # universal tracker support stuff, don't need to care about region dependency | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |     if world.using_ut: | 
					
						
							|  |  |  |         plando_connections.clear() | 
					
						
							|  |  |  |         # universal tracker stuff, won't do anything in normal gen | 
					
						
							|  |  |  |         for portal1, portal2 in world.passthrough["Entrance Rando"].items(): | 
					
						
							|  |  |  |             portal_name1 = "" | 
					
						
							|  |  |  |             portal_name2 = "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             for portal in portal_mapping: | 
					
						
							|  |  |  |                 if portal.scene_destination() == portal1: | 
					
						
							|  |  |  |                     portal_name1 = portal.name | 
					
						
							|  |  |  |                     # connected_regions.update(add_dependent_regions(portal.region, logic_rules)) | 
					
						
							|  |  |  |                 if portal.scene_destination() == portal2: | 
					
						
							|  |  |  |                     portal_name2 = portal.name | 
					
						
							|  |  |  |                     # connected_regions.update(add_dependent_regions(portal.region, logic_rules)) | 
					
						
							|  |  |  |             # shops have special handling | 
					
						
							|  |  |  |             if not portal_name2 and portal2 == "Shop, Previous Region_": | 
					
						
							|  |  |  |                 portal_name2 = "Shop Portal" | 
					
						
							|  |  |  |             plando_connections.append(PlandoConnection(portal_name1, portal_name2, "both")) | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |     non_dead_end_regions = set() | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |     for region_name, region_info in world.er_regions.items(): | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |         if not region_info.dead_end: | 
					
						
							|  |  |  |             non_dead_end_regions.add(region_name) | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         # if ice grappling to places is in logic, both places stop being dead ends | 
					
						
							|  |  |  |         elif region_info.dead_end == DeadEnd.restricted and ice_grappling: | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |             non_dead_end_regions.add(region_name) | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         # secret gathering place and zig skip get weird, special handling | 
					
						
							|  |  |  |         elif region_info.dead_end == DeadEnd.special: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |             if (region_name == "Secret Gathering Place" and laurels_location == "10_fairies") \ | 
					
						
							|  |  |  |                     or (region_name == "Zig Skip Exit" and fixed_shop): | 
					
						
							|  |  |  |                 non_dead_end_regions.add(region_name) | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  |     if plando_connections: | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |         for connection in plando_connections: | 
					
						
							|  |  |  |             p_entrance = connection.entrance | 
					
						
							|  |  |  |             p_exit = connection.exit | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |             # if you plando secret gathering place, need to know that during portal pairing | 
					
						
							|  |  |  |             if "Secret Gathering Place Exit" in [p_entrance, p_exit]: | 
					
						
							|  |  |  |                 waterfall_plando = True | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |             portal1_dead_end = True | 
					
						
							|  |  |  |             portal2_dead_end = True | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             portal1 = None | 
					
						
							|  |  |  |             portal2 = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             # search two_plus for both at once | 
					
						
							|  |  |  |             for portal in two_plus: | 
					
						
							|  |  |  |                 if p_entrance == portal.name: | 
					
						
							|  |  |  |                     portal1 = portal | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                     portal1_dead_end = False | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |                 if p_exit == portal.name: | 
					
						
							|  |  |  |                     portal2 = portal | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                     portal2_dead_end = False | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             # search dead_ends individually since we can't really remove items from two_plus during the loop | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |             if portal1: | 
					
						
							|  |  |  |                 two_plus.remove(portal1) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 # if not both, they're both dead ends | 
					
						
							|  |  |  |                 if not portal2: | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  |                     if world.options.entrance_rando.value not in EntranceRando.options.values(): | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |                         raise Exception(f"Tunic ER seed group {world.options.entrance_rando.value} paired a dead " | 
					
						
							|  |  |  |                                         "end to a dead end in their plando connections.") | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         raise Exception(f"{player_name} paired a dead end to a dead end in their " | 
					
						
							|  |  |  |                                         "plando connections.") | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |                 for portal in dead_ends: | 
					
						
							|  |  |  |                     if p_entrance == portal.name: | 
					
						
							|  |  |  |                         portal1 = portal | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 if not portal1: | 
					
						
							|  |  |  |                     raise Exception(f"Could not find entrance named {p_entrance} for " | 
					
						
							|  |  |  |                                     f"plando connections in {player_name}'s YAML.") | 
					
						
							|  |  |  |                 dead_ends.remove(portal1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |             if portal2: | 
					
						
							|  |  |  |                 two_plus.remove(portal2) | 
					
						
							|  |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |                 for portal in dead_ends: | 
					
						
							|  |  |  |                     if p_exit == portal.name: | 
					
						
							|  |  |  |                         portal2 = portal | 
					
						
							|  |  |  |                         break | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |                 # if it's not a dead end, it might be a shop | 
					
						
							|  |  |  |                 if p_exit == "Shop Portal": | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |                     portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |                                      destination="Previous Region", tag="_") | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |                     create_shop_region(world, regions) | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |                     shop_count -= 1 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                     # need to maintain an even number of portals total | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |                     if shop_count < 0: | 
					
						
							|  |  |  |                         shop_count += 2 | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |                 # and if it's neither shop nor dead end, it just isn't correct | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     if not portal2: | 
					
						
							|  |  |  |                         raise Exception(f"Could not find entrance named {p_exit} for " | 
					
						
							| 
									
										
										
										
											2025-04-04 18:53:13 -04:00
										 |  |  |                                         f"plando connections in {player_name}'s YAML.\n" | 
					
						
							|  |  |  |                                         f"If you are using Universal Tracker, the most likely reason for this error " | 
					
						
							|  |  |  |                                         f"is that the host generated with a newer version of the APWorld.\n" | 
					
						
							|  |  |  |                                         f"Please check the TUNIC Randomizer Github and place the newest APWorld in your " | 
					
						
							|  |  |  |                                         f"custom_worlds folder, and remove the one in lib/worlds if there is one there.") | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |                     dead_ends.remove(portal2) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |             # update the traversal chart to say you can get from portal1's region to portal2's and vice versa | 
					
						
							|  |  |  |             if not portal1_dead_end and not portal2_dead_end: | 
					
						
							|  |  |  |                 traversal_reqs.setdefault(portal1.region, dict())[portal2.region] = [] | 
					
						
							|  |  |  |                 traversal_reqs.setdefault(portal2.region, dict())[portal1.region] = [] | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  |             if (portal1.region == "Zig Skip Exit" and (portal2_dead_end or portal2.region == "Secret Gathering Place") | 
					
						
							|  |  |  |                     or portal2.region == "Zig Skip Exit" and (portal1_dead_end or portal1.region == "Secret Gathering Place")): | 
					
						
							|  |  |  |                 if world.options.entrance_rando.value not in EntranceRando.options.values(): | 
					
						
							|  |  |  |                     raise Exception(f"Tunic ER seed group {world.options.entrance_rando.value} paired a dead " | 
					
						
							|  |  |  |                                     "end to a dead end in their plando connections.") | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     raise Exception(f"{player_name} paired a dead end to a dead end in their " | 
					
						
							|  |  |  |                                     "plando connections.") | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  |             if (portal1.region == "Secret Gathering Place" and (portal2_dead_end or portal2.region == "Zig Skip Exit") | 
					
						
							|  |  |  |                     or portal2.region == "Secret Gathering Place" and (portal1_dead_end or portal1.region == "Zig Skip Exit")): | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                 # need to make sure you didn't pair this to a dead end or zig skip | 
					
						
							|  |  |  |                 if portal1_dead_end or portal2_dead_end or \ | 
					
						
							|  |  |  |                         portal1.region == "Zig Skip Exit" or portal2.region == "Zig Skip Exit": | 
					
						
							| 
									
										
										
										
											2024-06-03 04:44:37 -04:00
										 |  |  |                     if world.options.entrance_rando.value not in EntranceRando.options.values(): | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                         raise Exception(f"Tunic ER seed group {world.options.entrance_rando.value} paired a dead " | 
					
						
							|  |  |  |                                         "end to a dead end in their plando connections.") | 
					
						
							|  |  |  |                     else: | 
					
						
							|  |  |  |                         raise Exception(f"{player_name} paired a dead end to a dead end in their " | 
					
						
							|  |  |  |                                         "plando connections.") | 
					
						
							|  |  |  |             portal_pairs[portal1] = portal2 | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         # if we have plando connections, our connected regions may change somewhat | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |     if fixed_shop and not world.using_ut: | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         portal1 = None | 
					
						
							|  |  |  |         for portal in two_plus: | 
					
						
							|  |  |  |             if portal.scene_destination() == "Overworld Redux, Windmill_": | 
					
						
							|  |  |  |                 portal1 = portal | 
					
						
							|  |  |  |                 break | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |         if not portal1: | 
					
						
							|  |  |  |             raise Exception(f"Failed to do Fixed Shop option. " | 
					
						
							|  |  |  |                             f"Did {player_name} plando connection the Windmill Shop entrance?") | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", | 
					
						
							|  |  |  |                          destination="Previous Region", tag="_") | 
					
						
							|  |  |  |         create_shop_region(world, regions) | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         portal_pairs[portal1] = portal2 | 
					
						
							|  |  |  |         two_plus.remove(portal1) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |     random_object: Random = world.random | 
					
						
							| 
									
										
										
										
											2024-05-03 01:21:27 -04:00
										 |  |  |     # use the seed given in the options to shuffle the portals | 
					
						
							|  |  |  |     if isinstance(world.options.entrance_rando.value, str): | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |         random_object = Random(world.options.entrance_rando.value) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     # we want to start by making sure every region is accessible | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |     random_object.shuffle(two_plus) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     check_success = 0 | 
					
						
							|  |  |  |     portal1 = None | 
					
						
							|  |  |  |     portal2 = None | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |     previous_conn_num = 0 | 
					
						
							|  |  |  |     fail_count = 0 | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     while len(connected_regions) < len(non_dead_end_regions): | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         # if this is universal tracker, just break immediately and move on | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |         if world.using_ut: | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |             break | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         # if the connected regions length stays unchanged for too long, it's stuck in a loop | 
					
						
							|  |  |  |         # should, hopefully, only ever occur if someone plandos connections poorly | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |         if previous_conn_num == len(connected_regions): | 
					
						
							|  |  |  |             fail_count += 1 | 
					
						
							|  |  |  |             if fail_count >= 500: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                 raise Exception(f"Failed to pair regions. Check plando connections for {player_name} for errors. " | 
					
						
							|  |  |  |                                 "Unconnected regions:", non_dead_end_regions - connected_regions) | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |         else: | 
					
						
							|  |  |  |             fail_count = 0 | 
					
						
							|  |  |  |         previous_conn_num = len(connected_regions) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         # find a portal in a connected region | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         if check_success == 0: | 
					
						
							|  |  |  |             for portal in two_plus: | 
					
						
							|  |  |  |                 if portal.region in connected_regions: | 
					
						
							|  |  |  |                     portal1 = portal | 
					
						
							|  |  |  |                     two_plus.remove(portal) | 
					
						
							|  |  |  |                     check_success = 1 | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |         # then we find a portal in an inaccessible region | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         if check_success == 1: | 
					
						
							|  |  |  |             for portal in two_plus: | 
					
						
							|  |  |  |                 if portal.region not in connected_regions: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                     # if secret gathering place happens to get paired really late, you can end up running out | 
					
						
							|  |  |  |                     if not has_laurels and len(two_plus) < 80: | 
					
						
							|  |  |  |                         # if you plando'd secret gathering place with laurels at 10 fairies, you're the reason for this | 
					
						
							|  |  |  |                         if waterfall_plando: | 
					
						
							|  |  |  |                             cr = connected_regions.copy() | 
					
						
							|  |  |  |                             cr.add(portal.region) | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |                             if "Secret Gathering Place" not in update_reachable_regions(cr, traversal_reqs, has_laurels, logic_tricks): | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                                 continue | 
					
						
							| 
									
										
										
										
											2024-12-15 16:40:36 -05:00
										 |  |  |                         # if not waterfall_plando, then we just want to pair secret gathering place now | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                         elif portal.region != "Secret Gathering Place": | 
					
						
							|  |  |  |                             continue | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |                     portal2 = portal | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                     connected_regions.add(portal.region) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |                     two_plus.remove(portal) | 
					
						
							|  |  |  |                     check_success = 2 | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # once we have both portals, connect them and add the new region(s) to connected_regions | 
					
						
							|  |  |  |         if check_success == 2: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |             if "Secret Gathering Place" in connected_regions: | 
					
						
							|  |  |  |                 has_laurels = True | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |             connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |             portal_pairs[portal1] = portal2 | 
					
						
							|  |  |  |             check_success = 0 | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |             random_object.shuffle(two_plus) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  |     # for universal tracker, we want to skip shop gen | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |     if world.using_ut: | 
					
						
							|  |  |  |         shop_count = 0 | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  |      | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |     for i in range(shop_count): | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         portal1 = two_plus.pop() | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         if portal1 is None: | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |             raise Exception("TUNIC: Too many shops in the pool, or something else went wrong.") | 
					
						
							|  |  |  |         portal2 = Portal(name=f"Shop Portal {world.shop_num}", region=f"Shop {world.shop_num}", | 
					
						
							|  |  |  |                          destination="Previous Region", tag="_") | 
					
						
							|  |  |  |         create_shop_region(world, regions) | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |          | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         portal_pairs[portal1] = portal2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # connect dead ends to random non-dead ends | 
					
						
							|  |  |  |     # none of the key events are in dead ends, so we don't need to do gate_before_switch | 
					
						
							|  |  |  |     while len(dead_ends) > 0: | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |         if world.using_ut: | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |             break | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         portal1 = two_plus.pop() | 
					
						
							|  |  |  |         portal2 = dead_ends.pop() | 
					
						
							|  |  |  |         portal_pairs[portal1] = portal2 | 
					
						
							|  |  |  |     # then randomly connect the remaining portals to each other | 
					
						
							|  |  |  |     # every region is accessible, so gate_before_switch is not necessary | 
					
						
							|  |  |  |     while len(two_plus) > 1: | 
					
						
							| 
									
										
										
										
											2025-01-10 15:49:13 -05:00
										 |  |  |         if world.using_ut: | 
					
						
							| 
									
										
										
										
											2024-03-15 12:52:05 -04:00
										 |  |  |             break | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  |         portal1 = two_plus.pop() | 
					
						
							|  |  |  |         portal2 = two_plus.pop() | 
					
						
							|  |  |  |         portal_pairs[portal1] = portal2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if len(two_plus) == 1: | 
					
						
							| 
									
										
										
										
											2024-02-15 23:03:51 -05:00
										 |  |  |         raise Exception("two plus had an odd number of portals, investigate this. last portal is " + two_plus[0].name) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return portal_pairs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # loop through our list of paired portals and make two-way connections | 
					
						
							|  |  |  | def create_randomized_entrances(portal_pairs: Dict[Portal, Portal], regions: Dict[str, Region]) -> None: | 
					
						
							|  |  |  |     for portal1, portal2 in portal_pairs.items(): | 
					
						
							|  |  |  |         region1 = regions[portal1.region] | 
					
						
							|  |  |  |         region2 = regions[portal2.region] | 
					
						
							| 
									
										
										
										
											2024-03-21 11:50:07 -04:00
										 |  |  |         region1.connect(connecting_region=region2, name=portal1.name) | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |         region2.connect(connecting_region=region1, name=portal2.name) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  | def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[str, Dict[str, List[List[str]]]], | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |                              has_laurels: bool, logic: Tuple[bool, int, int]) -> Set[str]: | 
					
						
							|  |  |  |     zips, ice_grapples, ls = logic | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     # starting count, so we can run it again if this changes | 
					
						
							|  |  |  |     region_count = len(connected_regions) | 
					
						
							|  |  |  |     for origin, destinations in traversal_reqs.items(): | 
					
						
							|  |  |  |         if origin not in connected_regions: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  |         # check if we can traverse to any of the destinations | 
					
						
							|  |  |  |         for destination, req_lists in destinations.items(): | 
					
						
							|  |  |  |             if destination in connected_regions: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  |             met_traversal_reqs = False | 
					
						
							|  |  |  |             if len(req_lists) == 0: | 
					
						
							|  |  |  |                 met_traversal_reqs = True | 
					
						
							|  |  |  |             # loop through each set of possible requirements, with a fancy for else loop | 
					
						
							|  |  |  |             for reqs in req_lists: | 
					
						
							|  |  |  |                 for req in reqs: | 
					
						
							|  |  |  |                     if req == "Hyperdash": | 
					
						
							|  |  |  |                         if not has_laurels: | 
					
						
							|  |  |  |                             break | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |                     elif req == "Zip": | 
					
						
							|  |  |  |                         if not zips: | 
					
						
							|  |  |  |                             break | 
					
						
							|  |  |  |                     # if req is higher than logic option, then it breaks since it's not a valid connection | 
					
						
							|  |  |  |                     elif req.startswith("IG"): | 
					
						
							|  |  |  |                         if int(req[-1]) > ice_grapples: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                             break | 
					
						
							| 
									
										
										
										
											2024-09-08 08:42:59 -04:00
										 |  |  |                     elif req.startswith("LS"): | 
					
						
							|  |  |  |                         if int(req[-1]) > ls: | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |                             break | 
					
						
							|  |  |  |                     elif req not in connected_regions: | 
					
						
							|  |  |  |                         break | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     met_traversal_reqs = True | 
					
						
							|  |  |  |                     break | 
					
						
							|  |  |  |             if met_traversal_reqs: | 
					
						
							|  |  |  |                 connected_regions.add(destination) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     # if the length of connected_regions changed, we got new regions, so we want to check those new origins | 
					
						
							|  |  |  |     if region_count != len(connected_regions): | 
					
						
							|  |  |  |         connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic) | 
					
						
							| 
									
										
										
										
											2024-01-12 14:32:15 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-19 19:01:24 -04:00
										 |  |  |     return connected_regions | 
					
						
							| 
									
										
										
										
											2024-08-07 12:18:50 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # sort the portal dict by the name of the first portal, referring to the portal order in the master portal list | 
					
						
							|  |  |  | def sort_portals(portal_pairs: Dict[Portal, Portal]) -> Dict[str, str]: | 
					
						
							|  |  |  |     sorted_pairs: Dict[str, str] = {} | 
					
						
							|  |  |  |     reference_list: List[str] = [portal.name for portal in portal_mapping] | 
					
						
							|  |  |  |     reference_list.append("Shop Portal") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     # note: this is not necessary yet since the shop portals aren't numbered yet -- they will be when decoupled happens | 
					
						
							|  |  |  |     # due to plando, there can be a variable number of shops | 
					
						
							|  |  |  |     # I could either do it like this, or just go up to like 200, this seemed better | 
					
						
							|  |  |  |     # shop_count = 0 | 
					
						
							|  |  |  |     # for portal1, portal2 in portal_pairs.items(): | 
					
						
							|  |  |  |     #     if portal1.name.startswith("Shop"): | 
					
						
							|  |  |  |     #         shop_count += 1 | 
					
						
							|  |  |  |     #     if portal2.name.startswith("Shop"): | 
					
						
							|  |  |  |     #         shop_count += 1 | 
					
						
							|  |  |  |     # reference_list.extend([f"Shop Portal {i + 1}" for i in range(shop_count)]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for name in reference_list: | 
					
						
							|  |  |  |         for portal1, portal2 in portal_pairs.items(): | 
					
						
							|  |  |  |             if name == portal1.name: | 
					
						
							|  |  |  |                 sorted_pairs[portal1.name] = portal2.name | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |     return sorted_pairs |