| 
									
										
										
										
											2023-07-24 19:41:20 -05:00
										 |  |  | from functools import cached_property | 
					
						
							| 
									
										
										
										
											2024-11-27 03:28:00 +01:00
										 |  |  | from typing import TYPE_CHECKING | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 10:16:09 -05:00
										 |  |  | from BaseClasses import CollectionState, Entrance, EntranceType, Item, ItemClassification, Location, Region | 
					
						
							|  |  |  | from entrance_rando import ERPlacementState | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  | from .regions import LOCATIONS, MEGA_SHARDS | 
					
						
							|  |  |  | from .shop import FIGURINES, SHOP_ITEMS | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | if TYPE_CHECKING: | 
					
						
							|  |  |  |     from . import MessengerWorld | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  | class MessengerEntrance(Entrance): | 
					
						
							| 
									
										
										
										
											2024-11-27 03:28:00 +01:00
										 |  |  |     world: "MessengerWorld | None" = None | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-10 10:16:09 -05:00
										 |  |  |     def can_connect_to(self, other: Entrance, dead_end: bool, state: "ERPlacementState") -> bool: | 
					
						
							|  |  |  |         can_connect = super().can_connect_to(other, dead_end, state) | 
					
						
							|  |  |  |         world: MessengerWorld = getattr(self, "world", None) | 
					
						
							|  |  |  |         if not world or world.reachable_locs or not can_connect: | 
					
						
							|  |  |  |             return can_connect | 
					
						
							|  |  |  |         empty_state = CollectionState(world.multiworld, True) | 
					
						
							|  |  |  |         self.connected_region = other.connected_region | 
					
						
							|  |  |  |         empty_state.update_reachable_regions(world.player) | 
					
						
							|  |  |  |         world.reachable_locs = any(loc.can_reach(empty_state) and not loc.is_event for loc in world.get_locations()) | 
					
						
							|  |  |  |         self.connected_region = None | 
					
						
							|  |  |  |         return world.reachable_locs and (not state.coupled or self.name != other.name) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  | class MessengerRegion(Region): | 
					
						
							| 
									
										
										
										
											2025-03-10 10:16:09 -05:00
										 |  |  |     parent: str | None | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |     entrance_type = MessengerEntrance | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-27 03:28:00 +01:00
										 |  |  |     def __init__(self, name: str, world: "MessengerWorld", parent: str | None = None) -> None: | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  |         super().__init__(name, world.player, world.multiworld) | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |         self.parent = parent | 
					
						
							|  |  |  |         locations = [] | 
					
						
							|  |  |  |         if name in LOCATIONS: | 
					
						
							|  |  |  |             locations = [loc for loc in LOCATIONS[name]] | 
					
						
							|  |  |  |         # portal event locations since portals can be opened from their exit regions | 
					
						
							|  |  |  |         if name.endswith("Portal"): | 
					
						
							|  |  |  |             locations.append(name.replace(" -", "")) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if name == "The Shop": | 
					
						
							| 
									
										
										
										
											2023-07-22 00:45:46 -05:00
										 |  |  |             shop_locations = {f"The Shop - {shop_loc}": world.location_name_to_id[f"The Shop - {shop_loc}"] | 
					
						
							|  |  |  |                               for shop_loc in SHOP_ITEMS} | 
					
						
							|  |  |  |             self.add_locations(shop_locations, MessengerShopLocation) | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |         elif name == "The Craftsman's Corner": | 
					
						
							| 
									
										
										
										
											2025-03-10 10:16:09 -05:00
										 |  |  |             self.add_locations( | 
					
						
							|  |  |  |                 {figurine: world.location_name_to_id[figurine] for figurine in FIGURINES}, | 
					
						
							|  |  |  |                 MessengerLocation) | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |         elif name == "Tower HQ": | 
					
						
							| 
									
										
										
										
											2023-07-22 00:45:46 -05:00
										 |  |  |             locations.append("Money Wrench") | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if world.options.shuffle_shards and name in MEGA_SHARDS: | 
					
						
							|  |  |  |             locations += MEGA_SHARDS[name] | 
					
						
							| 
									
										
										
										
											2023-11-10 22:49:55 -06:00
										 |  |  |         loc_dict = {loc: world.location_name_to_id.get(loc, None) for loc in locations} | 
					
						
							| 
									
										
										
										
											2023-07-22 00:45:46 -05:00
										 |  |  |         self.add_locations(loc_dict, MessengerLocation) | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |         self.multiworld.regions.append(self) | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MessengerLocation(Location): | 
					
						
							|  |  |  |     game = "The Messenger" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-27 03:28:00 +01:00
										 |  |  |     def __init__(self, player: int, name: str, loc_id: int | None, parent: MessengerRegion) -> None: | 
					
						
							| 
									
										
										
										
											2023-07-22 00:45:46 -05:00
										 |  |  |         super().__init__(player, name, loc_id, parent) | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  |         if loc_id is None: | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |             if name == "Rescue Phantom": | 
					
						
							|  |  |  |                 name = "Do the Thing!" | 
					
						
							|  |  |  |             self.place_locked_item(MessengerItem(name, ItemClassification.progression, None, parent.player)) | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-06-27 18:39:52 -05:00
										 |  |  | class MessengerShopLocation(MessengerLocation): | 
					
						
							| 
									
										
										
										
											2025-03-10 10:16:09 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-24 19:41:20 -05:00
										 |  |  |     @cached_property | 
					
						
							| 
									
										
										
										
											2023-06-27 18:39:52 -05:00
										 |  |  |     def cost(self) -> int: | 
					
						
							| 
									
										
										
										
											2024-11-27 03:28:00 +01:00
										 |  |  |         name = self.name.removeprefix("The Shop - ") | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |         world = self.parent_region.multiworld.worlds[self.player] | 
					
						
							| 
									
										
										
										
											2023-07-24 19:41:20 -05:00
										 |  |  |         shop_data = SHOP_ITEMS[name] | 
					
						
							|  |  |  |         if shop_data.prerequisite: | 
					
						
							|  |  |  |             prereq_cost = 0 | 
					
						
							|  |  |  |             if isinstance(shop_data.prerequisite, set): | 
					
						
							|  |  |  |                 for prereq in shop_data.prerequisite: | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |                     loc = world.multiworld.get_location(prereq, self.player) | 
					
						
							|  |  |  |                     assert isinstance(loc, MessengerShopLocation) | 
					
						
							|  |  |  |                     prereq_cost += loc.cost | 
					
						
							| 
									
										
										
										
											2023-07-24 19:41:20 -05:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |                 loc = world.multiworld.get_location(shop_data.prerequisite, self.player) | 
					
						
							|  |  |  |                 assert isinstance(loc, MessengerShopLocation) | 
					
						
							|  |  |  |                 prereq_cost += loc.cost | 
					
						
							| 
									
										
										
										
											2023-07-24 19:41:20 -05:00
										 |  |  |             return world.shop_prices[name] + prereq_cost | 
					
						
							|  |  |  |         return world.shop_prices[name] | 
					
						
							| 
									
										
										
										
											2023-06-27 18:39:52 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-23 17:38:57 -06:00
										 |  |  |     def access_rule(self, state: CollectionState) -> bool: | 
					
						
							| 
									
										
										
										
											2024-03-11 17:23:41 -05:00
										 |  |  |         world = state.multiworld.worlds[self.player] | 
					
						
							| 
									
										
										
										
											2023-10-10 15:30:20 -05:00
										 |  |  |         can_afford = state.has("Shards", self.player, min(self.cost, world.total_shards)) | 
					
						
							| 
									
										
										
										
											2023-06-27 18:39:52 -05:00
										 |  |  |         return can_afford | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-12 09:05:50 -05:00
										 |  |  | class MessengerItem(Item): | 
					
						
							|  |  |  |     game = "The Messenger" |