diff --git a/worlds/zork_grand_inquisitor/__init__.py b/worlds/zork_grand_inquisitor/__init__.py index 791f41dd00a2..3d9f7e6297a5 100644 --- a/worlds/zork_grand_inquisitor/__init__.py +++ b/worlds/zork_grand_inquisitor/__init__.py @@ -12,6 +12,9 @@ def launch_client() -> None: LauncherComponents.Component( "Zork Grand Inquisitor Client", func=launch_client, - component_type=LauncherComponents.Type.CLIENT + component_type=LauncherComponents.Type.CLIENT, + icon="zork_grand_inquisitor" ) ) + +LauncherComponents.icon_paths["zork_grand_inquisitor"] = f"ap:{__name__}/zork_grand_inquisitor.png" diff --git a/worlds/zork_grand_inquisitor/client.py b/worlds/zork_grand_inquisitor/client.py index 8b8d7d3ebf58..ceab276ee152 100644 --- a/worlds/zork_grand_inquisitor/client.py +++ b/worlds/zork_grand_inquisitor/client.py @@ -4,21 +4,54 @@ import NetUtils import Utils -from typing import Any, Dict, List, Optional, Set, Tuple +from typing import Any, Dict, List, Optional, Set + +from .data_funcs import ( + item_names_to_id, + item_names_to_item, + location_names_to_id, + id_to_client_seed_information, + id_to_craftable_spell_behaviors, + id_to_deathsanity, + id_to_entrance_randomizer, + id_to_hotspots, + id_to_items, + id_to_landmarksanity, + id_to_locations, + id_to_goals, + id_to_starting_locations, +) -from .data_funcs import item_names_to_id, location_names_to_id, id_to_items, id_to_locations, id_to_goals from .enums import ZorkGrandInquisitorItems, ZorkGrandInquisitorLocations from .game_controller import GameController class ZorkGrandInquisitorCommandProcessor(CommonClient.ClientCommandProcessor): + ctx: "ZorkGrandInquisitorContext" + def _cmd_zork(self) -> None: """Attach to an open Zork Grand Inquisitor process.""" + if not self.ctx.server or not self.ctx.slot: + self.output("You must be connected to an Archipelago server before using /zork.") + return + result: bool = self.ctx.game_controller.open_process_handle() if result: self.ctx.process_attached_at_least_once = True self.output("Successfully attached to Zork Grand Inquisitor process.") + + self.ctx.game_controller.output_seed_information() + self.ctx.game_controller.output_starter_kit() + + Utils.async_start( + self.ctx.send_msgs([ + { + "cmd": "StatusUpdate", + "status": CommonClient.ClientStatus.CLIENT_PLAYING + } + ]) + ) else: self.output("Failed to attach to Zork Grand Inquisitor process.") @@ -38,6 +71,13 @@ def _cmd_hotspots(self) -> None: """List received Hotspots.""" self.ctx.game_controller.list_received_hotspots() + def _cmd_deathlink(self) -> None: + """Toggle deathlink status.""" + if not self.ctx.game_controller.option_death_link: + return + + self.ctx.death_link_status = not self.ctx.death_link_status + class ZorkGrandInquisitorContext(CommonClient.CommonContext): tags: Set[str] = {"AP"} @@ -53,6 +93,10 @@ class ZorkGrandInquisitorContext(CommonClient.CommonContext): id_to_locations: Dict[int, ZorkGrandInquisitorLocations] = id_to_locations() game_controller: GameController + data_storage_key: Optional[str] + death_link_status: bool = False + entrance_randomizer_data_by_name: Optional[Dict[str, str]] + locations_checked: Set[ZorkGrandInquisitorLocations] controller_task: Optional[asyncio.Task] @@ -64,20 +108,18 @@ def __init__(self, server_address: Optional[str], password: Optional[str]) -> No self.game_controller = GameController(logger=CommonClient.logger) + self.data_storage_key = None + self.entrance_randomizer_data_by_name = None + self.locations_checked = set() + self.controller_task = None self.process_attached_at_least_once = False self.can_display_process_message = True - def run_gui(self) -> None: - from kvui import GameManager - - class TextManager(GameManager): - logging_pairs: List[Tuple[str, str]] = [("Client", "Archipelago")] - base_title: str = "Archipelago Zork Grand Inquisitor Client" - - self.ui = TextManager(self) - self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI") + def make_gui(self): + from .client_gui.client_gui import ZorkGrandInquisitorManager + return ZorkGrandInquisitorManager async def server_auth(self, password_requested: bool = False): if password_requested and not self.password: @@ -86,31 +128,189 @@ async def server_auth(self, password_requested: bool = False): await self.get_username() await self.send_connect() + async def disconnect(self, allow_autoreconnect: bool = False): + try: + self.game_controller.close_process_handle() + except Exception: + pass + + self.game_controller.reset() + + self.data_storage_key = None + + self.items_received = [] + self.locations_info = {} + + self.ui.update_tabs() + + await super().disconnect(allow_autoreconnect) + def on_package(self, cmd: str, _args: Any) -> None: if cmd == "Connected": self.game = self.slot_info[self.slot].game # Options self.game_controller.option_goal = id_to_goals()[_args["slot_data"]["goal"]] - self.game_controller.option_deathsanity = _args["slot_data"]["deathsanity"] == 1 + + self.game_controller.option_artifacts_of_magic_required = ( + _args["slot_data"]["artifacts_of_magic_required"] + ) + + self.game_controller.option_artifacts_of_magic_total = ( + _args["slot_data"]["artifacts_of_magic_total"] + ) + + self.game_controller.option_landmarks_required = ( + _args["slot_data"]["landmarks_required"] + ) + + self.game_controller.option_deaths_required = ( + _args["slot_data"]["deaths_required"] + ) + + self.game_controller.option_starting_location = ( + id_to_starting_locations()[_args["slot_data"]["starting_location"]] + ) + + self.game_controller.option_hotspots = ( + id_to_hotspots()[_args["slot_data"]["hotspots"]] + ) + + self.game_controller.option_craftable_spells = ( + id_to_craftable_spell_behaviors()[_args["slot_data"]["craftable_spells"]] + ) + + self.game_controller.option_wild_voxam = _args["slot_data"]["wild_voxam"] == 1 + self.game_controller.option_wild_voxam_chance = _args["slot_data"]["wild_voxam_chance"] + + self.game_controller.option_deathsanity = ( + id_to_deathsanity()[_args["slot_data"]["deathsanity"]] + ) + + self.game_controller.option_landmarksanity = ( + id_to_landmarksanity()[_args["slot_data"]["landmarksanity"]] + ) + + self.game_controller.option_entrance_randomizer = ( + id_to_entrance_randomizer()[_args["slot_data"]["entrance_randomizer"]] + ) + + self.game_controller.option_entrance_randomizer_include_subway_destinations = ( + _args["slot_data"]["entrance_randomizer_include_subway_destinations"] == 1 + ) + + self.game_controller.option_trap_percentage = _args["slot_data"]["trap_percentage"] self.game_controller.option_grant_missable_location_checks = ( _args["slot_data"]["grant_missable_location_checks"] == 1 ) + self.game_controller.option_client_seed_information = ( + id_to_client_seed_information()[_args["slot_data"]["client_seed_information"]] + ) + + is_death_link = _args["slot_data"]["death_link"] == 1 + + self.game_controller.option_death_link = is_death_link # Represents the option; will never change + self.death_link_status = is_death_link # Represents the toggleable status + + # Starter Kit + self.game_controller.starter_kit = _args["slot_data"]["starter_kit"] + + # Initial Totemizer Destination + self.game_controller.initial_totemizer_destination = item_names_to_item()[ + _args["slot_data"]["initial_totemizer_destination"] + ] + + # Entrance Randomizer Data + self.game_controller.entrance_randomizer_data = _args["slot_data"]["entrance_randomizer_data"] + self.entrance_randomizer_data_by_name = _args["slot_data"]["entrance_randomizer_data_by_name"] + + # Save IDs + self.game_controller.save_ids = tuple(_args["slot_data"]["save_ids"]) + + # Data Storage + self.data_storage_key = f"zork_grand_inquisitor_{self.team}_{self.slot}" + + Utils.async_start( + self.send_msgs([ + { + "cmd": "Set", + "key": self.data_storage_key, + "want_reply": True, + "default": { + "discovered_regions": list(), + "discovered_entrances": list(), + }, + "operations": [ + {"operation": "default", "value": None}, + ], + }, + { + "cmd": "SetNotify", + "keys": [self.data_storage_key], + } + ]) + ) + + # Locations Checked + if "checked_locations" in _args: + locations_checked_update: Set[ZorkGrandInquisitorLocations] = set( + [self.id_to_locations[location_id] for location_id in _args["checked_locations"]] + ) + + self.locations_checked |= locations_checked_update + + # UI Tabs + self.ui.update_tabs() + elif cmd == "ReceivedItems": + self.ui.update_tabs() + elif cmd == "RoomUpdate": + if "checked_locations" in _args: + locations_checked_update: Set[ZorkGrandInquisitorLocations] = set( + [self.id_to_locations[location_id] for location_id in _args["checked_locations"]] + ) + + self.locations_checked |= locations_checked_update + + self.ui.update_tabs() + elif cmd == "SetReply": + if _args["key"] == self.data_storage_key: + self.ui.update_tabs() + + def on_deathlink(self, data: Dict[str, Any]) -> None: + self.last_death_link = max(data["time"], self.last_death_link) + self.game_controller.pending_death_link = (True, data.get("source"), data.get("cause")) + async def controller(self): while not self.exit_event.is_set(): await asyncio.sleep(0.1) # Enqueue Received Item Delta + goal_item_count: int = 0 + + received_traps: List[ZorkGrandInquisitorItems] = list() + network_item: NetUtils.NetworkItem for network_item in self.items_received: item: ZorkGrandInquisitorItems = self.id_to_items[network_item.item] - if item not in self.game_controller.received_items: + if item in self.game_controller.all_goal_items: + goal_item_count += 1 + continue + elif item in self.game_controller.all_trap_items: + received_traps.append(item) + continue + elif item not in self.game_controller.received_items: if item not in self.game_controller.received_items_queue: self.game_controller.received_items_queue.append(item) + if goal_item_count > self.game_controller.goal_item_count: + self.game_controller.goal_item_count = goal_item_count + self.game_controller.output_goal_item_update() + + self.game_controller.received_traps = received_traps + # Game Controller Update if self.game_controller.is_process_running(): self.game_controller.update() @@ -120,42 +320,114 @@ async def controller(self): if self.process_attached_at_least_once: process_message = ( - "Lost connection to Zork Grand Inquisitor process. Please restart the game and use the /zork " - "command to reattach." + "Connection to the Zork Grand Inquisitor process was lost. Ensure you are connected " + "to an Archipelago server and the game is running, then use the /zork command to reconnect." ) else: process_message = ( - "Please use the /zork command to attach to a running Zork Grand Inquisitor process." + "To start playing, connect to an Archipelago server and use the /zork command to " + "link to an active Zork Grand Inquisitor process." ) if self.can_display_process_message: CommonClient.logger.info(process_message) self.can_display_process_message = False - # Send Checked Locations - checked_location_ids: List[int] = list() + # Network Operations + if self.server and self.slot: + # Send Checked Locations + checked_location_ids: List[int] = list() - while len(self.game_controller.completed_locations_queue) > 0: - location: ZorkGrandInquisitorLocations = self.game_controller.completed_locations_queue.popleft() - location_id: int = self.location_name_to_id[location.value] + while len(self.game_controller.completed_locations_queue) > 0: + location: ZorkGrandInquisitorLocations = self.game_controller.completed_locations_queue.popleft() + location_id: int = self.location_name_to_id[location.value] - checked_location_ids.append(location_id) + checked_location_ids.append(location_id) - await self.send_msgs([ - { - "cmd": "LocationChecks", - "locations": checked_location_ids - } - ]) + await self.check_locations(checked_location_ids) - # Check for Goal Completion - if self.game_controller.goal_completed: - await self.send_msgs([ - { - "cmd": "StatusUpdate", - "status": CommonClient.ClientStatus.CLIENT_GOAL - } - ]) + # Check for Goal Completion + if self.game_controller.goal_completed: + await self.send_msgs([ + { + "cmd": "StatusUpdate", + "status": CommonClient.ClientStatus.CLIENT_GOAL + } + ]) + + # Update Data Storage + if self.data_storage_key is not None and self.data_storage_key in self.stored_data: + update_dict: Dict[str, Any] = dict() + + current_discovered_regions: Set[str] = set( + self.stored_data[self.data_storage_key]["discovered_regions"] + ) + + update_discovered_regions: Set[str] = ( + current_discovered_regions | self.game_controller.discovered_regions + ) + + if len(update_discovered_regions) > len(current_discovered_regions): + update_dict["discovered_regions"] = sorted(update_discovered_regions) + + current_discovered_entrances: Set[str] = set( + self.stored_data[self.data_storage_key]["discovered_entrances"] + ) + + update_discovered_entrances: Set[str] = ( + current_discovered_entrances | self.game_controller.discovered_entrances + ) + + if len(update_discovered_entrances) > len(current_discovered_entrances): + update_dict["discovered_entrances"] = sorted(update_discovered_entrances) + + if len(update_dict): + await self.send_msgs([ + { + "cmd": "Set", + "key": self.data_storage_key, + "want_reply": True, + "operations": [ + {"operation": "update", "value": update_dict}, + ], + } + ]) + + # Handle Energy Link + while len(self.game_controller.energy_link_queue) > 0: + energy_to_add: int = self.game_controller.energy_link_queue.popleft() + + await self.send_msgs([ + { + "cmd": "Set", + "key": f"EnergyLink{self.team}", + "operations": + [ + {"operation": "add", "value": energy_to_add}, + ], + }, + ]) + + CommonClient.logger.info(f"Added {energy_to_add} J to the Energy Link pool") + + # Handle Death Link + await self.update_death_link(self.death_link_status) + + if self.game_controller.outgoing_death_link[0]: + if self.death_link_status: + death_cause: Optional[str] = self.game_controller.outgoing_death_link[1] + + if death_cause: + death_cause = death_cause.replace( + "PLAYER", + self.player_names[self.slot] + ) + else: + death_cause = "" + + await self.send_death(death_cause) + + self.game_controller.outgoing_death_link = (False, None) def main() -> None: diff --git a/worlds/zork_grand_inquisitor/client_gui/__init__.py b/worlds/zork_grand_inquisitor/client_gui/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/worlds/zork_grand_inquisitor/client_gui/client_gui.py b/worlds/zork_grand_inquisitor/client_gui/client_gui.py new file mode 100644 index 000000000000..a2145f1a002e --- /dev/null +++ b/worlds/zork_grand_inquisitor/client_gui/client_gui.py @@ -0,0 +1,38 @@ +from typing import List, Tuple + +from kvui import GameManager + +from kivy.uix.layout import Layout +from kivy.uix.widget import Widget + +from ..client import ZorkGrandInquisitorContext + +from .client_gui_layouts import TrackerTabLayout, EntrancesTabLayout + + +class ZorkGrandInquisitorManager(GameManager): + ctx: ZorkGrandInquisitorContext + + logging_pairs: List[Tuple[str, str]] = [("Client", "Archipelago")] + base_title: str = "Archipelago Zork Grand Inquisitor Client" + + tracker_tab_layout: TrackerTabLayout + entrances_tab_layout: EntrancesTabLayout + + tracker_tab: Widget + entrances_tab: Widget + + def build(self) -> Layout: + container: Layout = super().build() + + self.tracker_tab_layout = TrackerTabLayout(self.ctx) + self.tracker_tab = self.add_client_tab("Tracker", self.tracker_tab_layout) + + self.entrances_tab_layout = EntrancesTabLayout(self.ctx) + self.entrances_tab = self.add_client_tab("Entrances", self.entrances_tab_layout) + + return container + + def update_tabs(self) -> None: + self.tracker_tab_layout.update() + self.entrances_tab_layout.update() diff --git a/worlds/zork_grand_inquisitor/client_gui/client_gui_layouts.py b/worlds/zork_grand_inquisitor/client_gui/client_gui_layouts.py new file mode 100644 index 000000000000..12706f2d5aa6 --- /dev/null +++ b/worlds/zork_grand_inquisitor/client_gui/client_gui_layouts.py @@ -0,0 +1,1118 @@ +from typing import Dict, List, Set, Tuple + +import kivy.utils + +from kivy.uix.boxlayout import BoxLayout +from kivy.uix.label import Label +from kivy.uix.widget import Widget + +from kivymd.app import MDApp +from kivymd.uix.scrollview import MDScrollView + +from ..client import ZorkGrandInquisitorContext +from ..data.entrance_randomizer_data import randomizable_entrances, randomizable_entrances_subway +from ..data.location_data import ZorkGrandInquisitorLocationData, location_data +from ..data.mapping_data import entrance_names, entrance_names_reverse + +from ..enums import ( + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorEntranceRandomizer, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorItems, + ZorkGrandInquisitorLandmarksanity, + ZorkGrandInquisitorLocations, + ZorkGrandInquisitorRegions, +) + + +class NotConnectedLayout(BoxLayout): + ctx: ZorkGrandInquisitorContext + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__(orientation="horizontal", size_hint_y=0.12) + + self.ctx = ctx + + self.add_widget( + Label(text="Please connect to an Archipelago server first to view this tab.", font_size="24dp") + ) + + def show(self): + self.opacity = 1.0 + self.size_hint_y = 0.12 + self.disabled = False + + def hide(self): + self.opacity = 0.0 + self.size_hint_y = None + self.height = "0dp" + self.disabled = True + + +class TrackerLocationLabel(Label): + ctx: ZorkGrandInquisitorContext + + region: ZorkGrandInquisitorRegions + location: ZorkGrandInquisitorLocations + + # requirements: bool + + color_: str + + in_logic: bool + checked: bool + + def __init__( + self, + ctx: ZorkGrandInquisitorContext, + region: ZorkGrandInquisitorRegions, + location: ZorkGrandInquisitorLocations, + # requirements: bool, + ) -> None: + theme_color: List[float] = MDApp.get_running_app().theme_cls.secondaryColor + self.color_ = "".join(["{:02X}".format(round(c * 255)) for c in theme_color[:3]]) + + super().__init__( + text=f"[b][color={self.color_}]{region.value}:[/color][/b] {location.value}", + markup=True, + font_size="16dp", + size_hint_y=None, + height="22dp", + halign="left", + valign="middle", + ) + + self.ctx = ctx + + self.region = region + self.location = location + + # self.requirements = requirements + + self.in_logic = False + self.checked = False + + self.bind(size=lambda label, size: setattr(label, "text_size", size)) + + def update(self) -> None: + self.checked = self.location in self.ctx.locations_checked + + if self.checked: + self.opacity = 0.1 + # elif self.in_logic: + # self.opacity = 1.0 + else: + # self.opacity = 0.25 + self.opacity = 1.0 + + +class TrackerLocationsLayout(MDScrollView): + ctx: ZorkGrandInquisitorContext + + layout: BoxLayout + + title_label: Label + location_labels: Dict[ZorkGrandInquisitorLocations, TrackerLocationLabel] + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__(size_hint=(0.45, 1.0)) + + self.ctx = ctx + + self.layout = BoxLayout(orientation="vertical", size_hint_y=None) + self.layout.bind(minimum_height=self.layout.setter("height")) + + self.location_labels = dict() + + self.title_label = Label( + text=f"[b]Locations ({len(self.ctx.checked_locations)} / {len(self.ctx.server_locations)})[/b]", + markup=True, + font_size="20dp", + size_hint_y=None, + height="40dp", + halign="left", + valign="middle", + ) + + self.title_label.bind(size=lambda label, size: setattr(label, "text_size", size)) + + self.layout.add_widget(self.title_label) + + locations_core: List[ZorkGrandInquisitorLocations] = [ + ZorkGrandInquisitorLocations.DONT_GO_SPENDING_IT_ALL_IN_ONE_PLACE, + ZorkGrandInquisitorLocations.OLD_SCRATCH_WINNER, + ZorkGrandInquisitorLocations.IN_CASE_OF_ADVENTURE, + ZorkGrandInquisitorLocations.IN_MAGIC_WE_TRUST, + ZorkGrandInquisitorLocations.INTO_THE_FOLIAGE, + ZorkGrandInquisitorLocations.INVISIBLE_FLOWERS, + ZorkGrandInquisitorLocations.THE_UNDERGROUND_UNDERGROUND, + ZorkGrandInquisitorLocations.UMBRELLA_FLOWERS, + ZorkGrandInquisitorLocations.AN_EXCELLENT_POPPING_UTENSIL, + ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE, + ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM, + ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT, + ZorkGrandInquisitorLocations.INFLATUS_THE_ETERNAL, + ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON, + ZorkGrandInquisitorLocations.THAR_SHE_BLOWS, + ZorkGrandInquisitorLocations.BOING_BOING_BOING, + ZorkGrandInquisitorLocations.BONK, + ZorkGrandInquisitorLocations.EGGPLANTS, + ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON, + ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY, + ZorkGrandInquisitorLocations.IT_DOESNT_APPEAR_TO_BE_FOOLED, + ZorkGrandInquisitorLocations.ITS_PLAYING_A_LITTLE_HARD_TO_GET, + ZorkGrandInquisitorLocations.LIT_SUNFLOWERS, + ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED, + ZorkGrandInquisitorLocations.NOTHIN_LIKE_A_GOOD_STOGIE, + ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES, + ZorkGrandInquisitorLocations.PROZORKED, + ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED, + ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO, + ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY, + ZorkGrandInquisitorLocations.CASTLE_WATCHING_A_FIELD_GUIDE, + ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER, + ZorkGrandInquisitorLocations.FROBUARY_3_UNDERGROUNDHOG_DAY, + ZorkGrandInquisitorLocations.HELLO_THIS_IS_SHONA_FROM_GURTH_PUBLISHING, + ZorkGrandInquisitorLocations.NATURAL_AND_SUPERNATURAL_CREATURES_OF_QUENDOR, + ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU, + ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG, + ZorkGrandInquisitorLocations.RIGHT_HELLO_YES_UH_THIS_IS_SNEFFLE, + ZorkGrandInquisitorLocations.RIGHT_UH_SORRY_ITS_ME_AGAIN_SNEFFLE, + ZorkGrandInquisitorLocations.TAMING_YOUR_SNAPDRAGON, + ZorkGrandInquisitorLocations.WHITE_HOUSE_TIME_TUNNEL, + ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF, + ZorkGrandInquisitorLocations.A_SMALLWAY, + ZorkGrandInquisitorLocations.ALARM_SYSTEM_IS_DOWN, + ZorkGrandInquisitorLocations.DUNCE_LOCKER, + ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE, + ZorkGrandInquisitorLocations.GETTING_SOME_CHANGE, + ZorkGrandInquisitorLocations.ITS_ALMOST_AS_IF_IT_WERE_INFINITE, + ZorkGrandInquisitorLocations.LOOK_AN_ICE_CREAM_BAR, + ZorkGrandInquisitorLocations.MIKES_PANTS, + ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO, + ZorkGrandInquisitorLocations.RESTOCKED_ON_GRUESDAY, + ZorkGrandInquisitorLocations.SUCKING_ROCKS, + ZorkGrandInquisitorLocations.GUE_TECH_ENTRANCE_EXAM, + ZorkGrandInquisitorLocations.PLEASE_DONT_THROCK_THE_GRASS, + ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED, + ZorkGrandInquisitorLocations.BEBURTT_DEMYSTIFIED, + ZorkGrandInquisitorLocations.BETTER_SPELL_MANUFACTURING_IN_UNDER_10_MINUTES, + ZorkGrandInquisitorLocations.CAVES_NOTES, + ZorkGrandInquisitorLocations.CRISIS_AVERTED, + ZorkGrandInquisitorLocations.HOW_TO_WIN_AT_DOUBLE_FANUCCI, + ZorkGrandInquisitorLocations.THE_ONLY_WAY_TO_WIN_IS_NOT_TO_PLAY, + ZorkGrandInquisitorLocations.TIME_TRAVEL_FOR_DUMMIES, + ZorkGrandInquisitorLocations.HEY_FREE_DIRT, + ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER, + ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE, + ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY, + ZorkGrandInquisitorLocations.I_AM_NOT_IMPRESSED, + ZorkGrandInquisitorLocations.NO_ONE_RETURNS_FROM_HADES, + ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT, + ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL, + ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL, + ZorkGrandInquisitorLocations.HAVE_A_HELL_OF_A_DAY, + ZorkGrandInquisitorLocations.MAKE_LOVE_NOT_WAR, + ZorkGrandInquisitorLocations.HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING, + ZorkGrandInquisitorLocations.PERMASEAL, + ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL, + ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS, + ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL, + ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE, + ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE, + ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO, + ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC, + ZorkGrandInquisitorLocations.ME_I_AM_THE_BOSS_OF_YOU, + ZorkGrandInquisitorLocations.TOTEMIZED_DAILY_BILLBOARD, + ZorkGrandInquisitorLocations.ELSEWHERE, + ZorkGrandInquisitorLocations.ARREST_THE_VANDAL, + ZorkGrandInquisitorLocations.CUT_THAT_OUT_YOU_LITTLE_CREEP, + ZorkGrandInquisitorLocations.FIRE_FIRE, + ZorkGrandInquisitorLocations.GO_AWAY, + ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE, + ZorkGrandInquisitorLocations.I_DONT_WANT_NO_TROUBLE, + ZorkGrandInquisitorLocations.IM_COMPLETELY_NUDE, + ZorkGrandInquisitorLocations.ITS_ONE_OF_THOSE_ADVENTURERS_AGAIN, + ZorkGrandInquisitorLocations.MEAD_LIGHT, + ZorkGrandInquisitorLocations.NO_AUTOGRAPHS, + ZorkGrandInquisitorLocations.NO_BONDAGE, + ZorkGrandInquisitorLocations.ONLY_YOU_CAN_PREVENT_FOOZLE_FIRES, + ZorkGrandInquisitorLocations.TALK_TO_ME_GRAND_INQUISITOR, + ZorkGrandInquisitorLocations.THATS_A_ROPE, + ZorkGrandInquisitorLocations.THATS_THE_SPIRIT, + ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID, + ZorkGrandInquisitorLocations.YOU_ONE_OF_THEM_AGITATORS_AINT_YA, + ZorkGrandInquisitorLocations.YOU_WANT_A_PIECE_OF_ME_DOCK_BOY, + ZorkGrandInquisitorLocations.PLANETFALL, + ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS, + ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE, + ZorkGrandInquisitorLocations.DINGWHACKER_DELUXE, + ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER, + ZorkGrandInquisitorLocations.UM_AH_UM_AH_UM_AH, + ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO_PAST, + ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER, + ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP, + ZorkGrandInquisitorLocations.IMBUE_BEBURTT, + ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP, + ZorkGrandInquisitorLocations.SNAVIG_REPAIRED, + ZorkGrandInquisitorLocations.SPELL_CHECK_COMPLETE, + ZorkGrandInquisitorLocations.ZIMDOR_IS_UNDAMAGED, + ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA, + ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE, + ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE, + ZorkGrandInquisitorLocations.PURPLE_BEAST_ALARM_SYSTEM, + ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE, + ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER, + ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS, + ZorkGrandInquisitorLocations.BRAVE_SOULS_WANTED, + ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP, + ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS, + ZorkGrandInquisitorLocations.NATIONAL_TREASURE, + ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY, + ZorkGrandInquisitorLocations.FLOOD_CONTROL_DAM_3_THE_NOT_REMOTELY_BORING_TALE, + ZorkGrandInquisitorLocations.SOUVENIR, + ZorkGrandInquisitorLocations.USELESS_BUT_FUN, + ZorkGrandInquisitorLocations.HOW_TO_HYPNOTIZE_YOURSELF, + ZorkGrandInquisitorLocations.VOYAGE_OF_CAPTAIN_ZAHAB, + ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE, + ZorkGrandInquisitorLocations.DOOOOOOWN, + ZorkGrandInquisitorLocations.DOWN, + ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL, + ZorkGrandInquisitorLocations.UP, + ZorkGrandInquisitorLocations.UUUUUP, + ZorkGrandInquisitorLocations.BROG_DO_GOOD, + ZorkGrandInquisitorLocations.BROG_EAT_ROCKS, + ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB, + ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME, + ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG, + ZorkGrandInquisitorLocations.HMMM_BIG_TOOTHPICK, + ZorkGrandInquisitorLocations.WHOOPS, + ] + + if self.ctx.game_controller.option_goal != ZorkGrandInquisitorGoals.THREE_ARTIFACTS: + locations_core.remove(ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT) + locations_core.remove(ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG) + locations_core.remove(ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP) + + location: ZorkGrandInquisitorLocations + for location in locations_core: + data: ZorkGrandInquisitorLocationData = location_data[location] + + location_label: TrackerLocationLabel = TrackerLocationLabel( + self.ctx, + data.region, + location, + # data.requirements, + ) + + location_label.update() + + self.location_labels[location] = location_label + self.layout.add_widget(location_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + if self.ctx.game_controller.option_deathsanity == ZorkGrandInquisitorDeathsanity.ON: + locations_deathsanity: List[ZorkGrandInquisitorLocations] = [ + ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH, + ZorkGrandInquisitorLocations.DEATH_CLIMBED_OUT_OF_THE_WELL, + ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON, + ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES, + ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES, + ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE, + ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED, + ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS, + ZorkGrandInquisitorLocations.DEATH_JUMPED_IN_BOTTOMLESS_PIT, + ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_INFINITY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_NEWARK_NEW_JERSEY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_SURFACE_OF_MERZ, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_STRAIGHT_TO_HELL, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_NEWARK_NEW_JERSEY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_HALLS_OF_INQUISITION, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_INFINITY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_SURFACE_OF_MERZ, + ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK, + ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER, + ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD, + ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE + ] + + self.layout.add_widget( + Label( + text="[b]Deathsanity[/b]", + markup=True, + font_size="18dp", + size_hint_y=None, + height="36dp", + halign="left", + valign="top", + ) + ) + + location: ZorkGrandInquisitorLocations + for location in locations_deathsanity: + data: ZorkGrandInquisitorLocationData = location_data[location] + + location_label: TrackerLocationLabel = TrackerLocationLabel( + self.ctx, + data.region, + location, + # data.requirements, + ) + + location_label.update() + + self.location_labels[location] = location_label + self.layout.add_widget(location_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + if self.ctx.game_controller.option_landmarksanity == ZorkGrandInquisitorLandmarksanity.ON: + locations_landmarksanity: List[ZorkGrandInquisitorLocations] = [ + ZorkGrandInquisitorLocations.LANDMARK_GREAT_UNDERGROUND_EMPIRE_ENTRANCE, + ZorkGrandInquisitorLocations.LANDMARK_UMBRELLA_TREE, + ZorkGrandInquisitorLocations.LANDMARK_UNDERGROUND_UNDERGROUND_ENTRANCE, + ZorkGrandInquisitorLocations.LANDMARK_DRAGON_ARCHIPELAGO, + ZorkGrandInquisitorLocations.LANDMARK_DUNGEON_MASTERS_HOUSE, + ZorkGrandInquisitorLocations.LANDMARK_MIRROR_ROOM, + ZorkGrandInquisitorLocations.LANDMARK_GUE_TECH_FOUNTAIN_INSIDE, + ZorkGrandInquisitorLocations.LANDMARK_INFINITE_CORRIDOR, + ZorkGrandInquisitorLocations.LANDMARK_GUE_TECH_FOUNTAIN_OUTSIDE, + ZorkGrandInquisitorLocations.LANDMARK_GATES_OF_HELL, + ZorkGrandInquisitorLocations.LANDMARK_HADES_SHORE, + ZorkGrandInquisitorLocations.LANDMARK_TOTEMIZER, + ZorkGrandInquisitorLocations.LANDMARK_INQUISITION_HEADQUARTERS, + ZorkGrandInquisitorLocations.LANDMARK_PORT_FOOZLE, + ZorkGrandInquisitorLocations.LANDMARK_JACKS_SHOP, + ZorkGrandInquisitorLocations.LANDMARK_PAST_PORT_FOOZLE, + ZorkGrandInquisitorLocations.LANDMARK_SPELL_CHECKER, + ZorkGrandInquisitorLocations.LANDMARK_FLOOD_CONTROL_DAM_3, + ZorkGrandInquisitorLocations.LANDMARK_WALKING_CASTLES_HEART, + ZorkGrandInquisitorLocations.LANDMARK_WHITE_HOUSE, + ] + + self.layout.add_widget( + Label( + text="[b]Landmarksanity[/b]", + markup=True, + font_size="18dp", + size_hint_y=None, + height="36dp", + halign="left", + valign="top", + ) + ) + + location: ZorkGrandInquisitorLocations + for location in locations_landmarksanity: + data: ZorkGrandInquisitorLocationData = location_data[location] + + location_label: TrackerLocationLabel = TrackerLocationLabel( + self.ctx, + data.region, + location, + # data.requirements, + ) + + location_label.update() + + self.location_labels[location] = location_label + self.layout.add_widget(location_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + self.add_widget(self.layout) + + def update(self) -> None: + self.title_label.text = ( + f"[b]Locations ({len(self.ctx.checked_locations)} / {len(self.ctx.server_locations)})[/b]" + ) + + location_label: TrackerLocationLabel + for location_label in self.location_labels.values(): + location_label.update() + + +class TrackerItemLabel(Label): + ctx: ZorkGrandInquisitorContext + + item: ZorkGrandInquisitorItems + received: bool + count: int + + def __init__(self, ctx: ZorkGrandInquisitorContext, item: ZorkGrandInquisitorItems) -> None: + super().__init__( + text=item.value, + font_size="16dp", + size_hint_y=None, + height="22dp", + halign="left", + valign="middle", + ) + + self.ctx = ctx + + self.item = item + self.received = False + self.count = 1 + + self.bind(size=lambda label, size: setattr(label, "text_size", size)) + + @property + def is_goal_item(self) -> bool: + return self.item in ( + ZorkGrandInquisitorItems.ARTIFACT_OF_MAGIC, + ZorkGrandInquisitorItems.DEATH, + ZorkGrandInquisitorItems.LANDMARK, + ) + + def update(self) -> None: + if self.is_goal_item: + self.count = self.ctx.game_controller.goal_item_count + self.received = self.count > 0 + else: + is_item_in_received: bool = self.item in self.ctx.game_controller.received_items + is_item_in_queue: bool = self.item in self.ctx.game_controller.received_items_queue + + self.received = is_item_in_received or is_item_in_queue + + if self.received: + self.opacity = 1.0 + else: + self.opacity = 0.25 + + if self.count > 1: + self.text = f"{self.item.value} x{self.count}" + else: + self.text = self.item.value + + +class TrackerItemsLayout(MDScrollView): + ctx: ZorkGrandInquisitorContext + + layout: BoxLayout + + item_labels: Dict[ZorkGrandInquisitorItems, TrackerItemLabel] + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__(size_hint=(0.2, 1.0)) + + self.ctx = ctx + + self.layout = BoxLayout(orientation="vertical", size_hint_y=None) + self.layout.bind(minimum_height=self.layout.setter("height")) + + self.item_labels = dict() + + title_label: Label = Label( + text="[b]Items[/b]", + markup=True, + font_size="20dp", + size_hint_y=None, + height="40dp", + halign="left", + valign="middle", + ) + + title_label.bind(size=lambda label, size: setattr(label, "text_size", size)) + + self.layout.add_widget(title_label) + + items_goal: List[ZorkGrandInquisitorItems] = list() + + if self.ctx.game_controller.option_goal == ZorkGrandInquisitorGoals.THREE_ARTIFACTS: + items_goal.extend([ + ZorkGrandInquisitorItems.COCONUT_OF_QUENDOR, + ZorkGrandInquisitorItems.CUBE_OF_FOUNDATION, + ZorkGrandInquisitorItems.SKULL_OF_YORUK, + ]) + elif self.ctx.game_controller.option_goal == ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: + items_goal.append(ZorkGrandInquisitorItems.ARTIFACT_OF_MAGIC) + elif self.ctx.game_controller.option_goal == ZorkGrandInquisitorGoals.ZORK_TOUR: + items_goal.append(ZorkGrandInquisitorItems.LANDMARK) + elif self.ctx.game_controller.option_goal == ZorkGrandInquisitorGoals.GRIM_JOURNEY: + items_goal.append(ZorkGrandInquisitorItems.DEATH) + + if len(items_goal): + item: ZorkGrandInquisitorItems + for item in items_goal: + item_label: TrackerItemLabel = TrackerItemLabel(self.ctx, item) + item_label.update() + + self.item_labels[item] = item_label + self.layout.add_widget(item_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_inventory: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.CIGAR, + ZorkGrandInquisitorItems.COCOA_INGREDIENTS, + ZorkGrandInquisitorItems.HAMMER, + ZorkGrandInquisitorItems.HUNGUS_LARD, + ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.MEAD_LIGHT, + ZorkGrandInquisitorItems.MONASTERY_ROPE, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE, + ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.PROZORK_TABLET, + ZorkGrandInquisitorItems.SANDWITCH_WRAPPER, + ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS, + ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV, + ZorkGrandInquisitorItems.SHOVEL, + ZorkGrandInquisitorItems.SNAPDRAGON, + ZorkGrandInquisitorItems.STUDENT_ID, + ZorkGrandInquisitorItems.SUBWAY_TOKEN, + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.WELL_ROPE, + ZorkGrandInquisitorItems.ZIMDOR_SCROLL, + ZorkGrandInquisitorItems.ZORK_ROCKS, + ] + + item: ZorkGrandInquisitorItems + for item in items_inventory: + item_label: TrackerItemLabel = TrackerItemLabel(self.ctx, item) + item_label.update() + + self.item_labels[item] = item_label + self.layout.add_widget(item_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_spells: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.SPELL_BEBURTT, + ZorkGrandInquisitorItems.SPELL_GLORF, + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ZorkGrandInquisitorItems.SPELL_KENDALL, + ZorkGrandInquisitorItems.SPELL_OBIDIL, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_REZROV, + ZorkGrandInquisitorItems.SPELL_SNAVIG, + ZorkGrandInquisitorItems.SPELL_THROCK, + ZorkGrandInquisitorItems.SPELL_YASTARD + ] + + item: ZorkGrandInquisitorItems + for item in items_spells: + item_label: TrackerItemLabel = TrackerItemLabel(self.ctx, item) + item_label.update() + + self.item_labels[item] = item_label + self.layout.add_widget(item_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_totems: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.TOTEM_BROG, + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.TOTEM_LUCY, + ] + + item: ZorkGrandInquisitorItems + for item in items_totems: + item_label: TrackerItemLabel = TrackerItemLabel(self.ctx, item) + item_label.update() + + self.item_labels[item] = item_label + self.layout.add_widget(item_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_brog: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, + ZorkGrandInquisitorItems.BROGS_GRUE_EGG, + ZorkGrandInquisitorItems.BROGS_PLANK, + ] + + item: ZorkGrandInquisitorItems + for item in items_brog: + item_label: TrackerItemLabel = TrackerItemLabel(self.ctx, item) + item_label.update() + + self.item_labels[item] = item_label + self.layout.add_widget(item_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_griff: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, + ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, + ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, + ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, + ] + + item: ZorkGrandInquisitorItems + for item in items_griff: + item_label: TrackerItemLabel = TrackerItemLabel(self.ctx, item) + item_label.update() + + self.item_labels[item] = item_label + self.layout.add_widget(item_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_lucy: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, + ] + + item: ZorkGrandInquisitorItems + for item in items_lucy: + item_label: TrackerItemLabel = TrackerItemLabel(self.ctx, item) + item_label.update() + + self.item_labels[item] = item_label + self.layout.add_widget(item_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + self.add_widget(self.layout) + + def update(self) -> None: + item_label: TrackerItemLabel + for item_label in self.item_labels.values(): + item_label.update() + + +class TrackerDestinationsHotspotsLabel(Label): + ctx: ZorkGrandInquisitorContext + + item: ZorkGrandInquisitorItems + received: bool + + def __init__(self, ctx: ZorkGrandInquisitorContext, item: ZorkGrandInquisitorItems) -> None: + super().__init__( + text=item.value, + font_size="16dp", + size_hint_y=None, + height="22dp", + halign="left", + valign="middle", + ) + + self.ctx = ctx + + self.item = item + self.received = False + + self.bind(size=lambda label, size: setattr(label, "text_size", size)) + + def update(self) -> None: + is_item_in_received: bool = self.item in self.ctx.game_controller.received_items + is_item_in_queue: bool = self.item in self.ctx.game_controller.received_items_queue + + self.received = is_item_in_received or is_item_in_queue + + if self.received: + self.opacity = 1.0 + else: + self.opacity = 0.25 + + +class TrackerDestinationsHotspotsLayout(MDScrollView): + ctx: ZorkGrandInquisitorContext + + layout: BoxLayout + + destination_hotspot_labels: Dict[ZorkGrandInquisitorItems, TrackerDestinationsHotspotsLabel] + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__(size_hint=(0.35, 1.0)) + + self.ctx = ctx + + self.layout = BoxLayout(orientation="vertical", size_hint_y=None) + self.layout.bind(minimum_height=self.layout.setter("height")) + + self.destination_hotspot_labels = dict() + + title_label: Label = Label( + text="[b]Destinations / Hotspots[/b]", + markup=True, + font_size="20dp", + size_hint_y=None, + height="40dp", + halign="left", + valign="middle", + ) + + title_label.bind(size=lambda label, size: setattr(label, "text_size", size)) + + self.layout.add_widget(title_label) + + items_destinations_subway: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, + ] + + item: ZorkGrandInquisitorItems + for item in items_destinations_subway: + destination_hotspot_label: TrackerDestinationsHotspotsLabel = TrackerDestinationsHotspotsLabel( + self.ctx, item + ) + + destination_hotspot_label.update() + + self.destination_hotspot_labels[item] = destination_hotspot_label + self.layout.add_widget(destination_hotspot_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_destinations_teleporter: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, + ] + + item: ZorkGrandInquisitorItems + for item in items_destinations_teleporter: + destination_hotspot_label: TrackerDestinationsHotspotsLabel = TrackerDestinationsHotspotsLabel( + self.ctx, item + ) + + destination_hotspot_label.update() + + self.destination_hotspot_labels[item] = destination_hotspot_label + self.layout.add_widget(destination_hotspot_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_destinations_totemizer: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ, + ] + + item: ZorkGrandInquisitorItems + for item in items_destinations_totemizer: + destination_hotspot_label: TrackerDestinationsHotspotsLabel = TrackerDestinationsHotspotsLabel( + self.ctx, item + ) + + destination_hotspot_label.update() + + self.destination_hotspot_labels[item] = destination_hotspot_label + self.layout.add_widget(destination_hotspot_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + items_hotspots: List[ZorkGrandInquisitorItems] = [ + ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX, + ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, + ZorkGrandInquisitorItems.HOTSPOT_BLINDS, + ZorkGrandInquisitorItems.HOTSPOT_BUCKET, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, + ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, + ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER, + ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND, + ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH, + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW, + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE, + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH, + ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE, + ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, + ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM, + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE, + ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ] + + item: ZorkGrandInquisitorItems + for item in items_hotspots: + destination_hotspot_label: TrackerDestinationsHotspotsLabel = TrackerDestinationsHotspotsLabel( + self.ctx, item + ) + + destination_hotspot_label.update() + + self.destination_hotspot_labels[item] = destination_hotspot_label + self.layout.add_widget(destination_hotspot_label) + + self.layout.add_widget(Widget(size_hint_y=None, height="20dp")) + + self.add_widget(self.layout) + + def update(self) -> None: + destination_hotspot_label: TrackerDestinationsHotspotsLabel + for destination_hotspot_label in self.destination_hotspot_labels.values(): + destination_hotspot_label.update() + + +class TrackerTabLayout(BoxLayout): + ctx: ZorkGrandInquisitorContext + + layout_content: BoxLayout + + layout_locations: TrackerLocationsLayout + layout_items: TrackerItemsLayout + layout_destinations_hotspots: TrackerDestinationsHotspotsLayout + + layout_not_connected: NotConnectedLayout + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__(orientation="vertical") + + self.ctx = ctx + + self.layout_not_connected = NotConnectedLayout(self.ctx) + self.add_widget(self.layout_not_connected) + + self.layout_content = BoxLayout(orientation="horizontal", spacing="16dp", padding=["8dp", "0dp"]) + self.add_widget(self.layout_content) + + self.update() + + def update(self) -> None: + if self.ctx.game_controller.save_ids is None: + self.layout_not_connected.show() + self.layout_content.clear_widgets() + + return + + self.layout_not_connected.hide() + + if not len(self.layout_content.children): + self.layout_locations = TrackerLocationsLayout(self.ctx) + self.layout_content.add_widget(self.layout_locations) + + self.layout_items = TrackerItemsLayout(self.ctx) + self.layout_content.add_widget(self.layout_items) + + self.layout_destinations_hotspots = TrackerDestinationsHotspotsLayout(self.ctx) + self.layout_content.add_widget(self.layout_destinations_hotspots) + + self.layout_locations.update() + self.layout_items.update() + self.layout_destinations_hotspots.update() + + +class NoEntranceRandomizerLayout(BoxLayout): + ctx: ZorkGrandInquisitorContext + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__(orientation="horizontal", size_hint_y=0.08) + + self.ctx = ctx + + self.add_widget( + Label(text="No entrances to track. This seed doesn't use the entrance randomizer.", font_size="24dp") + ) + + def show(self): + self.opacity = 1.0 + self.size_hint_y = 0.08 + self.disabled = False + + def hide(self): + self.opacity = 0.0 + self.size_hint_y = None + self.height = "0dp" + self.disabled = True + + +class EntranceLabel(Label): + ctx: ZorkGrandInquisitorContext + + entrance_name: str + entrance_markup: str + + color_: str + + def __init__(self, ctx: ZorkGrandInquisitorContext, entrance_name: str, entrance_markup: str) -> None: + super().__init__( + text=entrance_markup, + markup=True, + font_size="16dp", + size_hint_y=None, + height="22dp", + halign="left", + valign="bottom", + ) + + self.ctx = ctx + + self.entrance_name = entrance_name + self.entrance_markup = entrance_markup + + theme_color: List[float] = MDApp.get_running_app().theme_cls.secondaryColor + self.color_ = "".join(["{:02X}".format(round(c * 255)) for c in theme_color[:3]]) + + self.bind(size=lambda label, size: setattr(label, "text_size", size)) + + def update(self) -> None: + if self.ctx.data_storage_key is not None and self.ctx.data_storage_key in self.ctx.stored_data: + if self.entrance_name in self.ctx.stored_data[self.ctx.data_storage_key].get("discovered_entrances", []): + destination_entrance_name: str = self.ctx.entrance_randomizer_data_by_name[self.entrance_name] + destination_region: ZorkGrandInquisitorRegions = entrance_names_reverse[destination_entrance_name][1] + + self.text = self.entrance_markup + f" [b][color={self.color_}]{destination_region.value}[/color][/b]" + else: + self.text = self.entrance_markup + + +class EntrancesContent(MDScrollView): + ctx: ZorkGrandInquisitorContext + + layout: BoxLayout + + entrance_labels: Dict[str, EntranceLabel] + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__() + + self.ctx = ctx + + self.layout = BoxLayout(orientation="vertical", size_hint_y=None) + self.layout.bind(minimum_height=self.layout.setter("height")) + + theme_primary_color: str = kivy.utils.hex_colormap.get( + MDApp.get_running_app().theme_cls.primary_palette.lower(), + "springgreen", + ) + + self.entrance_labels = dict() + + allowable_entrances: Set[Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions]] = set( + randomizable_entrances + ) + + if self.ctx.game_controller.option_entrance_randomizer_include_subway_destinations: + allowable_entrances.update(randomizable_entrances_subway) + + entrance_data: List[Tuple[str, str]] = list() + + regions: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + entrance_name: str + for regions, entrance_name in entrance_names.items(): + if regions in allowable_entrances: + entrance_data.append( + ( + entrance_name, + f"[b][color={theme_primary_color}]{regions[0].value}:[/color][/b] {entrance_name} >>>", + ) + ) + + data: Tuple[str, str] + for data in sorted(entrance_data, key=lambda x: x[1]): + entrance_label: EntranceLabel = EntranceLabel(self.ctx, data[0], data[1]) + + self.layout.add_widget(entrance_label) + self.entrance_labels[data[0]] = entrance_label + + self.add_widget(self.layout) + + def update(self) -> None: + entrance_name: str + entrance_label: EntranceLabel + for entrance_name, entrance_label in self.entrance_labels.items(): + entrance_label.update() + + +class EntrancesTabLayout(BoxLayout): + ctx: ZorkGrandInquisitorContext + + layout_content: BoxLayout + layout_content_entrances: EntrancesContent + + layout_not_connected: NotConnectedLayout + layout_no_entrance_randomizer: NoEntranceRandomizerLayout + + def __init__(self, ctx: ZorkGrandInquisitorContext) -> None: + super().__init__(orientation="vertical") + + self.ctx = ctx + + self.layout_not_connected = NotConnectedLayout(self.ctx) + self.add_widget(self.layout_not_connected) + + self.layout_no_entrance_randomizer = NoEntranceRandomizerLayout(self.ctx) + self.layout_no_entrance_randomizer.hide() + self.add_widget(self.layout_no_entrance_randomizer) + + self.layout_content = BoxLayout(orientation="horizontal", spacing="16dp", padding=["8dp", "0dp"]) + self.add_widget(self.layout_content) + + self.update() + + def update(self) -> None: + if self.ctx.game_controller.save_ids is None: + self.layout_not_connected.show() + + self.layout_content.clear_widgets() + self.layout_no_entrance_randomizer.hide() + + return + + allowable_entrance_randomizer_values: Set[ZorkGrandInquisitorEntranceRandomizer] = { + ZorkGrandInquisitorEntranceRandomizer.COUPLED, + ZorkGrandInquisitorEntranceRandomizer.UNCOUPLED, + } + + if self.ctx.game_controller.option_entrance_randomizer not in allowable_entrance_randomizer_values: + self.layout_no_entrance_randomizer.show() + + self.layout_content.clear_widgets() + self.layout_not_connected.hide() + + return + + self.layout_not_connected.hide() + self.layout_no_entrance_randomizer.hide() + + if not len(self.layout_content.children): + self.layout_content_entrances = EntrancesContent(self.ctx) + self.layout_content.add_widget(self.layout_content_entrances) + + self.layout_content_entrances.update() diff --git a/worlds/zork_grand_inquisitor/data/entrance_data.py b/worlds/zork_grand_inquisitor/data/entrance_data.py new file mode 100644 index 000000000000..05200371cb9c --- /dev/null +++ b/worlds/zork_grand_inquisitor/data/entrance_data.py @@ -0,0 +1,473 @@ +from typing import Dict, List, Tuple, Union + +from ..enums import ( + ZorkGrandInquisitorEvents, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorItems, + ZorkGrandInquisitorRegions, +) + + +Entrance = Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + +EntranceRule = Union[ + Tuple[ + Tuple[ + Union[ + ZorkGrandInquisitorEvents, + ZorkGrandInquisitorItems, + ZorkGrandInquisitorRegions, + List[Union[ZorkGrandInquisitorItems, int]], + ], + ..., + ], + ..., + ], + None, +] + +EntranceRuleData = Dict[ + Tuple[ + ZorkGrandInquisitorRegions, + ZorkGrandInquisitorRegions, + ], + EntranceRule, +] + +entrance_rule_data: EntranceRuleData = { + (ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, ZorkGrandInquisitorRegions.CROSSROADS): None, + (ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL): ( + ( + ZorkGrandInquisitorItems.WELL_ROPE, + ( + ZorkGrandInquisitorItems.HOTSPOT_BUCKET, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), + ), + ), + (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL): None, + (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.DM_LAIR): ( + ( + ZorkGrandInquisitorItems.SWORD, + ( + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), + ), + ), + (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE): ( + ( + ZorkGrandInquisitorItems.SPELL_REZROV, + ( + ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), + ), + ), + (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): ( + ( + ZorkGrandInquisitorItems.SUBWAY_TOKEN, + ( + ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), + ), + ), + (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.TELEPORTER): None, + (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.CROSSROADS): None, + (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): ( + ( + ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR, + ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD, + ), + ), + (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.TELEPORTER): None, + (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.DM_LAIR): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_HOUSE_EXIT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ), + ), + (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.WALKING_CASTLE): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_BLINDS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ZorkGrandInquisitorItems.SPELL_OBIDIL, + ), + ), + (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.WHITE_HOUSE): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ), + ), + (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON): ( + ( + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ( + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO, + ), + ), + ), + (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, ZorkGrandInquisitorRegions.HADES_BEYOND_GATES): None, + (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO): None, + (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ), + ), + (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY): ( + ( + ZorkGrandInquisitorItems.SPELL_IGRAM, + ( + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ), + ), + (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ), + ), + (ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, ZorkGrandInquisitorRegions.CROSSROADS): None, + (ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, ZorkGrandInquisitorRegions.GUE_TECH): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ), + ), + (ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, ZorkGrandInquisitorRegions.GUE_TECH): None, + (ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): ( + ( + ZorkGrandInquisitorItems.STUDENT_ID, + ( + ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ), + ), + (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.GUE_TECH): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ), + ), + (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.TELEPORTER): None, + (ZorkGrandInquisitorRegions.HADES, ZorkGrandInquisitorRegions.HADES_BEYOND_GATES): ( + ( + ZorkGrandInquisitorItems.SPELL_SNAVIG, + ZorkGrandInquisitorItems.TOTEM_BROG, # Visually hiding this totem is tied to owning it; no choice + ), + ), + (ZorkGrandInquisitorRegions.HADES, ZorkGrandInquisitorRegions.HADES_SHORE): ( + ( + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.SPELL_SNAVIG, + ), + ), + (ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO): ( + ( + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ), + ), + (ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, ZorkGrandInquisitorRegions.HADES): None, + (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.HADES): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES, + ), + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ), + ), + (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SUBWAY_HADES): None, + (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.TELEPORTER): None, + (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.HADES_SHORE): ( + ( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT): ( + ( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): None, + (ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, ZorkGrandInquisitorRegions.MONASTERY): None, + (ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ), + ), + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST + ): None, + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ + ): None, + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL + ): None, + (ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZorkGrandInquisitorRegions.PORT_FOOZLE): None, + (ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL, ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL): ( + (ZorkGrandInquisitorItems.WELL_ROPE,), + ), + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST + ): None, + (ZorkGrandInquisitorRegions.PORT_FOOZLE, ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST): None, + (ZorkGrandInquisitorRegions.PORT_FOOZLE, ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP): ( + ( + ZorkGrandInquisitorItems.CIGAR, + ( + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ), + ), + (ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, ZorkGrandInquisitorRegions.PORT_FOOZLE): None, + (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT): None, + (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN): ( + ( + ZorkGrandInquisitorItems.TOTEM_LUCY, + ( + ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), + ), + ), + (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST): None, + (ZorkGrandInquisitorRegions.SPELL_LAB, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): None, + (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY): ( + ( + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ), + ), + (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.SPELL_LAB): ( + ( + ZorkGrandInquisitorItems.SWORD, + ( + ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ZorkGrandInquisitorEvents.DAM_DESTROYED, + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ), + ), + (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.TELEPORTER): None, + (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.CROSSROADS): None, + (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): ( + ( + ZorkGrandInquisitorItems.SPELL_KENDALL, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM, + ), + ), + (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_HADES): ( + ( + ZorkGrandInquisitorItems.SPELL_KENDALL, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES, + ), + ), + (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( + ( + ZorkGrandInquisitorItems.SPELL_KENDALL, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, + ), + ), + (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.SUBWAY_HADES): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_HADES, ZorkGrandInquisitorRegions.HADES_SHORE): None, + (ZorkGrandInquisitorRegions.SUBWAY_HADES, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_HADES, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_HADES, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.MONASTERY): ( + (ZorkGrandInquisitorItems.MONASTERY_ROPE,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_HADES): ( + (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES,), + ), + (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.TELEPORTER): None, + (ZorkGrandInquisitorRegions.TELEPORTER, ZorkGrandInquisitorRegions.CROSSROADS): ( + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS, + ), + ), + (ZorkGrandInquisitorRegions.TELEPORTER, ZorkGrandInquisitorRegions.DM_LAIR): ( + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, + ), + ), + (ZorkGrandInquisitorRegions.TELEPORTER, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): ( + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, + ), + ), + (ZorkGrandInquisitorRegions.TELEPORTER, ZorkGrandInquisitorRegions.HADES_SHORE): ( + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, + ), + ), + (ZorkGrandInquisitorRegions.TELEPORTER, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): ( + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, + ), + ), + (ZorkGrandInquisitorRegions.TELEPORTER, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, + ), + ), + (ZorkGrandInquisitorRegions.WALKING_CASTLE, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): None, + (ZorkGrandInquisitorRegions.WHITE_HOUSE, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): None, + (ZorkGrandInquisitorRegions.WHITE_HOUSE, ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR): ( + ( + ZorkGrandInquisitorItems.TOTEM_BROG, + ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, + ), + ), + (ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, ZorkGrandInquisitorRegions.WHITE_HOUSE): None, +} + +endgame_entrance_data_by_goal: Dict[ + ZorkGrandInquisitorGoals, + EntranceRuleData, +] = { + ZorkGrandInquisitorGoals.THREE_ARTIFACTS: { + (ZorkGrandInquisitorRegions.MENU, ZorkGrandInquisitorRegions.ENDGAME): ( + ( + ZorkGrandInquisitorItems.COCONUT_OF_QUENDOR, + ZorkGrandInquisitorItems.CUBE_OF_FOUNDATION, + ZorkGrandInquisitorItems.SKULL_OF_YORUK, + ), + ) + }, + ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: { + (ZorkGrandInquisitorRegions.WALKING_CASTLE, ZorkGrandInquisitorRegions.ENDGAME): ( + ( + [ZorkGrandInquisitorItems.ARTIFACT_OF_MAGIC, 999], # Will get replaced with the actual number + ), + ) + }, + ZorkGrandInquisitorGoals.SPELL_HEIST: { + (ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZorkGrandInquisitorRegions.ENDGAME): ( + ( + ZorkGrandInquisitorItems.SPELL_BEBURTT, + ZorkGrandInquisitorItems.SPELL_GLORF, + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ZorkGrandInquisitorItems.SPELL_KENDALL, + ZorkGrandInquisitorItems.SPELL_OBIDIL, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_REZROV, + ZorkGrandInquisitorItems.SPELL_SNAVIG, + ZorkGrandInquisitorItems.SPELL_THROCK, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ), + ) + }, + ZorkGrandInquisitorGoals.ZORK_TOUR: { + (ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZorkGrandInquisitorRegions.ENDGAME): ( + ( + [ZorkGrandInquisitorItems.LANDMARK, 999], # Will get replaced with the actual number + ), + ), + }, + ZorkGrandInquisitorGoals.GRIM_JOURNEY: { + (ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, ZorkGrandInquisitorRegions.ENDGAME): ( + ( + [ZorkGrandInquisitorItems.DEATH, 999], # Will get replaced with the actual number + ), + ), + }, +} diff --git a/worlds/zork_grand_inquisitor/data/entrance_randomizer_data.py b/worlds/zork_grand_inquisitor/data/entrance_randomizer_data.py new file mode 100644 index 000000000000..d8b4b70dd6ef --- /dev/null +++ b/worlds/zork_grand_inquisitor/data/entrance_randomizer_data.py @@ -0,0 +1,200 @@ +from typing import Dict, Set, Tuple + +from ..enums import ZorkGrandInquisitorRegions as ZGIRegions + + +entrances_to_game_location_teleports: Dict[ + Tuple[ZGIRegions, ZGIRegions], + Tuple[str, str], +] = { + (ZGIRegions.BOTTOM_OF_THE_WELL, ZGIRegions.CROSSROADS): ("u c 1 0", "25"), + (ZGIRegions.BOTTOM_OF_THE_WELL, ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL): ("p c 1 0", "250"), + (ZGIRegions.CROSSROADS, ZGIRegions.BOTTOM_OF_THE_WELL): ("u w 1 0", "30"), + (ZGIRegions.CROSSROADS, ZGIRegions.DM_LAIR): ("d g 1 0", "1410"), + (ZGIRegions.CROSSROADS, ZGIRegions.GUE_TECH_ENTRANCE): ("t e 1 0", "200"), + (ZGIRegions.CROSSROADS, ZGIRegions.SUBWAY_CROSSROADS): ("u s 1 0", "1440"), + (ZGIRegions.DM_LAIR, ZGIRegions.CROSSROADS): ("u c 3 0", "500"), + (ZGIRegions.DM_LAIR, ZGIRegions.DM_LAIR_INTERIOR): ("d v 1 0", "1590"), + (ZGIRegions.DM_LAIR_INTERIOR, ZGIRegions.DM_LAIR): ("d g 4 0", "120"), + (ZGIRegions.DM_LAIR_INTERIOR, ZGIRegions.WALKING_CASTLE): ("d c 1 0", "1150"), + (ZGIRegions.GUE_TECH, ZGIRegions.GUE_TECH_ENTRANCE): ("t e 3 0", "1010"), + (ZGIRegions.GUE_TECH, ZGIRegions.GUE_TECH_OUTSIDE): ("t e 4 0", "1120"), + (ZGIRegions.GUE_TECH_ENTRANCE, ZGIRegions.CROSSROADS): ("u c 4 0", "470"), + (ZGIRegions.GUE_TECH_ENTRANCE, ZGIRegions.GUE_TECH): ("t r 1 0", "150"), + (ZGIRegions.GUE_TECH_HALLWAY, ZGIRegions.SPELL_LAB_BRIDGE): ("t p 1 0", "1080"), + (ZGIRegions.GUE_TECH_OUTSIDE, ZGIRegions.GUE_TECH): ("t r 1 0", "150"), + (ZGIRegions.HADES, ZGIRegions.HADES_BEYOND_GATES): ("h p 6 0", "380"), + (ZGIRegions.HADES, ZGIRegions.HADES_SHORE): ("h p 1 0", "1480"), + (ZGIRegions.HADES_BEYOND_GATES, ZGIRegions.HADES): ("h p 5 0", "1350"), + (ZGIRegions.HADES_SHORE, ZGIRegions.HADES): ("h p 4 0", "340"), + (ZGIRegions.HADES_SHORE, ZGIRegions.SUBWAY_HADES): ("u h 1 0", "0"), + (ZGIRegions.MONASTERY, ZGIRegions.MONASTERY_EXHIBIT): ("m e 2 0", "900"), + (ZGIRegions.MONASTERY, ZGIRegions.SUBWAY_MONASTERY): ("u m 1 0", "1590"), + (ZGIRegions.MONASTERY_EXHIBIT, ZGIRegions.MONASTERY): ("m t 3 0", "0"), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST): ("p s 1 0", "680"), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ): ("p x 1 0", "50"), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL): ("p s 2 0", "1360"), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.PORT_FOOZLE): ("p e 1 0", "210"), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL, ZGIRegions.BOTTOM_OF_THE_WELL): ("u w 1 0", "0"), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST): ("p s 1 0", "170"), + (ZGIRegions.PORT_FOOZLE, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST): ("p s 1 0", "1430"), + (ZGIRegions.PORT_FOOZLE, ZGIRegions.PORT_FOOZLE_JACKS_SHOP): ("p p 1 0", "1130"), + (ZGIRegions.PORT_FOOZLE_JACKS_SHOP, ZGIRegions.PORT_FOOZLE): ("p e 1 0", "1630"), + (ZGIRegions.SPELL_LAB, ZGIRegions.SPELL_LAB_BRIDGE): ("t p 1 0", "140"), + (ZGIRegions.SPELL_LAB_BRIDGE, ZGIRegions.GUE_TECH_HALLWAY): ("t h 3 0", "740"), + (ZGIRegions.SPELL_LAB_BRIDGE, ZGIRegions.SPELL_LAB): ("t p 2 0", "1240"), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.CROSSROADS): ("u c 6 0", "1320"), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM): ("u e 2 0", "1030"), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_HADES): ("u h 2 0", "1030"), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_MONASTERY): ("u m 2 0", "1030"), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_CROSSROADS): ("u s 2 0", "1030"), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_HADES): ("u h 2 0", "1030"), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_MONASTERY): ("u m 2 0", "1030"), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.HADES_SHORE): ("h p 1 0", "530"), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_CROSSROADS): ("u s 2 0", "1030"), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM): ("u e 2 0", "1030"), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_MONASTERY): ("u m 2 0", "1030"), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.MONASTERY): ("m t 1 0", "1530"), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_CROSSROADS): ("u s 2 0", "1030"), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM): ("u e 2 0", "1030"), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_HADES): ("u h 2 0", "1030"), + (ZGIRegions.WALKING_CASTLE, ZGIRegions.DM_LAIR_INTERIOR): ("d v 1 0", "680"), +} + +entrances_to_game_locations: Dict[ + Tuple[Tuple[ZGIRegions, ZGIRegions]], + Tuple[Tuple[str, str], ...], +] = { + (ZGIRegions.BOTTOM_OF_THE_WELL, ZGIRegions.CROSSROADS): (("uw1f", "uc10"),), + (ZGIRegions.BOTTOM_OF_THE_WELL, ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL): (("uw10", "pc10"),), + (ZGIRegions.CROSSROADS, ZGIRegions.BOTTOM_OF_THE_WELL): (("uc10", "uw10"),), + (ZGIRegions.CROSSROADS, ZGIRegions.DM_LAIR): (("uc30", "dg10"),), + (ZGIRegions.CROSSROADS, ZGIRegions.GUE_TECH_ENTRANCE): (("uc40", "te10"),), + (ZGIRegions.CROSSROADS, ZGIRegions.SUBWAY_CROSSROADS): (("uc60", "us10"),), + (ZGIRegions.DM_LAIR, ZGIRegions.CROSSROADS): (("dg10", "uc30"),), + (ZGIRegions.DM_LAIR, ZGIRegions.DM_LAIR_INTERIOR): (("dg40", "dv10"),), + (ZGIRegions.DM_LAIR_INTERIOR, ZGIRegions.DM_LAIR): (("dv10", "dg40"),), + (ZGIRegions.DM_LAIR_INTERIOR, ZGIRegions.WALKING_CASTLE): (("dv10", "dc10"),), + (ZGIRegions.GUE_TECH, ZGIRegions.GUE_TECH_ENTRANCE): (("tr1g", "te30"),), + (ZGIRegions.GUE_TECH, ZGIRegions.GUE_TECH_OUTSIDE): (("tr1k", "te40"),), + (ZGIRegions.GUE_TECH_ENTRANCE, ZGIRegions.CROSSROADS): (("te10", "uc40"),), + (ZGIRegions.GUE_TECH_ENTRANCE, ZGIRegions.GUE_TECH): (("te3e", "tr10"),), + (ZGIRegions.GUE_TECH_HALLWAY, ZGIRegions.SPELL_LAB_BRIDGE): (("th30", "tp10"),), + (ZGIRegions.GUE_TECH_OUTSIDE, ZGIRegions.GUE_TECH): (("te40", "tr10"),), + (ZGIRegions.HADES, ZGIRegions.HADES_BEYOND_GATES): (("hp50", "hp60"),), + (ZGIRegions.HADES, ZGIRegions.HADES_SHORE): (("hp4e", "hp10"),), + (ZGIRegions.HADES_BEYOND_GATES, ZGIRegions.HADES): (("hp60", "hp50"),), + (ZGIRegions.HADES_SHORE, ZGIRegions.HADES): (("hp1f", "hp40"),), + (ZGIRegions.HADES_SHORE, ZGIRegions.SUBWAY_HADES): (("hp10", "uh10"),), + (ZGIRegions.MONASTERY, ZGIRegions.MONASTERY_EXHIBIT): (("mt2e", "me20"), ("mt30", "me20")), + (ZGIRegions.MONASTERY, ZGIRegions.SUBWAY_MONASTERY): (("mt1e", "um10"),), + (ZGIRegions.MONASTERY_EXHIBIT, ZGIRegions.MONASTERY): (("me5f", "mt30"),), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST): (("px10", "ps10"),), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ): (("ps10", "px10"),), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL): (("ps10", "ps20"),), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.PORT_FOOZLE): (("ps10", "pe10"),), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL, ZGIRegions.BOTTOM_OF_THE_WELL): (("pc1e", "uw10"),), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST): (("ps20", "ps10"),), + (ZGIRegions.PORT_FOOZLE, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST): (("pe10", "ps10"),), + (ZGIRegions.PORT_FOOZLE, ZGIRegions.PORT_FOOZLE_JACKS_SHOP): (("pe1e", "pp10"),), + (ZGIRegions.PORT_FOOZLE_JACKS_SHOP, ZGIRegions.PORT_FOOZLE): (("pp10", "pe10"),), + (ZGIRegions.SPELL_LAB, ZGIRegions.SPELL_LAB_BRIDGE): (("tp20", "tp50"),), + (ZGIRegions.SPELL_LAB_BRIDGE, ZGIRegions.GUE_TECH_HALLWAY): (("tp10", "th30"),), + (ZGIRegions.SPELL_LAB_BRIDGE, ZGIRegions.SPELL_LAB): (("tp10", "tp50"),), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.CROSSROADS): (("us1e", "uc60"),), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM): (("us2f", "ue2f"),), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_HADES): (("us2f", "uh2f"),), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_MONASTERY): (("us2f", "um2f"),), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_CROSSROADS): (("ue2f", "us2f"),), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_HADES): (("ue2f", "uh2f"),), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_MONASTERY): (("ue2f", "um2f"),), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.HADES_SHORE): (("uh10", "hp10"),), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_CROSSROADS): (("uh2f", "us2f"),), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM): (("uh2f", "ue2f"),), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_MONASTERY): (("uh2f", "um2f"),), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.MONASTERY): (("um10", "mt10"),), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_CROSSROADS): (("um2f", "us2f"),), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM): (("um2f", "ue2f"),), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_HADES): (("um2f", "uh2f"),), + (ZGIRegions.WALKING_CASTLE, ZGIRegions.DM_LAIR_INTERIOR): (("dc10", "dv10"),), +} + +entrances_to_game_locations_reverse: Dict[ + Tuple[str, str], + Tuple[ZGIRegions, ZGIRegions], +] = dict() + +entrance_pair: Tuple[ZGIRegions, ZGIRegions] +game_location_pairs: Tuple[Tuple[str, str], ...] +for entrance_pair, game_location_pairs in entrances_to_game_locations.items(): + game_location_pair: Tuple[str, str] + for game_location_pair in game_location_pairs: + entrances_to_game_locations_reverse[game_location_pair] = entrance_pair + +randomizable_entrances: Tuple[Tuple[ZGIRegions, ZGIRegions]] = ( + (ZGIRegions.BOTTOM_OF_THE_WELL, ZGIRegions.CROSSROADS), + (ZGIRegions.BOTTOM_OF_THE_WELL, ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL), + (ZGIRegions.CROSSROADS, ZGIRegions.BOTTOM_OF_THE_WELL), + (ZGIRegions.CROSSROADS, ZGIRegions.DM_LAIR), + (ZGIRegions.CROSSROADS, ZGIRegions.GUE_TECH_ENTRANCE), + (ZGIRegions.CROSSROADS, ZGIRegions.SUBWAY_CROSSROADS), + (ZGIRegions.DM_LAIR, ZGIRegions.CROSSROADS), + (ZGIRegions.DM_LAIR, ZGIRegions.DM_LAIR_INTERIOR), + (ZGIRegions.DM_LAIR_INTERIOR, ZGIRegions.DM_LAIR), + (ZGIRegions.DM_LAIR_INTERIOR, ZGIRegions.WALKING_CASTLE), + (ZGIRegions.GUE_TECH, ZGIRegions.GUE_TECH_ENTRANCE), + (ZGIRegions.GUE_TECH, ZGIRegions.GUE_TECH_OUTSIDE), + (ZGIRegions.GUE_TECH_ENTRANCE, ZGIRegions.CROSSROADS), + (ZGIRegions.GUE_TECH_ENTRANCE, ZGIRegions.GUE_TECH), + (ZGIRegions.GUE_TECH_HALLWAY, ZGIRegions.SPELL_LAB_BRIDGE), + (ZGIRegions.GUE_TECH_OUTSIDE, ZGIRegions.GUE_TECH), + (ZGIRegions.HADES, ZGIRegions.HADES_BEYOND_GATES), + (ZGIRegions.HADES, ZGIRegions.HADES_SHORE), + (ZGIRegions.HADES_BEYOND_GATES, ZGIRegions.HADES), + (ZGIRegions.HADES_SHORE, ZGIRegions.HADES), + (ZGIRegions.HADES_SHORE, ZGIRegions.SUBWAY_HADES), + (ZGIRegions.MONASTERY, ZGIRegions.MONASTERY_EXHIBIT), + (ZGIRegions.MONASTERY, ZGIRegions.SUBWAY_MONASTERY), + (ZGIRegions.MONASTERY_EXHIBIT, ZGIRegions.MONASTERY), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, ZGIRegions.PORT_FOOZLE), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL, ZGIRegions.BOTTOM_OF_THE_WELL), + (ZGIRegions.OUTSIDE_PORT_FOOZLE_WELL, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST), + (ZGIRegions.PORT_FOOZLE, ZGIRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST), + (ZGIRegions.PORT_FOOZLE, ZGIRegions.PORT_FOOZLE_JACKS_SHOP), + (ZGIRegions.PORT_FOOZLE_JACKS_SHOP, ZGIRegions.PORT_FOOZLE), + (ZGIRegions.SPELL_LAB, ZGIRegions.SPELL_LAB_BRIDGE), + (ZGIRegions.SPELL_LAB_BRIDGE, ZGIRegions.GUE_TECH_HALLWAY), + (ZGIRegions.SPELL_LAB_BRIDGE, ZGIRegions.SPELL_LAB), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.CROSSROADS), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.HADES_SHORE), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.MONASTERY), + (ZGIRegions.WALKING_CASTLE, ZGIRegions.DM_LAIR_INTERIOR), +) + +randomizable_entrances_subway: Tuple[Tuple[ZGIRegions, ZGIRegions]] = ( + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_HADES), + (ZGIRegions.SUBWAY_CROSSROADS, ZGIRegions.SUBWAY_MONASTERY), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_CROSSROADS), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_HADES), + (ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM, ZGIRegions.SUBWAY_MONASTERY), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_CROSSROADS), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM), + (ZGIRegions.SUBWAY_HADES, ZGIRegions.SUBWAY_MONASTERY), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_CROSSROADS), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_FLOOD_CONTROL_DAM), + (ZGIRegions.SUBWAY_MONASTERY, ZGIRegions.SUBWAY_HADES), +) + +relevant_game_locations: Set[str] = {"cd20", "qs1e", "sg60"} + +entrance_pairs: Tuple[Tuple[str, str]] +for entrance_pairs in entrances_to_game_locations.values(): + entrance_from: str + entrance_to: str + for entrance_from, entrance_to in entrance_pairs: + relevant_game_locations.add(entrance_from) + relevant_game_locations.add(entrance_to) diff --git a/worlds/zork_grand_inquisitor/data/entrance_rule_data.py b/worlds/zork_grand_inquisitor/data/entrance_rule_data.py deleted file mode 100644 index f48be5eb6b6a..000000000000 --- a/worlds/zork_grand_inquisitor/data/entrance_rule_data.py +++ /dev/null @@ -1,419 +0,0 @@ -from typing import Dict, Tuple, Union - -from ..enums import ZorkGrandInquisitorEvents, ZorkGrandInquisitorItems, ZorkGrandInquisitorRegions - - -entrance_rule_data: Dict[ - Tuple[ - ZorkGrandInquisitorRegions, - ZorkGrandInquisitorRegions, - ], - Union[ - Tuple[ - Tuple[ - Union[ - ZorkGrandInquisitorEvents, - ZorkGrandInquisitorItems, - ZorkGrandInquisitorRegions, - ], - ..., - ], - ..., - ], - None, - ], -] = { - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.DM_LAIR): ( - ( - ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, - ), - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, - ), - ), - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.GUE_TECH): ( - ( - ZorkGrandInquisitorItems.SPELL_REZROV, - ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR, - ), - ), - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, - ), - ), - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.HADES_SHORE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, - ), - ), - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.PORT_FOOZLE): None, - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, - ), - ), - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): ( - ( - ZorkGrandInquisitorItems.SUBWAY_TOKEN, - ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, - ), - ), - (ZorkGrandInquisitorRegions.CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, - ), - ), - (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.CROSSROADS): None, - (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): ( - ( - ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR, - ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD, - ), - ), - (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, - ), - ), - (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.HADES_SHORE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, - ), - ), - (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, - ), - ), - (ZorkGrandInquisitorRegions.DM_LAIR, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, - ), - ), - (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.DM_LAIR): None, - (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.WALKING_CASTLE): ( - ( - ZorkGrandInquisitorItems.HOTSPOT_BLINDS, - ZorkGrandInquisitorEvents.KNOWS_OBIDIL, - ), - ), - (ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, ZorkGrandInquisitorRegions.WHITE_HOUSE): ( - ( - ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, - ZorkGrandInquisitorItems.SPELL_NARWILE, - ZorkGrandInquisitorEvents.KNOWS_YASTARD, - ), - ), - (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON): ( - ( - ZorkGrandInquisitorItems.TOTEM_GRIFF, - ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW, - ), - ), - (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, ZorkGrandInquisitorRegions.HADES_BEYOND_GATES): None, - (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO): None, - (ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, ZorkGrandInquisitorRegions.ENDGAME): ( - ( - ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, - ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, - ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, - ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, - ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, - ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, - ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, - ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, - ZorkGrandInquisitorRegions.WHITE_HOUSE, - ZorkGrandInquisitorItems.TOTEM_BROG, # Needed here since White House is not broken down in 2 regions - ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, - ZorkGrandInquisitorItems.BROGS_GRUE_EGG, - ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, - ZorkGrandInquisitorItems.BROGS_PLANK, - ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, - ), - ), - (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.CROSSROADS): None, - (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY): ( - ( - ZorkGrandInquisitorItems.SPELL_IGRAM, - ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, - ), - ), - (ZorkGrandInquisitorRegions.GUE_TECH, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): ( - (ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR,), - ), - (ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, ZorkGrandInquisitorRegions.GUE_TECH): None, - (ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): ( - ( - ZorkGrandInquisitorItems.STUDENT_ID, - ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE, - ), - ), - (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.CROSSROADS): ( - (ZorkGrandInquisitorItems.MAP,), - ), - (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.DM_LAIR): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, - ), - ), - (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.GUE_TECH): None, - (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.HADES_SHORE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, - ), - ), - (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, - ), - ), - (ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, - ), - ), - (ZorkGrandInquisitorRegions.HADES, ZorkGrandInquisitorRegions.HADES_BEYOND_GATES): ( - ( - ZorkGrandInquisitorEvents.KNOWS_SNAVIG, - ZorkGrandInquisitorItems.TOTEM_BROG, # Visually hiding this totem is tied to owning it; no choice - ), - ), - (ZorkGrandInquisitorRegions.HADES, ZorkGrandInquisitorRegions.HADES_SHORE): ( - (ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,), - ), - (ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO): ( - ( - ZorkGrandInquisitorItems.SPELL_NARWILE, - ZorkGrandInquisitorEvents.KNOWS_YASTARD, - ), - ), - (ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, ZorkGrandInquisitorRegions.HADES): None, - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.CROSSROADS): ( - (ZorkGrandInquisitorItems.MAP,), - ), - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.DM_LAIR): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, - ), - ), - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, - ), - ), - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.HADES): ( - ( - ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, - ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, - ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ), - ), - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, - ), - ), - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): None, - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): ( - (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM,), - ), - (ZorkGrandInquisitorRegions.HADES_SHORE, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( - (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY,), - ), - (ZorkGrandInquisitorRegions.MENU, ZorkGrandInquisitorRegions.PORT_FOOZLE): None, - (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.HADES_SHORE): ( - ( - ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, - ), - ), - (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT): ( - ( - ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION, - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, - ), - ), - (ZorkGrandInquisitorRegions.MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): None, - (ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, ZorkGrandInquisitorRegions.MONASTERY): None, - (ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST): ( - ( - ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, - ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, - ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER, - ZorkGrandInquisitorItems.SPELL_NARWILE, - ZorkGrandInquisitorEvents.KNOWS_YASTARD, - ), - ), - (ZorkGrandInquisitorRegions.PORT_FOOZLE, ZorkGrandInquisitorRegions.CROSSROADS): ( - ( - ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE, - ZorkGrandInquisitorItems.ROPE, - ZorkGrandInquisitorItems.HOTSPOT_WELL, - ), - ), - (ZorkGrandInquisitorRegions.PORT_FOOZLE, ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP): ( - ( - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, - ), - ), - (ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, ZorkGrandInquisitorRegions.PORT_FOOZLE): None, - (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT): None, - (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN): ( - ( - ZorkGrandInquisitorItems.TOTEM_LUCY, - ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, - ), - ), - (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, ZorkGrandInquisitorRegions.ENDGAME): ( - ( - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, - ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, - ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, - ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, - ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, - ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, - ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, - ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, - ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, - ZorkGrandInquisitorRegions.WHITE_HOUSE, - ZorkGrandInquisitorItems.TOTEM_BROG, # Needed here since White House is not broken down in 2 regions - ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, - ZorkGrandInquisitorItems.BROGS_GRUE_EGG, - ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, - ZorkGrandInquisitorItems.BROGS_PLANK, - ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, - ), - ), - (ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST): None, - (ZorkGrandInquisitorRegions.SPELL_LAB, ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE): None, - (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.CROSSROADS): ( - (ZorkGrandInquisitorItems.MAP,), - ), - (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.DM_LAIR): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, - ), - ), - (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, - ), - ), - (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY): None, - (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.HADES_SHORE): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, - ), - ), - (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.SPELL_LAB): ( - ( - ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, - ZorkGrandInquisitorEvents.DAM_DESTROYED, - ZorkGrandInquisitorItems.SPELL_GOLGATEM, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM, - ), - ), - (ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( - ( - ZorkGrandInquisitorItems.MAP, - ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, - ), - ), - (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.CROSSROADS): None, - (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.HADES_SHORE): ( - ( - ZorkGrandInquisitorItems.SPELL_KENDALL, - ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES, - ), - ), - (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): ( - ( - ZorkGrandInquisitorItems.SPELL_KENDALL, - ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM, - ), - ), - (ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( - ( - ZorkGrandInquisitorItems.SPELL_KENDALL, - ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, - ), - ), - (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.HADES_SHORE): ( - (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES,), - ), - (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): None, - (ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, ZorkGrandInquisitorRegions.SUBWAY_MONASTERY): ( - (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY,), - ), - (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.HADES_SHORE): ( - (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES,), - ), - (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.MONASTERY): ( - ( - ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorEvents.ROPE_GLORFABLE, - ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT, - ), - ), - (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS): None, - (ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM): ( - (ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM,), - ), - (ZorkGrandInquisitorRegions.WALKING_CASTLE, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): None, - (ZorkGrandInquisitorRegions.WHITE_HOUSE, ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR): None, - (ZorkGrandInquisitorRegions.WHITE_HOUSE, ZorkGrandInquisitorRegions.ENDGAME): ( - ( - ZorkGrandInquisitorItems.TOTEM_BROG, # Needed here since White House is not broken down in 2 regions - ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, - ZorkGrandInquisitorItems.BROGS_GRUE_EGG, - ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, - ZorkGrandInquisitorItems.BROGS_PLANK, - ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, - ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, - ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, - ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, - ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, - ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, - ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, - ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, - ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, - ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, - ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, - ), - ), -} diff --git a/worlds/zork_grand_inquisitor/data/item_data.py b/worlds/zork_grand_inquisitor/data/item_data.py index c312bbce3d09..d4ed5e1e51a4 100644 --- a/worlds/zork_grand_inquisitor/data/item_data.py +++ b/worlds/zork_grand_inquisitor/data/item_data.py @@ -1,4 +1,4 @@ -from typing import Dict, NamedTuple, Optional, Tuple, Union +from typing import Dict, NamedTuple, Optional, Tuple from BaseClasses import ItemClassification @@ -41,207 +41,195 @@ class ZorkGrandInquisitorItemData(NamedTuple): classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), - ZorkGrandInquisitorItems.FLATHEADIA_FUDGE: ZorkGrandInquisitorItemData( - statemap_keys=(54,), + ZorkGrandInquisitorItems.CIGAR: ZorkGrandInquisitorItemData( + statemap_keys=(1,), archipelago_id=ITEM_OFFSET + 4, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), + ZorkGrandInquisitorItems.COCOA_INGREDIENTS: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 5, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), + ), ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP: ZorkGrandInquisitorItemData( statemap_keys=(86,), - archipelago_id=ITEM_OFFSET + 5, + archipelago_id=ITEM_OFFSET + 6, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH: ZorkGrandInquisitorItemData( statemap_keys=(84,), - archipelago_id=ITEM_OFFSET + 6, + archipelago_id=ITEM_OFFSET + 7, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT: ZorkGrandInquisitorItemData( statemap_keys=(9,), - archipelago_id=ITEM_OFFSET + 7, + archipelago_id=ITEM_OFFSET + 8, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN: ZorkGrandInquisitorItemData( statemap_keys=(16,), - archipelago_id=ITEM_OFFSET + 8, + archipelago_id=ITEM_OFFSET + 9, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.HAMMER: ZorkGrandInquisitorItemData( statemap_keys=(23,), - archipelago_id=ITEM_OFFSET + 9, + archipelago_id=ITEM_OFFSET + 10, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.HUNGUS_LARD: ZorkGrandInquisitorItemData( statemap_keys=(55,), - archipelago_id=ITEM_OFFSET + 10, - classification=ItemClassification.progression, - tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), - ), - ZorkGrandInquisitorItems.JAR_OF_HOTBUGS: ZorkGrandInquisitorItemData( - statemap_keys=(56,), archipelago_id=ITEM_OFFSET + 11, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), - ZorkGrandInquisitorItems.LANTERN: ZorkGrandInquisitorItemData( - statemap_keys=(4,), - archipelago_id=ITEM_OFFSET + 12, - classification=ItemClassification.progression, - tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), - ), ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER: ZorkGrandInquisitorItemData( statemap_keys=(88,), - archipelago_id=ITEM_OFFSET + 13, + archipelago_id=ITEM_OFFSET + 12, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1: ZorkGrandInquisitorItemData( statemap_keys=(116,), # With fly = 120 - archipelago_id=ITEM_OFFSET + 14, + archipelago_id=ITEM_OFFSET + 13, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2: ZorkGrandInquisitorItemData( statemap_keys=(117,), # With fly = 121 - archipelago_id=ITEM_OFFSET + 15, + archipelago_id=ITEM_OFFSET + 14, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3: ZorkGrandInquisitorItemData( statemap_keys=(118,), # With fly = 122 - archipelago_id=ITEM_OFFSET + 16, + archipelago_id=ITEM_OFFSET + 15, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4: ZorkGrandInquisitorItemData( statemap_keys=(119,), # With fly = 123 - archipelago_id=ITEM_OFFSET + 17, + archipelago_id=ITEM_OFFSET + 16, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.MAP: ZorkGrandInquisitorItemData( statemap_keys=(6,), - archipelago_id=ITEM_OFFSET + 18, + archipelago_id=ITEM_OFFSET + 17, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.MEAD_LIGHT: ZorkGrandInquisitorItemData( statemap_keys=(2,), - archipelago_id=ITEM_OFFSET + 19, - classification=ItemClassification.progression, - tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), - ), - ZorkGrandInquisitorItems.MOSS_OF_MAREILON: ZorkGrandInquisitorItemData( - statemap_keys=(57,), - archipelago_id=ITEM_OFFSET + 20, + archipelago_id=ITEM_OFFSET + 18, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), - ZorkGrandInquisitorItems.MUG: ZorkGrandInquisitorItemData( - statemap_keys=(35,), - archipelago_id=ITEM_OFFSET + 21, + ZorkGrandInquisitorItems.MONASTERY_ROPE: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 19, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.OLD_SCRATCH_CARD: ZorkGrandInquisitorItemData( statemap_keys=(17,), - archipelago_id=ITEM_OFFSET + 22, + archipelago_id=ITEM_OFFSET + 20, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE: ZorkGrandInquisitorItemData( statemap_keys=(36,), - archipelago_id=ITEM_OFFSET + 23, + archipelago_id=ITEM_OFFSET + 21, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER: ZorkGrandInquisitorItemData( statemap_keys=(3,), - archipelago_id=ITEM_OFFSET + 24, + archipelago_id=ITEM_OFFSET + 22, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS: ZorkGrandInquisitorItemData( - statemap_keys=(5827,), - archipelago_id=ITEM_OFFSET + 25, + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 23, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.PROZORK_TABLET: ZorkGrandInquisitorItemData( statemap_keys=(65,), - archipelago_id=ITEM_OFFSET + 26, - classification=ItemClassification.progression, - tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), - ), - ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB: ZorkGrandInquisitorItemData( - statemap_keys=(53,), - archipelago_id=ITEM_OFFSET + 27, + archipelago_id=ITEM_OFFSET + 24, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), - ZorkGrandInquisitorItems.ROPE: ZorkGrandInquisitorItemData( - statemap_keys=(83,), - archipelago_id=ITEM_OFFSET + 28, + ZorkGrandInquisitorItems.SANDWITCH_WRAPPER: ZorkGrandInquisitorItemData( + statemap_keys=(34,), + archipelago_id=ITEM_OFFSET + 25, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS: ZorkGrandInquisitorItemData( statemap_keys=(101,), # SNA = 41 - archipelago_id=ITEM_OFFSET + 29, + archipelago_id=ITEM_OFFSET + 26, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV: ZorkGrandInquisitorItemData( statemap_keys=(102,), # VIG = 48 - archipelago_id=ITEM_OFFSET + 30, + archipelago_id=ITEM_OFFSET + 27, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.SHOVEL: ZorkGrandInquisitorItemData( statemap_keys=(49,), - archipelago_id=ITEM_OFFSET + 31, + archipelago_id=ITEM_OFFSET + 28, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.SNAPDRAGON: ZorkGrandInquisitorItemData( statemap_keys=(50,), - archipelago_id=ITEM_OFFSET + 32, + archipelago_id=ITEM_OFFSET + 29, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.STUDENT_ID: ZorkGrandInquisitorItemData( statemap_keys=(39,), - archipelago_id=ITEM_OFFSET + 33, + archipelago_id=ITEM_OFFSET + 30, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.SUBWAY_TOKEN: ZorkGrandInquisitorItemData( statemap_keys=(20,), - archipelago_id=ITEM_OFFSET + 34, + archipelago_id=ITEM_OFFSET + 31, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.SWORD: ZorkGrandInquisitorItemData( statemap_keys=(21,), - archipelago_id=ITEM_OFFSET + 35, + archipelago_id=ITEM_OFFSET + 32, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), + ), + ZorkGrandInquisitorItems.WELL_ROPE: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 33, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.ZIMDOR_SCROLL: ZorkGrandInquisitorItemData( statemap_keys=(25,), - archipelago_id=ITEM_OFFSET + 36, + archipelago_id=ITEM_OFFSET + 34, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), ZorkGrandInquisitorItems.ZORK_ROCKS: ZorkGrandInquisitorItemData( statemap_keys=(37,), - archipelago_id=ITEM_OFFSET + 37, + archipelago_id=ITEM_OFFSET + 35, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.INVENTORY_ITEM,), ), @@ -270,129 +258,141 @@ class ZorkGrandInquisitorItemData(NamedTuple): classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), + ZorkGrandInquisitorItems.HOTSPOT_BUCKET: ZorkGrandInquisitorItemData( + statemap_keys=(13928,), + archipelago_id=ITEM_OFFSET + 100 + 4, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT,), + ), ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS: ZorkGrandInquisitorItemData( statemap_keys=(12691, 12692, 12693, 12694, 12695, 12696, 12697, 12698, 12699, 12700, 12701), - archipelago_id=ITEM_OFFSET + 100 + 4, + archipelago_id=ITEM_OFFSET + 100 + 5, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(12702,), - archipelago_id=ITEM_OFFSET + 100 + 5, + archipelago_id=ITEM_OFFSET + 100 + 6, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(12909,), - archipelago_id=ITEM_OFFSET + 100 + 6, + archipelago_id=ITEM_OFFSET + 100 + 7, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(12900,), - archipelago_id=ITEM_OFFSET + 100 + 7, + archipelago_id=ITEM_OFFSET + 100 + 8, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR: ZorkGrandInquisitorItemData( statemap_keys=(5010,), - archipelago_id=ITEM_OFFSET + 100 + 8, + archipelago_id=ITEM_OFFSET + 100 + 9, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(9539,), - archipelago_id=ITEM_OFFSET + 100 + 9, + archipelago_id=ITEM_OFFSET + 100 + 10, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER: ZorkGrandInquisitorItemData( statemap_keys=(19712,), - archipelago_id=ITEM_OFFSET + 100 + 10, + archipelago_id=ITEM_OFFSET + 100 + 11, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT: ZorkGrandInquisitorItemData( statemap_keys=(2586,), - archipelago_id=ITEM_OFFSET + 100 + 11, + archipelago_id=ITEM_OFFSET + 100 + 12, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER: ZorkGrandInquisitorItemData( statemap_keys=(11878,), - archipelago_id=ITEM_OFFSET + 100 + 12, + archipelago_id=ITEM_OFFSET + 100 + 13, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND: ZorkGrandInquisitorItemData( statemap_keys=(11751,), - archipelago_id=ITEM_OFFSET + 100 + 13, + archipelago_id=ITEM_OFFSET + 100 + 14, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH: ZorkGrandInquisitorItemData( statemap_keys=(15147, 15153), - archipelago_id=ITEM_OFFSET + 100 + 14, + archipelago_id=ITEM_OFFSET + 100 + 15, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW: ZorkGrandInquisitorItemData( statemap_keys=(1705,), - archipelago_id=ITEM_OFFSET + 100 + 15, + archipelago_id=ITEM_OFFSET + 100 + 16, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS: ZorkGrandInquisitorItemData( statemap_keys=(1425, 1426), - archipelago_id=ITEM_OFFSET + 100 + 16, + archipelago_id=ITEM_OFFSET + 100 + 17, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT,), + ), + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_HOUSE_EXIT: ZorkGrandInquisitorItemData( + statemap_keys=(4791,), + archipelago_id=ITEM_OFFSET + 100 + 18, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE: ZorkGrandInquisitorItemData( statemap_keys=(13106,), - archipelago_id=ITEM_OFFSET + 100 + 17, + archipelago_id=ITEM_OFFSET + 100 + 19, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS: ZorkGrandInquisitorItemData( statemap_keys=(13219, 13220, 13221, 13222), - archipelago_id=ITEM_OFFSET + 100 + 18, + archipelago_id=ITEM_OFFSET + 100 + 20, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS: ZorkGrandInquisitorItemData( statemap_keys=(14327, 14332, 14337, 14342), - archipelago_id=ITEM_OFFSET + 100 + 19, + archipelago_id=ITEM_OFFSET + 100 + 21, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(12528,), - archipelago_id=ITEM_OFFSET + 100 + 20, + archipelago_id=ITEM_OFFSET + 100 + 22, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS: ZorkGrandInquisitorItemData( statemap_keys=(12523, 12524, 12525), - archipelago_id=ITEM_OFFSET + 100 + 21, + archipelago_id=ITEM_OFFSET + 100 + 23, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE: ZorkGrandInquisitorItemData( statemap_keys=(13002,), - archipelago_id=ITEM_OFFSET + 100 + 22, + archipelago_id=ITEM_OFFSET + 100 + 24, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL: ZorkGrandInquisitorItemData( statemap_keys=(10726,), - archipelago_id=ITEM_OFFSET + 100 + 23, + archipelago_id=ITEM_OFFSET + 100 + 25, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR: ZorkGrandInquisitorItemData( statemap_keys=(12280,), - archipelago_id=ITEM_OFFSET + 100 + 24, + archipelago_id=ITEM_OFFSET + 100 + 26, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), @@ -420,292 +420,388 @@ class ZorkGrandInquisitorItemData(NamedTuple): 17726, 17727 ), - archipelago_id=ITEM_OFFSET + 100 + 25, + archipelago_id=ITEM_OFFSET + 100 + 27, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT,), + ), + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS: ZorkGrandInquisitorItemData( + statemap_keys=(11543, 12256, 11720), + archipelago_id=ITEM_OFFSET + 100 + 28, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS: ZorkGrandInquisitorItemData( statemap_keys=(8448, 8449, 8450, 8451, 8452, 8453, 8454, 8455, 8456, 8457, 8458, 8459), - archipelago_id=ITEM_OFFSET + 100 + 26, + archipelago_id=ITEM_OFFSET + 100 + 29, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER: ZorkGrandInquisitorItemData( statemap_keys=(8446,), - archipelago_id=ITEM_OFFSET + 100 + 27, + archipelago_id=ITEM_OFFSET + 100 + 30, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_HARRY: ZorkGrandInquisitorItemData( statemap_keys=(4260,), - archipelago_id=ITEM_OFFSET + 100 + 28, + archipelago_id=ITEM_OFFSET + 100 + 31, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY: ZorkGrandInquisitorItemData( statemap_keys=(18026,), - archipelago_id=ITEM_OFFSET + 100 + 29, + archipelago_id=ITEM_OFFSET + 100 + 32, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH: ZorkGrandInquisitorItemData( statemap_keys=(17623,), - archipelago_id=ITEM_OFFSET + 100 + 30, + archipelago_id=ITEM_OFFSET + 100 + 33, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR: ZorkGrandInquisitorItemData( statemap_keys=(13140,), - archipelago_id=ITEM_OFFSET + 100 + 31, + archipelago_id=ITEM_OFFSET + 100 + 34, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR: ZorkGrandInquisitorItemData( statemap_keys=(10441,), - archipelago_id=ITEM_OFFSET + 100 + 32, + archipelago_id=ITEM_OFFSET + 100 + 35, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS: ZorkGrandInquisitorItemData( statemap_keys=(19632, 19627), - archipelago_id=ITEM_OFFSET + 100 + 33, + archipelago_id=ITEM_OFFSET + 100 + 36, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR: ZorkGrandInquisitorItemData( statemap_keys=(3025,), - archipelago_id=ITEM_OFFSET + 100 + 34, + archipelago_id=ITEM_OFFSET + 100 + 37, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG: ZorkGrandInquisitorItemData( statemap_keys=(3036,), - archipelago_id=ITEM_OFFSET + 100 + 35, + archipelago_id=ITEM_OFFSET + 100 + 38, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_MIRROR: ZorkGrandInquisitorItemData( statemap_keys=(5031,), - archipelago_id=ITEM_OFFSET + 100 + 36, - classification=ItemClassification.progression, - tags=(ZorkGrandInquisitorTags.HOTSPOT,), - ), - ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT: ZorkGrandInquisitorItemData( - statemap_keys=(13597,), - archipelago_id=ITEM_OFFSET + 100 + 37, + archipelago_id=ITEM_OFFSET + 100 + 39, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE: ZorkGrandInquisitorItemData( statemap_keys=(13390,), - archipelago_id=ITEM_OFFSET + 100 + 38, + archipelago_id=ITEM_OFFSET + 100 + 40, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR: ZorkGrandInquisitorItemData( statemap_keys=(2455, 2447), - archipelago_id=ITEM_OFFSET + 100 + 39, + archipelago_id=ITEM_OFFSET + 100 + 41, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS: ZorkGrandInquisitorItemData( statemap_keys=(12389, 12390), - archipelago_id=ITEM_OFFSET + 100 + 40, + archipelago_id=ITEM_OFFSET + 100 + 42, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE: ZorkGrandInquisitorItemData( statemap_keys=(4302,), - archipelago_id=ITEM_OFFSET + 100 + 41, + archipelago_id=ITEM_OFFSET + 100 + 43, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE: ZorkGrandInquisitorItemData( statemap_keys=(16383, 16384), - archipelago_id=ITEM_OFFSET + 100 + 42, + archipelago_id=ITEM_OFFSET + 100 + 44, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE: ZorkGrandInquisitorItemData( - statemap_keys=(2769,), - archipelago_id=ITEM_OFFSET + 100 + 43, + statemap_keys=(2769, 2761, 2764, 2767), + archipelago_id=ITEM_OFFSET + 100 + 45, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON: ZorkGrandInquisitorItemData( statemap_keys=(4149,), - archipelago_id=ITEM_OFFSET + 100 + 44, + archipelago_id=ITEM_OFFSET + 100 + 46, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS: ZorkGrandInquisitorItemData( statemap_keys=(12584, 12585, 12586, 12587), - archipelago_id=ITEM_OFFSET + 100 + 45, + archipelago_id=ITEM_OFFSET + 100 + 47, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(12574,), - archipelago_id=ITEM_OFFSET + 100 + 46, + archipelago_id=ITEM_OFFSET + 100 + 48, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(13412,), - archipelago_id=ITEM_OFFSET + 100 + 47, + archipelago_id=ITEM_OFFSET + 100 + 49, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER: ZorkGrandInquisitorItemData( statemap_keys=(12170,), - archipelago_id=ITEM_OFFSET + 100 + 48, + archipelago_id=ITEM_OFFSET + 100 + 50, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT,), + ), + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT: ZorkGrandInquisitorItemData( + statemap_keys=(12045,), + archipelago_id=ITEM_OFFSET + 100 + 51, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM: ZorkGrandInquisitorItemData( statemap_keys=(16382,), - archipelago_id=ITEM_OFFSET + 100 + 49, + archipelago_id=ITEM_OFFSET + 100 + 52, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM: ZorkGrandInquisitorItemData( statemap_keys=(4209,), - archipelago_id=ITEM_OFFSET + 100 + 50, + archipelago_id=ITEM_OFFSET + 100 + 53, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE: ZorkGrandInquisitorItemData( statemap_keys=(11973,), - archipelago_id=ITEM_OFFSET + 100 + 51, + archipelago_id=ITEM_OFFSET + 100 + 54, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT: ZorkGrandInquisitorItemData( statemap_keys=(13168,), - archipelago_id=ITEM_OFFSET + 100 + 52, + archipelago_id=ITEM_OFFSET + 100 + 55, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY: ZorkGrandInquisitorItemData( statemap_keys=(15396,), - archipelago_id=ITEM_OFFSET + 100 + 53, + archipelago_id=ITEM_OFFSET + 100 + 56, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH: ZorkGrandInquisitorItemData( statemap_keys=(9706,), - archipelago_id=ITEM_OFFSET + 100 + 54, + archipelago_id=ITEM_OFFSET + 100 + 57, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS: ZorkGrandInquisitorItemData( statemap_keys=(9728, 9729, 9730), - archipelago_id=ITEM_OFFSET + 100 + 55, + archipelago_id=ITEM_OFFSET + 100 + 58, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.HOTSPOT,), ), - ZorkGrandInquisitorItems.HOTSPOT_WELL: ZorkGrandInquisitorItemData( - statemap_keys=(10314,), - archipelago_id=ITEM_OFFSET + 100 + 56, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 0, classification=ItemClassification.progression, - tags=(ZorkGrandInquisitorTags.HOTSPOT,), + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 1, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 2, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 3, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 4, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 5, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 6, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 7, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 8, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 9, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 100 + 80 + 10, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL,), ), # Spells + ZorkGrandInquisitorItems.SPELL_BEBURTT: ZorkGrandInquisitorItemData( + statemap_keys=(194,), + archipelago_id=ITEM_OFFSET + 200 + 0, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.SPELL,), + ), ZorkGrandInquisitorItems.SPELL_GLORF: ZorkGrandInquisitorItemData( statemap_keys=(202,), - archipelago_id=ITEM_OFFSET + 200 + 0, + archipelago_id=ITEM_OFFSET + 200 + 1, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), ZorkGrandInquisitorItems.SPELL_GOLGATEM: ZorkGrandInquisitorItemData( statemap_keys=(192,), - archipelago_id=ITEM_OFFSET + 200 + 1, + archipelago_id=ITEM_OFFSET + 200 + 2, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), ZorkGrandInquisitorItems.SPELL_IGRAM: ZorkGrandInquisitorItemData( statemap_keys=(199,), - archipelago_id=ITEM_OFFSET + 200 + 2, + archipelago_id=ITEM_OFFSET + 200 + 3, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), ZorkGrandInquisitorItems.SPELL_KENDALL: ZorkGrandInquisitorItemData( statemap_keys=(196,), - archipelago_id=ITEM_OFFSET + 200 + 3, + archipelago_id=ITEM_OFFSET + 200 + 4, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.SPELL,), + ), + ZorkGrandInquisitorItems.SPELL_OBIDIL: ZorkGrandInquisitorItemData( + statemap_keys=(193,), + archipelago_id=ITEM_OFFSET + 200 + 5, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), ZorkGrandInquisitorItems.SPELL_NARWILE: ZorkGrandInquisitorItemData( statemap_keys=(197,), - archipelago_id=ITEM_OFFSET + 200 + 4, + archipelago_id=ITEM_OFFSET + 200 + 6, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), ZorkGrandInquisitorItems.SPELL_REZROV: ZorkGrandInquisitorItemData( statemap_keys=(195,), - archipelago_id=ITEM_OFFSET + 200 + 5, + archipelago_id=ITEM_OFFSET + 200 + 7, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), - ZorkGrandInquisitorItems.SPELL_THROCK: ZorkGrandInquisitorItemData( + ZorkGrandInquisitorItems.SPELL_SNAVIG: ZorkGrandInquisitorItemData( + statemap_keys=(201,), + archipelago_id=ITEM_OFFSET + 200 + 8, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.SPELL,), + ), + ZorkGrandInquisitorItems.SPELL_THROCK: ZorkGrandInquisitorItemData( statemap_keys=(200,), - archipelago_id=ITEM_OFFSET + 200 + 6, + archipelago_id=ITEM_OFFSET + 200 + 9, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), - ZorkGrandInquisitorItems.SPELL_VOXAM: ZorkGrandInquisitorItemData( - statemap_keys=(191,), - archipelago_id=ITEM_OFFSET + 200 + 7, - classification=ItemClassification.useful, + ZorkGrandInquisitorItems.SPELL_YASTARD: ZorkGrandInquisitorItemData( + statemap_keys=(198,), + archipelago_id=ITEM_OFFSET + 200 + 10, + classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SPELL,), ), # Subway Destinations + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS: ZorkGrandInquisitorItemData( + statemap_keys=(13760, 13323, 13512, 13651), + archipelago_id=ITEM_OFFSET + 300 + 0, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.SUBWAY_DESTINATION,), + ), ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM: ZorkGrandInquisitorItemData( statemap_keys=(13757, 13297, 13486, 13625), - archipelago_id=ITEM_OFFSET + 300 + 0, + archipelago_id=ITEM_OFFSET + 300 + 1, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SUBWAY_DESTINATION,), ), ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES: ZorkGrandInquisitorItemData( statemap_keys=(13758, 13309, 13498, 13637), - archipelago_id=ITEM_OFFSET + 300 + 1, + archipelago_id=ITEM_OFFSET + 300 + 2, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SUBWAY_DESTINATION,), ), ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY: ZorkGrandInquisitorItemData( statemap_keys=(13759, 13316, 13505, 13644), - archipelago_id=ITEM_OFFSET + 300 + 2, + archipelago_id=ITEM_OFFSET + 300 + 3, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.SUBWAY_DESTINATION,), ), - # Teleporter Destinations + # Teleporter Destination + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS: ZorkGrandInquisitorItemData( + statemap_keys=(12918,), + archipelago_id=ITEM_OFFSET + 400 + 0, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,), + ), ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR: ZorkGrandInquisitorItemData( statemap_keys=(2203,), - archipelago_id=ITEM_OFFSET + 400 + 0, + archipelago_id=ITEM_OFFSET + 400 + 1, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,), ), ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH: ZorkGrandInquisitorItemData( statemap_keys=(7132,), - archipelago_id=ITEM_OFFSET + 400 + 1, + archipelago_id=ITEM_OFFSET + 400 + 2, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,), ), ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES: ZorkGrandInquisitorItemData( statemap_keys=(7119,), - archipelago_id=ITEM_OFFSET + 400 + 2, + archipelago_id=ITEM_OFFSET + 400 + 3, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,), ), ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY: ZorkGrandInquisitorItemData( statemap_keys=(7148,), - archipelago_id=ITEM_OFFSET + 400 + 3, + archipelago_id=ITEM_OFFSET + 400 + 4, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,), ), ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB: ZorkGrandInquisitorItemData( statemap_keys=(16545,), - archipelago_id=ITEM_OFFSET + 400 + 4, + archipelago_id=ITEM_OFFSET + 400 + 5, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TELEPORTER_DESTINATION,), ), @@ -719,19 +815,25 @@ class ZorkGrandInquisitorItemData(NamedTuple): ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY: ZorkGrandInquisitorItemData( statemap_keys=(9666,), archipelago_id=ITEM_OFFSET + 500 + 1, - classification=ItemClassification.filler, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,), + ), + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY: ZorkGrandInquisitorItemData( + statemap_keys=(9664,), + archipelago_id=ITEM_OFFSET + 500 + 2, + classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,), ), ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL: ZorkGrandInquisitorItemData( statemap_keys=(9668,), - archipelago_id=ITEM_OFFSET + 500 + 2, + archipelago_id=ITEM_OFFSET + 500 + 3, classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,), ), ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ: ZorkGrandInquisitorItemData( statemap_keys=(9662,), - archipelago_id=ITEM_OFFSET + 500 + 3, - classification=ItemClassification.filler, + archipelago_id=ITEM_OFFSET + 500 + 4, + classification=ItemClassification.progression, tags=(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION,), ), # Totems @@ -754,39 +856,570 @@ class ZorkGrandInquisitorItemData(NamedTuple): tags=(ZorkGrandInquisitorTags.TOTEM,), ), # Filler - ZorkGrandInquisitorItems.FILLER_INQUISITION_PROPAGANDA_FLYER: ZorkGrandInquisitorItemData( + ZorkGrandInquisitorItems.FILLER_AIMFIZ_SCROLL: ZorkGrandInquisitorItemData( statemap_keys=None, archipelago_id=ITEM_OFFSET + 700 + 0, classification=ItemClassification.filler, tags=(ZorkGrandInquisitorTags.FILLER,), maximum_quantity=None, ), - ZorkGrandInquisitorItems.FILLER_UNREADABLE_SPELL_SCROLL: ZorkGrandInquisitorItemData( + ZorkGrandInquisitorItems.FILLER_BAYALA_SCROLL: ZorkGrandInquisitorItemData( statemap_keys=None, archipelago_id=ITEM_OFFSET + 700 + 1, classification=ItemClassification.filler, tags=(ZorkGrandInquisitorTags.FILLER,), maximum_quantity=None, ), - ZorkGrandInquisitorItems.FILLER_MAGIC_CONTRABAND: ZorkGrandInquisitorItemData( + ZorkGrandInquisitorItems.FILLER_BITTYJOO_SCROLL: ZorkGrandInquisitorItemData( statemap_keys=None, archipelago_id=ITEM_OFFSET + 700 + 2, classification=ItemClassification.filler, tags=(ZorkGrandInquisitorTags.FILLER,), maximum_quantity=None, ), - ZorkGrandInquisitorItems.FILLER_FROBOZZ_ELECTRIC_GADGET: ZorkGrandInquisitorItemData( + ZorkGrandInquisitorItems.FILLER_BLORB_SCROLL: ZorkGrandInquisitorItemData( statemap_keys=None, archipelago_id=ITEM_OFFSET + 700 + 3, classification=ItemClassification.filler, tags=(ZorkGrandInquisitorTags.FILLER,), maximum_quantity=None, ), - ZorkGrandInquisitorItems.FILLER_NONSENSICAL_INQUISITION_PAPERWORK: ZorkGrandInquisitorItemData( + ZorkGrandInquisitorItems.FILLER_BLORPLE_SCROLL: ZorkGrandInquisitorItemData( statemap_keys=None, archipelago_id=ITEM_OFFSET + 700 + 4, classification=ItemClassification.filler, tags=(ZorkGrandInquisitorTags.FILLER,), maximum_quantity=None, ), + ZorkGrandInquisitorItems.FILLER_BOOZNIK_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 5, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_BORCH_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 6, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_CASKLY_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 7, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_CLEESH_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 8, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_CONBAK_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 9, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_DABHHU_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 10, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_DRILBO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 11, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_ESPNIS_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 12, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_EXEX_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 13, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_FAIFT_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 14, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_FILFRE_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 15, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_FIZMO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 16, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_FOBLUB_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 17, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_FRIPPLE_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 18, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_FROTZ_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 19, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_FWEEP_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 20, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GASPAR_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 21, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GILCH_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 22, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GIRGOL_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 23, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GHELOOH_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 24, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GIZGUM_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 25, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GLOTH_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 26, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GNUSTO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 27, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GOLMAC_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 28, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GONDAR_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 29, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GORCH_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 30, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_GUNCHO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 31, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_IMALI_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 32, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_IZYUK_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 33, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_JINDAK_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 34, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_KEPMKOMN_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 35, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_KOAASST_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 36, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_KRAK_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 37, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_KREBF_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 38, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_KULCAD_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 39, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_LESOCH_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 40, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_LEXDOM_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 41, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_LIDIBO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 42, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_LISKON_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 43, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_LOBAL_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 44, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_LOKTAR_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 45, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_MALYON_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 46, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_MEEF_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 47, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_MELBOR_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 48, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_MUSDEX_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 49, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_NERZO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 50, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_NIKMO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 51, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_NITFOL_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 52, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_OTSUNG_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 53, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_OZMOO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 54, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_PAXTEN_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 55, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_PULVER_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 56, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_QUELBO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 57, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_STEGAW_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 58, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_SWANZO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 59, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_TINSOT_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 60, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_TOSSIO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 61, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_UMBOZ_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 62, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_VARDIK_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 63, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_VAXUM_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 64, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_VEZZA_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 65, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_YOMIN_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 66, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_YONK_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 67, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_YOZOZZO_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 68, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_ZIMBOR_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 69, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_ZOOKA_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 70, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + ZorkGrandInquisitorItems.FILLER_ZUGTHUG_SCROLL: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 700 + 71, + classification=ItemClassification.filler, + tags=(ZorkGrandInquisitorTags.FILLER,), + maximum_quantity=None, + ), + # Goal Items + ZorkGrandInquisitorItems.COCONUT_OF_QUENDOR: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 800 + 0, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.GOAL_THREE_ARTIFACTS,), + ), + ZorkGrandInquisitorItems.CUBE_OF_FOUNDATION: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 800 + 1, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.GOAL_THREE_ARTIFACTS,), + ), + ZorkGrandInquisitorItems.SKULL_OF_YORUK: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 800 + 2, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.GOAL_THREE_ARTIFACTS,), + ), + ZorkGrandInquisitorItems.ARTIFACT_OF_MAGIC: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 800 + 3, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.GOAL_ARTIFACT_OF_MAGIC_HUNT,), + ), + ZorkGrandInquisitorItems.LANDMARK: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 800 + 4, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.GOAL_ZORK_TOUR,), + ), + ZorkGrandInquisitorItems.DEATH: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 800 + 5, + classification=ItemClassification.progression, + tags=(ZorkGrandInquisitorTags.GOAL_GRIM_JOURNEY,), + ), + # Trap Items + ZorkGrandInquisitorItems.TRAP_INFINITE_CORRIDOR: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 900 + 0, + classification=ItemClassification.trap, + tags=(ZorkGrandInquisitorTags.TRAP,), + ), + ZorkGrandInquisitorItems.TRAP_REVERSE_CONTROLS: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 900 + 1, + classification=ItemClassification.trap, + tags=(ZorkGrandInquisitorTags.TRAP,), + ), + ZorkGrandInquisitorItems.TRAP_TELEPORT: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 900 + 2, + classification=ItemClassification.trap, + tags=(ZorkGrandInquisitorTags.TRAP,), + ), + ZorkGrandInquisitorItems.TRAP_ZVISION: ZorkGrandInquisitorItemData( + statemap_keys=None, + archipelago_id=ITEM_OFFSET + 900 + 3, + classification=ItemClassification.trap, + tags=(ZorkGrandInquisitorTags.TRAP,), + ), } diff --git a/worlds/zork_grand_inquisitor/data/location_data.py b/worlds/zork_grand_inquisitor/data/location_data.py index 8b4e57392de8..34bc99f4f46d 100644 --- a/worlds/zork_grand_inquisitor/data/location_data.py +++ b/worlds/zork_grand_inquisitor/data/location_data.py @@ -16,12 +16,15 @@ class ZorkGrandInquisitorLocationData(NamedTuple): Tuple[str, str], Tuple[int, int], Tuple[int, Tuple[int, ...]], + Tuple[Tuple[int, ...], int], + Tuple[str, Tuple[str, ...]], ], ..., ] ] archipelago_id: Optional[int] region: ZorkGrandInquisitorRegions + description: Optional[str] tags: Optional[Tuple[ZorkGrandInquisitorTags, ...]] = None requirements: Optional[ Tuple[ @@ -53,65 +56,96 @@ class ZorkGrandInquisitorLocationData(NamedTuple): game_state_trigger=(("location", "tr2m"),), archipelago_id=LOCATION_OFFSET + 0, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Read the flyer on the bulletin board inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), ), + ZorkGrandInquisitorLocations.AN_EXCELLENT_POPPING_UTENSIL: ZorkGrandInquisitorLocationData( + game_state_trigger=((2196, 84),), + archipelago_id=LOCATION_OFFSET + 1, + region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, + description="Inspect the dragon tooth as Griff", + tags=(ZorkGrandInquisitorTags.CORE,), + requirements=( + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, + ), + ), ZorkGrandInquisitorLocations.ARREST_THE_VANDAL: ZorkGrandInquisitorLocationData( game_state_trigger=((10789, 1),), - archipelago_id=LOCATION_OFFSET + 1, + archipelago_id=LOCATION_OFFSET + 2, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Hide in the barrel after setting the grand inquisitor doll on fire in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ( + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ZorkGrandInquisitorItems.CIGAR, ), ), ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED: ZorkGrandInquisitorLocationData( game_state_trigger=((11787, 1), (11788, 1), (11789, 1)), - archipelago_id=LOCATION_OFFSET + 2, + archipelago_id=LOCATION_OFFSET + 3, region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="Activate the 3 artifact paintings in the GUE Tech hallways", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.A_BIG_FAT_SASSY_2_HEADED_MONSTER: ZorkGrandInquisitorLocationData( game_state_trigger=((8929, 1),), - archipelago_id=LOCATION_OFFSET + 3, + archipelago_id=LOCATION_OFFSET + 4, region=ZorkGrandInquisitorRegions.HADES, + description="Cast OBIDIL on the two-headed monster in Hades", tags=(ZorkGrandInquisitorTags.CORE,), - requirements=(ZorkGrandInquisitorEvents.KNOWS_OBIDIL,), + requirements=(ZorkGrandInquisitorItems.SPELL_OBIDIL,), ), ZorkGrandInquisitorLocations.A_LETTER_FROM_THE_WHITE_HOUSE: ZorkGrandInquisitorLocationData( game_state_trigger=((9124, 1),), - archipelago_id=LOCATION_OFFSET + 4, + archipelago_id=LOCATION_OFFSET + 5, region=ZorkGrandInquisitorRegions.HADES, + description="Collect the letter you mailed from the White House at the 666 mailbox in Hades", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE, - ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX, + ( + ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES, + ), ), ), ZorkGrandInquisitorLocations.A_SMALLWAY: ZorkGrandInquisitorLocationData( game_state_trigger=((11777, 1),), - archipelago_id=LOCATION_OFFSET + 5, + archipelago_id=LOCATION_OFFSET + 6, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Cast IGRAM on the INFINITE purple word before the infinite hallway inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ( + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ZorkGrandInquisitorItems.SPELL_IGRAM, ), ), ZorkGrandInquisitorLocations.BEAUTIFUL_THATS_PLENTY: ZorkGrandInquisitorLocationData( game_state_trigger=((13278, 1),), - archipelago_id=LOCATION_OFFSET + 6, + archipelago_id=LOCATION_OFFSET + 7, region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + description="Cast THROCK on the mossy grate at the Flood Control Dam #3 station", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE, + ( + ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM, + ), ZorkGrandInquisitorItems.SPELL_THROCK, ), ), ZorkGrandInquisitorLocations.BEBURTT_DEMYSTIFIED: ZorkGrandInquisitorLocationData( game_state_trigger=((16315, 1),), - archipelago_id=LOCATION_OFFSET + 7, + archipelago_id=LOCATION_OFFSET + 8, region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="Cast KENDALL while reading the Better Spell Manufacturing in Under 10 Minutes book inside locker #11 in the GUE Tech hallway", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE, @@ -120,495 +154,691 @@ class ZorkGrandInquisitorLocationData(NamedTuple): ), ZorkGrandInquisitorLocations.BETTER_SPELL_MANUFACTURING_IN_UNDER_10_MINUTES: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "th3x"),), - archipelago_id=LOCATION_OFFSET + 8, + archipelago_id=LOCATION_OFFSET + 9, region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="Read the book inside locker #11 in the GUE Tech hallway", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE,), ), ZorkGrandInquisitorLocations.BOING_BOING_BOING: ZorkGrandInquisitorLocationData( game_state_trigger=((4220, 1),), - archipelago_id=LOCATION_OFFSET + 9, + archipelago_id=LOCATION_OFFSET + 10, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Place the snapdragon on the spring mushroom and hit it with the hammer behind the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.HAMMER, ZorkGrandInquisitorItems.SNAPDRAGON, - ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.BONK: ZorkGrandInquisitorLocationData( game_state_trigger=((19491, 1),), - archipelago_id=LOCATION_OFFSET + 10, + archipelago_id=LOCATION_OFFSET + 11, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Hit the snapdragon with the hammer in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.HAMMER, - ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ( + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.BRAVE_SOULS_WANTED: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "us2g"),), - archipelago_id=LOCATION_OFFSET + 11, + archipelago_id=LOCATION_OFFSET + 12, region=ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + description="Look at the advertisement for old scratch in the Crossroads station", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.BROG_DO_GOOD: ZorkGrandInquisitorLocationData( game_state_trigger=((2644, 1),), - archipelago_id=LOCATION_OFFSET + 12, - region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + archipelago_id=LOCATION_OFFSET + 13, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + description="Throw a boiled grue egg at the stalactites in the White House basement", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.TOTEM_BROG, ZorkGrandInquisitorItems.BROGS_GRUE_EGG, - ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, - ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, + ( + ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ) ), ZorkGrandInquisitorLocations.BROG_EAT_ROCKS: ZorkGrandInquisitorLocationData( game_state_trigger=((2629, 1),), - archipelago_id=LOCATION_OFFSET + 13, - region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + archipelago_id=LOCATION_OFFSET + 14, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + description="Eat a rock in the White House basement", tags=(ZorkGrandInquisitorTags.CORE,), - requirements=( - ZorkGrandInquisitorItems.TOTEM_BROG, - ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, - ) ), ZorkGrandInquisitorLocations.BROG_KNOW_DUMB_THAT_DUMB: ZorkGrandInquisitorLocationData( game_state_trigger=((2650, 1),), - archipelago_id=LOCATION_OFFSET + 14, - region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + archipelago_id=LOCATION_OFFSET + 15, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + description="Throw a grue egg at the stalactites in the White House basement", tags=(ZorkGrandInquisitorTags.CORE,), - requirements=( - ZorkGrandInquisitorItems.TOTEM_BROG, - ZorkGrandInquisitorItems.BROGS_GRUE_EGG, - ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, - ) + requirements=(ZorkGrandInquisitorItems.BROGS_GRUE_EGG,), ), ZorkGrandInquisitorLocations.BROG_MUCH_BETTER_AT_THIS_GAME: ZorkGrandInquisitorLocationData( game_state_trigger=((15715, 1),), - archipelago_id=LOCATION_OFFSET + 15, - region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + archipelago_id=LOCATION_OFFSET + 16, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + description="Smash the skull cage with the plank in the White House basement", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.TOTEM_BROG, ZorkGrandInquisitorItems.BROGS_GRUE_EGG, - ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, - ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, + ( + ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ZorkGrandInquisitorItems.BROGS_PLANK, - ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, + ( + ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ) ), ZorkGrandInquisitorLocations.CASTLE_WATCHING_A_FIELD_GUIDE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "dv1t"),), - archipelago_id=LOCATION_OFFSET + 16, + archipelago_id=LOCATION_OFFSET + 17, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Read the book inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.CAVES_NOTES: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "th3y"),), - archipelago_id=LOCATION_OFFSET + 17, + archipelago_id=LOCATION_OFFSET + 18, region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="Read the book inside locker #11 in the GUE Tech hallway", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE,), ), ZorkGrandInquisitorLocations.CLOSING_THE_TIME_TUNNELS: ZorkGrandInquisitorLocationData( game_state_trigger=((9543, 1),), - archipelago_id=LOCATION_OFFSET + 18, + archipelago_id=LOCATION_OFFSET + 19, region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + description="Activate the display at the Monastery Exhibit", + tags=(ZorkGrandInquisitorTags.CORE,), + ), + ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "cd6k"), (1673, 1), (1660, 1), (1312, 1)), + archipelago_id=LOCATION_OFFSET + 20, + region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, + description="Escape with the Coconut of Quendor in Dragon Archipelago", tags=(ZorkGrandInquisitorTags.CORE,), + requirements=( + ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, + ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, + ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, + ( + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO, + ), + ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, + ), ), ZorkGrandInquisitorLocations.CRISIS_AVERTED: ZorkGrandInquisitorLocationData( game_state_trigger=((11769, 1),), - archipelago_id=LOCATION_OFFSET + 19, + archipelago_id=LOCATION_OFFSET + 21, region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="Stash the activated zork rocks in the dented locker in the GUE Tech hallway", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED, ZorkGrandInquisitorItems.SPELL_IGRAM, - ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, - ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER, + ( + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), ), ZorkGrandInquisitorLocations.CUT_THAT_OUT_YOU_LITTLE_CREEP: ZorkGrandInquisitorLocationData( game_state_trigger=((19350, 1),), - archipelago_id=LOCATION_OFFSET + 20, + archipelago_id=LOCATION_OFFSET + 22, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on the door in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER: ZorkGrandInquisitorLocationData( game_state_trigger=((17632, 1),), - archipelago_id=LOCATION_OFFSET + 21, + archipelago_id=LOCATION_OFFSET + 23, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Cast GOLGATEM on the lake when looking at the walking castle inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_BLINDS, + ( + ZorkGrandInquisitorItems.HOTSPOT_BLINDS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ZorkGrandInquisitorItems.SPELL_GOLGATEM, ), ), - ZorkGrandInquisitorLocations.DESPERATELY_SEEKING_TUTOR: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "tr2q"),), - archipelago_id=LOCATION_OFFSET + 22, - region=ZorkGrandInquisitorRegions.GUE_TECH, + ZorkGrandInquisitorLocations.DINGWHACKER_DELUXE: ZorkGrandInquisitorLocationData( + game_state_trigger=((2417, 1),), + archipelago_id=LOCATION_OFFSET + 24, + region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + description="Activate the pachinko machine in the Past Port Foozle tavern", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.DONT_EVEN_START_WITH_US_SPARKY: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "hp5e"), (8919, 2), (9, 100)), - archipelago_id=LOCATION_OFFSET + 23, + archipelago_id=LOCATION_OFFSET + 25, region=ZorkGrandInquisitorRegions.HADES, + description="Attack the two-headed monster with the sword in Hades", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.SWORD,), ), + ZorkGrandInquisitorLocations.DONT_GO_SPENDING_IT_ALL_IN_ONE_PLACE: ZorkGrandInquisitorLocationData( + game_state_trigger=((4512, 87),), + archipelago_id=LOCATION_OFFSET + 26, + region=ZorkGrandInquisitorRegions.ANYWHERE, + description="Inspect a Zorkmid coin", + tags=(ZorkGrandInquisitorTags.CORE,), + requirements=(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,), + ), ZorkGrandInquisitorLocations.DOOOOOOWN: ZorkGrandInquisitorLocationData( game_state_trigger=((3619, 3600),), - archipelago_id=LOCATION_OFFSET + 24, + archipelago_id=LOCATION_OFFSET + 27, region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description="Lower the mailbox flag as Griff outside the White House", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.TOTEM_GRIFF, - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ), ), ZorkGrandInquisitorLocations.DOWN: ZorkGrandInquisitorLocationData( game_state_trigger=((3619, 5300),), - archipelago_id=LOCATION_OFFSET + 25, + archipelago_id=LOCATION_OFFSET + 28, region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description="Lower the mailbox flag as Lucy outside the White House", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.TOTEM_LUCY, - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ), ), ZorkGrandInquisitorLocations.DRAGON_ARCHIPELAGO_TIME_TUNNEL: ZorkGrandInquisitorLocationData( game_state_trigger=((9216, 1),), - archipelago_id=LOCATION_OFFSET + 26, + archipelago_id=LOCATION_OFFSET + 29, region=ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, + description="Cast NARWILE on the time tunnel beyond the gates in Hades", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.SPELL_NARWILE,), ), ZorkGrandInquisitorLocations.DUNCE_LOCKER: ZorkGrandInquisitorLocationData( game_state_trigger=((11851, 1),), - archipelago_id=LOCATION_OFFSET + 27, + archipelago_id=LOCATION_OFFSET + 30, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Open the locker by purchasing item #11 from the vending machine inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), ), ZorkGrandInquisitorLocations.EGGPLANTS: ZorkGrandInquisitorLocationData( game_state_trigger=((3816, 11000),), - archipelago_id=LOCATION_OFFSET + 28, + archipelago_id=LOCATION_OFFSET + 31, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Activate the eggplants in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.ELSEWHERE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "pc1e"),), - archipelago_id=LOCATION_OFFSET + 29, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + archipelago_id=LOCATION_OFFSET + 32, + region=ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL, + description="Reach the well outside Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE: ZorkGrandInquisitorLocationData( game_state_trigger=((11784, 1),), - archipelago_id=LOCATION_OFFSET + 30, + archipelago_id=LOCATION_OFFSET + 33, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Activate the Belboz painting in the GUE Tech hallway", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), ), ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP: ZorkGrandInquisitorLocationData( game_state_trigger=((13743, 1),), - archipelago_id=LOCATION_OFFSET + 31, + archipelago_id=LOCATION_OFFSET + 34, region=ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + description="Cast KENDALL on the subway map in the Crossroads station", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.SPELL_KENDALL,), ), ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA: ZorkGrandInquisitorLocationData( game_state_trigger=((16368, 1),), - archipelago_id=LOCATION_OFFSET + 32, + archipelago_id=LOCATION_OFFSET + 35, region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Cast IGRAM on the invisible bridge guard at the Spell Lab", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=(ZorkGrandInquisitorItems.SPELL_IGRAM,), ), ZorkGrandInquisitorLocations.FIRE_FIRE: ZorkGrandInquisitorLocationData( - game_state_trigger=((10277, 1),), - archipelago_id=LOCATION_OFFSET + 33, + game_state_trigger=((10277, (1, 2)),), + archipelago_id=LOCATION_OFFSET + 36, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Set the grand inquisitor doll on fire in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ( + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ZorkGrandInquisitorItems.CIGAR, ), ), ZorkGrandInquisitorLocations.FLOOD_CONTROL_DAM_3_THE_NOT_REMOTELY_BORING_TALE: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "ue1h"),), - archipelago_id=LOCATION_OFFSET + 34, + game_state_trigger=((13259, 1),), + archipelago_id=LOCATION_OFFSET + 37, region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + description="Look inside the book at the Flood Control Dam #3 station", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON: ZorkGrandInquisitorLocationData( game_state_trigger=((4222, 1),), - archipelago_id=LOCATION_OFFSET + 35, + archipelago_id=LOCATION_OFFSET + 38, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Cast THROCK on the spring mushroom, place the snapdragon and hit the mushroom with the hammer in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SPELL_THROCK, ZorkGrandInquisitorItems.SNAPDRAGON, ZorkGrandInquisitorItems.HAMMER, - ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.FROBUARY_3_UNDERGROUNDHOG_DAY: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "dw2g"),), - archipelago_id=LOCATION_OFFSET + 36, + archipelago_id=LOCATION_OFFSET + 39, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Read the diary on the bed inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.GETTING_SOME_CHANGE: ZorkGrandInquisitorLocationData( game_state_trigger=((12892, 1),), - archipelago_id=LOCATION_OFFSET + 37, + archipelago_id=LOCATION_OFFSET + 40, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Use the 500 zorkmid bill on the change machine inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorEvents.ZORKMID_BILL_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT, + ( + ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), ), ZorkGrandInquisitorLocations.GO_AWAY: ZorkGrandInquisitorLocationData( game_state_trigger=((10654, 1),), - archipelago_id=LOCATION_OFFSET + 38, + archipelago_id=LOCATION_OFFSET + 41, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on the window in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), - ZorkGrandInquisitorLocations.GUE_TECH_DEANS_LIST: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "tr2k"),), - archipelago_id=LOCATION_OFFSET + 39, - region=ZorkGrandInquisitorRegions.GUE_TECH, + ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "sg6e"), (17103, 1), (15715, 1), (15707, 1)), + archipelago_id=LOCATION_OFFSET + 42, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + description="Collect the Skull of Yoruk in the White House basement", tags=(ZorkGrandInquisitorTags.CORE,), + requirements=( + ZorkGrandInquisitorItems.BROGS_GRUE_EGG, + ( + ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), + ZorkGrandInquisitorItems.BROGS_PLANK, + ( + ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), + ) ), ZorkGrandInquisitorLocations.GUE_TECH_ENTRANCE_EXAM: ZorkGrandInquisitorLocationData( game_state_trigger=((11082, 1), (11307, 1), (11536, 1)), - archipelago_id=LOCATION_OFFSET + 40, - region=ZorkGrandInquisitorRegions.GUE_TECH, - tags=(ZorkGrandInquisitorTags.CORE,), - ), - ZorkGrandInquisitorLocations.GUE_TECH_HEALTH_MEMO: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "tr2j"),), - archipelago_id=LOCATION_OFFSET + 41, - region=ZorkGrandInquisitorRegions.GUE_TECH, - tags=(ZorkGrandInquisitorTags.CORE,), - ), - ZorkGrandInquisitorLocations.GUE_TECH_MAGEMEISTERS: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "tr2n"),), - archipelago_id=LOCATION_OFFSET + 42, - region=ZorkGrandInquisitorRegions.GUE_TECH, + archipelago_id=LOCATION_OFFSET + 43, + region=ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + description="Solve the entrance exam outside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.HAVE_A_HELL_OF_A_DAY: ZorkGrandInquisitorLocationData( game_state_trigger=((8443, 1),), - archipelago_id=LOCATION_OFFSET + 43, + archipelago_id=LOCATION_OFFSET + 44, region=ZorkGrandInquisitorRegions.HADES_SHORE, + description="Answer all the questions correctly on the phone in Hades. Solution: 82895 or KENDALL + *", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, - ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES, + ), ) ), ZorkGrandInquisitorLocations.HELLO_THIS_IS_SHONA_FROM_GURTH_PUBLISHING: ZorkGrandInquisitorLocationData( game_state_trigger=((4698, 1),), - archipelago_id=LOCATION_OFFSET + 44, + archipelago_id=LOCATION_OFFSET + 45, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Listen to the message on the answering machine inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE: ZorkGrandInquisitorLocationData( game_state_trigger=((10421, 1),), - archipelago_id=LOCATION_OFFSET + 45, + archipelago_id=LOCATION_OFFSET + 46, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Activate the dock winch with the plastic six-pack holder on the hook in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH, + ( + ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER, ), ), ZorkGrandInquisitorLocations.HEY_FREE_DIRT: ZorkGrandInquisitorLocationData( game_state_trigger=((11747, 1),), - archipelago_id=LOCATION_OFFSET + 46, + archipelago_id=LOCATION_OFFSET + 47, region=ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, + description="Dig in the dirt pile with the shovel outside of GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND, + ( + ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ZorkGrandInquisitorItems.SHOVEL, ), ), - ZorkGrandInquisitorLocations.HI_MY_NAME_IS_DOUG: ZorkGrandInquisitorLocationData( - game_state_trigger=((4698, 2),), - archipelago_id=LOCATION_OFFSET + 47, - region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + ZorkGrandInquisitorLocations.HMMM_BIG_TOOTHPICK: ZorkGrandInquisitorLocationData( + game_state_trigger=((2194, 69),), + archipelago_id=LOCATION_OFFSET + 48, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + description="Inspect the plank as Brog", tags=(ZorkGrandInquisitorTags.CORE,), + requirements=(ZorkGrandInquisitorItems.BROGS_PLANK,), ), ZorkGrandInquisitorLocations.HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "mt2h"),), - archipelago_id=LOCATION_OFFSET + 48, - region=ZorkGrandInquisitorRegions.MONASTERY, - tags=(ZorkGrandInquisitorTags.CORE,), - ), - ZorkGrandInquisitorLocations.HOLD_ON_FOR_AN_IMPORTANT_MESSAGE: ZorkGrandInquisitorLocationData( - game_state_trigger=((4698, 5),), archipelago_id=LOCATION_OFFSET + 49, - region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Look at the infographic on the wall at the Monastery", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.HOW_TO_HYPNOTIZE_YOURSELF: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "uh1e"),), archipelago_id=LOCATION_OFFSET + 50, - region=ZorkGrandInquisitorRegions.HADES_SHORE, + region=ZorkGrandInquisitorRegions.SUBWAY_HADES, + description="Read the book in the Hades station", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.HOW_TO_WIN_AT_DOUBLE_FANUCCI: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "th3s"),), archipelago_id=LOCATION_OFFSET + 51, region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="Read the book inside locker #8 in the GUE Tech hallway", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE,), ), ZorkGrandInquisitorLocations.IMBUE_BEBURTT: ZorkGrandInquisitorLocationData( - game_state_trigger=((194, 1),), + game_state_trigger=((12166, 1),), archipelago_id=LOCATION_OFFSET + 52, region=ZorkGrandInquisitorRegions.SPELL_LAB, + description="Create a BEBURTT scroll by using the machines in the Spell Lab and running it through the spell checker. Solution: O M R I T", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ( + ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), ), ), ZorkGrandInquisitorLocations.IM_COMPLETELY_NUDE: ZorkGrandInquisitorLocationData( game_state_trigger=((19344, 1),), archipelago_id=LOCATION_OFFSET + 53, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on the door in Port Foozle", + tags=(ZorkGrandInquisitorTags.CORE,), + ), + ZorkGrandInquisitorLocations.INFLATUS_THE_ETERNAL: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "cd4h"),), + archipelago_id=LOCATION_OFFSET + 54, + region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, + description="Visit the skeleton in Dragon Archipelago", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.INTO_THE_FOLIAGE: ZorkGrandInquisitorLocationData( game_state_trigger=((13060, 1),), - archipelago_id=LOCATION_OFFSET + 54, + archipelago_id=LOCATION_OFFSET + 55, region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Use the sword on the foliage blocking the door at the Crossroads", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ( + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), ), ), ZorkGrandInquisitorLocations.INVISIBLE_FLOWERS: ZorkGrandInquisitorLocationData( game_state_trigger=((12967, 1),), - archipelago_id=LOCATION_OFFSET + 55, + archipelago_id=LOCATION_OFFSET + 56, region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Cast IGRAM on the umbrella flowers at the Crossroads", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.SPELL_IGRAM,), ), ZorkGrandInquisitorLocations.IN_CASE_OF_ADVENTURE: ZorkGrandInquisitorLocationData( game_state_trigger=((12931, 1),), - archipelago_id=LOCATION_OFFSET + 56, + archipelago_id=LOCATION_OFFSET + 57, region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Break the glass case with the hammer at the Crossroads", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.HAMMER, - ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE, + ( + ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), ), ), ZorkGrandInquisitorLocations.IN_MAGIC_WE_TRUST: ZorkGrandInquisitorLocationData( game_state_trigger=((13062, 1),), - archipelago_id=LOCATION_OFFSET + 57, + archipelago_id=LOCATION_OFFSET + 58, region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Cast REZROV on the door at the Crossroads", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SPELL_REZROV, - ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), ), ), + ZorkGrandInquisitorLocations.ITS_ALMOST_AS_IF_IT_WERE_INFINITE: ZorkGrandInquisitorLocationData( + game_state_trigger=((11005, 15),), + archipelago_id=LOCATION_OFFSET + 59, + region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Go DEEP in the infinite corridor inside GUE Tech", + tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), + ), ZorkGrandInquisitorLocations.ITS_ONE_OF_THOSE_ADVENTURERS_AGAIN: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "pe3j"),), - archipelago_id=LOCATION_OFFSET + 58, + archipelago_id=LOCATION_OFFSET + 60, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Look at the door in Port Foozle", + tags=(ZorkGrandInquisitorTags.CORE,), + ), + ZorkGrandInquisitorLocations.ITS_PLAYING_A_LITTLE_HARD_TO_GET: ZorkGrandInquisitorLocationData( + game_state_trigger=((3816, 1006),), + archipelago_id=LOCATION_OFFSET + 61, + region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Cast OBIDIL on the snapdragon in the Dungeon Master's Lair", + tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), + requirements=( + ZorkGrandInquisitorItems.SPELL_OBIDIL, + ( + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ), + ), + ZorkGrandInquisitorLocations.IT_DOESNT_APPEAR_TO_BE_FOOLED: ZorkGrandInquisitorLocationData( + game_state_trigger=((3816, 1009),), + archipelago_id=LOCATION_OFFSET + 62, + region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Cast BEBURTT on the snapdragon in the Dungeon Master's Lair", + tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), + requirements=( + ZorkGrandInquisitorItems.SPELL_BEBURTT, + ( + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ), + ), + ZorkGrandInquisitorLocations.I_AM_NOT_IMPRESSED: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", ("hp4f", "hp1g")), (8419, 1)), + archipelago_id=LOCATION_OFFSET + 63, + region=ZorkGrandInquisitorRegions.HADES, + description="Cast SNAVIG on Charon in Hades and interact with him", tags=(ZorkGrandInquisitorTags.CORE,), + requirements=(ZorkGrandInquisitorItems.SPELL_SNAVIG,), ), ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY: ZorkGrandInquisitorLocationData( game_state_trigger=((3816, 1008),), - archipelago_id=LOCATION_OFFSET + 59, + archipelago_id=LOCATION_OFFSET + 64, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Cast THROCK on the snapdragon in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.SPELL_THROCK, - ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ( + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.I_DONT_WANT_NO_TROUBLE: ZorkGrandInquisitorLocationData( game_state_trigger=((10694, 1),), - archipelago_id=LOCATION_OFFSET + 60, + archipelago_id=LOCATION_OFFSET + 65, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on the door in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), - ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE: ZorkGrandInquisitorLocationData( - game_state_trigger=((9637, 1),), - archipelago_id=LOCATION_OFFSET + 61, - region=ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - tags=(ZorkGrandInquisitorTags.CORE,), - requirements=( - ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorEvents.ROPE_GLORFABLE, - ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT, - ), - ), ZorkGrandInquisitorLocations.I_LIKE_YOUR_STYLE: ZorkGrandInquisitorLocationData( game_state_trigger=((16374, 1),), - archipelago_id=LOCATION_OFFSET + 62, + archipelago_id=LOCATION_OFFSET + 66, region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Cast GOLGATEM over the Spell Lab chasm", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, + ( + ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), ZorkGrandInquisitorEvents.DAM_DESTROYED, ZorkGrandInquisitorItems.SPELL_GOLGATEM, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), ), ), ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "tp1e"), (9, 87), (1011, 1)), - archipelago_id=LOCATION_OFFSET + 63, + archipelago_id=LOCATION_OFFSET + 67, region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Try to bribe the invisible bridge guard at the Spell Lab with a zorkmid", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,), ), ZorkGrandInquisitorLocations.LIT_SUNFLOWERS: ZorkGrandInquisitorLocationData( game_state_trigger=((4129, 1),), - archipelago_id=LOCATION_OFFSET + 64, + archipelago_id=LOCATION_OFFSET + 68, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Cast THROCK on the sunflowers in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.SPELL_THROCK,), ), - ZorkGrandInquisitorLocations.MAGIC_FOREVER: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "pc1e"), (10304, 1), (5221, 1)), - archipelago_id=LOCATION_OFFSET + 65, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + ZorkGrandInquisitorLocations.LOOK_AN_ICE_CREAM_BAR: ZorkGrandInquisitorLocationData( + game_state_trigger=((12517, 1),), + archipelago_id=LOCATION_OFFSET + 69, + region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Open the rightmost door of the frozen treat machine inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE, - ZorkGrandInquisitorItems.ROPE, - ZorkGrandInquisitorItems.HOTSPOT_WELL, - ), + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ( + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ) ), ZorkGrandInquisitorLocations.MAILED_IT_TO_HELL: ZorkGrandInquisitorLocationData( game_state_trigger=((2498, (1, 2)),), - archipelago_id=LOCATION_OFFSET + 66, + archipelago_id=LOCATION_OFFSET + 70, region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description="Mail the GLORF scroll with either Griff or Lucy outside the White House", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( (ZorkGrandInquisitorItems.TOTEM_GRIFF, ZorkGrandInquisitorItems.TOTEM_LUCY), - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR, - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ), ), ZorkGrandInquisitorLocations.MAKE_LOVE_NOT_WAR: ZorkGrandInquisitorLocationData( - game_state_trigger=((8623, 21),), - archipelago_id=LOCATION_OFFSET + 67, + game_state_trigger=(((8623, 8734), 21),), + archipelago_id=LOCATION_OFFSET + 71, region=ZorkGrandInquisitorRegions.HADES_SHORE, + description="Attack Charon with the sword in Hades", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorEvents.CHARON_CALLED, @@ -617,632 +847,970 @@ class ZorkGrandInquisitorLocationData(NamedTuple): ), ZorkGrandInquisitorLocations.MEAD_LIGHT: ZorkGrandInquisitorLocationData( game_state_trigger=((10485, 1),), - archipelago_id=LOCATION_OFFSET + 68, + archipelago_id=LOCATION_OFFSET + 72, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on Jack's door with the mead light in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.MEAD_LIGHT, - ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), ), ), + ZorkGrandInquisitorLocations.ME_I_AM_THE_BOSS_OF_YOU: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "px1k"),), + archipelago_id=LOCATION_OFFSET + 73, + region=ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, + description="Look at the sign at the inquisition headquarters outside Port Foozle", + tags=(ZorkGrandInquisitorTags.CORE,), + ), ZorkGrandInquisitorLocations.MIKES_PANTS: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "tr2p"),), - archipelago_id=LOCATION_OFFSET + 69, + archipelago_id=LOCATION_OFFSET + 74, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Read the flyer on the bulletin board inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED: ZorkGrandInquisitorLocationData( game_state_trigger=((4217, 1),), - archipelago_id=LOCATION_OFFSET + 70, + archipelago_id=LOCATION_OFFSET + 75, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Hit the spring mushroom with the hammer behind the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.HAMMER, - ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.NATIONAL_TREASURE: ZorkGrandInquisitorLocationData( game_state_trigger=((14318, 1),), - archipelago_id=LOCATION_OFFSET + 71, + archipelago_id=LOCATION_OFFSET + 76, region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + description="Destroy Flood Control Dam #3 by casting REZROV on the closed floodgate and then closing all floodgates", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SPELL_REZROV, - ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS, - ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM, + ), ), ), ZorkGrandInquisitorLocations.NATURAL_AND_SUPERNATURAL_CREATURES_OF_QUENDOR: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "dv1p"),), - archipelago_id=LOCATION_OFFSET + 72, + archipelago_id=LOCATION_OFFSET + 77, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Read the book inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.NOOOOOOOOOOOOO: ZorkGrandInquisitorLocationData( game_state_trigger=((12706, 1),), - archipelago_id=LOCATION_OFFSET + 73, + archipelago_id=LOCATION_OFFSET + 78, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Open the locker by purchasing item #8 from the vending machine inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), ), ZorkGrandInquisitorLocations.NOTHIN_LIKE_A_GOOD_STOGIE: ZorkGrandInquisitorLocationData( game_state_trigger=((4237, 1),), - archipelago_id=LOCATION_OFFSET + 74, + archipelago_id=LOCATION_OFFSET + 79, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Place the cigar in Harry's ashtray in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ( + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ZorkGrandInquisitorItems.CIGAR, ), ), ZorkGrandInquisitorLocations.NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT: ZorkGrandInquisitorLocationData( game_state_trigger=((8935, 1),), - archipelago_id=LOCATION_OFFSET + 75, + archipelago_id=LOCATION_OFFSET + 80, region=ZorkGrandInquisitorRegions.HADES, + description="Cast SNAVIG on the two-headed monster in Hades", tags=(ZorkGrandInquisitorTags.CORE,), - requirements=(ZorkGrandInquisitorEvents.KNOWS_SNAVIG,), + requirements=(ZorkGrandInquisitorItems.SPELL_SNAVIG,), ), ZorkGrandInquisitorLocations.NO_AUTOGRAPHS: ZorkGrandInquisitorLocationData( game_state_trigger=((10476, 1),), - archipelago_id=LOCATION_OFFSET + 76, + archipelago_id=LOCATION_OFFSET + 81, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on Jack's door in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), - requirements=(ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR,), + requirements=( + ( + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ), ), ZorkGrandInquisitorLocations.NO_BONDAGE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "pe2e"), (10262, 2), (15150, 83)), - archipelago_id=LOCATION_OFFSET + 77, + archipelago_id=LOCATION_OFFSET + 82, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Activate the dock winch with the rope on the hook in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( - ZorkGrandInquisitorItems.ROPE, - ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH, + ZorkGrandInquisitorEvents.ROPE_GLORFABLE, + ( + ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), ), ), + ZorkGrandInquisitorLocations.NO_ONE_RETURNS_FROM_HADES: ZorkGrandInquisitorLocationData( + game_state_trigger=((15204, 1),), + archipelago_id=LOCATION_OFFSET + 83, + region=ZorkGrandInquisitorRegions.HADES, + description="Try to pay the toll to Charon after going beyond the gates in Hades", + tags=(ZorkGrandInquisitorTags.CORE,), + requirements=(ZorkGrandInquisitorEvents.BEYOND_GATES_OF_HELL_ACCESSED,) + ), ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP: ZorkGrandInquisitorLocationData( game_state_trigger=((12164, 1),), - archipelago_id=LOCATION_OFFSET + 78, + archipelago_id=LOCATION_OFFSET + 84, region=ZorkGrandInquisitorRegions.SPELL_LAB, + description="Run the soggy OBIDIL scroll through the spell checker in the Spell Lab", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ZorkGrandInquisitorItems.SANDWITCH_WRAPPER, ), ), ZorkGrandInquisitorLocations.OH_DEAR_GOD_ITS_A_DRAGON: ZorkGrandInquisitorLocationData( game_state_trigger=((1300, 1),), - archipelago_id=LOCATION_OFFSET + 79, + archipelago_id=LOCATION_OFFSET + 85, region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, + description="Plug the dragon's nostrils with the inflatables and use the air pump on them in Dragon Archipelago", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, - ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ( + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO, + ), ), ), ZorkGrandInquisitorLocations.OH_VERY_FUNNY_GUYS: ZorkGrandInquisitorLocationData( game_state_trigger=((2448, 1),), - archipelago_id=LOCATION_OFFSET + 80, + archipelago_id=LOCATION_OFFSET + 86, region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, + description="Knock on the door as Brog in Past Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.TOTEM_BROG, - ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), ), ), ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU: ZorkGrandInquisitorLocationData( game_state_trigger=((4869, 1),), - archipelago_id=LOCATION_OFFSET + 81, + archipelago_id=LOCATION_OFFSET + 87, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Make a cup of cocoa inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.FLATHEADIA_FUDGE, + ZorkGrandInquisitorItems.COCOA_INGREDIENTS, ZorkGrandInquisitorItems.HUNGUS_LARD, - ZorkGrandInquisitorItems.JAR_OF_HOTBUGS, - ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB, - ZorkGrandInquisitorItems.MOSS_OF_MAREILON, - ZorkGrandInquisitorItems.MUG, ), ), ZorkGrandInquisitorLocations.OLD_SCRATCH_WINNER: ZorkGrandInquisitorLocationData( game_state_trigger=((4512, 32),), - archipelago_id=LOCATION_OFFSET + 82, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, # This can be done anywhere if the item requirement is met + archipelago_id=LOCATION_OFFSET + 88, + region=ZorkGrandInquisitorRegions.ANYWHERE, + description="Solve the maze on the old scratch ticket", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.OLD_SCRATCH_CARD,), ), ZorkGrandInquisitorLocations.ONLY_YOU_CAN_PREVENT_FOOZLE_FIRES: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "pe5n"),), - archipelago_id=LOCATION_OFFSET + 83, + archipelago_id=LOCATION_OFFSET + 89, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Look at the poster in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL: ZorkGrandInquisitorLocationData( - game_state_trigger=((8730, 1),), - archipelago_id=LOCATION_OFFSET + 84, + game_state_trigger=((8651, 1),), + archipelago_id=LOCATION_OFFSET + 90, region=ZorkGrandInquisitorRegions.HADES, + description="Cast SNAVIG on Charon and punch out in Hades", tags=(ZorkGrandInquisitorTags.CORE,), - requirements=(ZorkGrandInquisitorEvents.KNOWS_SNAVIG,), + requirements=( + ZorkGrandInquisitorItems.SPELL_SNAVIG, + ZorkGrandInquisitorItems.TOTEM_BROG, # Visually hiding this totem is tied to owning it; no choice + ), ), ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES: ZorkGrandInquisitorLocationData( game_state_trigger=((4241, 1),), - archipelago_id=LOCATION_OFFSET + 85, + archipelago_id=LOCATION_OFFSET + 91, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Plug the hive with the hungus lard, remove it and slice the hive with the sword in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.HUNGUS_LARD, ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ( + ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.PERMASEAL: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "mt1g"),), - archipelago_id=LOCATION_OFFSET + 86, + archipelago_id=LOCATION_OFFSET + 92, region=ZorkGrandInquisitorRegions.MONASTERY, + description="Look at the PermaSeal container in the Monastery", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.PLANETFALL: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "pp1j"),), - archipelago_id=LOCATION_OFFSET + 87, + archipelago_id=LOCATION_OFFSET + 93, region=ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, + description="Look at the computer inside Jack's shop in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.PLEASE_DONT_THROCK_THE_GRASS: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "te1g"),), - archipelago_id=LOCATION_OFFSET + 88, - region=ZorkGrandInquisitorRegions.GUE_TECH, + archipelago_id=LOCATION_OFFSET + 94, + region=ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + description="Look at the sign outside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.PORT_FOOZLE_TIME_TUNNEL: ZorkGrandInquisitorLocationData( game_state_trigger=((9404, 1),), - archipelago_id=LOCATION_OFFSET + 89, + archipelago_id=LOCATION_OFFSET + 95, region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + description="Cast NARWILE on the time tunnel in the Monastery Exhibit", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, - ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER, ZorkGrandInquisitorItems.SPELL_NARWILE, ), ), ZorkGrandInquisitorLocations.PROZORKED: ZorkGrandInquisitorLocationData( game_state_trigger=((4115, 1),), - archipelago_id=LOCATION_OFFSET + 90, + archipelago_id=LOCATION_OFFSET + 96, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Give the snapdragon the prozork tablet in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.PROZORK_TABLET, - ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ( + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), + ZorkGrandInquisitorLocations.PURPLE_BEAST_ALARM_SYSTEM: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "tp1f"),), + archipelago_id=LOCATION_OFFSET + 97, + region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Look at the sign next to the guard at the Spell Lab Bridge", + tags=(ZorkGrandInquisitorTags.CORE,), + ), ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG: ZorkGrandInquisitorLocationData( game_state_trigger=((4512, 98),), - archipelago_id=LOCATION_OFFSET + 91, + archipelago_id=LOCATION_OFFSET + 98, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Combine the mirrored scroll fragments in the mirror room inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS, ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV, - ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ( + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.RESTOCKED_ON_GRUESDAY: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "tr2h"),), - archipelago_id=LOCATION_OFFSET + 92, + archipelago_id=LOCATION_OFFSET + 99, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Read the flyer on the bulletin board inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.RIGHT_HELLO_YES_UH_THIS_IS_SNEFFLE: ZorkGrandInquisitorLocationData( game_state_trigger=((4698, 3),), - archipelago_id=LOCATION_OFFSET + 93, + archipelago_id=LOCATION_OFFSET + 100, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Listen to the message on the answering machine inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.RIGHT_UH_SORRY_ITS_ME_AGAIN_SNEFFLE: ZorkGrandInquisitorLocationData( game_state_trigger=((4698, 4),), - archipelago_id=LOCATION_OFFSET + 94, + archipelago_id=LOCATION_OFFSET + 101, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Listen to the message on the answering machine inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.SNAVIG_REPAIRED: ZorkGrandInquisitorLocationData( - game_state_trigger=((201, 1),), - archipelago_id=LOCATION_OFFSET + 95, + game_state_trigger=((12161, 1),), + archipelago_id=LOCATION_OFFSET + 102, region=ZorkGrandInquisitorRegions.SPELL_LAB, + description="Run the torn SNAVIG scroll through the spell checker in the Spell Lab", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), ), ), ZorkGrandInquisitorLocations.SOUVENIR: ZorkGrandInquisitorLocationData( game_state_trigger=((13408, 1),), - archipelago_id=LOCATION_OFFSET + 96, + archipelago_id=LOCATION_OFFSET + 103, region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + description="Press a zorkmid at the Flood Control Dam #3 station", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ( + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM, + ), ), ), + ZorkGrandInquisitorLocations.SPELL_CHECK_COMPLETE: ZorkGrandInquisitorLocationData( + game_state_trigger=((12168, 1),), + archipelago_id=LOCATION_OFFSET + 104, + region=ZorkGrandInquisitorRegions.SPELL_LAB, + description="Insert a blank scroll sheet into the spell checker in the Spell Lab", + tags=(ZorkGrandInquisitorTags.CORE,), + requirements=( + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ) + ), ZorkGrandInquisitorLocations.STRAIGHT_TO_HELL: ZorkGrandInquisitorLocationData( game_state_trigger=((9719, 1),), - archipelago_id=LOCATION_OFFSET + 97, + archipelago_id=LOCATION_OFFSET + 105, region=ZorkGrandInquisitorRegions.MONASTERY, + description="Set the totemizer destination to Straight to Hell, turn off PermaSeal and totemize yourself in the Monastery", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), ), ), ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER: ZorkGrandInquisitorLocationData( game_state_trigger=((14511, 1), (14524, 5)), - archipelago_id=LOCATION_OFFSET + 98, + archipelago_id=LOCATION_OFFSET + 106, region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + description="Win the game of strip grue, fire, water against Jack inside the tavern in Past Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, - ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, - ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ( + ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), ), ), ZorkGrandInquisitorLocations.SUCKING_ROCKS: ZorkGrandInquisitorLocationData( game_state_trigger=((12859, 1),), - archipelago_id=LOCATION_OFFSET + 99, + archipelago_id=LOCATION_OFFSET + 107, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Place and activate the perma-suck machine while the zork rocks are loose in the vending machine inside GUE Tech", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT, + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), ), ZorkGrandInquisitorLocations.TALK_TO_ME_GRAND_INQUISITOR: ZorkGrandInquisitorLocationData( game_state_trigger=((10299, 1),), - archipelago_id=LOCATION_OFFSET + 100, + archipelago_id=LOCATION_OFFSET + 108, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Activate the grand inquisitor doll in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), - requirements=(ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL,), + requirements=( + ( + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ), ), ZorkGrandInquisitorLocations.TAMING_YOUR_SNAPDRAGON: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "dv1h"),), - archipelago_id=LOCATION_OFFSET + 101, + archipelago_id=LOCATION_OFFSET + 109, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Read the book inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.THAR_SHE_BLOWS: ZorkGrandInquisitorLocationData( game_state_trigger=((1311, 1), (1312, 1)), - archipelago_id=LOCATION_OFFSET + 102, + archipelago_id=LOCATION_OFFSET + 110, region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, + description="Pop the sea captain with the dragon tooth while the inflatables are tied with the rope and the coconut is in the raft inside the dragon's mouth in the Dragon Archipelago", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, - ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ( + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO, + ), ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, ), ), ZorkGrandInquisitorLocations.THATS_A_ROPE: ZorkGrandInquisitorLocationData( game_state_trigger=((10486, 1),), - archipelago_id=LOCATION_OFFSET + 103, + archipelago_id=LOCATION_OFFSET + 111, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on Jack's door with the rope in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( - ZorkGrandInquisitorItems.ROPE, - ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorEvents.ROPE_GLORFABLE, + ( + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), ), ), ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS: ZorkGrandInquisitorLocationData( game_state_trigger=((13805, 1),), - archipelago_id=LOCATION_OFFSET + 104, + archipelago_id=LOCATION_OFFSET + 112, region=ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + description="Randomly press buttons on the complex subway map in the Crossroads station", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), ), ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "tp1e"), (9, 83), (1011, 1)), - archipelago_id=LOCATION_OFFSET + 105, + archipelago_id=LOCATION_OFFSET + 113, region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Use the rope on the invisible bridge guard at the Spell Lab", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=(ZorkGrandInquisitorEvents.ROPE_GLORFABLE,), ), ZorkGrandInquisitorLocations.THATS_THE_SPIRIT: ZorkGrandInquisitorLocationData( game_state_trigger=((10341, 95),), - archipelago_id=LOCATION_OFFSET + 106, + archipelago_id=LOCATION_OFFSET + 114, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Turn up the volume of the loudspeaker in Port Foozle to the maximum level", tags=(ZorkGrandInquisitorTags.CORE,), - requirements=(ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS,), + requirements=( + ( + ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ), ), ZorkGrandInquisitorLocations.THE_ALCHEMICAL_DEBACLE: ZorkGrandInquisitorLocationData( game_state_trigger=((9459, 1),), - archipelago_id=LOCATION_OFFSET + 107, + archipelago_id=LOCATION_OFFSET + 115, region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + description="Activate the display at the Monastery Exhibit", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.THE_ENDLESS_FIRE: ZorkGrandInquisitorLocationData( game_state_trigger=((9473, 1),), - archipelago_id=LOCATION_OFFSET + 108, + archipelago_id=LOCATION_OFFSET + 116, region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + description="Activate the display at the Monastery Exhibit", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.THE_FLATHEADIAN_FUDGE_FIASCO: ZorkGrandInquisitorLocationData( game_state_trigger=((9520, 1),), - archipelago_id=LOCATION_OFFSET + 109, + archipelago_id=LOCATION_OFFSET + 117, region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + description="Activate the display at the Monastery Exhibit", tags=(ZorkGrandInquisitorTags.CORE,), ), + ZorkGrandInquisitorLocations.THE_ONLY_WAY_TO_WIN_IS_NOT_TO_PLAY: ZorkGrandInquisitorLocationData( + game_state_trigger=((16286, 1),), + archipelago_id=LOCATION_OFFSET + 118, + region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="LCast KENDALL while reading How to Win at Double Fanucci inside locker #8 in the GUE Tech hallway", + tags=(ZorkGrandInquisitorTags.CORE,), + requirements=( + ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE, + ZorkGrandInquisitorItems.SPELL_KENDALL, + ), + ), ZorkGrandInquisitorLocations.THE_PERILS_OF_MAGIC: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "me1j"),), - archipelago_id=LOCATION_OFFSET + 110, + archipelago_id=LOCATION_OFFSET + 119, region=ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + description="Look at the sign at the Monastery Exhibit", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.THE_UNDERGROUND_UNDERGROUND: ZorkGrandInquisitorLocationData( game_state_trigger=((13167, 1),), - archipelago_id=LOCATION_OFFSET + 111, + archipelago_id=LOCATION_OFFSET + 120, region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Go through the turnstile after inserting a subway token at the Crossroads station", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SUBWAY_TOKEN, - ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, + ( + ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS, + ), ), ), ZorkGrandInquisitorLocations.THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "cd60"), (1524, 1)), - archipelago_id=LOCATION_OFFSET + 112, + archipelago_id=LOCATION_OFFSET + 121, region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, + description="Visit the Dragon Archipelago as Lucy", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.TOTEM_LUCY,), ), ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED: ZorkGrandInquisitorLocationData( game_state_trigger=((4219, 1),), - archipelago_id=LOCATION_OFFSET + 113, + archipelago_id=LOCATION_OFFSET + 122, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Cast THROCK on the spring mushroom and hit it with the hammer behind the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.HAMMER, ZorkGrandInquisitorItems.SPELL_THROCK, - ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.TIME_TRAVEL_FOR_DUMMIES: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "th3z"),), - archipelago_id=LOCATION_OFFSET + 114, + archipelago_id=LOCATION_OFFSET + 123, region=ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + description="Read the book inside locker #11 in the GUE Tech hallway", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE,), ), ZorkGrandInquisitorLocations.TOTEMIZED_DAILY_BILLBOARD: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "px1h"),), - archipelago_id=LOCATION_OFFSET + 115, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + archipelago_id=LOCATION_OFFSET + 124, + region=ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, + description="Look at the billboard at the inquisition headquarters outside Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.UH_OH_BROG_CANT_SWIM: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "cd60"), (1520, 1)), - archipelago_id=LOCATION_OFFSET + 116, + archipelago_id=LOCATION_OFFSET + 125, region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, + description="Visit the Dragon Archipelago as Brog", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.TOTEM_BROG,), ), + ZorkGrandInquisitorLocations.UM_AH_UM_AH_UM_AH: ZorkGrandInquisitorLocationData( + game_state_trigger=((16997, 4),), + archipelago_id=LOCATION_OFFSET + 126, + region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + description="Read the mind of the bouncer 4 times inside the Past Port Foozle tavern", + tags=(ZorkGrandInquisitorTags.CORE,), + ), ZorkGrandInquisitorLocations.UMBRELLA_FLOWERS: ZorkGrandInquisitorLocationData( game_state_trigger=((12926, 1),), - archipelago_id=LOCATION_OFFSET + 117, + archipelago_id=LOCATION_OFFSET + 127, region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Cast BEBURTT on the umbrella flowers at the Crossroads", tags=(ZorkGrandInquisitorTags.CORE,), - requirements=(ZorkGrandInquisitorEvents.KNOWS_BEBURTT,), + requirements=(ZorkGrandInquisitorItems.SPELL_BEBURTT,), ), ZorkGrandInquisitorLocations.UP: ZorkGrandInquisitorLocationData( game_state_trigger=((3619, 5200),), - archipelago_id=LOCATION_OFFSET + 118, + archipelago_id=LOCATION_OFFSET + 128, region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description="Raise the mailbox flag as Lucy outside the White House", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.TOTEM_LUCY, - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ), ), ZorkGrandInquisitorLocations.USELESS_BUT_FUN: ZorkGrandInquisitorLocationData( game_state_trigger=((14321, 1),), - archipelago_id=LOCATION_OFFSET + 119, + archipelago_id=LOCATION_OFFSET + 129, region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + description="Cast GOLGATEM above Flood Control Dam #3", tags=(ZorkGrandInquisitorTags.CORE,), requirements=(ZorkGrandInquisitorItems.SPELL_GOLGATEM,), ), ZorkGrandInquisitorLocations.UUUUUP: ZorkGrandInquisitorLocationData( game_state_trigger=((3619, 3500),), - archipelago_id=LOCATION_OFFSET + 120, + archipelago_id=LOCATION_OFFSET + 130, region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description="Raise the mailbox flag as Griff outside the White House", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.TOTEM_GRIFF, - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ), ), ZorkGrandInquisitorLocations.VOYAGE_OF_CAPTAIN_ZAHAB: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "uh1h"),), - archipelago_id=LOCATION_OFFSET + 121, - region=ZorkGrandInquisitorRegions.HADES_SHORE, + archipelago_id=LOCATION_OFFSET + 131, + region=ZorkGrandInquisitorRegions.SUBWAY_HADES, + description="Read the book at the Hades station", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO: ZorkGrandInquisitorLocationData( game_state_trigger=((4034, 1),), - archipelago_id=LOCATION_OFFSET + 122, + archipelago_id=LOCATION_OFFSET + 132, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Add the mead light to Harry's bird bath and cast ZIMDOR on it in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR, ZorkGrandInquisitorItems.MEAD_LIGHT, ZorkGrandInquisitorItems.ZIMDOR_SCROLL, - ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH, + ( + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), + ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO_PAST: ZorkGrandInquisitorLocationData( + game_state_trigger=((17006, 5001),), + archipelago_id=LOCATION_OFFSET + 133, + region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + description="Read the mind of the bartender 2 times inside the Past Port Foozle tavern", + tags=(ZorkGrandInquisitorTags.CORE,), + ), ZorkGrandInquisitorLocations.WE_DONT_SERVE_YOUR_KIND_HERE: ZorkGrandInquisitorLocationData( game_state_trigger=((2461, 1),), - archipelago_id=LOCATION_OFFSET + 123, + archipelago_id=LOCATION_OFFSET + 134, region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, + description="Knock on the door as Griff in Past Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.TOTEM_GRIFF, - ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), ), ), ZorkGrandInquisitorLocations.WE_GOT_A_HIGH_ROLLER: ZorkGrandInquisitorLocationData( game_state_trigger=((15472, 1),), - archipelago_id=LOCATION_OFFSET + 124, + archipelago_id=LOCATION_OFFSET + 135, region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + description="Win the game of Alpine's Quandry inside the tavern in Past Port Foozle. Solution: 4+1 (Fly), 1, 2, 3 (or 3, 2)", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, - ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, - ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ( + ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), ), ), ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID: ZorkGrandInquisitorLocationData( game_state_trigger=((10484, 1),), - archipelago_id=LOCATION_OFFSET + 125, + archipelago_id=LOCATION_OFFSET + 136, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on Jack's door with the plastic six-pack holder in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER, - ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), ), ), ZorkGrandInquisitorLocations.WHITE_HOUSE_TIME_TUNNEL: ZorkGrandInquisitorLocationData( game_state_trigger=((4983, 1),), - archipelago_id=LOCATION_OFFSET + 126, + archipelago_id=LOCATION_OFFSET + 137, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Cast NARWILE on the time tunnel inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ZorkGrandInquisitorItems.SPELL_NARWILE, ), ), + ZorkGrandInquisitorLocations.WHOOPS: ZorkGrandInquisitorLocationData( + game_state_trigger=((15959, (1, 2)),), + archipelago_id=LOCATION_OFFSET + 138, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + description="Throw a grue egg at the stalactites off to the side in the White House basement", + tags=(ZorkGrandInquisitorTags.CORE,), + requirements=(ZorkGrandInquisitorItems.BROGS_GRUE_EGG,) + ), ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "dc10"), (1596, 1)), - archipelago_id=LOCATION_OFFSET + 127, + archipelago_id=LOCATION_OFFSET + 139, region=ZorkGrandInquisitorRegions.WALKING_CASTLE, + description="Cast OBIDIL on the walking castle inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "dm2g"),), - archipelago_id=LOCATION_OFFSET + 128, + archipelago_id=LOCATION_OFFSET + 140, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Read the diary on the bed in the mirror room inside the house in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), - requirements=(ZorkGrandInquisitorItems.HOTSPOT_MIRROR,), + requirements=( + ( + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ), ), ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "dg4e"), (4266, 1), (9, 21), (4035, 1)), - archipelago_id=LOCATION_OFFSET + 129, + archipelago_id=LOCATION_OFFSET + 141, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Attack Harry with the sword in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ( + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER: ZorkGrandInquisitorLocationData( game_state_trigger=((16405, 1),), - archipelago_id=LOCATION_OFFSET + 130, + archipelago_id=LOCATION_OFFSET + 142, region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Cast REZROV on the invisible bridge guard at the Spell Lab", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), requirements=(ZorkGrandInquisitorItems.SPELL_REZROV,), ), ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS: ZorkGrandInquisitorLocationData( game_state_trigger=((16342, 1),), - archipelago_id=LOCATION_OFFSET + 131, + archipelago_id=LOCATION_OFFSET + 143, region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Cut the rope bridge with the sword at the Spell Lab", tags=(ZorkGrandInquisitorTags.CORE,), requirements=( ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, + ( + ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ), + ), + ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "qs1e"), (14511, 1), (14524, 5)), + archipelago_id=LOCATION_OFFSET + 144, + region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + description="Win the Cube of Foundation against Jack inside the tavern in Past Port Foozle", + tags=(ZorkGrandInquisitorTags.CORE,), + requirements=( + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, + ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, + ( + ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), ), ), ZorkGrandInquisitorLocations.YOU_ONE_OF_THEM_AGITATORS_AINT_YA: ZorkGrandInquisitorLocationData( game_state_trigger=((10586, 1),), - archipelago_id=LOCATION_OFFSET + 132, + archipelago_id=LOCATION_OFFSET + 145, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Knock on the window in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE,), ), ZorkGrandInquisitorLocations.YOU_WANT_A_PIECE_OF_ME_DOCK_BOY: ZorkGrandInquisitorLocationData( game_state_trigger=((15151, 1),), - archipelago_id=LOCATION_OFFSET + 133, + archipelago_id=LOCATION_OFFSET + 146, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Activate the dock winch in Port Foozle", tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), - requirements=(ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH,), + requirements=( + ( + ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ), + ), + ZorkGrandInquisitorLocations.ZIMDOR_IS_UNDAMAGED: ZorkGrandInquisitorLocationData( + game_state_trigger=((12167, 1),), + archipelago_id=LOCATION_OFFSET + 147, + region=ZorkGrandInquisitorRegions.SPELL_LAB, + description="Insert the ZIMDOR scroll into the spell checker in the Spell Lab", + tags=(ZorkGrandInquisitorTags.CORE, ZorkGrandInquisitorTags.MISSABLE), + requirements=( + ZorkGrandInquisitorItems.ZIMDOR_SCROLL, + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB, + ), + ) ), # Deathsanity ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 1)), archipelago_id=LOCATION_OFFSET + 200 + 0, region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Don't hide in the barrel after setting the grand inquisitor doll on fire in Port Foozle", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=( - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ( + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE, + ), + ZorkGrandInquisitorItems.CIGAR, ), ), ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 20)), archipelago_id=LOCATION_OFFSET + 200 + 1, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Attack the quelbee hive with the sword in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.SWORD, - ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ( + ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.DEATH_CLIMBED_OUT_OF_THE_WELL: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 21)), archipelago_id=LOCATION_OFFSET + 200 + 2, - region=ZorkGrandInquisitorRegions.CROSSROADS, + region=ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, + description="Climb out of the well after entering the Underground", tags=(ZorkGrandInquisitorTags.DEATHSANITY,), + requirements=(ZorkGrandInquisitorItems.WELL_ROPE,), ), ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 18)), archipelago_id=LOCATION_OFFSET + 200 + 3, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, - tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), + region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description="Go down the stairs inside the White House as Griff or Lucy", + tags=(ZorkGrandInquisitorTags.DEATHSANITY,), requirements=( - ZorkGrandInquisitorItems.ROPE, - ZorkGrandInquisitorItems.HOTSPOT_WELL, + ( + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.TOTEM_LUCY, + ), ), ), ZorkGrandInquisitorLocations.DEATH_JUMPED_IN_BOTTOMLESS_PIT: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 3)), archipelago_id=LOCATION_OFFSET + 200 + 4, - region=ZorkGrandInquisitorRegions.GUE_TECH, + region=ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + description="Jump in one of the bottomless pits outside GUE Tech", tags=(ZorkGrandInquisitorTags.DEATHSANITY,), ), ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 37)), archipelago_id=LOCATION_OFFSET + 200 + 5, region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + description="Lose the game of strip grue, fire, water against Jack inside the tavern in Past Port Foozle", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_1, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_2, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_3, ZorkGrandInquisitorItems.LUCYS_PLAYING_CARD_4, - ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, - ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ( + ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST, + ), ), ), ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 23)), archipelago_id=LOCATION_OFFSET + 200 + 6, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + region=ZorkGrandInquisitorRegions.ANYWHERE, + description="Make 3 mistakes while solving the maze on the old scratch ticket", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=(ZorkGrandInquisitorItems.OLD_SCRATCH_CARD,), ), @@ -1250,111 +1818,416 @@ class ZorkGrandInquisitorLocationData(NamedTuple): game_state_trigger=(("location", "gjde"), (2201, 29)), archipelago_id=LOCATION_OFFSET + 200 + 7, region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Plug the hive with the hungus lard, remove it and wait in the Dungeon Master's Lair", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.HUNGUS_LARD, - ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ( + ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), ), ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 30)), archipelago_id=LOCATION_OFFSET + 200 + 8, region=ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + description="Annoy the invisible bridge guard at the Spell Lab until he kills you", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), ), ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 4)), archipelago_id=LOCATION_OFFSET + 200 + 9, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Cast IGRAM on the CORRIDOR purple word before the infinite hallway inside GUE Tech and move forward", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.SPELL_IGRAM, - ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ( + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), ), ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 11)), archipelago_id=LOCATION_OFFSET + 200 + 10, region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, + description="Don't move out of the dragon's mouth after popping an inflatable at the Dragon Archipelago", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=( ZorkGrandInquisitorItems.GRIFFS_AIR_PUMP, ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_RAFT, ZorkGrandInquisitorItems.GRIFFS_INFLATABLE_SEA_CAPTAIN, - ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ( + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO, + ), ZorkGrandInquisitorItems.GRIFFS_DRAGON_TOOTH, ), ), ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 34)), archipelago_id=LOCATION_OFFSET + 200 + 11, - region=ZorkGrandInquisitorRegions.GUE_TECH, + region=ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + description="Cast THROCK on the grass outside GUE Tech", tags=(ZorkGrandInquisitorTags.DEATHSANITY,), requirements=( ZorkGrandInquisitorItems.SPELL_THROCK, - ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS, + ( + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), ), - ZorkGrandInquisitorLocations.DEATH_TOTEMIZED: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "gjde"), (2201, (9, 32, 33))), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_INFINITY: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 9)), archipelago_id=LOCATION_OFFSET + 200 + 12, region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Infinity and with PermalSeal turned off at the Monastery", tags=(ZorkGrandInquisitorTags.DEATHSANITY,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, - ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), ), ), - ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY: ZorkGrandInquisitorLocationData( - game_state_trigger=(("location", "gjde"), (2201, (5, 6, 7, 8, 13))), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_NEWARK_NEW_JERSEY: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 33)), archipelago_id=LOCATION_OFFSET + 200 + 13, region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Newark, New Jersey and with PermalSeal turned off at the Monastery", + tags=(ZorkGrandInquisitorTags.DEATHSANITY,), + requirements=( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_HALLS_OF_INQUISITION: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 8)), + archipelago_id=LOCATION_OFFSET + 200 + 14, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Halls of Inquisition and with PermalSeal turned on at the Monastery.and with PermalSeal turned on at the Monastery", + tags=(ZorkGrandInquisitorTags.DEATHSANITY,), + requirements=( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_INFINITY: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 7)), + archipelago_id=LOCATION_OFFSET + 200 + 15, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Infinity and with PermalSeal turned on at the Monastery", + tags=(ZorkGrandInquisitorTags.DEATHSANITY,), + requirements=( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_NEWARK_NEW_JERSEY: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 6)), + archipelago_id=LOCATION_OFFSET + 200 + 16, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Newark, New Jersey and with PermalSeal turned on at the Monastery", + tags=(ZorkGrandInquisitorTags.DEATHSANITY,), + requirements=( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_STRAIGHT_TO_HELL: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 5)), + archipelago_id=LOCATION_OFFSET + 200 + 17, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Straight to Hell and with PermalSeal turned on at the Monastery", + tags=(ZorkGrandInquisitorTags.DEATHSANITY,), + requirements=( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_SURFACE_OF_MERZ: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 13)), + archipelago_id=LOCATION_OFFSET + 200 + 18, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Surface of Merz and with PermalSeal turned on at the Monastery", + tags=(ZorkGrandInquisitorTags.DEATHSANITY,), + requirements=( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), + ), + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_SURFACE_OF_MERZ: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "gjde"), (2201, 32)), + archipelago_id=LOCATION_OFFSET + 200 + 19, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Totemize yourself with the destination set to Surface of Merz and with PermalSeal turned off at the Monastery", tags=(ZorkGrandInquisitorTags.DEATHSANITY,), - requirements=(ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH,), + requirements=( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ, + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY, + ), + ), ), ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 10)), - archipelago_id=LOCATION_OFFSET + 200 + 14, + archipelago_id=LOCATION_OFFSET + 200 + 20, region=ZorkGrandInquisitorRegions.HADES, + description="Punch out with the wrong card after casting SNAVIG on Charon in Hades", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), - requirements=(ZorkGrandInquisitorEvents.KNOWS_SNAVIG,), + requirements=(ZorkGrandInquisitorItems.SPELL_SNAVIG,), ), ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED: ZorkGrandInquisitorLocationData( game_state_trigger=(("location", "gjde"), (2201, 19)), - archipelago_id=LOCATION_OFFSET + 200 + 15, + archipelago_id=LOCATION_OFFSET + 200 + 21, region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Hold on to the activated zork rocks until they explode inside GUE Tech", tags=(ZorkGrandInquisitorTags.DEATHSANITY, ZorkGrandInquisitorTags.MISSABLE), requirements=(ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED,), ), - # Events - ZorkGrandInquisitorEvents.CHARON_CALLED: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, + # Landmarksanity + ZorkGrandInquisitorLocations.LANDMARK_DRAGON_ARCHIPELAGO: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "cd60"),), + archipelago_id=LOCATION_OFFSET + 300 + 0, + region=ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, + description="Arrive in Dragon Archipelago", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + requirements=( + ( + ZorkGrandInquisitorItems.TOTEM_BROG, + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.TOTEM_LUCY, + ), + ), + ), + ZorkGrandInquisitorLocations.LANDMARK_DUNGEON_MASTERS_HOUSE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "dg40"),), + archipelago_id=LOCATION_OFFSET + 300 + 1, + region=ZorkGrandInquisitorRegions.DM_LAIR, + description="Stand in front of the house in the Dungeon Master's Lair", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_FLOOD_CONTROL_DAM_3: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "ue1e"),), + archipelago_id=LOCATION_OFFSET + 300 + 2, + region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + description="Look at the dam at the Flood Control Dam #3 station", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_GATES_OF_HELL: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "hp50"),), + archipelago_id=LOCATION_OFFSET + 300 + 3, + region=ZorkGrandInquisitorRegions.HADES, + description="Stand in front of the gates in Hades", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_GREAT_UNDERGROUND_EMPIRE_ENTRANCE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "uw10"),), + archipelago_id=LOCATION_OFFSET + 300 + 4, + region=ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, + description="Stand in front of the entrance to the Great Underground at the bottom of the well", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_GUE_TECH_FOUNTAIN_INSIDE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "tr10"),), + archipelago_id=LOCATION_OFFSET + 300 + 5, + region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Stand in front of the fountain inside GUE Tech", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_GUE_TECH_FOUNTAIN_OUTSIDE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "te50"),), + archipelago_id=LOCATION_OFFSET + 300 + 6, + region=ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, + description="Stand in front of the fountain outside GUE Tech", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_HADES_SHORE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "hp10"),), + archipelago_id=LOCATION_OFFSET + 300 + 7, region=ZorkGrandInquisitorRegions.HADES_SHORE, + description="Arrive at the shore in Hades", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_INFINITE_CORRIDOR: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "th10"),), + archipelago_id=LOCATION_OFFSET + 300 + 8, + region=ZorkGrandInquisitorRegions.GUE_TECH, + description="Step in the infinite corridor inside GUE Tech", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_INQUISITION_HEADQUARTERS: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "px10"),), + archipelago_id=LOCATION_OFFSET + 300 + 9, + region=ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, + description="Stand outside the inquisition headquarters on the outskirts of Port Foozle", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_JACKS_SHOP: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "pp10"),), + archipelago_id=LOCATION_OFFSET + 300 + 10, + region=ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, + description="Enter Jack's Shop in Port Foozle", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_MIRROR_ROOM: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "dm10"),), + archipelago_id=LOCATION_OFFSET + 300 + 11, + region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description="Enter the mirror room inside the house in the Dungeon Master's Lair", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), requirements=( - ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, - ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), - event_item_name=ZorkGrandInquisitorEvents.CHARON_CALLED.value, ), - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE: ZorkGrandInquisitorLocationData( + ZorkGrandInquisitorLocations.LANDMARK_PAST_PORT_FOOZLE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "qe10"),), + archipelago_id=LOCATION_OFFSET + 300 + 12, + region=ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, + description="Arrive in Past Port Foozle", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + requirements=( + ( + ZorkGrandInquisitorItems.TOTEM_BROG, + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.TOTEM_LUCY, + ), + ), + ), + ZorkGrandInquisitorLocations.LANDMARK_PORT_FOOZLE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "pe10"),), + archipelago_id=LOCATION_OFFSET + 300 + 13, + region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + description="Arrive in Port Foozle", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_SPELL_CHECKER: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "tp40"),), + archipelago_id=LOCATION_OFFSET + 300 + 14, + region=ZorkGrandInquisitorRegions.SPELL_LAB, + description="Stand in front of the Spell Checker in the Spell Lab", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_TOTEMIZER: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "mt10"),), + archipelago_id=LOCATION_OFFSET + 300 + 15, + region=ZorkGrandInquisitorRegions.MONASTERY, + description="Stand in front of the Totemizer in the Monastery", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_UMBRELLA_TREE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "uc1e"),), + archipelago_id=LOCATION_OFFSET + 300 + 16, + region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Stand in front of the umbrella tree at the Crossroads", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_UNDERGROUND_UNDERGROUND_ENTRANCE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "uc60"),), + archipelago_id=LOCATION_OFFSET + 300 + 17, + region=ZorkGrandInquisitorRegions.CROSSROADS, + description="Stand in front of the subway entrance at the Crossroads", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_WALKING_CASTLES_HEART: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "dc1h"), (1596, 1)), + archipelago_id=LOCATION_OFFSET + 300 + 18, + region=ZorkGrandInquisitorRegions.WALKING_CASTLE, + description="Look at the heart of the walking castle inside the house in the Dungeon Master's Lair", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + ), + ZorkGrandInquisitorLocations.LANDMARK_WHITE_HOUSE: ZorkGrandInquisitorLocationData( + game_state_trigger=(("location", "sw40"),), + archipelago_id=LOCATION_OFFSET + 300 + 19, + region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description="Arrive at the White House", + tags=(ZorkGrandInquisitorTags.LANDMARKSANITY,), + requirements=( + ( + ZorkGrandInquisitorItems.TOTEM_BROG, + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.TOTEM_LUCY, + ), + ), + ), + # Events + ZorkGrandInquisitorEvents.BEYOND_GATES_OF_HELL_ACCESSED: ZorkGrandInquisitorLocationData( game_state_trigger=None, archipelago_id=None, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + region=ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, + description=None, + event_item_name=ZorkGrandInquisitorEvents.BEYOND_GATES_OF_HELL_ACCESSED.value, + ), + ZorkGrandInquisitorEvents.CHARON_CALLED: ZorkGrandInquisitorLocationData( + game_state_trigger=None, + archipelago_id=None, + region=ZorkGrandInquisitorRegions.HADES_SHORE, + description=None, requirements=( - ZorkGrandInquisitorItems.LANTERN, - ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES, + ), ), - event_item_name=ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE.value, + event_item_name=ZorkGrandInquisitorEvents.CHARON_CALLED.value, ), ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE: ZorkGrandInquisitorLocationData( game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.GUE_TECH, + description=None, requirements=( ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), event_item_name=ZorkGrandInquisitorEvents.DALBOZ_LOCKER_OPENABLE.value, ), @@ -1362,10 +2235,17 @@ class ZorkGrandInquisitorLocationData(NamedTuple): game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + description=None, requirements=( ZorkGrandInquisitorItems.SPELL_REZROV, - ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS, - ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM, + ), ), event_item_name=ZorkGrandInquisitorEvents.DAM_DESTROYED.value, ), @@ -1373,11 +2253,15 @@ class ZorkGrandInquisitorLocationData(NamedTuple): game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.DM_LAIR, + description=None, requirements=( ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR, ZorkGrandInquisitorItems.MEAD_LIGHT, ZorkGrandInquisitorItems.ZIMDOR_SCROLL, - ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH, + ( + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), event_item_name=ZorkGrandInquisitorEvents.DOOR_DRANK_MEAD.value, ), @@ -1385,9 +2269,13 @@ class ZorkGrandInquisitorLocationData(NamedTuple): game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.DM_LAIR, + description=None, requirements=( - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ( + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), + ZorkGrandInquisitorItems.CIGAR, ), event_item_name=ZorkGrandInquisitorEvents.DOOR_SMOKED_CIGAR.value, ), @@ -1395,117 +2283,76 @@ class ZorkGrandInquisitorLocationData(NamedTuple): game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.GUE_TECH, + description=None, requirements=( ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), event_item_name=ZorkGrandInquisitorEvents.DUNCE_LOCKER_OPENABLE.value, ), - ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, - region=ZorkGrandInquisitorRegions.GUE_TECH, - requirements=( - ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT, - ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS, - ), - event_item_name=ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL.value, - ), ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG: ZorkGrandInquisitorLocationData( game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + description=None, requirements=( ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS, ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV, - ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ( + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR, + ), ), event_item_name=ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG.value, ), - ZorkGrandInquisitorEvents.KNOWS_BEBURTT: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, - region=ZorkGrandInquisitorRegions.SPELL_LAB, - requirements=( - ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, - ), - event_item_name=ZorkGrandInquisitorEvents.KNOWS_BEBURTT.value, - ), - ZorkGrandInquisitorEvents.KNOWS_OBIDIL: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, - region=ZorkGrandInquisitorRegions.SPELL_LAB, - requirements=( - ZorkGrandInquisitorEvents.HAS_REPAIRABLE_OBIDIL, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, - ), - event_item_name=ZorkGrandInquisitorEvents.KNOWS_OBIDIL.value, - ), - ZorkGrandInquisitorEvents.KNOWS_SNAVIG: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, - region=ZorkGrandInquisitorRegions.SPELL_LAB, - requirements=( - ZorkGrandInquisitorEvents.HAS_REPAIRABLE_SNAVIG, - ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, - ), - event_item_name=ZorkGrandInquisitorEvents.KNOWS_SNAVIG.value, - ), - ZorkGrandInquisitorEvents.KNOWS_YASTARD: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, - region=ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, - requirements=( - ZorkGrandInquisitorItems.FLATHEADIA_FUDGE, - ZorkGrandInquisitorItems.HUNGUS_LARD, - ZorkGrandInquisitorItems.JAR_OF_HOTBUGS, - ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB, - ZorkGrandInquisitorItems.MOSS_OF_MAREILON, - ZorkGrandInquisitorItems.MUG, - ), - event_item_name=ZorkGrandInquisitorEvents.KNOWS_YASTARD.value, - ), - ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE: ZorkGrandInquisitorLocationData( + ZorkGrandInquisitorEvents.ROPE_GLORFABLE: ZorkGrandInquisitorLocationData( game_state_trigger=None, archipelago_id=None, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + region=ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, + description=None, requirements=( - ZorkGrandInquisitorEvents.CIGAR_ACCESSIBLE, - ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.WELL_ROPE, + ZorkGrandInquisitorItems.SPELL_GLORF, ), - event_item_name=ZorkGrandInquisitorEvents.LANTERN_DALBOZ_ACCESSIBLE.value, - ), - ZorkGrandInquisitorEvents.ROPE_GLORFABLE: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, - region=ZorkGrandInquisitorRegions.CROSSROADS, - requirements=(ZorkGrandInquisitorItems.SPELL_GLORF,), event_item_name=ZorkGrandInquisitorEvents.ROPE_GLORFABLE.value, ), ZorkGrandInquisitorEvents.VICTORY: ZorkGrandInquisitorLocationData( game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.ENDGAME, + description=None, event_item_name=ZorkGrandInquisitorEvents.VICTORY.value, ), ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE: ZorkGrandInquisitorLocationData( game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.WHITE_HOUSE, + description=None, requirements=( (ZorkGrandInquisitorItems.TOTEM_GRIFF, ZorkGrandInquisitorItems.TOTEM_LUCY), - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, - ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR, + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE, + ), ), event_item_name=ZorkGrandInquisitorEvents.WHITE_HOUSE_LETTER_MAILABLE.value, ), ZorkGrandInquisitorEvents.ZORKMID_BILL_ACCESSIBLE: ZorkGrandInquisitorLocationData( game_state_trigger=None, archipelago_id=None, - region=ZorkGrandInquisitorRegions.PORT_FOOZLE, + region=ZorkGrandInquisitorRegions.ANYWHERE, + description=None, requirements=(ZorkGrandInquisitorItems.OLD_SCRATCH_CARD,), event_item_name=ZorkGrandInquisitorEvents.ZORKMID_BILL_ACCESSIBLE.value, ), @@ -1513,23 +2360,19 @@ class ZorkGrandInquisitorLocationData(NamedTuple): game_state_trigger=None, archipelago_id=None, region=ZorkGrandInquisitorRegions.GUE_TECH, + description=None, requirements=( ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT, + ( + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ZorkGrandInquisitorItems.ZORK_ROCKS, - ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS, + ( + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH, + ), ), event_item_name=ZorkGrandInquisitorEvents.ZORK_ROCKS_ACTIVATED.value, ), - ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE: ZorkGrandInquisitorLocationData( - game_state_trigger=None, - archipelago_id=None, - region=ZorkGrandInquisitorRegions.GUE_TECH, - requirements=( - ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, - ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, - ), - event_item_name=ZorkGrandInquisitorEvents.ZORK_ROCKS_SUCKABLE.value, - ), } diff --git a/worlds/zork_grand_inquisitor/data/mapping_data.py b/worlds/zork_grand_inquisitor/data/mapping_data.py new file mode 100644 index 000000000000..bec97f7ff3a0 --- /dev/null +++ b/worlds/zork_grand_inquisitor/data/mapping_data.py @@ -0,0 +1,1260 @@ +from typing import Dict, Optional, Tuple, Union + +from ..enums import ( + ZorkGrandInquisitorClientSeedInformation, + ZorkGrandInquisitorCraftableSpellBehaviors, + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorEntranceRandomizer, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorHotspots, + ZorkGrandInquisitorItems, + ZorkGrandInquisitorLandmarksanity, + ZorkGrandInquisitorRegions, + ZorkGrandInquisitorStartingLocations, +) + + +death_cause_labels: Dict[int, str] = { + 1: "PLAYER got their noggin smitten in twain", + 3: "PLAYER decided to jump into a bottomless pit", + 4: "PLAYER decided to step into the infinite", + 5: "PLAYER became an evil spawn's plaything", + 6: "PLAYER started a career as a talking manhole cover", + 7: "PLAYER got sucked into a starship's tractor beam", + 8: "PLAYER became a paperweight", + 9: "PLAYER rolled into the airless expanse of the cosmos", + 10: "PLAYER got their head bitten off", + 11: "PLAYER was swallowed whole by a dragon", + 13: "PLAYER decided to spend an eternity staring at scenic vistas", + 18: "PLAYER was eaten by a grue", + 19: "PLAYER was vaporized by Zork Rocks", + 20: "PLAYER got stung by a thousand quelbees", + 21: "PLAYER broke curfew", + 22: "PLAYER lost their soul to a scratch-and-win card", + 29: "PLAYER was outsmarted by bees", + 30: "PLAYER got pureed by a six-armed invisible guard", + 32: "PLAYER's head exploded", + 33: "PLAYER died of arteriosclerosis", + 34: "PLAYER decided to ignore the sign and THROCK the grass", + 37: "PLAYER lost a game of strip grue, fire, water", +} + +# Avoid spells in early items to prevent clash with craftable spells +early_items_for_starting_location: Dict[ + ZorkGrandInquisitorStartingLocations, Optional[Tuple[Tuple[ZorkGrandInquisitorItems, ...], ...]] +] = { + ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: ( + ( + ZorkGrandInquisitorItems.WELL_ROPE, + ), + ), + ZorkGrandInquisitorStartingLocations.CROSSROADS: None, + ZorkGrandInquisitorStartingLocations.DM_LAIR: None, + ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: None, + ZorkGrandInquisitorStartingLocations.GUE_TECH: None, + ZorkGrandInquisitorStartingLocations.SPELL_LAB: None, + ZorkGrandInquisitorStartingLocations.HADES_SHORE: None, + ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: None, + ZorkGrandInquisitorStartingLocations.MONASTERY: None, + ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: None, +} + +endgame_connecting_regions_for_goal: Dict[ + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorRegions, +] = { + ZorkGrandInquisitorGoals.THREE_ARTIFACTS: ZorkGrandInquisitorRegions.MENU, + ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: ZorkGrandInquisitorRegions.WALKING_CASTLE, + ZorkGrandInquisitorGoals.SPELL_HEIST: ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorGoals.ZORK_TOUR: ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorGoals.GRIM_JOURNEY: ( + ZorkGrandInquisitorRegions.HADES_BEYOND_GATES + ), +} + +entrance_names: Dict[ + Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions], + str, +] = { + ( + ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, + ZorkGrandInquisitorRegions.CROSSROADS + ): "Down the Dragon Staircase", + ( + ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL + ): "Teleport Up the Well", + ( + ZorkGrandInquisitorRegions.CROSSROADS, + ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL + ): "Up the Dragon Staircase", + ( + ZorkGrandInquisitorRegions.CROSSROADS, + ZorkGrandInquisitorRegions.DM_LAIR + ): "Through the Overgrown Door", + ( + ZorkGrandInquisitorRegions.CROSSROADS, + ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE + ): "Through the 'In Magic We Trust' Door", + ( + ZorkGrandInquisitorRegions.CROSSROADS, + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS + ): "Through the Subway Turnstile", + ( + ZorkGrandInquisitorRegions.CROSSROADS, + ZorkGrandInquisitorRegions.TELEPORTER + ): "At the Crossroads Teleportation Station", + ( + ZorkGrandInquisitorRegions.DM_LAIR, + ZorkGrandInquisitorRegions.CROSSROADS + ): "Exit Through the Overgrown Door", + ( + ZorkGrandInquisitorRegions.DM_LAIR, + ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR + ): "Through Sloshed Harry", + ( + ZorkGrandInquisitorRegions.DM_LAIR, + ZorkGrandInquisitorRegions.TELEPORTER + ): "At the Dungeon Master's Lair Teleportation Station", + ( + ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + ZorkGrandInquisitorRegions.DM_LAIR + ): "Exit Through Sloshed Harry", + ( + ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + ZorkGrandInquisitorRegions.WALKING_CASTLE + ): "Through the Walking Castle Porticullis", + ( + ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + ZorkGrandInquisitorRegions.WHITE_HOUSE + ): "Through the White House Time Tunnel", + ( + ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, + ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON + ): "Towards the Dragon", + ( + ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, + ZorkGrandInquisitorRegions.HADES_BEYOND_GATES + ): "Exit Through the Dragon Archipelago Time Tunnel", + ( + ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, + ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO + ): "Towards the Dragon Archipelago Time Tunnel", + ( + ZorkGrandInquisitorRegions.GUE_TECH, + ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE + ): "Through the Right Window", + ( + ZorkGrandInquisitorRegions.GUE_TECH, + ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY + ): "To the (Infinite) Corridor", + ( + ZorkGrandInquisitorRegions.GUE_TECH, + ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE + ): "Through the Entrance Door", + ( + ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + ZorkGrandInquisitorRegions.CROSSROADS + ): "Exit Through the 'In Magic We Trust' Door", + ( + ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + ZorkGrandInquisitorRegions.GUE_TECH + ): "Through the 3rd Pillar's Window", + ( + ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + ZorkGrandInquisitorRegions.GUE_TECH + ): "To the Lobby", + ( + ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE + ): "Through the Student ID Door", + ( + ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, + ZorkGrandInquisitorRegions.GUE_TECH + ): "Through the Pillar's Window", + ( + ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, + ZorkGrandInquisitorRegions.TELEPORTER + ): "At the GUE Tech Teleportation Station", + ( + ZorkGrandInquisitorRegions.HADES, + ZorkGrandInquisitorRegions.HADES_BEYOND_GATES + ): "Through the Gates of Hell", + ( + ZorkGrandInquisitorRegions.HADES, + ZorkGrandInquisitorRegions.HADES_SHORE + ): "Return Across the River Styx", + ( + ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, + ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO + ): "Through the Dragon Archipelago Time Tunnel", + ( + ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, + ZorkGrandInquisitorRegions.HADES + ): "Exit Through the Gates of Hell", + ( + ZorkGrandInquisitorRegions.HADES_SHORE, + ZorkGrandInquisitorRegions.HADES + ): "Across the River Styx", + ( + ZorkGrandInquisitorRegions.HADES_SHORE, + ZorkGrandInquisitorRegions.SUBWAY_HADES + ): "Exit to the Subway Station", + ( + ZorkGrandInquisitorRegions.HADES_SHORE, + ZorkGrandInquisitorRegions.TELEPORTER + ): "At the Hades Teleportation Station", + ( + ZorkGrandInquisitorRegions.MONASTERY, + ZorkGrandInquisitorRegions.HADES_SHORE + ): "Through the Totemizer Set to 'Straight to Hell'", + ( + ZorkGrandInquisitorRegions.MONASTERY, + ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT + ): "Through the Totemizer Set to 'Hall of Inquisition'", + ( + ZorkGrandInquisitorRegions.MONASTERY, + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY + ): "Down the Hatch", + ( + ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + ZorkGrandInquisitorRegions.MONASTERY + ): "To the Totemizer", + ( + ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST + ): "Through the Past Port Foozle Time Tunnel", + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST + ): "Along the Path Down the Hill", + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ + ): "Along the Path Up the Hill", + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL + ): "Along the Path Into the Woods", + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorRegions.PORT_FOOZLE + ): "Along the Path Towards Port Foozle", + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL, + ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL + ): "Down the Well", + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST + ): "Along the Path Out of the Woods", + ( + ZorkGrandInquisitorRegions.PORT_FOOZLE, + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST + ): "Along the Path Out of Port Foozle", + ( + ZorkGrandInquisitorRegions.PORT_FOOZLE, + ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP + ): "Through Jack's Door", + ( + ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, + ZorkGrandInquisitorRegions.PORT_FOOZLE + ): "Exit Through Jack's Door", + ( + ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, + ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT + ): "Exit Through the Past Port Foozle Time Tunnel", + ( + ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, + ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN + ): "Through the Tavern Door", + ( + ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST + ): "Exit Through the Tavern Door", + ( + ZorkGrandInquisitorRegions.SPELL_LAB, + ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE + ): "Return Across the Bridge", + ( + ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY + ): "Exit through the Student ID Door", + ( + ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + ZorkGrandInquisitorRegions.SPELL_LAB + ): "Across the Bridge", + ( + ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + ZorkGrandInquisitorRegions.TELEPORTER + ): "At the Spell Lab Teleportation Station", + ( + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + ZorkGrandInquisitorRegions.CROSSROADS + ): "Up the Escalator", + ( + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM + ): "Subway Ride from Crossroads to FCD #3", + ( + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + ZorkGrandInquisitorRegions.SUBWAY_HADES + ): "Subway Ride from Crossroads to Hades", + ( + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY + ): "Subway Ride from Crossroads to Monastery", + ( + ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS + ): "Subway Ride from FCD #3 to Crossroads", + ( + ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + ZorkGrandInquisitorRegions.SUBWAY_HADES + ): "Subway Ride from FCD #3 to Hades", + ( + ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY + ): "Subway Ride from FCD #3 to Monastery", + ( + ZorkGrandInquisitorRegions.SUBWAY_HADES, + ZorkGrandInquisitorRegions.HADES_SHORE + ): "Towards the Hades Shore", + ( + ZorkGrandInquisitorRegions.SUBWAY_HADES, + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS + ): "Subway Ride from Hades to Crossroads", + ( + ZorkGrandInquisitorRegions.SUBWAY_HADES, + ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM + ): "Subway Ride from Hades to FCD #3", + ( + ZorkGrandInquisitorRegions.SUBWAY_HADES, + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY + ): "Subway Ride from Hades to Monastery", + ( + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, + ZorkGrandInquisitorRegions.MONASTERY + ): "Climb up the Rope", + ( + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, + ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS + ): "Subway Ride from Monastery to Crossroads", + ( + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, + ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM + ): "Subway Ride from Monastery to FCD #3", + ( + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, + ZorkGrandInquisitorRegions.SUBWAY_HADES + ): "Subway Ride from Monastery to Hades", + ( + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, + ZorkGrandInquisitorRegions.TELEPORTER + ): "At the Monastery Teleportation Station", + ( + ZorkGrandInquisitorRegions.TELEPORTER, + ZorkGrandInquisitorRegions.CROSSROADS + ): "Teleport to Crossroads", + ( + ZorkGrandInquisitorRegions.TELEPORTER, + ZorkGrandInquisitorRegions.DM_LAIR + ): "Teleport to Dungeon Master's Lair", + ( + ZorkGrandInquisitorRegions.TELEPORTER, + ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE + ): "Teleport to GUE Tech", + ( + ZorkGrandInquisitorRegions.TELEPORTER, + ZorkGrandInquisitorRegions.HADES_SHORE + ): "Teleport to Hades", + ( + ZorkGrandInquisitorRegions.TELEPORTER, + ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE + ): "Teleport to Spell Lab", + ( + ZorkGrandInquisitorRegions.TELEPORTER, + ZorkGrandInquisitorRegions.SUBWAY_MONASTERY + ): "Teleport to Monastery", + ( + ZorkGrandInquisitorRegions.WALKING_CASTLE, + ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR + ): "Exit Through the Walking Castle Porticullis", + ( + ZorkGrandInquisitorRegions.WHITE_HOUSE, + ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR + ): "Exit Through the White House Time Tunnel", + ( + ZorkGrandInquisitorRegions.WHITE_HOUSE, + ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR + ): "Through the White House Front Door", + ( + ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + ZorkGrandInquisitorRegions.WHITE_HOUSE + ): "Exit Through the White House Front Door", +} + +entrance_names_reverse: Dict[str, Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions]] = { + name: entrance for entrance, name in entrance_names.items() +} + +game_location_to_region: Dict[ + str, + ZorkGrandInquisitorRegions, +] = { + "cd20": ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, + "cd60": ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, + "dc10": ZorkGrandInquisitorRegions.WALKING_CASTLE, + "dg10": ZorkGrandInquisitorRegions.DM_LAIR, + "dg30": ZorkGrandInquisitorRegions.DM_LAIR, + "dg40": ZorkGrandInquisitorRegions.DM_LAIR, + "dv10": ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + "hp10": ZorkGrandInquisitorRegions.HADES_SHORE, + "hp40": ZorkGrandInquisitorRegions.HADES, + "hp50": ZorkGrandInquisitorRegions.HADES, + "hp60": ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, + "me10": ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + "me20": ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, + "mt10": ZorkGrandInquisitorRegions.MONASTERY, + "mt20": ZorkGrandInquisitorRegions.MONASTERY, + "mt30": ZorkGrandInquisitorRegions.MONASTERY, + "pc10": ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL, + "pe10": ZorkGrandInquisitorRegions.PORT_FOOZLE, + "pp10": ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, + "ps10": ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + "ps20": ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_WELL, + "px10": ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_INQUISITION_HQ, + "qb10": ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, + "qe10": ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, + "sg10": ZorkGrandInquisitorRegions.WHITE_HOUSE_INTERIOR, + "sw40": ZorkGrandInquisitorRegions.WHITE_HOUSE, + "te10": ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + "te30": ZorkGrandInquisitorRegions.GUE_TECH_ENTRANCE, + "te40": ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, + "te50": ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, + "th10": ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + "th30": ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, + "tp10": ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, + "tp20": ZorkGrandInquisitorRegions.SPELL_LAB, + "tr10": ZorkGrandInquisitorRegions.GUE_TECH, + "tr20": ZorkGrandInquisitorRegions.GUE_TECH, + "tr30": ZorkGrandInquisitorRegions.GUE_TECH, + "uc10": ZorkGrandInquisitorRegions.CROSSROADS, + "uc30": ZorkGrandInquisitorRegions.CROSSROADS, + "uc40": ZorkGrandInquisitorRegions.CROSSROADS, + "uc50": ZorkGrandInquisitorRegions.CROSSROADS, + "uc60": ZorkGrandInquisitorRegions.CROSSROADS, + "ue10": ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + "ue20": ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + "uh10": ZorkGrandInquisitorRegions.SUBWAY_HADES, + "uh20": ZorkGrandInquisitorRegions.SUBWAY_HADES, + "um10": ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, + "um20": ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, + "us10": ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + "us20": ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, + "uw10": ZorkGrandInquisitorRegions.BOTTOM_OF_THE_WELL, +} + +hotspot_to_regional_hotspot: Dict[ZorkGrandInquisitorItems, ZorkGrandInquisitorItems] = { + ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES + ), + ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST + ), + ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB + ), + ZorkGrandInquisitorItems.HOTSPOT_BLINDS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_BUCKET: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS + ), + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY + ), + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY + ), + ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE + ), + ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE + ), + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO + ), + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO + ), + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS + ), + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_HOUSE_EXIT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM + ), + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM + ), + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS + ), + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE + ), + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES + ), + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES + ), + ZorkGrandInquisitorItems.HOTSPOT_HARRY: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS + ), + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE + ), + ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE + ), + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE + ), + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE + ), + ZorkGrandInquisitorItems.HOTSPOT_MIRROR: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM + ), + ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST + ), + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB + ), + ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE + ), + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM + ), + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB + ), + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB + ), + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB + ), + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR + ), + ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH + ), + ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS + ), + ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST + ), + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY + ), + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS: ( + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY + ), +} + +hotspots_for_regional_hotspot: Dict[ZorkGrandInquisitorItems, Tuple[ZorkGrandInquisitorItems, ...]] = { + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_CROSSROADS: ( + ZorkGrandInquisitorItems.HOTSPOT_BUCKET, + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE, + ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DM_LAIR: ( + ZorkGrandInquisitorItems.HOTSPOT_BLINDS, + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_HOUSE_EXIT, + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH, + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO: ( + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW, + ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM: ( + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE, + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_GUE_TECH: ( + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER, + ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_HADES: ( + ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_MONASTERY: ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE: ( + ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH, + ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL, + ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_PORT_FOOZLE_PAST: ( + ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS, + ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_SPELL_LAB: ( + ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, + ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE, + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT, + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM, + ), + ZorkGrandInquisitorItems.HOTSPOT_REGIONAL_WHITE_HOUSE: ( + ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE, + ), +} + +labels_for_enum_items: Dict[ + Union[ + ZorkGrandInquisitorCraftableSpellBehaviors, + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorEntranceRandomizer, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorHotspots, + ZorkGrandInquisitorLandmarksanity, + ZorkGrandInquisitorStartingLocations, + ], + str +] = { + ZorkGrandInquisitorClientSeedInformation.REVEAL_NOTHING: "Reveal Nothing", + ZorkGrandInquisitorClientSeedInformation.REVEAL_GOAL: "Reveal Goal", + ZorkGrandInquisitorClientSeedInformation.REVEAL_GOAL_AND_OPTIONS: "Reveal Goal and Options", + ZorkGrandInquisitorCraftableSpellBehaviors.VANILLA: "Vanilla", + ZorkGrandInquisitorCraftableSpellBehaviors.ANY_SPELL: "Any Spell", + ZorkGrandInquisitorCraftableSpellBehaviors.ANYTHING: "Anything", + ZorkGrandInquisitorDeathsanity.OFF: "Off", + ZorkGrandInquisitorDeathsanity.ON: "On", + ZorkGrandInquisitorEntranceRandomizer.DISABLED: "Disabled", + ZorkGrandInquisitorEntranceRandomizer.COUPLED: "Enabled (Coupled Entrances)", + ZorkGrandInquisitorEntranceRandomizer.UNCOUPLED: "Enabled (Uncoupled Entrances)", + ZorkGrandInquisitorGoals.THREE_ARTIFACTS: "Three Artifacts", + ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: "Artifact of Magic Hunt", + ZorkGrandInquisitorGoals.SPELL_HEIST: "Spell Heist", + ZorkGrandInquisitorGoals.ZORK_TOUR: "Zork Tour", + ZorkGrandInquisitorGoals.GRIM_JOURNEY: "Grim Journey", + ZorkGrandInquisitorHotspots.ENABLED: "Enabled", + ZorkGrandInquisitorHotspots.REQUIRE_ITEM_PER_REGION: "Require Item Per Region", + ZorkGrandInquisitorHotspots.REQUIRE_ITEM_PER_HOTSPOT: "Require Item Per Hotspot", + ZorkGrandInquisitorLandmarksanity.OFF: "Off", + ZorkGrandInquisitorLandmarksanity.ON: "On", + ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: "Port Foozle", + ZorkGrandInquisitorStartingLocations.CROSSROADS: "Crossroads", + ZorkGrandInquisitorStartingLocations.DM_LAIR: "Dungeon Master's Lair", + ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: "Dungeon Master's House", + ZorkGrandInquisitorStartingLocations.GUE_TECH: "GUE Tech", + ZorkGrandInquisitorStartingLocations.SPELL_LAB: "Spell Lab", + ZorkGrandInquisitorStartingLocations.HADES_SHORE: "Hades Shore", + ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: "Flood Control Dam #3", + ZorkGrandInquisitorStartingLocations.MONASTERY: "Monastery Totemizer", + ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: "Monastery Exhibit", +} + +starter_kit_for_entrance_randomizer: Tuple[ZorkGrandInquisitorItems, ...] = ( + ZorkGrandInquisitorItems.HAMMER, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ZorkGrandInquisitorItems.SPELL_REZROV, + ZorkGrandInquisitorItems.SPELL_THROCK, + ZorkGrandInquisitorItems.SWORD, +) + +starter_kits_for_starting_location: Dict[ + ZorkGrandInquisitorStartingLocations, Optional[Tuple[Tuple[ZorkGrandInquisitorItems, ...], ...]] +] = { + ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_BUCKET, + ), + ), + ZorkGrandInquisitorStartingLocations.CROSSROADS: ( + ( + ZorkGrandInquisitorItems.WELL_ROPE, + ZorkGrandInquisitorItems.HOTSPOT_BUCKET, + ), + ( + ZorkGrandInquisitorItems.SPELL_BEBURTT, + ZorkGrandInquisitorItems.SUBWAY_TOKEN, + ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ), + ( + ZorkGrandInquisitorItems.SPELL_REZROV, + ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ), + ( + ZorkGrandInquisitorItems.HAMMER, + ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE, + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ZorkGrandInquisitorItems.SPELL_THROCK, + ), + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS, + ), + ), + ZorkGrandInquisitorStartingLocations.DM_LAIR: ( + ( + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.HUNGUS_LARD, + ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE, + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ), + ( + ZorkGrandInquisitorItems.HAMMER, + ZorkGrandInquisitorItems.SPELL_THROCK, + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ), + ( + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.CIGAR, + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, + ), + ( + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, + ZorkGrandInquisitorItems.SHOVEL, + ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND, + ), + ( + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.CIGAR, + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY, + ZorkGrandInquisitorItems.MEAD_LIGHT, + ZorkGrandInquisitorItems.ZIMDOR_SCROLL, + ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH, + ), + ), + ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_BLINDS, + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ZorkGrandInquisitorItems.SPELL_OBIDIL, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS, + ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV, + ZorkGrandInquisitorItems.COCOA_INGREDIENTS, + ZorkGrandInquisitorItems.HUNGUS_LARD, + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_MIRROR, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ZorkGrandInquisitorItems.TOTEM_LUCY, + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG, + ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ZorkGrandInquisitorItems.TOTEM_BROG, + ZorkGrandInquisitorItems.BROGS_FLICKERING_TORCH, + ZorkGrandInquisitorItems.BROGS_GRUE_EGG, + ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT, + ), + ), + ZorkGrandInquisitorStartingLocations.GUE_TECH: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR, + ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND, + ZorkGrandInquisitorItems.SHOVEL, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ), + ( + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS, + ), + ( + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT, + ZorkGrandInquisitorItems.PERMA_SUCK_MACHINE, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.ZORK_ROCKS, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS, + ), + ( + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ), + ( + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS, + ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT, + ZorkGrandInquisitorItems.ZORK_ROCKS, + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER, + ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE, + ZorkGrandInquisitorItems.STUDENT_ID, + ), + ), + ZorkGrandInquisitorStartingLocations.SPELL_LAB: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, + ZorkGrandInquisitorItems.SANDWITCH_WRAPPER, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.MONASTERY_ROPE, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.SANDWITCH_WRAPPER, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.SWORD, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM, + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ZorkGrandInquisitorItems.SANDWITCH_WRAPPER, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ZorkGrandInquisitorItems.SPELL_REZROV, + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT, + ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ), + ), + ZorkGrandInquisitorStartingLocations.HADES_SHORE: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.SPELL_SNAVIG, + ZorkGrandInquisitorItems.TOTEM_BROG, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.SPELL_OBIDIL, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM, + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ), + ( + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ZorkGrandInquisitorItems.SPELL_KENDALL, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.MONASTERY_ROPE, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, + ), + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, + ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE, + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM, + ZorkGrandInquisitorItems.HAMMER, + ), + ), + ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS, + ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS, + ZorkGrandInquisitorItems.SPELL_REZROV, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, + ), + ( + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ), + ( + ZorkGrandInquisitorItems.SPELL_THROCK, + ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, + ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON, + ), + ( + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH, + ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS, + ), + ( + ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS, + ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER, + ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS, + ZorkGrandInquisitorItems.SWORD, + ), + ), + ZorkGrandInquisitorStartingLocations.MONASTERY: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION, + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, + ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, + ZorkGrandInquisitorItems.OLD_SCRATCH_CARD, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS, + ZorkGrandInquisitorItems.SUBWAY_TOKEN, + ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT, + ), + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, + ZorkGrandInquisitorItems.SPELL_REZROV, + ), + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, + ZorkGrandInquisitorItems.HOTSPOT_HARRY, + ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE, + ZorkGrandInquisitorItems.SWORD, + ), + ), + ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: ( + ( + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT, + ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER, + ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_YASTARD, + ZorkGrandInquisitorItems.TOTEM_GRIFF, + ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR, + ), + ( + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES, + ), + ( + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM, + ), + ( + ZorkGrandInquisitorItems.MAP, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS, + ), + ( + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS, + ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH, + ), + ), +} + +starting_location_to_region: Dict[ + ZorkGrandInquisitorStartingLocations, ZorkGrandInquisitorRegions +] = { + ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorStartingLocations.CROSSROADS: ZorkGrandInquisitorRegions.CROSSROADS, + ZorkGrandInquisitorStartingLocations.DM_LAIR: ZorkGrandInquisitorRegions.DM_LAIR, + ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, + ZorkGrandInquisitorStartingLocations.GUE_TECH: ZorkGrandInquisitorRegions.GUE_TECH, + ZorkGrandInquisitorStartingLocations.SPELL_LAB: ZorkGrandInquisitorRegions.SPELL_LAB, + ZorkGrandInquisitorStartingLocations.HADES_SHORE: ZorkGrandInquisitorRegions.SUBWAY_HADES, + ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, + ZorkGrandInquisitorStartingLocations.MONASTERY: ZorkGrandInquisitorRegions.MONASTERY, + ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, +} + +voxam_cast_game_locations: Dict[ + ZorkGrandInquisitorStartingLocations, + Tuple[Tuple[str, int], ...] +] = { + ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: ( + ("px1j", 0), + ("ps20", 1), + ("pe20", 1), + ("pe2j", 0), + ("pe30", 1), + ("pe40", 1), + ("pe50", 1), + ("pe5e", 0), + ("pe5f", 0), + ("pe6e", 0), + ), + ZorkGrandInquisitorStartingLocations.CROSSROADS: ( + ("uc10", 1), + ("uc20", 1), + ("uc30", 1), + ("uc3e", 0), + ("uc40", 1), + ("uc4e", 0), + ("uc50", 1), + ("uc6e", 0), + ), + ZorkGrandInquisitorStartingLocations.DM_LAIR: ( + ("dg10", 1), + ("dg20", 1), + ("dg4f", 0), + ("dg30", 1), + ("dg3e", 0), + ), + ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: ( + ("dv10", 1), + ("dv1j", 0), + ("dw10", 1), + ("dw1g", 0), + ), + ZorkGrandInquisitorStartingLocations.GUE_TECH: ( + ("tr20", 1), + ("tr1k", 0), + ("tr1g", 0), + ("tr2g", 0), + ("tr50", 1), + ("tr5e", 0), + ("tr5f", 0), + ("tr5g", 0), + ("tr4g", 0), + ("tr4f", 0), + ("th30", 1), + ("th50", 1), + ("th60", 1), + ("th40", 1), + ), + ZorkGrandInquisitorStartingLocations.SPELL_LAB: ( + ("tp20", 1), + ("tp50", 1), + ("tp10", 1), + ("tp2f", 0), + ("tp2g", 0), + ("tp2e", 0), + ("tp30", 1), + ("tp3f", 0), + ("tp3e", 0), + ("tp4f", 0), + ("tp4e", 0), + ), + ZorkGrandInquisitorStartingLocations.HADES_SHORE: ( + ("uh10", 1), + ("uh20", 1), + ("uh2f", 0), + ("uh2e", 0), + ), + ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: ( + ("ue10", 1), + ("ue20", 1), + ("ue2g", 0), + ("ue2e", 0), + ("ue2j", 0), + ("ue2k", 0), + ("ue2f", 0), + ), + ZorkGrandInquisitorStartingLocations.MONASTERY: ( + ("mt20", 1), + ("mt1e", 0), + ("mt1f", 0), + ("mt2g", 0), + ("mt2e", 0), + ("mt30", 1), + ), + ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: ( + ("me10", 1), + ("me1f", 0), + ("me1h", 0), + ("me1g", 0), + ("me20", 1), + ("me2h", 0), + ("me2j", 0), + ("me5f", 0), + ("me2m", 0), + ), +} diff --git a/worlds/zork_grand_inquisitor/data/missable_location_data.py b/worlds/zork_grand_inquisitor/data/missable_location_data.py new file mode 100644 index 000000000000..97f439333b74 --- /dev/null +++ b/worlds/zork_grand_inquisitor/data/missable_location_data.py @@ -0,0 +1,274 @@ +from typing import Dict, NamedTuple, Optional, Tuple + +from ..enums import ZorkGrandInquisitorItems, ZorkGrandInquisitorLocations + + +class ZorkGrandInquisitorMissableLocationGrantConditionsData(NamedTuple): + game_location_condition: Optional[str] + location_condition: Tuple[ZorkGrandInquisitorLocations, ...] + item_conditions: Optional[Tuple[ZorkGrandInquisitorItems, ...]] + + +missable_location_grant_conditions_data: Dict[ + ZorkGrandInquisitorLocations, ZorkGrandInquisitorMissableLocationGrantConditionsData +] = { + ZorkGrandInquisitorLocations.BOING_BOING_BOING: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg3e", + location_condition=(ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.BONK: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg2f", + location_condition=(ZorkGrandInquisitorLocations.PROZORKED,), + item_conditions=(ZorkGrandInquisitorItems.HAMMER,), + ) + , + ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe6e", + location_condition=(ZorkGrandInquisitorLocations.ARREST_THE_VANDAL,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg4f", + location_condition=(ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="qs1e", + location_condition=(ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition=None, + location_condition=(ZorkGrandInquisitorLocations.OLD_SCRATCH_WINNER,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg4f", + location_condition=(ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="tp1e", + location_condition=(ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="th10", + location_condition=(ZorkGrandInquisitorLocations.A_SMALLWAY,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="cd20", + location_condition=(ZorkGrandInquisitorLocations.THAR_SHE_BLOWS,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="hp60", + location_condition=(ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="th3j", + location_condition=(ZorkGrandInquisitorLocations.CRISIS_AVERTED,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dc10", + location_condition=(ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE,), + item_conditions=(ZorkGrandInquisitorItems.SPELL_GOLGATEM,), + ) + , + ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="th10", + location_condition=(ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="tp1e", + location_condition=(ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,), + item_conditions=(ZorkGrandInquisitorItems.SPELL_IGRAM,), + ) + , + ZorkGrandInquisitorLocations.ITS_ALMOST_AS_IF_IT_WERE_INFINITE: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="th10", + location_condition=(ZorkGrandInquisitorLocations.A_SMALLWAY,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.ITS_PLAYING_A_LITTLE_HARD_TO_GET: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg2f", + location_condition=(ZorkGrandInquisitorLocations.PROZORKED,), + item_conditions=(ZorkGrandInquisitorItems.SPELL_OBIDIL,), + ) + , + ZorkGrandInquisitorLocations.IT_DOESNT_APPEAR_TO_BE_FOOLED: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg2f", + location_condition=(ZorkGrandInquisitorLocations.PROZORKED,), + item_conditions=(ZorkGrandInquisitorItems.SPELL_BEBURTT,), + ) + , + ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg2f", + location_condition=(ZorkGrandInquisitorLocations.PROZORKED,), + item_conditions=(ZorkGrandInquisitorItems.SPELL_THROCK,), + ) + , + ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="tp1e", + location_condition=(ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,), + item_conditions=(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,), + ) + , + ZorkGrandInquisitorLocations.MEAD_LIGHT: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe1e", + location_condition=( + ZorkGrandInquisitorLocations.FIRE_FIRE, + ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO, + ), + item_conditions=(ZorkGrandInquisitorItems.MEAD_LIGHT,), + ) + , + ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dg3e", + location_condition=(ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.NO_AUTOGRAPHS: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe1e", + location_condition=(ZorkGrandInquisitorLocations.FIRE_FIRE,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.NO_BONDAGE: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe20", + location_condition=(ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE,), + item_conditions=( + ZorkGrandInquisitorItems.WELL_ROPE, + ZorkGrandInquisitorItems.SPELL_GLORF, + ), + ) + , + ZorkGrandInquisitorLocations.TALK_TO_ME_GRAND_INQUISITOR: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe5e", + location_condition=(ZorkGrandInquisitorLocations.FIRE_FIRE,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.THATS_A_ROPE: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe1e", + location_condition=(ZorkGrandInquisitorLocations.FIRE_FIRE,), + item_conditions=( + ZorkGrandInquisitorItems.WELL_ROPE, + ZorkGrandInquisitorItems.SPELL_GLORF, + ), + ) + , + ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="us2e", + location_condition=(ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="tp1e", + location_condition=(ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,), + item_conditions=( + ZorkGrandInquisitorItems.WELL_ROPE, + ZorkGrandInquisitorItems.SPELL_GLORF, + ), + ) + , + ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe1e", + location_condition=( + ZorkGrandInquisitorLocations.FIRE_FIRE, + ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE, + ), + item_conditions=(ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER,), + ) + , + ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dw10", + location_condition=(ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="dv10", + location_condition=(ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO,), + item_conditions=( + ZorkGrandInquisitorItems.SWORD, + ZorkGrandInquisitorItems.HOTSPOT_HARRY + ), + ) + , + ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="tp1e", + location_condition=(ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS,), + item_conditions=(ZorkGrandInquisitorItems.SPELL_REZROV,), + ) + , + ZorkGrandInquisitorLocations.YOU_WANT_A_PIECE_OF_ME_DOCK_BOY: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="pe20", + location_condition=(ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE,), + item_conditions=None, + ) + , + ZorkGrandInquisitorLocations.ZIMDOR_IS_UNDAMAGED: + ZorkGrandInquisitorMissableLocationGrantConditionsData( + game_location_condition="tp4g", + location_condition=(ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO,), + item_conditions=( + ZorkGrandInquisitorItems.ZIMDOR_SCROLL, + ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER, + ), + ) + , +} diff --git a/worlds/zork_grand_inquisitor/data/missable_location_grant_conditions_data.py b/worlds/zork_grand_inquisitor/data/missable_location_grant_conditions_data.py deleted file mode 100644 index ef6eacb78ceb..000000000000 --- a/worlds/zork_grand_inquisitor/data/missable_location_grant_conditions_data.py +++ /dev/null @@ -1,200 +0,0 @@ -from typing import Dict, NamedTuple, Optional, Tuple - -from ..enums import ZorkGrandInquisitorItems, ZorkGrandInquisitorLocations - - -class ZorkGrandInquisitorMissableLocationGrantConditionsData(NamedTuple): - location_condition: ZorkGrandInquisitorLocations - item_conditions: Optional[Tuple[ZorkGrandInquisitorItems, ...]] - - -missable_location_grant_conditions_data: Dict[ - ZorkGrandInquisitorLocations, ZorkGrandInquisitorMissableLocationGrantConditionsData -] = { - ZorkGrandInquisitorLocations.BOING_BOING_BOING: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.FLYING_SNAPDRAGON, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.BONK: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.PROZORKED, - item_conditions=(ZorkGrandInquisitorItems.HAMMER,), - ) - , - ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.ARREST_THE_VANDAL, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.MAGIC_FOREVER, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.STRIP_GRUE_FIRE_WATER, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.OLD_SCRATCH_WINNER, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.OUTSMART_THE_QUELBEES, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.A_SMALLWAY, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.THAR_SHE_BLOWS, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.OPEN_THE_GATES_OF_HELL, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.CRISIS_AVERTED, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.DENIED_BY_THE_LAKE_MONSTER: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE, - item_conditions=(ZorkGrandInquisitorItems.SPELL_GOLGATEM,), - ) - , - ZorkGrandInquisitorLocations.EMERGENCY_MAGICATRONIC_MESSAGE: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.ARTIFACTS_EXPLAINED, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.FAT_LOT_OF_GOOD_THATLL_DO_YA: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS, - item_conditions=(ZorkGrandInquisitorItems.SPELL_IGRAM,), - ) - , - ZorkGrandInquisitorLocations.I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.PROZORKED, - item_conditions=(ZorkGrandInquisitorItems.SPELL_THROCK,), - ) - , - ZorkGrandInquisitorLocations.I_SPIT_ON_YOUR_FILTHY_COINAGE: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS, - item_conditions=(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS,), - ) - , - ZorkGrandInquisitorLocations.MEAD_LIGHT: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE, - item_conditions=(ZorkGrandInquisitorItems.MEAD_LIGHT,), - ) - , - ZorkGrandInquisitorLocations.MUSHROOM_HAMMERED: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.THROCKED_MUSHROOM_HAMMERED, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.NO_AUTOGRAPHS: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.NO_BONDAGE: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE, - item_conditions=(ZorkGrandInquisitorItems.ROPE,), - ) - , - ZorkGrandInquisitorLocations.TALK_TO_ME_GRAND_INQUISITOR: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.THATS_A_ROPE: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE, - item_conditions=(ZorkGrandInquisitorItems.ROPE,), - ) - , - ZorkGrandInquisitorLocations.THATS_IT_JUST_KEEP_HITTING_THOSE_BUTTONS: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.ENJOY_YOUR_TRIP, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.THATS_STILL_A_ROPE: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS, - item_conditions=(ZorkGrandInquisitorItems.SPELL_GLORF,), - ) - , - ZorkGrandInquisitorLocations.WHAT_ARE_YOU_STUPID: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.FIRE_FIRE, - item_conditions=(ZorkGrandInquisitorItems.PLASTIC_SIX_PACK_HOLDER,), - ) - , - ZorkGrandInquisitorLocations.YAD_GOHDNUORGREDNU_3_YRAUBORF: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG, - item_conditions=None, - ) - , - ZorkGrandInquisitorLocations.YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.WANT_SOME_RYE_COURSE_YA_DO, - item_conditions=(ZorkGrandInquisitorItems.SWORD, ZorkGrandInquisitorItems.HOTSPOT_HARRY), - ) - , - ZorkGrandInquisitorLocations.YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.YOU_GAINED_86_EXPERIENCE_POINTS, - item_conditions=(ZorkGrandInquisitorItems.SPELL_REZROV,), - ) - , - ZorkGrandInquisitorLocations.YOU_WANT_A_PIECE_OF_ME_DOCK_BOY: - ZorkGrandInquisitorMissableLocationGrantConditionsData( - location_condition=ZorkGrandInquisitorLocations.HELP_ME_CANT_BREATHE, - item_conditions=None, - ) - , -} diff --git a/worlds/zork_grand_inquisitor/data/region_data.py b/worlds/zork_grand_inquisitor/data/region_data.py deleted file mode 100644 index 1aed160f3088..000000000000 --- a/worlds/zork_grand_inquisitor/data/region_data.py +++ /dev/null @@ -1,183 +0,0 @@ -from typing import Dict, NamedTuple, Optional, Tuple - -from ..enums import ZorkGrandInquisitorRegions - - -class ZorkGrandInquisitorRegionData(NamedTuple): - exits: Optional[Tuple[ZorkGrandInquisitorRegions, ...]] - - -region_data: Dict[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegionData] = { - ZorkGrandInquisitorRegions.CROSSROADS: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.DM_LAIR, - ZorkGrandInquisitorRegions.GUE_TECH, - ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.PORT_FOOZLE, - ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, - ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.DM_LAIR: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.CROSSROADS, - ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, - ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.DM_LAIR, - ZorkGrandInquisitorRegions.WALKING_CASTLE, - ZorkGrandInquisitorRegions.WHITE_HOUSE, - ) - ), - ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON, - ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, - ) - ), - ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO_DRAGON: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, - ZorkGrandInquisitorRegions.ENDGAME, - ) - ), - ZorkGrandInquisitorRegions.ENDGAME: ZorkGrandInquisitorRegionData(exits=None), - ZorkGrandInquisitorRegions.GUE_TECH: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.CROSSROADS, - ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, - ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, - ) - ), - ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.GUE_TECH, - ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, - ) - ), - ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.CROSSROADS, - ZorkGrandInquisitorRegions.DM_LAIR, - ZorkGrandInquisitorRegions.GUE_TECH, - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.HADES: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, - ZorkGrandInquisitorRegions.HADES_SHORE, - ) - ), - ZorkGrandInquisitorRegions.HADES_BEYOND_GATES: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.DRAGON_ARCHIPELAGO, - ZorkGrandInquisitorRegions.HADES, - ) - ), - ZorkGrandInquisitorRegions.HADES_SHORE: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.CROSSROADS, - ZorkGrandInquisitorRegions.DM_LAIR, - ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, - ZorkGrandInquisitorRegions.HADES, - ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE, - ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, - ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.MENU: ZorkGrandInquisitorRegionData( - exits=(ZorkGrandInquisitorRegions.PORT_FOOZLE,) - ), - ZorkGrandInquisitorRegions.MONASTERY: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.MONASTERY, - ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, - ) - ), - ZorkGrandInquisitorRegions.PORT_FOOZLE: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.CROSSROADS, - ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP, - ) - ), - ZorkGrandInquisitorRegions.PORT_FOOZLE_JACKS_SHOP: ZorkGrandInquisitorRegionData( - exits=(ZorkGrandInquisitorRegions.PORT_FOOZLE,) - ), - ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.MONASTERY_EXHIBIT, - ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN, - ) - ), - ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST_TAVERN: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.ENDGAME, - ZorkGrandInquisitorRegions.PORT_FOOZLE_PAST, - ) - ), - ZorkGrandInquisitorRegions.SPELL_LAB: ZorkGrandInquisitorRegionData( - exits=(ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE,) - ), - ZorkGrandInquisitorRegions.SPELL_LAB_BRIDGE: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.CROSSROADS, - ZorkGrandInquisitorRegions.DM_LAIR, - ZorkGrandInquisitorRegions.GUE_TECH_OUTSIDE, - ZorkGrandInquisitorRegions.GUE_TECH_HALLWAY, - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.SPELL_LAB, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.CROSSROADS, - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY, - ) - ), - ZorkGrandInquisitorRegions.SUBWAY_MONASTERY: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.HADES_SHORE, - ZorkGrandInquisitorRegions.MONASTERY, - ZorkGrandInquisitorRegions.SUBWAY_CROSSROADS, - ZorkGrandInquisitorRegions.SUBWAY_FLOOD_CONTROL_DAM, - ) - ), - ZorkGrandInquisitorRegions.WALKING_CASTLE: ZorkGrandInquisitorRegionData( - exits=(ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR,) - ), - ZorkGrandInquisitorRegions.WHITE_HOUSE: ZorkGrandInquisitorRegionData( - exits=( - ZorkGrandInquisitorRegions.DM_LAIR_INTERIOR, - ZorkGrandInquisitorRegions.ENDGAME, - ) - ), -} diff --git a/worlds/zork_grand_inquisitor/data/transform_data.py b/worlds/zork_grand_inquisitor/data/transform_data.py new file mode 100644 index 000000000000..20945d0a77c9 --- /dev/null +++ b/worlds/zork_grand_inquisitor/data/transform_data.py @@ -0,0 +1,194 @@ +from typing import Dict, Optional, Tuple, Union + +from ..enums import ( + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorItemTransforms, + ZorkGrandInquisitorItems, + ZorkGrandInquisitorLandmarksanity, + ZorkGrandInquisitorLocations, + ZorkGrandInquisitorLocationTransforms, + ZorkGrandInquisitorStartingLocations, +) + + +item_data_transforms: Dict[ + Union[ + ZorkGrandInquisitorStartingLocations, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorLandmarksanity, + ], + Optional[Dict[ZorkGrandInquisitorItemTransforms, Tuple[ZorkGrandInquisitorItems, ...]]] +] = { + ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS, + ) + }, + ZorkGrandInquisitorStartingLocations.CROSSROADS: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS, + ) + }, + ZorkGrandInquisitorStartingLocations.DM_LAIR: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, + ) + }, + ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR, + ) + }, + ZorkGrandInquisitorStartingLocations.GUE_TECH: None, + ZorkGrandInquisitorStartingLocations.SPELL_LAB: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB, + ) + }, + ZorkGrandInquisitorStartingLocations.HADES_SHORE: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES, + ) + }, + ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM, + ) + }, + ZorkGrandInquisitorStartingLocations.MONASTERY: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.MONASTERY_ROPE, + ) + }, + ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY, + ZorkGrandInquisitorItems.MONASTERY_ROPE, + ) + }, + ZorkGrandInquisitorGoals.THREE_ARTIFACTS: None, + ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: None, + ZorkGrandInquisitorGoals.SPELL_HEIST: None, + ZorkGrandInquisitorGoals.ZORK_TOUR: None, + ZorkGrandInquisitorGoals.GRIM_JOURNEY: None, + ZorkGrandInquisitorDeathsanity.OFF: { + ZorkGrandInquisitorItemTransforms.MAKE_FILLER: ( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY, + ) + }, + ZorkGrandInquisitorDeathsanity.ON: None, + ZorkGrandInquisitorLandmarksanity.OFF: None, + ZorkGrandInquisitorLandmarksanity.ON: None, +} + +location_data_transforms: Dict[ + Union[ + ZorkGrandInquisitorStartingLocations, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorLandmarksanity, + ], + Optional[Dict[ZorkGrandInquisitorLocationTransforms, Tuple[ZorkGrandInquisitorLocations, ...]]] +] = { + ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: None, + ZorkGrandInquisitorStartingLocations.CROSSROADS: None, + ZorkGrandInquisitorStartingLocations.DM_LAIR: None, + ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: None, + ZorkGrandInquisitorStartingLocations.GUE_TECH: None, + ZorkGrandInquisitorStartingLocations.SPELL_LAB: None, + ZorkGrandInquisitorStartingLocations.HADES_SHORE: None, + ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: None, + ZorkGrandInquisitorStartingLocations.MONASTERY: None, + ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: None, + ZorkGrandInquisitorGoals.THREE_ARTIFACTS: None, + ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: { + ZorkGrandInquisitorLocationTransforms.REMOVE: ( + ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT, + ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG, + ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP, + ), + }, + ZorkGrandInquisitorGoals.SPELL_HEIST: { + ZorkGrandInquisitorLocationTransforms.REMOVE: ( + ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT, + ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG, + ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP, + ), + }, + ZorkGrandInquisitorGoals.ZORK_TOUR: { + ZorkGrandInquisitorLocationTransforms.REMOVE: ( + ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT, + ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG, + ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP, + ), + }, + ZorkGrandInquisitorGoals.GRIM_JOURNEY: { + ZorkGrandInquisitorLocationTransforms.REMOVE: ( + ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT, + ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG, + ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP, + ), + }, + ZorkGrandInquisitorDeathsanity.OFF: { + ZorkGrandInquisitorLocationTransforms.REMOVE: ( + ZorkGrandInquisitorLocations.DEATH_ARRESTED_WITH_JACK, + ZorkGrandInquisitorLocations.DEATH_ATTACKED_THE_QUELBEES, + ZorkGrandInquisitorLocations.DEATH_CLIMBED_OUT_OF_THE_WELL, + ZorkGrandInquisitorLocations.DEATH_EATEN_BY_A_GRUE, + ZorkGrandInquisitorLocations.DEATH_JUMPED_IN_BOTTOMLESS_PIT, + ZorkGrandInquisitorLocations.DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER, + ZorkGrandInquisitorLocations.DEATH_LOST_SOUL_TO_OLD_SCRATCH, + ZorkGrandInquisitorLocations.DEATH_OUTSMARTED_BY_THE_QUELBEES, + ZorkGrandInquisitorLocations.DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD, + ZorkGrandInquisitorLocations.DEATH_STEPPED_INTO_THE_INFINITE, + ZorkGrandInquisitorLocations.DEATH_SWALLOWED_BY_A_DRAGON, + ZorkGrandInquisitorLocations.DEATH_THROCKED_THE_GRASS, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_INFINITY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_NEWARK_NEW_JERSEY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_HALLS_OF_INQUISITION, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_INFINITY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_NEWARK_NEW_JERSEY, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_STRAIGHT_TO_HELL, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_PERMANENTLY_SURFACE_OF_MERZ, + ZorkGrandInquisitorLocations.DEATH_TOTEMIZED_SURFACE_OF_MERZ, + ZorkGrandInquisitorLocations.DEATH_YOURE_NOT_CHARON, + ZorkGrandInquisitorLocations.DEATH_ZORK_ROCKS_EXPLODED, + ), + }, + ZorkGrandInquisitorDeathsanity.ON: None, + ZorkGrandInquisitorLandmarksanity.OFF: { + ZorkGrandInquisitorLocationTransforms.REMOVE: ( + ZorkGrandInquisitorLocations.LANDMARK_DRAGON_ARCHIPELAGO, + ZorkGrandInquisitorLocations.LANDMARK_DUNGEON_MASTERS_HOUSE, + ZorkGrandInquisitorLocations.LANDMARK_FLOOD_CONTROL_DAM_3, + ZorkGrandInquisitorLocations.LANDMARK_GATES_OF_HELL, + ZorkGrandInquisitorLocations.LANDMARK_GREAT_UNDERGROUND_EMPIRE_ENTRANCE, + ZorkGrandInquisitorLocations.LANDMARK_GUE_TECH_FOUNTAIN_INSIDE, + ZorkGrandInquisitorLocations.LANDMARK_GUE_TECH_FOUNTAIN_OUTSIDE, + ZorkGrandInquisitorLocations.LANDMARK_HADES_SHORE, + ZorkGrandInquisitorLocations.LANDMARK_INFINITE_CORRIDOR, + ZorkGrandInquisitorLocations.LANDMARK_INQUISITION_HEADQUARTERS, + ZorkGrandInquisitorLocations.LANDMARK_JACKS_SHOP, + ZorkGrandInquisitorLocations.LANDMARK_MIRROR_ROOM, + ZorkGrandInquisitorLocations.LANDMARK_PAST_PORT_FOOZLE, + ZorkGrandInquisitorLocations.LANDMARK_PORT_FOOZLE, + ZorkGrandInquisitorLocations.LANDMARK_SPELL_CHECKER, + ZorkGrandInquisitorLocations.LANDMARK_TOTEMIZER, + ZorkGrandInquisitorLocations.LANDMARK_UMBRELLA_TREE, + ZorkGrandInquisitorLocations.LANDMARK_UNDERGROUND_UNDERGROUND_ENTRANCE, + ZorkGrandInquisitorLocations.LANDMARK_WALKING_CASTLES_HEART, + ZorkGrandInquisitorLocations.LANDMARK_WHITE_HOUSE, + ), + }, + ZorkGrandInquisitorLandmarksanity.ON: None, +} diff --git a/worlds/zork_grand_inquisitor/data_funcs.py b/worlds/zork_grand_inquisitor/data_funcs.py index 2a7bff1fbb6b..199805817e28 100644 --- a/worlds/zork_grand_inquisitor/data_funcs.py +++ b/worlds/zork_grand_inquisitor/data_funcs.py @@ -1,15 +1,27 @@ -from typing import Dict, List, Set, Tuple, Union +from typing import Any, Dict, List, Set, Tuple, Union -from .data.entrance_rule_data import entrance_rule_data +from BaseClasses import ItemClassification + +from .data.entrance_data import Entrance, EntranceRule, EntranceRuleData, endgame_entrance_data_by_goal from .data.item_data import item_data, ZorkGrandInquisitorItemData from .data.location_data import location_data, ZorkGrandInquisitorLocationData +from .data.transform_data import item_data_transforms, location_data_transforms from .enums import ( + ZorkGrandInquisitorClientSeedInformation, + ZorkGrandInquisitorCraftableSpellBehaviors, + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorEntranceRandomizer, ZorkGrandInquisitorEvents, ZorkGrandInquisitorGoals, + ZorkGrandInquisitorHotspots, ZorkGrandInquisitorItems, + ZorkGrandInquisitorItemTransforms, + ZorkGrandInquisitorLandmarksanity, ZorkGrandInquisitorLocations, + ZorkGrandInquisitorLocationTransforms, ZorkGrandInquisitorRegions, + ZorkGrandInquisitorStartingLocations, ZorkGrandInquisitorTags, ) @@ -22,7 +34,7 @@ def item_names_to_item() -> Dict[str, ZorkGrandInquisitorItems]: return {item.value: item for item in item_data} -def location_names_to_id() -> Dict[str, int]: +def location_names_to_id() -> Dict[Any, int]: return { location.value: data.archipelago_id for location, data in location_data.items() @@ -30,7 +42,7 @@ def location_names_to_id() -> Dict[str, int]: } -def location_names_to_location() -> Dict[str, ZorkGrandInquisitorLocations]: +def location_names_to_location() -> Dict[Any, ZorkGrandInquisitorLocations]: return { location.value: location for location, data in location_data.items() @@ -38,14 +50,38 @@ def location_names_to_location() -> Dict[str, ZorkGrandInquisitorLocations]: } +def id_to_client_seed_information() -> Dict[int, ZorkGrandInquisitorClientSeedInformation]: + return {info.value: info for info in ZorkGrandInquisitorClientSeedInformation} + + +def id_to_craftable_spell_behaviors() -> Dict[int, ZorkGrandInquisitorCraftableSpellBehaviors]: + return {behavior.value: behavior for behavior in ZorkGrandInquisitorCraftableSpellBehaviors} + + +def id_to_deathsanity() -> Dict[int, ZorkGrandInquisitorDeathsanity]: + return {deathsanity.value: deathsanity for deathsanity in ZorkGrandInquisitorDeathsanity} + + +def id_to_entrance_randomizer() -> Dict[int, ZorkGrandInquisitorEntranceRandomizer]: + return {er.value: er for er in ZorkGrandInquisitorEntranceRandomizer} + + def id_to_goals() -> Dict[int, ZorkGrandInquisitorGoals]: return {goal.value: goal for goal in ZorkGrandInquisitorGoals} +def id_to_hotspots() -> Dict[int, ZorkGrandInquisitorHotspots]: + return {hotspot.value: hotspot for hotspot in ZorkGrandInquisitorHotspots} + + def id_to_items() -> Dict[int, ZorkGrandInquisitorItems]: return {data.archipelago_id: item for item, data in item_data.items()} +def id_to_landmarksanity() -> Dict[int, ZorkGrandInquisitorLandmarksanity]: + return {landmarksanity.value: landmarksanity for landmarksanity in ZorkGrandInquisitorLandmarksanity} + + def id_to_locations() -> Dict[int, ZorkGrandInquisitorLocations]: return { data.archipelago_id: location @@ -54,6 +90,13 @@ def id_to_locations() -> Dict[int, ZorkGrandInquisitorLocations]: } +def id_to_starting_locations() -> Dict[int, ZorkGrandInquisitorStartingLocations]: + return { + starting_location.value: starting_location + for starting_location in ZorkGrandInquisitorStartingLocations + } + + def item_groups() -> Dict[str, List[str]]: groups: Dict[str, List[str]] = dict() @@ -109,9 +152,12 @@ def location_groups() -> Dict[str, List[str]]: return {k: v for k, v in groups.items() if len(v)} -def locations_by_region(include_deathsanity: bool = False) -> Dict[ - ZorkGrandInquisitorRegions, List[ZorkGrandInquisitorLocations] -]: +def locations_by_region_for_world( + world_location_data: Dict[ + Union[ZorkGrandInquisitorLocations, ZorkGrandInquisitorEvents], + ZorkGrandInquisitorLocationData, + ] +) -> Dict[ZorkGrandInquisitorRegions, List[ZorkGrandInquisitorLocations]]: mapping: Dict[ZorkGrandInquisitorRegions, List[ZorkGrandInquisitorLocations]] = dict() region: ZorkGrandInquisitorRegions @@ -120,12 +166,7 @@ def locations_by_region(include_deathsanity: bool = False) -> Dict[ location: ZorkGrandInquisitorLocations data: ZorkGrandInquisitorLocationData - for location, data in location_data.items(): - if not include_deathsanity and ZorkGrandInquisitorTags.DEATHSANITY in ( - data.tags or tuple() - ): - continue - + for location, data in world_location_data.items(): mapping[data.region].append(location) return mapping @@ -138,6 +179,88 @@ def locations_with_tag(tag: ZorkGrandInquisitorTags) -> Set[ZorkGrandInquisitorL return {location for location, data in location_data.items() if data.tags is not None and tag in data.tags} +def prepare_item_data( + starting_location: ZorkGrandInquisitorStartingLocations, + goal: ZorkGrandInquisitorGoals, + deathsanity: ZorkGrandInquisitorDeathsanity, + landmarksanity: ZorkGrandInquisitorLandmarksanity, + entrance_randomizer: ZorkGrandInquisitorEntranceRandomizer, +) -> Dict[ZorkGrandInquisitorItems, ZorkGrandInquisitorItemData]: + transformed_item_data: Dict[ZorkGrandInquisitorItems, ZorkGrandInquisitorItemData] = dict() + + # Filter items + item: ZorkGrandInquisitorItems + data: ZorkGrandInquisitorItemData + for item, data in item_data.items(): + # Filter here... + transformed_item_data[item] = data + + # Apply transformations + items_to_make_filler: Set[ZorkGrandInquisitorItems] = set() + + for context in (starting_location, goal, deathsanity, landmarksanity): + if item_data_transforms[context] is not None: + transform: ZorkGrandInquisitorItemTransforms + items: Tuple[ZorkGrandInquisitorItems, ...] + for transform, items in item_data_transforms[context].items(): + if transform == ZorkGrandInquisitorItemTransforms.MAKE_FILLER: + if context == starting_location: + if entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + continue + + item: ZorkGrandInquisitorItems + for item in items: + items_to_make_filler.add(item) + + item: ZorkGrandInquisitorItems + for item in items_to_make_filler: + transformed_item_data[item] = transformed_item_data[item]._replace( + classification=ItemClassification.filler + ) + + return transformed_item_data + + +def prepare_location_data( + starting_location: ZorkGrandInquisitorStartingLocations, + goal: ZorkGrandInquisitorGoals, + deathsanity: ZorkGrandInquisitorDeathsanity, + landmarksanity: ZorkGrandInquisitorLandmarksanity, +) -> Dict[ + Union[ZorkGrandInquisitorLocations, ZorkGrandInquisitorEvents], ZorkGrandInquisitorLocationData +]: + transformed_location_data: Dict[ + Union[ZorkGrandInquisitorLocations, ZorkGrandInquisitorEvents], ZorkGrandInquisitorLocationData + ] = dict() + + # Filter locations + location: Union[ZorkGrandInquisitorLocations, ZorkGrandInquisitorEvents] + data: ZorkGrandInquisitorLocationData + for location, data in location_data.items(): + # Filter here... + transformed_location_data[location] = data + + # Apply transformations + locations_to_remove: List[ZorkGrandInquisitorLocations] = list() + + for context in (starting_location, goal, deathsanity, landmarksanity): + if location_data_transforms[context] is not None: + transform: ZorkGrandInquisitorLocationTransforms + locations: Tuple[ZorkGrandInquisitorLocations, ...] + for transform, locations in location_data_transforms[context].items(): + if transform == ZorkGrandInquisitorLocationTransforms.REMOVE: + location: ZorkGrandInquisitorLocations + for location in locations: + locations_to_remove.append(location) + + location: ZorkGrandInquisitorLocations + for location in locations_to_remove: + if location in transformed_location_data: + del transformed_location_data[location] + + return transformed_location_data + + def location_access_rule_for(location: ZorkGrandInquisitorLocations, player: int) -> str: data: ZorkGrandInquisitorLocationData = location_data[location] @@ -181,25 +304,32 @@ def location_access_rule_for(location: ZorkGrandInquisitorLocations, player: int return lambda_string +def entrances_by_region_for_world( + entrance_rule_data: EntranceRuleData +) -> Dict[ZorkGrandInquisitorRegions, List[Entrance]]: + entrances_by_region: Dict[ZorkGrandInquisitorRegions, List[Entrance]] = { + ZorkGrandInquisitorRegions.ANYWHERE: list(), + ZorkGrandInquisitorRegions.ENDGAME: list(), + } + + region_from: ZorkGrandInquisitorRegions + region_to: ZorkGrandInquisitorRegions + for region_from, region_to in entrance_rule_data.keys(): + if region_from not in entrances_by_region: + entrances_by_region[region_from] = list() + + entrances_by_region[region_from].append((region_from, region_to)) + + return entrances_by_region + + def entrance_access_rule_for( region_origin: ZorkGrandInquisitorRegions, region_destination: ZorkGrandInquisitorRegions, - player: int + player: int, + dataset: EntranceRuleData ) -> str: - data: Union[ - Tuple[ - Tuple[ - Union[ - ZorkGrandInquisitorEvents, - ZorkGrandInquisitorItems, - ZorkGrandInquisitorRegions, - ], - ..., - ], - ..., - ], - None, - ] = entrance_rule_data[(region_origin, region_destination)] + data: EntranceRule = dataset[(region_origin, region_destination)] if data is None: return "lambda state: True" @@ -235,6 +365,20 @@ def entrance_access_rule_for( lambda_string += f"state.has(\"{requirement.value}\", {player})" elif requirement_type == ZorkGrandInquisitorRegions: lambda_string += f"state.can_reach(\"{requirement.value}\", \"Region\", {player})" + elif isinstance(requirement, list): + lambda_string += f"state.has(\"{requirement[0].value}\", {player}, {requirement[1]})" + elif isinstance(requirement, tuple): + lambda_string += "(" + + iii: int + sub_requirement: ZorkGrandInquisitorItems + for iii, sub_requirement in enumerate(requirement): + lambda_string += f"state.has(\"{sub_requirement.value}\", {player})" + + if iii < len(requirement) - 1: + lambda_string += " or " + + lambda_string += ")" if ii < len(requirement_group) - 1: lambda_string += " and " @@ -245,3 +389,64 @@ def entrance_access_rule_for( lambda_string += " or " return lambda_string + + +def goal_access_rule_for( + region: ZorkGrandInquisitorRegions, + goal: ZorkGrandInquisitorGoals, + player: int, + artifacts_of_magic_required: int, + landmarks_required: int, + deaths_required: int, +) -> str: + dataset: Dict[ + Tuple[ + ZorkGrandInquisitorRegions, + ZorkGrandInquisitorRegions, + ], + Union[ + Tuple[ + Tuple[ + Union[ + ZorkGrandInquisitorEvents, + ZorkGrandInquisitorItems, + ZorkGrandInquisitorRegions, + List[Union[ZorkGrandInquisitorItems, int]], + ], + ..., + ], + ..., + ], + None, + ], + ] = endgame_entrance_data_by_goal[goal] + + # Replace placeholder with actual number of goal items required + if goal == ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: + dataset[ + ( + ZorkGrandInquisitorRegions.WALKING_CASTLE, + ZorkGrandInquisitorRegions.ENDGAME + ) + ][0][0][1] = artifacts_of_magic_required + elif goal == ZorkGrandInquisitorGoals.ZORK_TOUR: + dataset[ + ( + ZorkGrandInquisitorRegions.OUTSIDE_PORT_FOOZLE_SIGNPOST, + ZorkGrandInquisitorRegions.ENDGAME + ) + ][0][0][1] = landmarks_required + elif goal == ZorkGrandInquisitorGoals.GRIM_JOURNEY: + dataset[ + ( + ZorkGrandInquisitorRegions.HADES_BEYOND_GATES, + ZorkGrandInquisitorRegions.ENDGAME + ) + ][0][0][1] = deaths_required + + return entrance_access_rule_for( + region, + ZorkGrandInquisitorRegions.ENDGAME, + player, + dataset=dataset, + ) diff --git a/worlds/zork_grand_inquisitor/enums.py b/worlds/zork_grand_inquisitor/enums.py index ecbb38a949b4..f38e45e06dd1 100644 --- a/worlds/zork_grand_inquisitor/enums.py +++ b/worlds/zork_grand_inquisitor/enums.py @@ -1,44 +1,142 @@ import enum +class ZorkGrandInquisitorClientSeedInformation(enum.Enum): + REVEAL_NOTHING = 0 + REVEAL_GOAL = 1 + REVEAL_GOAL_AND_OPTIONS = 2 + + +class ZorkGrandInquisitorCraftableSpellBehaviors(enum.Enum): + VANILLA = 0 + ANY_SPELL = 1 + ANYTHING = 2 + + +class ZorkGrandInquisitorDeathsanity(enum.Enum): + OFF = 0 + ON = 1 + + +class ZorkGrandInquisitorEntranceRandomizer(enum.Enum): + DISABLED = 0 + COUPLED = 1 + UNCOUPLED = 2 + + class ZorkGrandInquisitorEvents(enum.Enum): + BEYOND_GATES_OF_HELL_ACCESSED = "Event: Beyond Gates of Hell Accessed" CHARON_CALLED = "Event: Charon Called" - CIGAR_ACCESSIBLE = "Event: Cigar Accessible" DALBOZ_LOCKER_OPENABLE = "Event: Dalboz Locker Openable" DAM_DESTROYED = "Event: Dam Destroyed" DOOR_DRANK_MEAD = "Event: Door Drank Mead" DOOR_SMOKED_CIGAR = "Event: Door Smoked Cigar" DUNCE_LOCKER_OPENABLE = "Event: Dunce Locker Openable" - HAS_REPAIRABLE_OBIDIL = "Event: Has Repairable OBIDIL" HAS_REPAIRABLE_SNAVIG = "Event: Has Repairable SNAVIG" - KNOWS_BEBURTT = "Event: Knows BEBURTT" - KNOWS_OBIDIL = "Event: Knows OBIDIL" - KNOWS_SNAVIG = "Event: Knows SNAVIG" - KNOWS_YASTARD = "Event: Knows YASTARD" - LANTERN_DALBOZ_ACCESSIBLE = "Event: Lantern (Dalboz) Accessible" ROPE_GLORFABLE = "Event: Rope GLORFable" VICTORY = "Victory" WHITE_HOUSE_LETTER_MAILABLE = "Event: White House Letter Mailable" ZORKMID_BILL_ACCESSIBLE = "Event: 500 Zorkmid Bill Accessible" ZORK_ROCKS_ACTIVATED = "Event: Zork Rocks Activated" - ZORK_ROCKS_SUCKABLE = "Event: Zork Rocks Suckable" class ZorkGrandInquisitorGoals(enum.Enum): THREE_ARTIFACTS = 0 + ARTIFACT_OF_MAGIC_HUNT = 1 + SPELL_HEIST = 2 + ZORK_TOUR = 3 + GRIM_JOURNEY = 4 + + +class ZorkGrandInquisitorHotspots(enum.Enum): + ENABLED = 0 + REQUIRE_ITEM_PER_REGION = 1 + REQUIRE_ITEM_PER_HOTSPOT = 2 class ZorkGrandInquisitorItems(enum.Enum): + ARTIFACT_OF_MAGIC = "Artifact of Magic" BROGS_BICKERING_TORCH = "Brog's Bickering Torch" BROGS_FLICKERING_TORCH = "Brog's Flickering Torch" BROGS_GRUE_EGG = "Brog's Grue Egg" BROGS_PLANK = "Brog's Plank" - FILLER_FROBOZZ_ELECTRIC_GADGET = "Frobozz Electric Gadget" - FILLER_INQUISITION_PROPAGANDA_FLYER = "Inquisition Propaganda Flyer" - FILLER_MAGIC_CONTRABAND = "Magic Contraband" - FILLER_NONSENSICAL_INQUISITION_PAPERWORK = "Nonsensical Inquisition Paperwork" - FILLER_UNREADABLE_SPELL_SCROLL = "Unreadable Spell Scroll" - FLATHEADIA_FUDGE = "Flatheadia Fudge" + CIGAR = "Cigar" + COCOA_INGREDIENTS = "Cocoa Ingredients" + COCONUT_OF_QUENDOR = "Coconut of Quendor" + CUBE_OF_FOUNDATION = "Cube of Foundation" + DEATH = "Grim Journey Death" + FILLER_AIMFIZ_SCROLL = "AIMFIZ Scroll: Transport Caster to Someone Else's Location" + FILLER_BAYALA_SCROLL = "BAYALA Scroll: Deform Body" + FILLER_BITTYJOO_SCROLL = "BITTYJOO Scroll: Make Lies Undetectable" + FILLER_BLORB_SCROLL = "BLORB Scroll: Safely Protect a Small Object" + FILLER_BLORPLE_SCROLL = "BLORPLE Scroll: Explore an Object's Mystic Connections" + FILLER_BOOZNIK_SCROLL = "BOOZNIK Scroll: Reverse Spells in Spellbook" + FILLER_BORCH_SCROLL = "BORCH Scroll: Put Insects to Sleep" + FILLER_CASKLY_SCROLL = "CASKLY Scroll: Cause Perfection" + FILLER_CLEESH_SCROLL = "CLEESH Scroll: Change a Creature Into a Small Amphibian" + FILLER_CONBAK_SCROLL = "CONBAK Scroll: Build Strong Bodies 12 Different Ways" + FILLER_DABHHU_SCROLL = "DABHHU Scroll: Ensure Complete Obedience" + FILLER_DRILBO_SCROLL = "DRILBO Scroll: Strip a Floor of Yellowed Wax" + FILLER_ESPNIS_SCROLL = "ESPNIS Scroll: Sleep" + FILLER_EXEX_SCROLL = "EXEX Scroll: Make Things Move With Greater Speed" + FILLER_FAIFT_SCROLL = "FAIFT Scroll: Change Appearance to Look Younger" + FILLER_FILFRE_SCROLL = "FILFRE Scroll: Create Gratuitous Fireworks" + FILLER_FIZMO_SCROLL = "FIZMO Scroll: Cause Stopped-Up Pipes to Unclog" + FILLER_FOBLUB_SCROLL = "FOBLUB Scroll: Glue Audience to Seats" + FILLER_FRIPPLE_SCROLL = "FRIPPLE Scroll: Erect a Magical Barrier" + FILLER_FROTZ_SCROLL = "FROTZ Scroll: Cause Something to Give Off Light" + FILLER_FWEEP_SCROLL = "FWEEP Scroll: Turn Caster Into a Bat" + FILLER_GASPAR_SCROLL = "GASPAR Scroll: Provide for Your Own Resurrection" + FILLER_GILCH_SCROLL = "GILCH Scroll: Astral Travel" + FILLER_GIRGOL_SCROLL = "GIRGOL Scroll: Stop Time" + FILLER_GHELOOH_SCROLL = "GHEL-OOH Scroll: Suspend Subject in a Gelatinous Substance" + FILLER_GIZGUM_SCROLL = "GIZGUM Scroll: Predict Visits From Relatives" + FILLER_GLOTH_SCROLL = "GLOTH Scroll: Fold Dough 83 Times" + FILLER_GNUSTO_SCROLL = "GNUSTO Scroll: Write a Magic Spell Into a Spellbook" + FILLER_GOLMAC_SCROLL = "GOLMAC Scroll: Travel Temporally" + FILLER_GONDAR_SCROLL = "GONDAR Scroll: Extinguish Fire" + FILLER_GORCH_SCROLL = "GORCH Scroll: Create Ladder" + FILLER_GUNCHO_SCROLL = "GUNCHO Scroll: Banish Victim to Another Plane of Existence" + FILLER_IMALI_SCROLL = "IMALI Scroll: Worsen Eyesight" + FILLER_IZYUK_SCROLL = "IZYUK Scroll: Fly Like a Bird" + FILLER_JINDAK_SCROLL = "JINDAK Scroll: Detect Magic" + FILLER_KEPMKOMN_SCROLL = "KEPMKOMN Scroll: Cause Massive Destruction to Edifices" + FILLER_KOAASST_SCROLL = "KOAASST Scroll: Play Soothing Ambient Music" + FILLER_KRAK_SCROLL = "KRAK Scroll: Slow Time Drastically" + FILLER_KREBF_SCROLL = "KREBF Scroll: Repair Willful Damage" + FILLER_KULCAD_SCROLL = "KULCAD Scroll: Dispel a Magic Spell" + FILLER_LESOCH_SCROLL = "LESOCH Scroll: Gust of Wind" + FILLER_LEXDOM_SCROLL = "LEXDOM Scroll: Create Lock and Key" + FILLER_LIDIBO_SCROLL = "LIDIBO Scroll: Make a Creature Think You Are Really Ugly" + FILLER_LISKON_SCROLL = "LISKON Scroll: Shrink a Living Thing" + FILLER_LOBAL_SCROLL = "LOBAL Scroll: Sharpen Hearing" + FILLER_LOKTAR_SCROLL = "LOKTAR Scroll: Cause Temporal Distension" + FILLER_MALYON_SCROLL = "MALYON Scroll: Animate Inanimate Objects" + FILLER_MEEF_SCROLL = "MEEF Scroll: Cause Plants to Wilt" + FILLER_MELBOR_SCROLL = "MELBOR Scroll: Protect From Harm by Evil Beings" + FILLER_MUSDEX_SCROLL = "MUSDEX Scroll: Improve Muscle Tone" + FILLER_NERZO_SCROLL = "NERZO Scroll: Balance Checkbook" + FILLER_NIKMO_SCROLL = "NIKMO Scroll: Cause Urge to Initiate a Temporary Relationship" + FILLER_NITFOL_SCROLL = "NITFOL Scroll: Converse With Beasts in Their Own Tongue" + FILLER_OTSUNG_SCROLL = "OTSUNG Scroll: Erase Spell Written in Spellbook" + FILLER_OZMOO_SCROLL = "OZMOO Scroll: Survive Unnatural Death" + FILLER_PAXTEN_SCROLL = "PAX-TEN Scroll: Slow Productivity Through Confusion" + FILLER_PULVER_SCROLL = "PULVER Scroll: Dry Liquids" + FILLER_QUELBO_SCROLL = "QUELBO Scroll: Transmute Coconuts Into Gold" + FILLER_STEGAW_SCROLL = "STEGAW Scroll: Turn Eggs Into Ripe Guano" + FILLER_SWANZO_SCROLL = "SWANZO Scroll: Exorcise an Inhabiting Presence" + FILLER_TINSOT_SCROLL = "TINSOT Scroll: Freeze Into Place" + FILLER_TOSSIO_SCROLL = "TOSSIO Scroll: Turn Granite Into Fettuccini" + FILLER_UMBOZ_SCROLL = "UMBOZ Scroll: Obviate Need for Dusting" + FILLER_VARDIK_SCROLL = "VARDIK Scroll: Shield a Mind From an Evil Spirit" + FILLER_VAXUM_SCROLL = "VAXUM Scroll: Make a Hostile Creature Your Friend" + FILLER_VEZZA_SCROLL = "VEZZA Scroll: View the Future" + FILLER_YOMIN_SCROLL = "YOMIN Scroll: Mind Probe" + FILLER_YONK_SCROLL = "YONK Scroll: Augment the Power of Certain Spells" + FILLER_YOZOZZO_SCROLL = "YOZOZZO Scroll: Turn Person Into a Mallard" + FILLER_ZIMBOR_SCROLL = "ZIMBOR Scroll: Turn One Really Big City Into Lots of Tiny, Little Ashes" + FILLER_ZOOKA_SCROLL = "ZOOKA Scroll: Turn Eggs Into Overripe Cabbage" + FILLER_ZUGTHUG_SCROLL = "ZUGTHUG Scroll: Automatically Correct Speling Errors" GRIFFS_AIR_PUMP = "Griff's Air Pump" GRIFFS_DRAGON_TOOTH = "Griff's Dragon Tooth" GRIFFS_INFLATABLE_RAFT = "Griff's Inflatable Raft" @@ -48,6 +146,7 @@ class ZorkGrandInquisitorItems(enum.Enum): HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS = "Hotspot: Alpine's Quandry Card Slots" HOTSPOT_BLANK_SCROLL_BOX = "Hotspot: Blank Scroll Box" HOTSPOT_BLINDS = "Hotspot: Blinds" + HOTSPOT_BUCKET = "Hotspot: Bucket" HOTSPOT_CANDY_MACHINE_BUTTONS = "Hotspot: Candy Machine Buttons" HOTSPOT_CANDY_MACHINE_COIN_SLOT = "Hotspot: Candy Machine Coin Slot" HOTSPOT_CANDY_MACHINE_VACUUM_SLOT = "Hotspot: Candy Machine Vacuum Slot" @@ -62,6 +161,7 @@ class ZorkGrandInquisitorItems(enum.Enum): HOTSPOT_DRAGON_CLAW = "Hotspot: Dragon Claw" HOTSPOT_DRAGON_NOSTRILS = "Hotspot: Dragon Nostrils" HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE = "Hotspot: Dungeon Master's Lair Entrance" + HOTSPOT_DUNGEON_MASTERS_HOUSE_EXIT = "Hotspot: Dungeon Master's House Exit" HOTSPOT_FLOOD_CONTROL_BUTTONS = "Hotspot: Flood Control Buttons" HOTSPOT_FLOOD_CONTROL_DOORS = "Hotspot: Flood Control Doors" HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT = "Hotspot: Frozen Treat Machine Coin Slot" @@ -70,6 +170,7 @@ class ZorkGrandInquisitorItems(enum.Enum): HOTSPOT_GRAND_INQUISITOR_DOLL = "Hotspot: Grand Inquisitor Doll" HOTSPOT_GUE_TECH_DOOR = "Hotspot: GUE Tech Door" HOTSPOT_GUE_TECH_GRASS = "Hotspot: GUE Tech Grass" + HOTSPOT_GUE_TECH_WINDOWS = "Hotspot: GUE Tech Windows" HOTSPOT_HADES_PHONE_BUTTONS = "Hotspot: Hades Phone Buttons" HOTSPOT_HADES_PHONE_RECEIVER = "Hotspot: Hades Phone Receiver" HOTSPOT_HARRY = "Hotspot: Harry" @@ -81,11 +182,21 @@ class ZorkGrandInquisitorItems(enum.Enum): HOTSPOT_MAILBOX_DOOR = "Hotspot: Mailbox Door" HOTSPOT_MAILBOX_FLAG = "Hotspot: Mailbox Flag" HOTSPOT_MIRROR = "Hotspot: Mirror" - HOTSPOT_MONASTERY_VENT = "Hotspot: Monastery Vent" HOTSPOT_MOSSY_GRATE = "Hotspot: Mossy Grate" HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR = "Hotspot: Port Foozle Past Tavern Door" HOTSPOT_PURPLE_WORDS = "Hotspot: Purple Words" HOTSPOT_QUELBEE_HIVE = "Hotspot: Quelbee Hive" + HOTSPOT_REGIONAL_CROSSROADS = "Hotspots: Crossroads" + HOTSPOT_REGIONAL_DM_LAIR = "Hotspots: Dungeon Master's Lair" + HOTSPOT_REGIONAL_DRAGON_ARCHIPELAGO = "Hotspots: Dragon Archipelago" + HOTSPOT_REGIONAL_FLOOD_CONTROL_DAM = "Hotspots: Flood Control Dam #3" + HOTSPOT_REGIONAL_GUE_TECH = "Hotspots: GUE Tech" + HOTSPOT_REGIONAL_HADES = "Hotspots: Hades" + HOTSPOT_REGIONAL_MONASTERY = "Hotspots: Monastery" + HOTSPOT_REGIONAL_PORT_FOOZLE = "Hotspots: Port Foozle" + HOTSPOT_REGIONAL_PORT_FOOZLE_PAST = "Hotspots: Past Port Foozle" + HOTSPOT_REGIONAL_SPELL_LAB = "Hotspots: Spell Lab" + HOTSPOT_REGIONAL_WHITE_HOUSE = "Hotspots: White House" HOTSPOT_ROPE_BRIDGE = "Hotspot: Rope Bridge" HOTSPOT_SKULL_CAGE = "Hotspot: Skull Cage" HOTSPOT_SNAPDRAGON = "Hotspot: Snapdragon" @@ -93,6 +204,7 @@ class ZorkGrandInquisitorItems(enum.Enum): HOTSPOT_SODA_MACHINE_COIN_SLOT = "Hotspot: Soda Machine Coin Slot" HOTSPOT_SOUVENIR_COIN_SLOT = "Hotspot: Souvenir Coin Slot" HOTSPOT_SPELL_CHECKER = "Hotspot: Spell Checker" + HOTSPOT_SPELL_LAB_BRIDGE_EXIT = "Hotspot: Spell Lab Bridge Exit" HOTSPOT_SPELL_LAB_CHASM = "Hotspot: Spell Lab Chasm" HOTSPOT_SPRING_MUSHROOM = "Hotspot: Spring Mushroom" HOTSPOT_STUDENT_ID_MACHINE = "Hotspot: Student ID Machine" @@ -100,10 +212,8 @@ class ZorkGrandInquisitorItems(enum.Enum): HOTSPOT_TAVERN_FLY = "Hotspot: Tavern Fly" HOTSPOT_TOTEMIZER_SWITCH = "Hotspot: Totemizer Switch" HOTSPOT_TOTEMIZER_WHEELS = "Hotspot: Totemizer Wheels" - HOTSPOT_WELL = "Hotspot: Well" HUNGUS_LARD = "Hungus Lard" - JAR_OF_HOTBUGS = "Jar of Hotbugs" - LANTERN = "Lantern" + LANDMARK = "Zork Tour Landmark" LARGE_TELEGRAPH_HAMMER = "Large Telegraph Hammer" LUCYS_PLAYING_CARD_1 = "Lucy's Playing Card: 1 Pip" LUCYS_PLAYING_CARD_2 = "Lucy's Playing Card: 2 Pips" @@ -111,33 +221,37 @@ class ZorkGrandInquisitorItems(enum.Enum): LUCYS_PLAYING_CARD_4 = "Lucy's Playing Card: 4 Pips" MAP = "Map" MEAD_LIGHT = "Mead Light" - MOSS_OF_MAREILON = "Moss of Mareilon" - MUG = "Mug" + MONASTERY_ROPE = "Monastery Rope" OLD_SCRATCH_CARD = "Old Scratch Card" PERMA_SUCK_MACHINE = "Perma-Suck Machine" PLASTIC_SIX_PACK_HOLDER = "Plastic Six-Pack Holder" POUCH_OF_ZORKMIDS = "Pouch of Zorkmids" PROZORK_TABLET = "Prozork Tablet" - QUELBEE_HONEYCOMB = "Quelbee Honeycomb" - ROPE = "Rope" + SANDWITCH_WRAPPER = "Sandwitch Wrapper" SCROLL_FRAGMENT_ANS = "Scroll Fragment: ANS" SCROLL_FRAGMENT_GIV = "Scroll Fragment: GIV" SHOVEL = "Shovel" + SKULL_OF_YORUK = "Skull of Yoruk" SNAPDRAGON = "Snapdragon" + SPELL_BEBURTT = "Spell: BEBURTT" SPELL_GLORF = "Spell: GLORF" SPELL_GOLGATEM = "Spell: GOLGATEM" SPELL_IGRAM = "Spell: IGRAM" SPELL_KENDALL = "Spell: KENDALL" + SPELL_OBIDIL = "Spell: OBIDIL" SPELL_NARWILE = "Spell: NARWILE" SPELL_REZROV = "Spell: REZROV" + SPELL_SNAVIG = "Spell: SNAVIG" SPELL_THROCK = "Spell: THROCK" - SPELL_VOXAM = "Spell: VOXAM" + SPELL_YASTARD = "Spell: YASTARD" STUDENT_ID = "Student ID" + SUBWAY_DESTINATION_CROSSROADS = "Subway Destination: Crossroads" SUBWAY_DESTINATION_FLOOD_CONTROL_DAM = "Subway Destination: Flood Control Dam #3" SUBWAY_DESTINATION_HADES = "Subway Destination: Hades" SUBWAY_DESTINATION_MONASTERY = "Subway Destination: Monastery" SUBWAY_TOKEN = "Subway Token" SWORD = "Sword" + TELEPORTER_DESTINATION_CROSSROADS = "Teleporter Destination: Crossroads" TELEPORTER_DESTINATION_DM_LAIR = "Teleporter Destination: Dungeon Master's Lair" TELEPORTER_DESTINATION_GUE_TECH = "Teleporter Destination: GUE Tech" TELEPORTER_DESTINATION_HADES = "Teleporter Destination: Hades" @@ -148,14 +262,30 @@ class ZorkGrandInquisitorItems(enum.Enum): TOTEM_LUCY = "Totem: Lucy" TOTEMIZER_DESTINATION_HALL_OF_INQUISITION = "Totemizer Destination: Hall of Inquisition" TOTEMIZER_DESTINATION_INFINITY = "Totemizer Destination: Infinity" + TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY = "Totemizer Destination: Newark, New Jersey" TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL = "Totemizer Destination: Straight to Hell" TOTEMIZER_DESTINATION_SURFACE_OF_MERZ = "Totemizer Destination: Surface of Merz" + TRAP_INFINITE_CORRIDOR = "Infinite Corridor Trap" + TRAP_REVERSE_CONTROLS = "Reverse Controls Trap" + TRAP_TELEPORT = "Teleport Trap" + TRAP_ZVISION = "ZVision Trap" + WELL_ROPE = "Well Rope" ZIMDOR_SCROLL = "ZIMDOR Scroll" ZORK_ROCKS = "Zork Rocks" +class ZorkGrandInquisitorItemTransforms(enum.Enum): + MAKE_FILLER = "Make Filler" + + +class ZorkGrandInquisitorLandmarksanity(enum.Enum): + OFF = 0 + ON = 1 + + class ZorkGrandInquisitorLocations(enum.Enum): ALARM_SYSTEM_IS_DOWN = "Alarm System is Down" + AN_EXCELLENT_POPPING_UTENSIL = "An Excellent Popping Utensil" ARREST_THE_VANDAL = "Arrest the Vandal!" ARTIFACTS_EXPLAINED = "Artifacts, Explained" A_BIG_FAT_SASSY_2_HEADED_MONSTER = "A Big, Fat, SASSY 2-Headed Monster" @@ -174,27 +304,35 @@ class ZorkGrandInquisitorLocations(enum.Enum): CASTLE_WATCHING_A_FIELD_GUIDE = "Castle Watching: A Field Guide" CAVES_NOTES = "Cave's Notes" CLOSING_THE_TIME_TUNNELS = "Closing the Time Tunnels" + COME_TO_PAPA_YOU_NUT = "Come to Papa. You Nut" CRISIS_AVERTED = "Crisis Averted" CUT_THAT_OUT_YOU_LITTLE_CREEP = "Cut That Out You Little Creep!" - DEATH_ARRESTED_WITH_JACK = "Death: Arrested With Jack" - DEATH_ATTACKED_THE_QUELBEES = "Death: Attacked the Quelbees" - DEATH_CLIMBED_OUT_OF_THE_WELL = "Death: Climbed Out of the Well" - DEATH_EATEN_BY_A_GRUE = "Death: Eaten by a Grue" - DEATH_JUMPED_IN_BOTTOMLESS_PIT = "Death: Jumped in Bottomless Pit" - DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER = "Death: Lost Game of Strip Grue, Fire, Water" - DEATH_LOST_SOUL_TO_OLD_SCRATCH = "Death: Lost Soul to Old Scratch" - DEATH_OUTSMARTED_BY_THE_QUELBEES = "Death: Outsmarted by the Quelbees" - DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD = "Death: Sliced up by the Invisible Guard" - DEATH_STEPPED_INTO_THE_INFINITE = "Death: Step Into the Infinite" - DEATH_SWALLOWED_BY_A_DRAGON = "Death: Swallowed by a Dragon" - DEATH_THROCKED_THE_GRASS = "Death: THROCKed the Grass" - DEATH_TOTEMIZED = "Death: Totemized?" - DEATH_TOTEMIZED_PERMANENTLY = "Death: Totemized... Permanently" - DEATH_YOURE_NOT_CHARON = "Death: You're Not Charon!?" - DEATH_ZORK_ROCKS_EXPLODED = "Death: Zork Rocks Exploded" + DEATH_ARRESTED_WITH_JACK = "Death: Interesting Philosophical Thought" + DEATH_ATTACKED_THE_QUELBEES = "Death: Just Under Half a Second" + DEATH_CLIMBED_OUT_OF_THE_WELL = "Death: Breaking Curfew" + DEATH_EATEN_BY_A_GRUE = "Death: Pitch Black Cave in a Zork Game" + DEATH_JUMPED_IN_BOTTOMLESS_PIT = "Death: Old Age" + DEATH_LOST_GAME_OF_STRIP_GRUE_FIRE_WATER = "Death: Mind-Boggling Unlucky Streak" + DEATH_LOST_SOUL_TO_OLD_SCRATCH = "Death: Contractually Obligated" + DEATH_OUTSMARTED_BY_THE_QUELBEES = "Death: Antennae Nose Plugs" + DEATH_SLICED_UP_BY_THE_INVISIBLE_GUARD = "Death: Chop, Slice, Puree" + DEATH_STEPPED_INTO_THE_INFINITE = "Death: Tiny Step for Mankind" + DEATH_SWALLOWED_BY_A_DRAGON = "Death: Spare Loft Behind Uvula" + DEATH_THROCKED_THE_GRASS = "Death: Expressly Forbidden" + DEATH_TOTEMIZED_INFINITY = "Death: Airless Expanse of the Cosmos" + DEATH_TOTEMIZED_NEWARK_NEW_JERSEY = "Death: Arteriosclerosis" + DEATH_TOTEMIZED_PERMANENTLY_HALLS_OF_INQUISITION = "Death: Ms. Peeper's Paperweight" + DEATH_TOTEMIZED_PERMANENTLY_INFINITY = "Death: S.S. Feinstein's Tractor Beam" + DEATH_TOTEMIZED_PERMANENTLY_NEWARK_NEW_JERSEY = "Death: Manhole Cover in New Jersey" + DEATH_TOTEMIZED_PERMANENTLY_STRAIGHT_TO_HELL = "Death: Evil Spawn's Plaything" + DEATH_TOTEMIZED_PERMANENTLY_SURFACE_OF_MERZ = "Death: Eternity Gazing at the Scenic Vista" + DEATH_TOTEMIZED_SURFACE_OF_MERZ = "Death: Very, Very Pretty" + DEATH_YOURE_NOT_CHARON = "Death: Not Charon" + DEATH_ZORK_ROCKS_EXPLODED = "Death: Pretty Painless" DENIED_BY_THE_LAKE_MONSTER = "Denied by the Lake Monster" - DESPERATELY_SEEKING_TUTOR = "Desperately Seeking Tutor" + DINGWHACKER_DELUXE = "Dingwhacker Deluxe" DONT_EVEN_START_WITH_US_SPARKY = "Don't Even Start With Us, Sparky" + DONT_GO_SPENDING_IT_ALL_IN_ONE_PLACE = "Don't Go Spending it All in One Place" DOOOOOOWN = "Doooooown" DOWN = "Down" DRAGON_ARCHIPELAGO_TIME_TUNNEL = "Dragon Archipelago Time Tunnel" @@ -209,37 +347,59 @@ class ZorkGrandInquisitorLocations(enum.Enum): FLYING_SNAPDRAGON = "Flying Snapdragon" FROBUARY_3_UNDERGROUNDHOG_DAY = "Frobruary 3 - Undergroundhog Day" GETTING_SOME_CHANGE = "Getting Some Change" + GOOD_PUZZLE_SMART_BROG = "Good Puzzle. Smart Brog" GO_AWAY = "GO AWAY!" - GUE_TECH_DEANS_LIST = "GUE Tech Dean's List" GUE_TECH_ENTRANCE_EXAM = "GUE Tech Entrance Exam" - GUE_TECH_HEALTH_MEMO = "GUE Tech Health Memo" - GUE_TECH_MAGEMEISTERS = "GUE Tech Magemeisters" HAVE_A_HELL_OF_A_DAY = "Have a Hell of a Day!" HELLO_THIS_IS_SHONA_FROM_GURTH_PUBLISHING = "Hello, This is Shona from Gurth Publishing" HELP_ME_CANT_BREATHE = "Help... Me. Can't... Breathe" HEY_FREE_DIRT = "Hey, Free Dirt!" - HI_MY_NAME_IS_DOUG = "Hi, My Name is Doug" + HMMM_BIG_TOOTHPICK = "Hmmm. Big Toothpick" HMMM_INFORMATIVE_YET_DEEPLY_DISTURBING = "Hmmm. Informative. Yet Deeply Disturbing" - HOLD_ON_FOR_AN_IMPORTANT_MESSAGE = "Hold on for an Important Message" HOW_TO_HYPNOTIZE_YOURSELF = "How to Hypnotize Yourself" HOW_TO_WIN_AT_DOUBLE_FANUCCI = "How to Win at Double Fanucci" IMBUE_BEBURTT = "Imbue BEBURTT" IM_COMPLETELY_NUDE = "I'm Completely Nude" + INFLATUS_THE_ETERNAL = "Inflatus the Eternal" INTO_THE_FOLIAGE = "Into the Foliage" INVISIBLE_FLOWERS = "Invisible Flowers" IN_CASE_OF_ADVENTURE = "In Case of Adventure, Break Glass!" IN_MAGIC_WE_TRUST = "In Magic We Trust" + ITS_ALMOST_AS_IF_IT_WERE_INFINITE = "It's Almost as if it Were... Infinite" ITS_ONE_OF_THOSE_ADVENTURERS_AGAIN = "It's One of Those Adventurers Again..." + ITS_PLAYING_A_LITTLE_HARD_TO_GET = "It's Playing a Little Hard to Get" + IT_DOESNT_APPEAR_TO_BE_FOOLED = "It Doesn't Appear to be Fooled" + I_AM_NOT_IMPRESSED = "I Am Not Impressed" I_DONT_THINK_YOU_WOULDVE_WANTED_THAT_TO_WORK_ANYWAY = "I Don't Think You Would've Wanted That to Work Anyway" I_DONT_WANT_NO_TROUBLE = "I Don't Want No Trouble!" - I_HOPE_YOU_CAN_CLIMB_UP_THERE = "I Hope You Can Climb Up There With All This Junk" I_LIKE_YOUR_STYLE = "I Like Your Style!" I_SPIT_ON_YOUR_FILTHY_COINAGE = "I Spit on Your Filthy Coinage" + LANDMARK_DRAGON_ARCHIPELAGO = "Landmark Visited: Dragon Archipelago" + LANDMARK_DUNGEON_MASTERS_HOUSE = "Landmark Visited: Dungeon Master's House" + LANDMARK_FLOOD_CONTROL_DAM_3 = "Landmark Visited: Flood Control Dam #3" + LANDMARK_GATES_OF_HELL = "Landmark Visited: Gates of Hell" + LANDMARK_GREAT_UNDERGROUND_EMPIRE_ENTRANCE = "Landmark Visited: Great Underground Empire Entrance" + LANDMARK_GUE_TECH_FOUNTAIN_INSIDE = "Landmark Visited: GUE Tech Fountain (Inside)" + LANDMARK_GUE_TECH_FOUNTAIN_OUTSIDE = "Landmark Visited: GUE Tech Fountain (Outside)" + LANDMARK_HADES_SHORE = "Landmark Visited: Hades Shore" + LANDMARK_INFINITE_CORRIDOR = "Landmark Visited: Infinite Corridor" + LANDMARK_INQUISITION_HEADQUARTERS = "Landmark Visited: Inquisition Headquarters" + LANDMARK_JACKS_SHOP = "Landmark Visited: Jack's Shop" + LANDMARK_MIRROR_ROOM = "Landmark Visited: Mirror Room" + LANDMARK_PAST_PORT_FOOZLE = "Landmark Visited: Past Port Foozle" + LANDMARK_PORT_FOOZLE = "Landmark Visited: Port Foozle" + LANDMARK_SPELL_CHECKER = "Landmark Visited: Spell Checker" + LANDMARK_TOTEMIZER = "Landmark Visited: Totemizer" + LANDMARK_UMBRELLA_TREE = "Landmark Visited: Umbrella Tree" + LANDMARK_UNDERGROUND_UNDERGROUND_ENTRANCE = "Landmark Visited: Underground Underground Entrance" + LANDMARK_WALKING_CASTLES_HEART = "Landmark Visited: Walking Castle's Heart" + LANDMARK_WHITE_HOUSE = "Landmark Visited: White House" LIT_SUNFLOWERS = "Lit Sunflowers" - MAGIC_FOREVER = "Magic Forever!" + LOOK_AN_ICE_CREAM_BAR = "Look! An Ice Cream Bar" MAILED_IT_TO_HELL = "Mailed it to Hell" MAKE_LOVE_NOT_WAR = "Make Love, Not War" MEAD_LIGHT = "Mead Light?" + ME_I_AM_THE_BOSS_OF_YOU = "Me! I am the Boss of You!" MIKES_PANTS = "Mike's Pants" MUSHROOM_HAMMERED = "Mushroom, Hammered" NATIONAL_TREASURE = "300 Year Old National Treasure" @@ -249,6 +409,7 @@ class ZorkGrandInquisitorLocations(enum.Enum): NOW_YOU_LOOK_LIKE_US_WHICH_IS_AN_IMPROVEMENT = "Now You Look Like Us, Which is an Improvement" NO_AUTOGRAPHS = "No Autographs" NO_BONDAGE = "No Bondage" + NO_ONE_RETURNS_FROM_HADES = "NO ONE Returns from Hades" OBIDIL_DRIED_UP = "OBIDIL, Dried Up" OH_DEAR_GOD_ITS_A_DRAGON = "Oh Dear God, It's a Dragon!" OH_VERY_FUNNY_GUYS = "Oh, Very Funny Guys" @@ -262,12 +423,14 @@ class ZorkGrandInquisitorLocations(enum.Enum): PLEASE_DONT_THROCK_THE_GRASS = "Please Don't THROCK the Grass" PORT_FOOZLE_TIME_TUNNEL = "Port Foozle Time Tunnel" PROZORKED = "Prozorked" + PURPLE_BEAST_ALARM_SYSTEM = "Purple Beast Alarm System" REASSEMBLE_SNAVIG = "Reassemble SNAVIG" RESTOCKED_ON_GRUESDAY = "Restocked on Gruesday" RIGHT_HELLO_YES_UH_THIS_IS_SNEFFLE = "Right. Hello. Yes. Uh, This is Sneffle" RIGHT_UH_SORRY_ITS_ME_AGAIN_SNEFFLE = "Right. Uh, Sorry. It's Me Again. Sneffle" SNAVIG_REPAIRED = "SNAVIG, Repaired" SOUVENIR = "Souvenir" + SPELL_CHECK_COMPLETE = "Spell Check Complete" STRAIGHT_TO_HELL = "Straight to Hell" STRIP_GRUE_FIRE_WATER = "Strip Grue, Fire, Water" SUCKING_ROCKS = "Sucking Rocks" @@ -281,6 +444,7 @@ class ZorkGrandInquisitorLocations(enum.Enum): THE_ALCHEMICAL_DEBACLE = "The Alchemical Debacle" THE_ENDLESS_FIRE = "The Endless Fire" THE_FLATHEADIAN_FUDGE_FIASCO = "The Flatheadian Fudge Fiasco" + THE_ONLY_WAY_TO_WIN_IS_NOT_TO_PLAY = "The Only Way to Win is Not to Play" THE_PERILS_OF_MAGIC = "The Perils of Magic" THE_UNDERGROUND_UNDERGROUND = "The Underground Underground" THIS_DOESNT_LOOK_ANYTHING_LIKE_THE_BROCHURE = "This Doesn't Look Anything Like the Brochure" @@ -289,40 +453,55 @@ class ZorkGrandInquisitorLocations(enum.Enum): TOTEMIZED_DAILY_BILLBOARD = "Totemized Daily Billboard Functioning Correctly" UH_OH_BROG_CANT_SWIM = "Uh-Oh. Brog Can't Swim" UMBRELLA_FLOWERS = "Umbrella Flowers" + UM_AH_UM_AH_UM_AH = "Um. Ah. Um. Ah. Um. Ah." UP = "Up" USELESS_BUT_FUN = "Useless, But Fun" UUUUUP = "Uuuuup" VOYAGE_OF_CAPTAIN_ZAHAB = "Voyage of Captain Zahab" WANT_SOME_RYE_COURSE_YA_DO = "Want Some Rye? Course Ya Do!" + WANT_SOME_RYE_COURSE_YA_DO_PAST = "Want Some Rye? ... Course Ya Do!" WE_DONT_SERVE_YOUR_KIND_HERE = "We Don't Serve Your Kind Here" WE_GOT_A_HIGH_ROLLER = "We Got a High Roller!" WHAT_ARE_YOU_STUPID = "What Are You, Stupid?" WHITE_HOUSE_TIME_TUNNEL = "White House Time Tunnel" + WHOOPS = "Whoops!" WOW_IVE_NEVER_GONE_INSIDE_HIM_BEFORE = "Wow! I've Never Gone Inside Him Before!" YAD_GOHDNUORGREDNU_3_YRAUBORF = "yaD gohdnuorgrednU - 3 yrauborF" YOUR_PUNY_WEAPONS_DONT_PHASE_ME_BABY = "Your Puny Weapons Don't Phase Me, Baby!" YOU_DONT_GO_MESSING_WITH_A_MANS_ZIPPER = "You Don't Go Messing With a Man's Zipper" YOU_GAINED_86_EXPERIENCE_POINTS = "You Gained 86 Experience Points" + YOU_LOSE_MUFFET_ANTE_UP = "You Lose, Muffet. Ante Up" YOU_ONE_OF_THEM_AGITATORS_AINT_YA = "You One of Them Agitators, Ain't Ya?" YOU_WANT_A_PIECE_OF_ME_DOCK_BOY = "You Want a Piece of Me, Dock Boy? or Girl" + ZIMDOR_IS_UNDAMAGED = "ZIMDOR is Undamaged" + + +class ZorkGrandInquisitorLocationTransforms(enum.Enum): + REMOVE = "Remove" class ZorkGrandInquisitorRegions(enum.Enum): + ANYWHERE = "Anywhere" + BOTTOM_OF_THE_WELL = "Bottom of the Well" CROSSROADS = "Crossroads" DM_LAIR = "Dungeon Master's Lair" - DM_LAIR_INTERIOR = "Dungeon Master's Lair - Interior" + DM_LAIR_INTERIOR = "Dungeon Master's Lair - House" DRAGON_ARCHIPELAGO = "Dragon Archipelago" DRAGON_ARCHIPELAGO_DRAGON = "Dragon Archipelago - Dragon" ENDGAME = "Endgame" GUE_TECH = "GUE Tech" + GUE_TECH_ENTRANCE = "GUE Tech - Entrance Area" GUE_TECH_HALLWAY = "GUE Tech - Hallway" GUE_TECH_OUTSIDE = "GUE Tech - Outside" HADES = "Hades" - HADES_BEYOND_GATES = "Hades - Beyond Gates" + HADES_BEYOND_GATES = "Hades - Beyond the Gates of Hell" HADES_SHORE = "Hades - Shore" MENU = "Menu" MONASTERY = "Monastery" MONASTERY_EXHIBIT = "Monastery - Exhibit" + OUTSIDE_PORT_FOOZLE_INQUISITION_HQ = "Outside Port Foozle - Inquisition Headquarters" + OUTSIDE_PORT_FOOZLE_SIGNPOST = "Outside Port Foozle - Signpost" + OUTSIDE_PORT_FOOZLE_WELL = "Outside Port Foozle - Well" PORT_FOOZLE = "Port Foozle" PORT_FOOZLE_JACKS_SHOP = "Port Foozle - Jack's Shop" PORT_FOOZLE_PAST = "Port Foozle Past" @@ -331,20 +510,44 @@ class ZorkGrandInquisitorRegions(enum.Enum): SPELL_LAB_BRIDGE = "Spell Lab - Bridge" SUBWAY_CROSSROADS = "Subway Platform - Crossroads" SUBWAY_FLOOD_CONTROL_DAM = "Subway Platform - Flood Control Dam #3" + SUBWAY_HADES = "Subway Platform - Hades" SUBWAY_MONASTERY = "Subway Platform - Monastery" + TELEPORTER = "Teleportation Station" WALKING_CASTLE = "Walking Castle" WHITE_HOUSE = "White House" + WHITE_HOUSE_INTERIOR = "White House - Interior" + + +class ZorkGrandInquisitorStartingLocations(enum.Enum): + PORT_FOOZLE = 0 + CROSSROADS = 1 + DM_LAIR = 2 + DM_LAIR_INTERIOR = 3 + GUE_TECH = 4 + SPELL_LAB = 5 + HADES_SHORE = 6 + SUBWAY_FLOOD_CONTROL_DAM = 7 + MONASTERY = 8 + MONASTERY_EXHIBIT = 9 class ZorkGrandInquisitorTags(enum.Enum): CORE = "Core" DEATHSANITY = "Deathsanity" FILLER = "Filler" + GOAL_ARTIFACT_OF_MAGIC_HUNT = "Goal: Artifact of Magic Hunt" + GOAL_GRIM_JOURNEY = "Goal: Grim Journey" + GOAL_SPELL_HEIST = "Goal: Spell Heist" + GOAL_THREE_ARTIFACTS = "Goal: Three Artifacts" + GOAL_ZORK_TOUR = "Goal: Zork Tour" HOTSPOT = "Hotspot" + HOTSPOT_REGIONAL = "Regional Hotspot" INVENTORY_ITEM = "Inventory Item" + LANDMARKSANITY = "Landmarksanity" MISSABLE = "Missable" SPELL = "Spell" SUBWAY_DESTINATION = "Subway Destination" TELEPORTER_DESTINATION = "Teleporter Destination" TOTEMIZER_DESTINATION = "Totemizer Destination" TOTEM = "Totem" + TRAP = "Trap" diff --git a/worlds/zork_grand_inquisitor/game_controller.py b/worlds/zork_grand_inquisitor/game_controller.py index 7a60a1460829..4135c777576c 100644 --- a/worlds/zork_grand_inquisitor/game_controller.py +++ b/worlds/zork_grand_inquisitor/game_controller.py @@ -1,13 +1,29 @@ +# TODO: Tracker items seem to be delayed by 1 item received? UI update firing before game_controller sets updated? + import collections +import datetime import functools import logging +import random +import traceback # TODO: Only in dev +import time -from typing import Dict, Optional, Set, Tuple, Union +from typing import Dict, List, Optional, Set, Tuple, Union +from .data.entrance_randomizer_data import entrances_to_game_locations_reverse, relevant_game_locations from .data.item_data import item_data, ZorkGrandInquisitorItemData from .data.location_data import location_data, ZorkGrandInquisitorLocationData -from .data.missable_location_grant_conditions_data import ( +from .data.mapping_data import ( + death_cause_labels, + entrance_names, + game_location_to_region, + hotspots_for_regional_hotspot, + labels_for_enum_items, + voxam_cast_game_locations, +) + +from .data.missable_location_data import ( missable_location_grant_conditions_data, ZorkGrandInquisitorMissableLocationGrantConditionsData, ) @@ -15,9 +31,17 @@ from .data_funcs import game_id_to_items, items_with_tag, locations_with_tag from .enums import ( + ZorkGrandInquisitorClientSeedInformation, + ZorkGrandInquisitorCraftableSpellBehaviors, + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorEntranceRandomizer, ZorkGrandInquisitorGoals, + ZorkGrandInquisitorHotspots, ZorkGrandInquisitorItems, + ZorkGrandInquisitorLandmarksanity, ZorkGrandInquisitorLocations, + ZorkGrandInquisitorRegions, + ZorkGrandInquisitorStartingLocations, ZorkGrandInquisitorTags, ) @@ -35,7 +59,10 @@ class GameController: completed_locations_queue: collections.deque received_items_queue: collections.deque + all_spell_items: Set[ZorkGrandInquisitorItems] all_hotspot_items: Set[ZorkGrandInquisitorItems] + all_goal_items: Set[ZorkGrandInquisitorItems] + all_trap_items: Set[ZorkGrandInquisitorItems] game_id_to_items: Dict[int, ZorkGrandInquisitorItems] @@ -43,11 +70,56 @@ class GameController: available_inventory_slots: Set[int] + goal_item_count: int goal_completed: bool + game_location: Optional[str] + game_location_since: int + option_goal: Optional[ZorkGrandInquisitorGoals] - option_deathsanity: Optional[bool] + option_artifacts_of_magic_required: Optional[int] + option_artifacts_of_magic_total: Optional[int] + option_landmarks_required: Optional[int] + option_deaths_required: Optional[int] + option_starting_location: Optional[ZorkGrandInquisitorStartingLocations] + option_hotspots: Optional[ZorkGrandInquisitorHotspots] + option_craftable_spells: Optional[ZorkGrandInquisitorCraftableSpellBehaviors] + option_wild_voxam: Optional[bool] + option_wild_voxam_chance: Optional[int] + option_deathsanity: Optional[ZorkGrandInquisitorDeathsanity] + option_landmarksanity: Optional[ZorkGrandInquisitorLandmarksanity] + option_entrance_randomizer: Optional[ZorkGrandInquisitorEntranceRandomizer] + option_entrance_randomizer_include_subway_destinations: Optional[bool] + option_trap_percentage: Optional[int] option_grant_missable_location_checks: Optional[bool] + option_client_seed_information: Optional[ZorkGrandInquisitorClientSeedInformation] + option_death_link: Optional[bool] + + starter_kit: Optional[List[str]] + initial_totemizer_destination: Optional[ZorkGrandInquisitorItems] + + entrance_randomizer_data: Dict[Tuple[str, str], Tuple[str, str]] + entrance_randomizer_last_locations_visited: collections.deque + + discovered_regions: Set[str] + discovered_entrances: Set[str] + + received_traps: List[ZorkGrandInquisitorItems] + + active_trap: Optional[ZorkGrandInquisitorItems] + active_trap_until: Optional[datetime.datetime] + + energy_link_queue: collections.deque + pause_energy_link_monitoring: bool + + pending_death_link: Tuple[bool, Optional[str], Optional[str]] + outgoing_death_link: Tuple[bool, Optional[str]] + pause_death_monitoring: bool + + save_ids: Optional[Tuple[int, int, int]] + + valid_save_message_shown: bool + invalid_save_message_shown: bool def __init__(self, logger=None) -> None: self.logger = logger @@ -60,12 +132,22 @@ def __init__(self, logger=None) -> None: self.completed_locations_queue = collections.deque() self.received_items_queue = collections.deque() + self.all_spell_items = items_with_tag(ZorkGrandInquisitorTags.SPELL) + self.all_hotspot_items = ( items_with_tag(ZorkGrandInquisitorTags.HOTSPOT) | items_with_tag(ZorkGrandInquisitorTags.SUBWAY_DESTINATION) | items_with_tag(ZorkGrandInquisitorTags.TOTEMIZER_DESTINATION) ) + self.all_goal_items = { + ZorkGrandInquisitorItems.ARTIFACT_OF_MAGIC, + ZorkGrandInquisitorItems.LANDMARK, + ZorkGrandInquisitorItems.DEATH, + } + + self.all_trap_items = items_with_tag(ZorkGrandInquisitorTags.TRAP) + self.game_id_to_items = game_id_to_items() self.possible_inventory_items = ( @@ -76,11 +158,56 @@ def __init__(self, logger=None) -> None: self.available_inventory_slots = set() + self.goal_item_count = 0 self.goal_completed = False + self.game_location = None + self.game_location_since = 0 + self.option_goal = None + self.option_artifacts_of_magic_required = None + self.option_artifacts_of_magic_total = None + self.option_landmarks_required = None + self.option_deaths_required = None + self.option_starting_location = None + self.option_hotspots = None + self.option_craftable_spells = None + self.option_wild_voxam = None + self.option_wild_voxam_chance = None self.option_deathsanity = None + self.option_landmarksanity = None + self.option_entrance_randomizer = None + self.option_entrance_randomizer_include_subway_destinations = None + self.option_trap_percentage = None self.option_grant_missable_location_checks = None + self.option_client_seed_information = None + self.option_death_link = None + + self.starter_kit = None + self.initial_totemizer_destination = None + + self.entrance_randomizer_data = dict() + self.entrance_randomizer_last_locations_visited = collections.deque(maxlen=2) + + self.discovered_regions = {ZorkGrandInquisitorRegions.ANYWHERE.value} + self.discovered_entrances = set() + + self.received_traps = list() + + self.active_trap = None + self.active_trap_until = None + + self.energy_link_queue = collections.deque() + self.pause_energy_link_monitoring = False + + self.pending_death_link = (False, None, None) + self.outgoing_death_link = (False, None) + self.pause_death_monitoring = False + + self.save_ids = None + + self.valid_save_message_shown = False + self.invalid_save_message_shown = False @functools.cached_property def brog_items(self) -> Set[ZorkGrandInquisitorItems]: @@ -113,10 +240,21 @@ def lucy_items(self) -> Set[ZorkGrandInquisitorItems]: def totem_items(self) -> Set[ZorkGrandInquisitorItems]: return self.brog_items | self.griff_items | self.lucy_items + @property + def game_location_required_duration(self) -> int: + if self.option_entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + return 1000 + + return 0 + @functools.cached_property def missable_locations(self) -> Set[ZorkGrandInquisitorLocations]: return locations_with_tag(ZorkGrandInquisitorTags.MISSABLE) + @property + def is_deathsanity(self) -> bool: + return self.option_deathsanity == ZorkGrandInquisitorDeathsanity.ON + def log(self, message) -> None: if self.logger: self.logger.info(message) @@ -134,6 +272,106 @@ def close_process_handle(self) -> bool: def is_process_running(self) -> bool: return self.game_state_manager.is_process_running + def output_seed_information(self) -> None: + if self.option_goal is not None: + self.log("Seed Information:") + + if self.option_client_seed_information == ZorkGrandInquisitorClientSeedInformation.REVEAL_NOTHING: + self.log(" REDACTED by the Inquisition") + return + + self.log(f" Goal: {labels_for_enum_items[self.option_goal]}") + + if self.option_client_seed_information == ZorkGrandInquisitorClientSeedInformation.REVEAL_GOAL: + return + + if self.option_goal == ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: + self.log(f" Artifacts of Magic Required: {self.option_artifacts_of_magic_required}") + self.log(f" Artifacts of Magic Total: {self.option_artifacts_of_magic_total}") + elif self.option_goal == ZorkGrandInquisitorGoals.ZORK_TOUR: + self.log(f" Landmarks Required: {self.option_landmarks_required}") + elif self.option_goal == ZorkGrandInquisitorGoals.GRIM_JOURNEY: + self.log(f" Deaths Required: {self.option_deaths_required}") + + self.log(f" Starting Location: {labels_for_enum_items[self.option_starting_location]}") + self.log(f" Hotspots: {labels_for_enum_items[self.option_hotspots]}") + self.log(f" Craftable Spells: {labels_for_enum_items[self.option_craftable_spells]}") + + if self.option_wild_voxam: + self.log(f" Wild VOXAM: On ({self.option_wild_voxam_chance}% chance)") + else: + self.log(f" Wild VOXAM: Off") + + self.log(f" Deathsanity: {labels_for_enum_items[self.option_deathsanity]}") + self.log(f" Landmarksanity: {labels_for_enum_items[self.option_landmarksanity]}") + self.log(f" Entrance Randomizer: {labels_for_enum_items[self.option_entrance_randomizer]}") + + if self.option_entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + if self.option_entrance_randomizer_include_subway_destinations: + self.log(" Entrance Randomizer - Include Subway Destinations: On") + else: + self.log(" Entrance Randomizer - Include Subway Destinations: Off") + + self.log(f" Trap Percentage: {self.option_trap_percentage}%") + + if self.option_grant_missable_location_checks: + self.log(f" Grant Missable Location Checks: On") + else: + self.log(f" Grant Missable Location Checks: Off") + + if self.option_death_link: + self.log(f" Death Link: On") + else: + self.log(f" Death Link: Off") + + def output_starter_kit(self) -> None: + if self.starter_kit is None: + return + + self.log("Starter Kit:") + + if len(self.starter_kit): + item: str + for item in self.starter_kit: + if self.option_hotspots == ZorkGrandInquisitorHotspots.ENABLED: + if item.startswith("Hotspot"): + continue + elif item in ( + "Hotspot: Dungeon Master's Lair Entrance", + "Hotspot: Spell Lab Bridge Exit", + ): + continue + + self.log(f" {item}") + else: + self.log(" Nothing") + + def output_goal_item_update(self) -> None: + if self.goal_completed: + return + + if self.option_goal == ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: + self.log( + f"Received {self.goal_item_count} of {self.option_artifacts_of_magic_required} required Artifacts of Magic" + ) + + if self.goal_item_count >= self.option_artifacts_of_magic_required: + self.log("All needed Artifacts of Magic have been found! Get to the Walking Castle to win") + elif self.option_goal == ZorkGrandInquisitorGoals.ZORK_TOUR: + self.log( + f"Visited {self.goal_item_count} of {self.option_landmarks_required} required Landmarks" + ) + + if self.goal_item_count >= self.option_landmarks_required: + self.log("All needed Landmarks have been visited! Get to the Port Foozle signpost to win") + elif self.option_goal == ZorkGrandInquisitorGoals.GRIM_JOURNEY: + self.log( + f"Experienced {self.goal_item_count} of {self.option_deaths_required} required Deaths" + ) + + if self.goal_item_count >= self.option_deaths_required: + self.log("All needed Deaths have been experienced! Go beyond the gates of hell to win") + def list_received_brog_items(self) -> None: self.log("Received Brog Items:") @@ -174,11 +412,20 @@ def list_received_lucy_items(self) -> None: self.log(f" {item}") def list_received_hotspots(self) -> None: + if self.option_hotspots == ZorkGrandInquisitorHotspots.ENABLED: + self.log("Hotspots are enabled for this seed and don't require items") + return + self.log("Received Hotspots:") self._process_received_items() - hotspot_items: Set[ZorkGrandInquisitorItems] = items_with_tag(ZorkGrandInquisitorTags.HOTSPOT) + hotspot_items: Set[ZorkGrandInquisitorItems] = set() + if self.option_hotspots == ZorkGrandInquisitorHotspots.REQUIRE_ITEM_PER_REGION: + hotspot_items = items_with_tag(ZorkGrandInquisitorTags.HOTSPOT_REGIONAL) + elif self.option_hotspots == ZorkGrandInquisitorHotspots.REQUIRE_ITEM_PER_HOTSPOT: + hotspot_items = items_with_tag(ZorkGrandInquisitorTags.HOTSPOT) + received_hotspots: Set[ZorkGrandInquisitorItems] = self.received_items & hotspot_items if not len(received_hotspots): @@ -193,11 +440,26 @@ def update(self) -> None: try: self.game_state_manager.refresh_game_location() + if self.game_state_manager.game_location != self.game_location: + self.game_location = self.game_state_manager.game_location + self.game_location_since = int(time.time() * 1000) + + if not self._check_for_valid_save(): + return + + self._apply_initial_totemizer_destination() + self._apply_starting_location() + self._apply_permanent_game_state() self._apply_conditional_game_state() self._apply_permanent_game_flags() + self._manage_game_location() + + if self.option_entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + self._manage_entrance_randomizer() + self._check_for_completed_locations() if self.option_grant_missable_location_checks: @@ -210,14 +472,170 @@ def update(self) -> None: self._apply_conditional_teleports() + if self.option_trap_percentage: + self._manage_traps() + + if self._player_is_at("dg3e"): + self._manage_energy_link() + + if self.option_death_link: + self._handle_death_link() + self._check_for_victory() except Exception as e: self.log_debug(e) + traceback.print_exc() + + def reset(self) -> None: + self.received_items = set() + self.completed_locations = set() + + self.completed_locations_queue = collections.deque() + self.received_items_queue = collections.deque() + + self.available_inventory_slots = set() + + self.goal_item_count = 0 + self.goal_completed = False + + self.game_location = None + self.game_location_since = 0 + + self.option_goal = None + self.option_artifacts_of_magic_required = None + self.option_artifacts_of_magic_total = None + self.option_landmarks_required = None + self.option_deaths_required = None + self.option_starting_location = None + self.option_hotspots = None + self.option_craftable_spells = None + self.option_wild_voxam = None + self.option_wild_voxam_chance = None + self.option_deathsanity = None + self.option_landmarksanity = None + self.option_entrance_randomizer = None + self.option_entrance_randomizer_include_subway_destinations = None + self.option_trap_percentage = None + self.option_grant_missable_location_checks = None + self.option_client_seed_information = None + self.option_death_link = None + + self.starter_kit = None + self.initial_totemizer_destination = None + + self.entrance_randomizer_data = dict() + self.entrance_randomizer_last_locations_visited = collections.deque(maxlen=2) + + self.discovered_regions = {ZorkGrandInquisitorRegions.ANYWHERE.value} + self.discovered_entrances = set() + + self.received_traps = list() + + self.active_trap = None + self.active_trap_until = None + + self.energy_link_queue = collections.deque() + self.pause_energy_link_monitoring = False + + self.pending_death_link = (False, None, None) + self.outgoing_death_link = (False, None) + self.pause_death_monitoring = False + + self.save_ids = None + + self.valid_save_message_shown = False + self.invalid_save_message_shown = False + + def _check_for_valid_save(self) -> bool: + if self._player_is_at("gary"): + return False + + save_ids: Tuple[int, int, int] = ( + self._read_game_state_value_for(19997), + self._read_game_state_value_for(19998), + self._read_game_state_value_for(19999), + ) + + if save_ids == (0, 0, 0): + self._write_game_state_value_for(19997, self.save_ids[0]) + self._write_game_state_value_for(19998, self.save_ids[1]) + self._write_game_state_value_for(19999, self.save_ids[2]) + elif save_ids != self.save_ids: + if not self.invalid_save_message_shown: + self.log( + "Unexpected save file for this seed. Please load a valid save file or start a new game." + ) + + self.invalid_save_message_shown = True + self.valid_save_message_shown = False + + return False + + if not self.valid_save_message_shown: + self.log("Valid save file detected. Have fun!") + + self.valid_save_message_shown = True + self.invalid_save_message_shown = False + + return True + + def _apply_initial_totemizer_destination(self) -> None: + if self.initial_totemizer_destination is None: + return None + + if self._read_game_state_value_for(19986) == 0: + mapping: Dict[ZorkGrandInquisitorItems, int] = { + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION: 0, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ: 1, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY: 2, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY: 3, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL: 4, + } + + self._write_game_state_value_for(9617, mapping[self.initial_totemizer_destination]) + self._write_game_state_value_for(19986, 1) + + def _apply_starting_location(self, force: bool = False) -> None: + if self.option_starting_location is None: + return None + + if self._read_game_state_value_for(19985) == 0 or force: + if self.option_starting_location == ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: + self.game_state_manager.set_game_location("ps10", 825) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.CROSSROADS: + self.game_state_manager.set_game_location("uc10", 1200) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.DM_LAIR: + self.game_state_manager.set_game_location("dg10", 1410) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: + self.game_state_manager.set_game_location("dv10", 1673) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.GUE_TECH: + self.game_state_manager.set_game_location("tr20", 150) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.SPELL_LAB: + self.game_state_manager.set_game_location("tp20", 1244) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.HADES_SHORE: + self.game_state_manager.set_game_location("uh10", 950) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: + self.game_state_manager.set_game_location("ue10", 1578) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.MONASTERY: + self.game_state_manager.set_game_location("mt20", 0) + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: + self.game_state_manager.set_game_location("me10", 1023) + + self._write_game_state_value_for(19985, 1) + time.sleep(0.1) + + self.game_state_manager.refresh_game_location() + + if self.game_state_manager.game_location != self.game_location: + self.game_location = self.game_state_manager.game_location + self.game_location_since = int(time.time() * 1000) def _apply_permanent_game_state(self) -> None: self._write_game_state_value_for(10934, 1) # Rope Taken self._write_game_state_value_for(10418, 1) # Mead Light Taken self._write_game_state_value_for(10275, 0) # Lantern in Crate + self._write_game_state_value_for(10297, 0) # Lantern on Jack's Table + self._write_game_state_value_for(5221, 1) # Player has Lantern self._write_game_state_value_for(13929, 1) # Great Underground Door Open self._write_game_state_value_for(13968, 1) # Subway Token Taken self._write_game_state_value_for(12930, 1) # Hammer Taken @@ -238,6 +656,7 @@ def _apply_permanent_game_state(self) -> None: self._write_game_state_value_for(3716, 1) # NARWILE Scroll Taken self._write_game_state_value_for(17147, 1) # Lucy Totem Taken self._write_game_state_value_for(9818, 1) # Middle Telegraph Hammer Taken + self._write_game_state_value_for(5032, 0) # Always Consider SNAVIG to not be Reassembled self._write_game_state_value_for(3766, 0) # ANS Scroll in Window self._write_game_state_value_for(4980, 0) # ANS Scroll in Window self._write_game_state_value_for(3768, 0) # GIV Scroll in Window @@ -254,6 +673,7 @@ def _apply_permanent_game_state(self) -> None: self._write_game_state_value_for(1341, 1) # Griff's Inflatable Raft Taken self._write_game_state_value_for(1477, 1) # Griff's Air Pump Taken self._write_game_state_value_for(1814, 1) # Griff's Dragon Tooth Taken + self._write_game_state_value_for(15424, 1) # Initial State of Card Game self._write_game_state_value_for(15403, 0) # Lucy's Cards Taken self._write_game_state_value_for(15404, 1) # Lucy's Cards Taken self._write_game_state_value_for(15405, 4) # Lucy's Cards Taken @@ -263,54 +683,58 @@ def _apply_permanent_game_state(self) -> None: self._write_game_state_value_for(13934, 1) # Skip Well Cutscenes self._write_game_state_value_for(13935, 1) # Skip Well Cutscenes self._write_game_state_value_for(13384, 1) # Skip Meanwhile... Cutscene + self._write_game_state_value_for(18275, 1) # Skip Flashback Cutscene self._write_game_state_value_for(8620, 1) # First Coin Paid to Charon self._write_game_state_value_for(8731, 1) # First Coin Paid to Charon + self._write_game_state_value_for(191, 1) # VOXAM Learned + self._write_game_state_value_for(19243, 0) # Keep VOXAM Miscast Counter at 0 + self._write_game_state_value_for(15384, 0) # Never Consider All Artifacts to be Placed def _apply_conditional_game_state(self): - # Can teleport to Dungeon Master's Lair + # Teleporter Destinations + if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_CROSSROADS): + self._write_game_state_value_for(12918, 1) + else: + self._write_game_state_value_for(12918, 0) + if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_DM_LAIR): self._write_game_state_value_for(2203, 1) else: self._write_game_state_value_for(2203, 0) - # Can teleport to GUE Tech if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_GUE_TECH): self._write_game_state_value_for(7132, 1) else: self._write_game_state_value_for(7132, 0) - # Can Teleport to Spell Lab if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_SPELL_LAB): self._write_game_state_value_for(16545, 1) else: self._write_game_state_value_for(16545, 0) - # Can Teleport to Hades if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_HADES): self._write_game_state_value_for(7119, 1) else: self._write_game_state_value_for(7119, 0) - # Can Teleport to Monastery Station if self._player_has(ZorkGrandInquisitorItems.TELEPORTER_DESTINATION_MONASTERY): self._write_game_state_value_for(7148, 1) else: self._write_game_state_value_for(7148, 0) - # Initial Totemizer Destination - should_force_initial_totemizer_destination: bool = True - - if self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION): - should_force_initial_totemizer_destination = False - elif self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL): - should_force_initial_totemizer_destination = False - elif self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY): - should_force_initial_totemizer_destination = False - elif self._player_has(ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ): - should_force_initial_totemizer_destination = False + # Monastery Rope + if self._player_has(ZorkGrandInquisitorItems.MONASTERY_ROPE): + self._write_game_state_value_for(9637, 1) + else: + self._write_game_state_value_for(9637, 0) - if should_force_initial_totemizer_destination: - self._write_game_state_value_for(9617, 2) + # Well Rope + if self._player_has(ZorkGrandInquisitorItems.WELL_ROPE): + self._write_game_state_value_for(10304, 1) + self._write_game_state_value_for(13938, 0) + else: + self._write_game_state_value_for(10304, 0) + self._write_game_state_value_for(13938, 1) # Pouch of Zorkmids if self._player_has(ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS): @@ -318,6 +742,22 @@ def _apply_conditional_game_state(self): else: self._write_game_state_value_for(5827, 0) + # Cocoa Ingredients + is_cocoa_brewed: bool = ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU in self.completed_locations + + if self._player_has(ZorkGrandInquisitorItems.COCOA_INGREDIENTS) and not is_cocoa_brewed: + self._write_game_state_value_for(4750, 1) # Jar of Hotbugs + self._write_game_state_value_for(4763, 1) # Moss of Mareilon + self._write_game_state_value_for(4766, 1) # Flatheadia Fudge + self._write_game_state_value_for(4772, 1) # Mug + self._write_game_state_value_for(4769, 1) # Quelbee Honeycomb + else: + self._write_game_state_value_for(4750, 0) + self._write_game_state_value_for(4763, 0) + self._write_game_state_value_for(4766, 0) + self._write_game_state_value_for(4772, 0) + self._write_game_state_value_for(4769, 0) + # Brog Torches if self._player_is_brog() and self._player_has(ZorkGrandInquisitorItems.BROGS_BICKERING_TORCH): self._write_game_state_value_for(10999, 1) @@ -329,22 +769,107 @@ def _apply_conditional_game_state(self): else: self._write_game_state_value_for(10998, 0) - # Monastery Rope - if ZorkGrandInquisitorLocations.I_HOPE_YOU_CAN_CLIMB_UP_THERE in self.completed_locations: - self._write_game_state_value_for(9637, 1) + # Lucy Strip Grue, Fire, Water Losses + if self._read_game_state_value_for(14568) < 8: + self._write_game_state_value_for(14568, 8) def _apply_permanent_game_flags(self) -> None: + self._write_game_flags_value_for(13597, 2) # Monastery Vent self._write_game_flags_value_for(9437, 2) # Monastery Exhibit Door to Outside self._write_game_flags_value_for(3074, 2) # White House Door self._write_game_flags_value_for(13005, 2) # Map self._write_game_flags_value_for(13006, 2) # Sword self._write_game_flags_value_for(13007, 2) # Sword + self._write_game_flags_value_for(4854, 2) # Hungus Lard self._write_game_flags_value_for(13389, 2) # Moss of Mareilon self._write_game_flags_value_for(4301, 2) # Quelbee Honeycomb self._write_game_flags_value_for(12895, 2) # Change Machine Money self._write_game_flags_value_for(4150, 2) # Prozorked Snapdragon self._write_game_flags_value_for(13413, 2) # Letter Opener self._write_game_flags_value_for(15403, 2) # Lucy's Cards + self._write_game_flags_value_for(4876, 2) # Cocoa Ingredient - Jar of Hotbugs + self._write_game_flags_value_for(4877, 2) # Cocoa Ingredient - Moss of Mareilon + self._write_game_flags_value_for(4874, 2) # Cocoa Ingredient - Flatheadia Fudge + self._write_game_flags_value_for(4875, 2) # Cocoa Ingredient - Mug + self._write_game_flags_value_for(4873, 2) # Cocoa Ingredient - Quelbee Honeycomb + self._write_game_flags_value_for(10809, 2) # Back of Jack's Shop + self._write_game_flags_value_for(10314, 2) # Well Rope + self._write_game_flags_value_for(10848, 2) # Keep Spellbar Enabled (ps10) + self._write_game_flags_value_for(10862, 2) # Keep Spellbar Enabled (ps1e) + self._write_game_flags_value_for(10868, 2) # Keep Spellbar Enabled (ps20) + self._write_game_flags_value_for(10302, 2) # Keep Spellbar Enabled (pc10) + self._write_game_flags_value_for(10311, 2) # Keep Spellbar Enabled (pc1e) + self._write_game_flags_value_for(10918, 2) # Keep Spellbar Enabled (px10) + self._write_game_flags_value_for(10967, 2) # Keep Spellbar Enabled (px1h) + self._write_game_flags_value_for(10984, 2) # Keep Spellbar Enabled (px1j) + self._write_game_flags_value_for(10993, 2) # Keep Spellbar Enabled (px1k) + self._write_game_flags_value_for(10414, 2) # Keep Spellbar Enabled (pe10) + self._write_game_flags_value_for(10492, 2) # Keep Spellbar Enabled (pe20) + self._write_game_flags_value_for(10516, 2) # Keep Spellbar Enabled (pe2e) + self._write_game_flags_value_for(10575, 2) # Keep Spellbar Enabled (pe2j) + self._write_game_flags_value_for(10589, 2) # Keep Spellbar Enabled (pe30) + self._write_game_flags_value_for(10639, 2) # Keep Spellbar Enabled (pe3k) + self._write_game_flags_value_for(10659, 2) # Keep Spellbar Enabled (pe40) + self._write_game_flags_value_for(10677, 2) # Keep Spellbar Enabled (pe4g) + self._write_game_flags_value_for(10697, 2) # Keep Spellbar Enabled (pe50) + self._write_game_flags_value_for(10773, 2) # Keep Spellbar Enabled (pe5h) + self._write_game_flags_value_for(10756, 2) # Keep Spellbar Enabled (pe5f) + self._write_game_flags_value_for(10786, 2) # Keep Spellbar Enabled (pe6e) + self._write_game_flags_value_for(10722, 2) # Keep Spellbar Enabled (pe5e) + self._write_game_flags_value_for(19603, 2) # Keep Spellbar Enabled (pe5n) + self._write_game_flags_value_for(10620, 2) # Keep Spellbar Enabled (pe3j) + self._write_game_flags_value_for(10439, 2) # Keep Spellbar Enabled (pe1e) + self._write_game_flags_value_for(10805, 2) # Keep Spellbar Enabled (pp10) + self._write_game_flags_value_for(10805, 2) # Keep Spellbar Enabled (pp10) + self._write_game_flags_value_for(10838, 2) # Keep Spellbar Enabled (pp1j) + self._write_game_flags_value_for(8435, 0) # Always Allow Moving to Hades Phone + self._write_game_flags_value_for(4991, 0) # Always Allow Moving to DM Lair Mirror + + def _manage_game_location(self) -> None: + if self._read_game_state_value_for(19985) == 0: + return + + if self.game_location not in game_location_to_region: + return + + if self._player_is_at_for_at_least(self.game_location, 1000): + self.discovered_regions.add(game_location_to_region[self.game_location].value) + + def _manage_entrance_randomizer(self) -> None: + if self._read_game_state_value_for(19985) == 0: + return + + if self.game_location not in relevant_game_locations: + return + + entrance_randomizer_last_locations_visited_length: int = len(self.entrance_randomizer_last_locations_visited) + + if entrance_randomizer_last_locations_visited_length == 0: + self.entrance_randomizer_last_locations_visited.append(self.game_location) + elif entrance_randomizer_last_locations_visited_length == 1: + if self.entrance_randomizer_last_locations_visited[0] != self.game_location: + self.entrance_randomizer_last_locations_visited.append(self.game_location) + elif entrance_randomizer_last_locations_visited_length == 2: + if self.entrance_randomizer_last_locations_visited[1] != self.game_location: + self.entrance_randomizer_last_locations_visited.append(self.game_location) + + if len(self.entrance_randomizer_last_locations_visited) == 2: + location_pairing_key: str = "-".join(self.entrance_randomizer_last_locations_visited) + + if location_pairing_key in self.entrance_randomizer_data: + next_game_location: str = "".join(self.entrance_randomizer_data[location_pairing_key].split(" ")[:-1]) + offset: int = int(self.entrance_randomizer_data[location_pairing_key].split(" ")[-1]) + + self.game_state_manager.set_game_location(next_game_location, offset) + + entrance_pair: Tuple[str, str] = ( + self.entrance_randomizer_last_locations_visited[0], + self.entrance_randomizer_last_locations_visited[1] + ) + + self.discovered_entrances.add(entrance_names[entrances_to_game_locations_reverse[entrance_pair]]) + + self.entrance_randomizer_last_locations_visited.clear() def _check_for_completed_locations(self) -> None: location: ZorkGrandInquisitorLocations @@ -357,13 +882,20 @@ def _check_for_completed_locations(self) -> None: is_location_completed: bool = True - trigger: [Union[str, int]] - value: Union[str, int, Tuple[int, ...]] + trigger: Union[str, int, Tuple[int, ...]] + value: Union[str, int, Tuple[int, ...], Tuple[str, ...]] for trigger, value in data.game_state_trigger: if trigger == "location": - if not self._player_is_at(value): - is_location_completed = False - break + if isinstance(value, str): + if not self._player_is_at_for_at_least(value, self.game_location_required_duration): + is_location_completed = False + break + elif isinstance(value, tuple): + if not any( + self._player_is_at_for_at_least(key, self.game_location_required_duration) for key in value + ): + is_location_completed = False + break elif isinstance(trigger, int): if isinstance(value, int): if self._read_game_state_value_for(trigger) != value: @@ -376,6 +908,12 @@ def _check_for_completed_locations(self) -> None: else: is_location_completed = False break + elif isinstance(trigger, tuple): + game_state_values: List[int] = [self._read_game_state_value_for(key) for key in trigger] + + if value not in game_state_values: + is_location_completed = False + break else: is_location_completed = False break @@ -384,6 +922,15 @@ def _check_for_completed_locations(self) -> None: self.completed_locations.add(location) self.completed_locations_queue.append(location) + self._after_location_completed(location) + + def _after_location_completed(self, location: ZorkGrandInquisitorLocations) -> None: + # Write certain events to unused game state that otherwise don't have a permanent way to track + if location == ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP: + self._write_game_state_value_for(19951, 1) + elif location == ZorkGrandInquisitorLocations.REASSEMBLE_SNAVIG: + self._write_game_state_value_for(19952, 1) + def _check_for_missable_locations_to_grant(self) -> None: missable_location: ZorkGrandInquisitorLocations for missable_location in self.missable_locations: @@ -392,7 +939,7 @@ def _check_for_missable_locations_to_grant(self) -> None: data: ZorkGrandInquisitorLocationData = location_data[missable_location] - if ZorkGrandInquisitorTags.DEATHSANITY in data.tags and not self.option_deathsanity: + if ZorkGrandInquisitorTags.DEATHSANITY in data.tags and not self.is_deathsanity: continue condition_data: ZorkGrandInquisitorMissableLocationGrantConditionsData = ( @@ -403,7 +950,17 @@ def _check_for_missable_locations_to_grant(self) -> None: self.log_debug(f"Missable Location {missable_location.value} has no grant conditions") continue - if condition_data.location_condition in self.completed_locations: + if condition_data.game_location_condition is not None: + if not self._player_is_at_for_at_least( + condition_data.game_location_condition, self.game_location_required_duration + ): + continue + + location_condition_intersection: Set[ZorkGrandInquisitorLocations] = ( + set(condition_data.location_condition) & self.completed_locations + ) + + if len(location_condition_intersection): grant_location: bool = True item: ZorkGrandInquisitorItems @@ -425,6 +982,11 @@ def _process_received_items(self) -> None: self.received_items.add(item) + if ZorkGrandInquisitorTags.HOTSPOT_REGIONAL in data.tags: + hotspot_item: ZorkGrandInquisitorItems + for hotspot_item in hotspots_for_regional_hotspot[item]: + self.received_items.add(hotspot_item) + def _manage_hotspots(self) -> None: hotspot_item: ZorkGrandInquisitorItems for hotspot_item in self.all_hotspot_items: @@ -436,13 +998,13 @@ def _manage_hotspots(self) -> None: self._write_game_flags_value_for(key, 2) else: if hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_666_MAILBOX: - if self.game_state_manager.game_location == "hp5g": + if self.game_location == "hp5g": if self._read_game_state_value_for(9113) == 0: self._write_game_flags_value_for(9116, 0) else: self._write_game_flags_value_for(9116, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_ALPINES_QUANDRY_CARD_SLOTS: - if self.game_state_manager.game_location == "qb2g": + if self.game_location == "qb2g": if self._read_game_state_value_for(15433) == 0: self._write_game_flags_value_for(15434, 0) else: @@ -463,57 +1025,62 @@ def _manage_hotspots(self) -> None: else: self._write_game_flags_value_for(15440, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_BLANK_SCROLL_BOX: - if self.game_state_manager.game_location == "tp2g": + if self.game_location == "tp2g": if self._read_game_state_value_for(12095) == 1: self._write_game_flags_value_for(9115, 2) else: self._write_game_flags_value_for(9115, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_BLINDS: - if self.game_state_manager.game_location == "dv1e": + if self.game_location == "dv1e": if self._read_game_state_value_for(4743) == 0: self._write_game_flags_value_for(4799, 0) else: self._write_game_flags_value_for(4799, 2) + elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_BUCKET: + has_well_rope: bool = self._player_has(ZorkGrandInquisitorItems.WELL_ROPE) + + if self.game_location == "uw10" and has_well_rope: + self._write_game_flags_value_for(13928, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_BUTTONS: - if self.game_state_manager.game_location == "tr5g": + if self.game_location == "tr5g": key: int for key in data.statemap_keys: self._write_game_flags_value_for(key, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_COIN_SLOT: - if self.game_state_manager.game_location == "tr5g": + if self.game_location == "tr5g": self._write_game_flags_value_for(12702, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CANDY_MACHINE_VACUUM_SLOT: - if self.game_state_manager.game_location == "tr5m": + if self.game_location == "tr5m": self._write_game_flags_value_for(12909, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CHANGE_MACHINE_SLOT: - if self.game_state_manager.game_location == "tr5j": + if self.game_location == "tr5j": if self._read_game_state_value_for(12892) == 0: self._write_game_flags_value_for(12900, 0) else: self._write_game_flags_value_for(12900, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CLOSET_DOOR: - if self.game_state_manager.game_location == "dw1e": + if self.game_location == "dw1e": if self._read_game_state_value_for(4983) == 0: self._write_game_flags_value_for(5010, 0) else: self._write_game_flags_value_for(5010, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_HAMMER_SLOT: - if self.game_state_manager.game_location == "me2j": + if self.game_location == "me2j": if self._read_game_state_value_for(9491) == 2: self._write_game_flags_value_for(9539, 0) else: self._write_game_flags_value_for(9539, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_CLOSING_THE_TIME_TUNNELS_LEVER: - if self.game_state_manager.game_location == "me2j": + if self.game_location == "me2j": if self._read_game_state_value_for(9546) == 2 or self._read_game_state_value_for(9419) == 1: self._write_game_flags_value_for(19712, 2) else: self._write_game_flags_value_for(19712, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_COOKING_POT: - if self.game_state_manager.game_location == "sg1f": + if self.game_location == "sg1f": self._write_game_flags_value_for(2586, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DENTED_LOCKER: - if self.game_state_manager.game_location == "th3j": + if self.game_location == "th3j": five_is_open: bool = self._read_game_state_value_for(11847) == 1 six_is_open: bool = self._read_game_state_value_for(11840) == 1 seven_is_open: bool = self._read_game_state_value_for(11841) == 1 @@ -527,20 +1094,20 @@ def _manage_hotspots(self) -> None: else: self._write_game_flags_value_for(11878, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DIRT_MOUND: - if self.game_state_manager.game_location == "te5e": + if self.game_location == "te5e": if self._read_game_state_value_for(11747) == 0: self._write_game_flags_value_for(11751, 0) else: self._write_game_flags_value_for(11751, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DOCK_WINCH: - if self.game_state_manager.game_location == "pe2e": + if self.game_location == "pe2e": self._write_game_flags_value_for(15147, 0) self._write_game_flags_value_for(15153, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DRAGON_CLAW: - if self.game_state_manager.game_location == "cd70": + if self.game_location == "cd70": self._write_game_flags_value_for(1705, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DRAGON_NOSTRILS: - if self.game_state_manager.game_location == "cd3h": + if self.game_location == "cd3h": raft_in_left: bool = self._read_game_state_value_for(1301) == 1 raft_in_right: bool = self._read_game_state_value_for(1304) == 1 raft_inflated: bool = self._read_game_state_value_for(1379) == 1 @@ -564,14 +1131,17 @@ def _manage_hotspots(self) -> None: self._write_game_flags_value_for(1426, 2) else: self._write_game_flags_value_for(1426, 0) + elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_HOUSE_EXIT: + if self.game_location == "dv10": + self._write_game_flags_value_for(4791, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_LAIR_ENTRANCE: - if self.game_state_manager.game_location == "uc3e": + if self.game_location == "uc3e": if self._read_game_state_value_for(13060) == 0: self._write_game_flags_value_for(13106, 0) else: self._write_game_flags_value_for(13106, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_BUTTONS: - if self.game_state_manager.game_location == "ue1e": + if self.game_location == "ue1e": if self._read_game_state_value_for(14318) == 0: self._write_game_flags_value_for(13219, 0) self._write_game_flags_value_for(13220, 0) @@ -583,7 +1153,7 @@ def _manage_hotspots(self) -> None: self._write_game_flags_value_for(13221, 2) self._write_game_flags_value_for(13222, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FLOOD_CONTROL_DOORS: - if self.game_state_manager.game_location == "ue1e": + if self.game_location == "ue1e": if self._read_game_state_value_for(14318) == 0: self._write_game_flags_value_for(14327, 0) self._write_game_flags_value_for(14332, 0) @@ -595,10 +1165,10 @@ def _manage_hotspots(self) -> None: self._write_game_flags_value_for(14337, 2) self._write_game_flags_value_for(14342, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_COIN_SLOT: - if self.game_state_manager.game_location == "tr5e": + if self.game_location == "tr5e": self._write_game_flags_value_for(12528, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_FROZEN_TREAT_MACHINE_DOORS: - if self.game_state_manager.game_location == "tr5e": + if self.game_location == "tr5e": if self._read_game_state_value_for(12220) == 0: self._write_game_flags_value_for(12523, 2) self._write_game_flags_value_for(12524, 2) @@ -608,30 +1178,38 @@ def _manage_hotspots(self) -> None: self._write_game_flags_value_for(12524, 0) self._write_game_flags_value_for(12525, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GLASS_CASE: - if self.game_state_manager.game_location == "uc1g": + if self.game_location == "uc1g": if self._read_game_state_value_for(12931) == 1 or self._read_game_state_value_for(12929) == 1: self._write_game_flags_value_for(13002, 2) else: self._write_game_flags_value_for(13002, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL: - if self.game_state_manager.game_location == "pe5e": + if self.game_location == "pe5e": if self._read_game_state_value_for(10277) == 0: self._write_game_flags_value_for(10726, 0) else: self._write_game_flags_value_for(10726, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_DOOR: - if self.game_state_manager.game_location == "tr1k": + if self.game_location == "tr1k": if self._read_game_state_value_for(12212) == 0: self._write_game_flags_value_for(12280, 0) else: self._write_game_flags_value_for(12280, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_GRASS: - if self.game_state_manager.game_location in ("te10", "te1g", "te20", "te30", "te40"): + if self.game_location in ("te10", "te1g", "te20", "te30", "te40"): key: int for key in data.statemap_keys: self._write_game_flags_value_for(key, 0) + elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_GUE_TECH_WINDOWS: + if self.game_location == "te3e": + if self._read_game_state_value_for(11536) == 1: + self._write_game_flags_value_for(11543, 0) + elif self.game_location == "tr1g": + self._write_game_flags_value_for(12256, 0) + elif self.game_location == "te40": + self._write_game_flags_value_for(11720, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_BUTTONS: - if self.game_state_manager.game_location == "hp1e": + if self.game_location == "hp1e": if self._read_game_state_value_for(8431) == 1: key: int for key in data.statemap_keys: @@ -641,71 +1219,65 @@ def _manage_hotspots(self) -> None: for key in data.statemap_keys: self._write_game_flags_value_for(key, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HADES_PHONE_RECEIVER: - if self.game_state_manager.game_location == "hp1e": + if self.game_location == "hp1e": if self._read_game_state_value_for(8431) == 1: self._write_game_flags_value_for(8446, 2) else: self._write_game_flags_value_for(8446, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HARRY: - if self.game_state_manager.game_location == "dg4e": + if self.game_location == "dg4e": if self._read_game_state_value_for(4237) == 1 and self._read_game_state_value_for(4034) == 1: self._write_game_flags_value_for(4260, 2) else: self._write_game_flags_value_for(4260, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HARRYS_ASHTRAY: - if self.game_state_manager.game_location == "dg4h": + if self.game_location == "dg4h": if self._read_game_state_value_for(4279) == 1: self._write_game_flags_value_for(18026, 2) else: self._write_game_flags_value_for(18026, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_HARRYS_BIRD_BATH: - if self.game_state_manager.game_location == "dg4g": + if self.game_location == "dg4g": if self._read_game_state_value_for(4034) == 1: self._write_game_flags_value_for(17623, 2) else: self._write_game_flags_value_for(17623, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_IN_MAGIC_WE_TRUST_DOOR: - if self.game_state_manager.game_location == "uc4e": + if self.game_location == "uc4e": if self._read_game_state_value_for(13062) == 1: self._write_game_flags_value_for(13140, 2) else: self._write_game_flags_value_for(13140, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR: - if self.game_state_manager.game_location == "pe1e": + if self.game_location == "pe1e": if self._read_game_state_value_for(10451) == 1: self._write_game_flags_value_for(10441, 2) else: self._write_game_flags_value_for(10441, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_LOUDSPEAKER_VOLUME_BUTTONS: - if self.game_state_manager.game_location == "pe2j": + if self.game_location == "pe2j": self._write_game_flags_value_for(19632, 0) self._write_game_flags_value_for(19627, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_DOOR: - if self.game_state_manager.game_location == "sw4e": + if self.game_location == "sw4e": if self._read_game_state_value_for(2989) == 1: self._write_game_flags_value_for(3025, 2) else: self._write_game_flags_value_for(3025, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MAILBOX_FLAG: - if self.game_state_manager.game_location == "sw4e": + if self.game_location == "sw4e": self._write_game_flags_value_for(3036, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MIRROR: - if self.game_state_manager.game_location == "dw1f": + if self.game_location == "dw1f": self._write_game_flags_value_for(5031, 0) - elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MONASTERY_VENT: - if self.game_state_manager.game_location == "um1e": - if self._read_game_state_value_for(9637) == 0: - self._write_game_flags_value_for(13597, 0) - else: - self._write_game_flags_value_for(13597, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_MOSSY_GRATE: - if self.game_state_manager.game_location == "ue2g": + if self.game_location == "ue2g": if self._read_game_state_value_for(13278) == 0: self._write_game_flags_value_for(13390, 0) else: self._write_game_flags_value_for(13390, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_PORT_FOOZLE_PAST_TAVERN_DOOR: - if self.game_state_manager.game_location == "qe1e": + if self.game_location == "qe1e": if self._player_is_brog(): self._write_game_flags_value_for(2447, 0) elif self._player_is_griff(): @@ -716,7 +1288,7 @@ def _manage_hotspots(self) -> None: else: self._write_game_flags_value_for(2455, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_PURPLE_WORDS: - if self.game_state_manager.game_location == "tr3h": + if self.game_location == "tr3h": if self._read_game_state_value_for(11777) == 1: self._write_game_flags_value_for(12389, 2) else: @@ -724,13 +1296,13 @@ def _manage_hotspots(self) -> None: self._write_game_state_value_for(12390, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_QUELBEE_HIVE: - if self.game_state_manager.game_location == "dg4f": + if self.game_location == "dg4f": if self._read_game_state_value_for(4241) == 1: self._write_game_flags_value_for(4302, 2) else: self._write_game_flags_value_for(4302, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_ROPE_BRIDGE: - if self.game_state_manager.game_location == "tp1e": + if self.game_location == "tp1e": if self._read_game_state_value_for(16342) == 1: self._write_game_flags_value_for(16383, 2) self._write_game_flags_value_for(16384, 2) @@ -738,106 +1310,124 @@ def _manage_hotspots(self) -> None: self._write_game_flags_value_for(16383, 0) self._write_game_flags_value_for(16384, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SKULL_CAGE: - if self.game_state_manager.game_location == "sg6e": + if self.game_location == "sg6e": if self._read_game_state_value_for(15715) == 1: self._write_game_flags_value_for(2769, 2) + self._write_game_flags_value_for(2761, 2) + self._write_game_flags_value_for(2764, 2) + self._write_game_flags_value_for(2767, 2) else: self._write_game_flags_value_for(2769, 0) + self._write_game_flags_value_for(2761, 0) + self._write_game_flags_value_for(2764, 0) + self._write_game_flags_value_for(2767, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SNAPDRAGON: - if self.game_state_manager.game_location == "dg2f": + if self.game_location == "dg2f": if self._read_game_state_value_for(4114) == 1 or self._read_game_state_value_for(4115) == 1: self._write_game_flags_value_for(4149, 2) else: self._write_game_flags_value_for(4149, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_BUTTONS: - if self.game_state_manager.game_location == "tr5f": + if self.game_location == "tr5f": self._write_game_flags_value_for(12584, 0) self._write_game_flags_value_for(12585, 0) self._write_game_flags_value_for(12586, 0) self._write_game_flags_value_for(12587, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SODA_MACHINE_COIN_SLOT: - if self.game_state_manager.game_location == "tr5f": + if self.game_location == "tr5f": self._write_game_flags_value_for(12574, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SOUVENIR_COIN_SLOT: - if self.game_state_manager.game_location == "ue2j": + if self.game_location == "ue2j": if self._read_game_state_value_for(13408) == 1: self._write_game_flags_value_for(13412, 2) else: self._write_game_flags_value_for(13412, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SPELL_CHECKER: - if self.game_state_manager.game_location == "tp4g": + if self.game_location == "tp4g": self._write_game_flags_value_for(12170, 0) + elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT: + if self.game_location == "tp10": + self._write_game_flags_value_for(12045, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_CHASM: - if self.game_state_manager.game_location == "tp1e": + if self.game_location == "tp1e": if self._read_game_state_value_for(16342) == 1 and self._read_game_state_value_for(16374) == 0: self._write_game_flags_value_for(16382, 0) else: self._write_game_flags_value_for(16382, 2) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SPRING_MUSHROOM: - if self.game_state_manager.game_location == "dg3e": + if self.game_location == "dg3e": self._write_game_flags_value_for(4209, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_STUDENT_ID_MACHINE: - if self.game_state_manager.game_location == "th3r": + if self.game_location == "th3r": self._write_game_flags_value_for(11973, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_SUBWAY_TOKEN_SLOT: - if self.game_state_manager.game_location == "uc6e": + if self.game_location == "uc6e": self._write_game_flags_value_for(13168, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_TAVERN_FLY: - if self.game_state_manager.game_location == "qb2e": + if self.game_location == "qb2e": if self._read_game_state_value_for(15395) == 1: self._write_game_flags_value_for(15396, 2) else: self._write_game_flags_value_for(15396, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_SWITCH: - if self.game_state_manager.game_location == "mt2e": + if self.game_location == "mt2e": self._write_game_flags_value_for(9706, 0) elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_TOTEMIZER_WHEELS: - if self.game_state_manager.game_location == "mt2g": + if self.game_location == "mt2g": self._write_game_flags_value_for(9728, 0) self._write_game_flags_value_for(9729, 0) self._write_game_flags_value_for(9730, 0) - elif hotspot_item == ZorkGrandInquisitorItems.HOTSPOT_WELL: - if self.game_state_manager.game_location == "pc1e": - self._write_game_flags_value_for(10314, 0) + elif hotspot_item == ZorkGrandInquisitorItems.SUBWAY_DESTINATION_CROSSROADS: + if self.game_location == "us2e": + self._write_game_flags_value_for(13760, 0) + elif self.game_location == "ue2e": + self._write_game_flags_value_for(13323, 0) + elif self.game_location == "uh2e": + self._write_game_flags_value_for(13512, 0) + elif self.game_location == "um2e": + self._write_game_flags_value_for(13651, 0) elif hotspot_item == ZorkGrandInquisitorItems.SUBWAY_DESTINATION_FLOOD_CONTROL_DAM: - if self.game_state_manager.game_location == "us2e": + if self.game_location == "us2e": self._write_game_flags_value_for(13757, 0) - elif self.game_state_manager.game_location == "ue2e": + elif self.game_location == "ue2e": self._write_game_flags_value_for(13297, 0) - elif self.game_state_manager.game_location == "uh2e": + elif self.game_location == "uh2e": self._write_game_flags_value_for(13486, 0) - elif self.game_state_manager.game_location == "um2e": + elif self.game_location == "um2e": self._write_game_flags_value_for(13625, 0) elif hotspot_item == ZorkGrandInquisitorItems.SUBWAY_DESTINATION_HADES: - if self.game_state_manager.game_location == "us2e": + if self.game_location == "us2e": self._write_game_flags_value_for(13758, 0) - elif self.game_state_manager.game_location == "ue2e": + elif self.game_location == "ue2e": self._write_game_flags_value_for(13309, 0) - elif self.game_state_manager.game_location == "uh2e": + elif self.game_location == "uh2e": self._write_game_flags_value_for(13498, 0) - elif self.game_state_manager.game_location == "um2e": + elif self.game_location == "um2e": self._write_game_flags_value_for(13637, 0) elif hotspot_item == ZorkGrandInquisitorItems.SUBWAY_DESTINATION_MONASTERY: - if self.game_state_manager.game_location == "us2e": + if self.game_location == "us2e": self._write_game_flags_value_for(13759, 0) - elif self.game_state_manager.game_location == "ue2e": + elif self.game_location == "ue2e": self._write_game_flags_value_for(13316, 0) - elif self.game_state_manager.game_location == "uh2e": + elif self.game_location == "uh2e": self._write_game_flags_value_for(13505, 0) - elif self.game_state_manager.game_location == "um2e": + elif self.game_location == "um2e": self._write_game_flags_value_for(13644, 0) elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION: - if self.game_state_manager.game_location == "mt1f": + if self.game_location == "mt1f": self._write_game_flags_value_for(9660, 0) + elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ: + if self.game_location == "mt1f": + self._write_game_flags_value_for(9662, 0) + elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY: + if self.game_location == "mt1f": + self._write_game_flags_value_for(9664, 0) elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY: - if self.game_state_manager.game_location == "mt1f": + if self.game_location == "mt1f": self._write_game_flags_value_for(9666, 0) elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL: - if self.game_state_manager.game_location == "mt1f": + if self.game_location == "mt1f": self._write_game_flags_value_for(9668, 0) - elif hotspot_item == ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ: - if self.game_state_manager.game_location == "mt1f": - self._write_game_flags_value_for(9662, 0) def _manage_items(self) -> None: if self._player_is_afgncaap(): @@ -895,29 +1485,261 @@ def _manage_items(self) -> None: seen_items.add(item) def _apply_conditional_teleports(self) -> None: + # Skip Well Cutscene if self._player_is_at("uw1x"): self.game_state_manager.set_game_location("uw10", 0) - if self._player_is_at("uw1k") and self._read_game_state_value_for(13938) == 0: - self.game_state_manager.set_game_location("pc10", 250) + # Skip Y'Gael Cutscene + if self._player_is_at("ej10"): + self.game_state_manager.set_game_location("uc10", 1200) + # Skip Power Outage Cutscene if self._player_is_at("ue1q"): self.game_state_manager.set_game_location("ue1e", 0) - if self._player_is_at("ej10"): - self.game_state_manager.set_game_location("uc10", 1200) + # Bucket -> Surface + if self._player_is_at("uw1k") and self._read_game_state_value_for(13938) == 0: + self.game_state_manager.set_game_location("pc10", 250) + + # Monastery Subway Station -> Monastery + if self._player_is_at("um1e") and self._read_game_state_value_for(9637) == 1: + self.game_state_manager.set_game_location("mt10", 1531) + + # VOXAM Cast + zork_rocks_inert: bool = self._read_game_state_value_for(11767) == 0 if self._read_game_state_value_for(9) == 224: + time.sleep(0.1) self._write_game_state_value_for(9, 0) - self.game_state_manager.set_game_location("uc10", 1200) + + if zork_rocks_inert: + self._cast_voxam() + + def _cast_voxam(self, force_wild: bool = False) -> None: + if self.option_entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + self.entrance_randomizer_last_locations_visited.clear() + + if not self.option_wild_voxam and not force_wild: + self._apply_starting_location(force=True) + + voxam_roll: int = random.randint(1, 100) + + if (voxam_roll <= self.option_wild_voxam_chance) or force_wild: + starting_location: ZorkGrandInquisitorStartingLocations = ( + random.choice(tuple(voxam_cast_game_locations.keys())) + ) + + game_location: Tuple[Tuple[str, int], ...] = ( + random.choice(voxam_cast_game_locations[starting_location]) + ) + + game_location_offset: int = 0 + + if game_location[1] == 1: + game_location_offset = random.randint(0, 1800) + + self.game_state_manager.set_game_location( + game_location[0], game_location_offset + ) + else: + self._apply_starting_location(force=True) + + def _manage_traps(self) -> None: + if not self._player_is_afgncaap() or self._read_game_state_value_for(19985) == 0: + return None + + zork_rocks_inert: bool = self._read_game_state_value_for(11767) == 0 + + if not zork_rocks_inert: + return None + + if self.active_trap_until: + if datetime.datetime.now() > self.active_trap_until: + if self.active_trap == ZorkGrandInquisitorItems.TRAP_REVERSE_CONTROLS: + self._deactivate_trap_reverse_controls() + elif self.active_trap == ZorkGrandInquisitorItems.TRAP_ZVISION: + self._deactivate_trap_zvision() + + self.active_trap = None + self.active_trap_until = None + + if self.active_trap is not None: + if self.active_trap == ZorkGrandInquisitorItems.TRAP_REVERSE_CONTROLS: + self._activate_trap_reverse_controls() + elif self.active_trap == ZorkGrandInquisitorItems.TRAP_ZVISION: + self._activate_trap_zvision() + + return None + + processed_trap_counters: Dict[ZorkGrandInquisitorItems, int] = { + ZorkGrandInquisitorItems.TRAP_INFINITE_CORRIDOR: self._read_game_state_value_for(19990), + ZorkGrandInquisitorItems.TRAP_REVERSE_CONTROLS: self._read_game_state_value_for(19991), + ZorkGrandInquisitorItems.TRAP_TELEPORT: self._read_game_state_value_for(19992), + ZorkGrandInquisitorItems.TRAP_ZVISION: self._read_game_state_value_for(19993), + } + + traps_remaining: int = len(self.received_traps) - sum(processed_trap_counters.values()) - 1 + traps_remaining_message: str = f"Traps remaining: {traps_remaining}" if traps_remaining else "" + + trap: ZorkGrandInquisitorItems + for trap in self.received_traps: + if processed_trap_counters[trap]: + processed_trap_counters[trap] -= 1 + continue + + game_state_key: int = -1 + if trap == ZorkGrandInquisitorItems.TRAP_INFINITE_CORRIDOR: + game_state_key = 19990 + + if not self._player_is_at_starting_location(): + self._activate_trap_infinite_corridor() + self.log(f"Infinite Corridor Trap! {traps_remaining_message}") + elif trap == ZorkGrandInquisitorItems.TRAP_REVERSE_CONTROLS: + game_state_key = 19991 + + if not self._player_is_at_starting_location(): + self.active_trap = ZorkGrandInquisitorItems.TRAP_REVERSE_CONTROLS + self.active_trap_until = datetime.datetime.now() + datetime.timedelta(seconds=30) + + self._activate_trap_reverse_controls() + + self.log(f"Reverse Controls Trap for 30 seconds! {traps_remaining_message}") + elif trap == ZorkGrandInquisitorItems.TRAP_TELEPORT: + game_state_key = 19992 + + if not self._player_is_at_starting_location(): + self._activate_trap_teleport() + self.log(f"Teleport Trap! {traps_remaining_message}") + elif trap == ZorkGrandInquisitorItems.TRAP_ZVISION: + game_state_key = 19993 + + if not self._player_is_at_starting_location(): + self.active_trap = ZorkGrandInquisitorItems.TRAP_ZVISION + self.active_trap_until = datetime.datetime.now() + datetime.timedelta(seconds=30) + + self._activate_trap_zvision() + + self.log(f"ZVision Trap for 30 seconds! {traps_remaining_message}") + + current_count: int = self._read_game_state_value_for(game_state_key) + self._write_game_state_value_for(game_state_key, current_count + 1) + + break + + def _activate_trap_infinite_corridor(self) -> None: + if self.option_entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + self.entrance_randomizer_last_locations_visited.clear() + + depth = random.randint(10, 20) + + self._write_game_state_value_for(11005, depth) + self.game_state_manager.set_game_location("th20", random.randint(0, 1800)) + + time.sleep(0.1) + + self._write_game_state_value_for(11005, depth) + + def _activate_trap_reverse_controls(self) -> None: + self.game_state_manager.set_panorama_reversed(True) + + def _deactivate_trap_reverse_controls(self) -> None: + self.game_state_manager.set_panorama_reversed(False) + + def _activate_trap_teleport(self) -> None: + self._cast_voxam(force_wild=True) + time.sleep(0.1) + + def _activate_trap_zvision(self) -> None: + self.game_state_manager.set_zvision(True) + + def _deactivate_trap_zvision(self) -> None: + self.game_state_manager.set_zvision(False) + + def _manage_energy_link(self) -> None: + mushroom_hammered: bool = self._read_game_state_value_for(4217) == 1 + mushroom_hammered_throck: bool = self._read_game_state_value_for(4219) == 1 + mushroom_hammered_snapdragon: bool = self._read_game_state_value_for(4220) == 1 + mushroom_hammered_snapdragon_throck: bool = self._read_game_state_value_for(4222) == 1 + + any_mushroom_hammered: bool = ( + mushroom_hammered + or mushroom_hammered_throck + or mushroom_hammered_snapdragon + or mushroom_hammered_snapdragon_throck + ) + + # Pause Monitoring Flag + if self.pause_energy_link_monitoring and not any_mushroom_hammered: + self.pause_energy_link_monitoring = False + + if not self.pause_energy_link_monitoring and any_mushroom_hammered: + # Contribute Energy + if mushroom_hammered: + self.energy_link_queue.append(750) + elif mushroom_hammered_throck: + self.energy_link_queue.append(1500) + elif mushroom_hammered_snapdragon: + self.energy_link_queue.append(750) + elif mushroom_hammered_snapdragon_throck: + self.energy_link_queue.append(1500) + + self.pause_energy_link_monitoring = True + + def _handle_death_link(self) -> None: + # Pause Monitoring Flag + if self.pause_death_monitoring and not self._player_is_at("gjde"): + self.pause_death_monitoring = False + + # Incoming Death Link + if not self._player_is_at("gjde") and self.pending_death_link[0]: + self._write_game_state_value_for(2201, 35) + self.game_state_manager.set_game_location("gjde", 0) + + if self.pending_death_link[2]: + self.log(f"Death Link: {self.pending_death_link[2]}") + else: + self.log(f"Death Link: Triggered by {self.pending_death_link[1]}") + + self.pending_death_link = (False, None, None) + + # Outgoing Death Link + if not self.pause_death_monitoring: + death_cause_id: int = self._read_game_state_value_for(2201) + + if self._player_is_at("gjde") and death_cause_id != 35: + death_cause: str = death_cause_labels.get( + death_cause_id, + "PLAYER died of unknown causes" + ) + + self.outgoing_death_link = (True, death_cause) + self.pause_death_monitoring = True def _check_for_victory(self) -> None: + duration: int = self.game_location_required_duration + if self.option_goal == ZorkGrandInquisitorGoals.THREE_ARTIFACTS: coconut_is_placed = self._read_game_state_value_for(2200) == 1 cube_is_placed = self._read_game_state_value_for(2322) == 1 skull_is_placed = self._read_game_state_value_for(2321) == 1 self.goal_completed = coconut_is_placed and cube_is_placed and skull_is_placed + elif self.option_goal == ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: + if self.goal_item_count >= self.option_artifacts_of_magic_required: + if self._player_is_at_for_at_least("dc10", duration) and self._player_is_afgncaap(): + self.goal_completed = True + elif self.option_goal == ZorkGrandInquisitorGoals.SPELL_HEIST: + if not len(self.all_spell_items - self.received_items): + if self._player_is_at_for_at_least("ps1e", duration): + self.goal_completed = True + elif self.option_goal == ZorkGrandInquisitorGoals.ZORK_TOUR: + if self.goal_item_count >= self.option_landmarks_required: + if self._player_is_at_for_at_least("ps1e", duration): + self.goal_completed = True + elif self.option_goal == ZorkGrandInquisitorGoals.GRIM_JOURNEY: + if self.goal_item_count >= self.option_deaths_required: + if self._player_is_at_for_at_least("hp60", duration): + self.goal_completed = True def _determine_game_state_inventory(self) -> Set[ZorkGrandInquisitorItems]: game_state_inventory: Set[ZorkGrandInquisitorItems] = set() @@ -930,7 +1752,16 @@ def _determine_game_state_inventory(self) -> Set[ZorkGrandInquisitorItems]: game_state_inventory.add(self.game_id_to_items[item_on_cursor]) # Item in Inspector - item_in_inspector: int = self._read_game_state_value_for(4512) + item_in_inspector: int = 0 + + if self._player_is_afgncaap(): + item_in_inspector = self._read_game_state_value_for(4512) + elif self._player_is_brog(): + item_in_inspector = self._read_game_state_value_for(2194) + elif self._player_is_griff(): + item_in_inspector = self._read_game_state_value_for(2196) + elif self._player_is_lucy(): + item_in_inspector = self._read_game_state_value_for(2198) if item_in_inspector != 0: if item_in_inspector in self.game_id_to_items: @@ -951,7 +1782,7 @@ def _determine_game_state_inventory(self) -> Set[ZorkGrandInquisitorItems]: # Spells i: int - for i in range(191, 203): + for i in range(192, 203): if self._read_game_state_value_for(i) == 1: if i in self.game_id_to_items: game_state_inventory.add(self.game_id_to_items[i]) @@ -969,11 +1800,11 @@ def _determine_game_state_inventory(self) -> Set[ZorkGrandInquisitorItems]: return game_state_inventory def _add_to_inventory(self, item: ZorkGrandInquisitorItems) -> None: - if item == ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS: - return None - data: ZorkGrandInquisitorItemData = item_data[item] + if data.statemap_keys is None: + return None + if ZorkGrandInquisitorTags.INVENTORY_ITEM in data.tags: if len(self.available_inventory_slots): # Inventory slot overflow protection inventory_slot: int = self.available_inventory_slots.pop() @@ -984,11 +1815,11 @@ def _add_to_inventory(self, item: ZorkGrandInquisitorItems) -> None: self._write_game_state_value_for(data.statemap_keys[0], 1) def _remove_from_inventory(self, item: ZorkGrandInquisitorItems) -> None: - if item == ZorkGrandInquisitorItems.POUCH_OF_ZORKMIDS: - return None - data: ZorkGrandInquisitorItemData = item_data[item] + if data.statemap_keys is None: + return None + if ZorkGrandInquisitorTags.INVENTORY_ITEM in data.tags: inventory_slot: Optional[int] = self._inventory_slot_for(item) @@ -1050,7 +1881,14 @@ def _filter_received_inventory_items( i: int for i in range(151, 171): - inventory_item_values.add(self._read_game_state_value_for(i)) + inventory_item_value: int = self._read_game_state_value_for(i) + + # Always get rid of blue sword. Causes issues with ER + if inventory_item_value == 100: + inventory_item_value = 21 + self._write_game_state_value_for(i, inventory_item_value) + + inventory_item_values.add(inventory_item_value) cursor_item_value: int = self._read_game_state_value_for(9) inspector_item_value: int = self._read_game_state_value_for(4512) @@ -1060,12 +1898,7 @@ def _filter_received_inventory_items( item: ZorkGrandInquisitorItems for item in received_inventory_items: - if item == ZorkGrandInquisitorItems.FLATHEADIA_FUDGE: - if self._read_game_state_value_for(4766) == 1: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(4869) == 1: - to_filter_inventory_items.add(item) - elif item == ZorkGrandInquisitorItems.HUNGUS_LARD: + if item == ZorkGrandInquisitorItems.HUNGUS_LARD: if self._read_game_state_value_for(4870) == 1: to_filter_inventory_items.add(item) elif ( @@ -1073,16 +1906,6 @@ def _filter_received_inventory_items( and self._read_game_state_value_for(4309) == 0 ): to_filter_inventory_items.add(item) - elif item == ZorkGrandInquisitorItems.JAR_OF_HOTBUGS: - if self._read_game_state_value_for(4750) == 1: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(4869) == 1: - to_filter_inventory_items.add(item) - elif item == ZorkGrandInquisitorItems.LANTERN: - if self._read_game_state_value_for(10477) == 1: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(5221) == 1: - to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.LARGE_TELEGRAPH_HAMMER: if self._read_game_state_value_for(9491) == 3: to_filter_inventory_items.add(item) @@ -1096,16 +1919,6 @@ def _filter_received_inventory_items( to_filter_inventory_items.add(item) elif self._read_game_state_value_for(4034) == 1: to_filter_inventory_items.add(item) - elif item == ZorkGrandInquisitorItems.MOSS_OF_MAREILON: - if self._read_game_state_value_for(4763) == 1: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(4869) == 1: - to_filter_inventory_items.add(item) - elif item == ZorkGrandInquisitorItems.MUG: - if self._read_game_state_value_for(4772) == 1: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(4869) == 1: - to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.OLD_SCRATCH_CARD: if 32 in inventory_item_values: to_filter_inventory_items.add(item) @@ -1122,59 +1935,34 @@ def _filter_received_inventory_items( elif item == ZorkGrandInquisitorItems.PROZORK_TABLET: if self._read_game_state_value_for(4115) == 1: to_filter_inventory_items.add(item) - elif item == ZorkGrandInquisitorItems.QUELBEE_HONEYCOMB: - if self._read_game_state_value_for(4769) == 1: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(4869) == 1: - to_filter_inventory_items.add(item) - elif item == ZorkGrandInquisitorItems.ROPE: - if 22 in inventory_item_values: - to_filter_inventory_items.add(item) - elif 111 in inventory_item_values: - to_filter_inventory_items.add(item) - elif ( - self._read_game_state_value_for(10304) == 1 - and not self._read_game_state_value_for(13938) == 1 - ): - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(15150) == 83: + elif item == ZorkGrandInquisitorItems.SANDWITCH_WRAPPER: + if self._read_game_state_value_for(19951) == 1: to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.SCROLL_FRAGMENT_ANS: if 41 in inventory_item_values: to_filter_inventory_items.add(item) - elif 98 in inventory_item_values: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(201) == 1: + elif self._read_game_state_value_for(19952) == 1: to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.SCROLL_FRAGMENT_GIV: if 48 in inventory_item_values: to_filter_inventory_items.add(item) - elif 98 in inventory_item_values: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(201) == 1: + elif self._read_game_state_value_for(19952) == 1: to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.SNAPDRAGON: if self._read_game_state_value_for(4199) == 1: to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.STUDENT_ID: if self._read_game_state_value_for(11838) == 1: - to_filter_inventory_items.add(item) + if self._read_game_state_value_for(9) != 39: + to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.SUBWAY_TOKEN: if self._read_game_state_value_for(13167) == 1: to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.SWORD: if 22 in inventory_item_values: to_filter_inventory_items.add(item) - elif 100 in inventory_item_values: - to_filter_inventory_items.add(item) - elif 111 in inventory_item_values: - to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.ZIMDOR_SCROLL: - if 105 in inventory_item_values: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(17620) == 3: - to_filter_inventory_items.add(item) - elif self._read_game_state_value_for(4034) == 1: + if self._read_game_state_value_for(12167) == 1: to_filter_inventory_items.add(item) elif item == ZorkGrandInquisitorItems.ZORK_ROCKS: if self._read_game_state_value_for(12486) == 1: @@ -1370,7 +2158,34 @@ def _player_doesnt_have(self, item: ZorkGrandInquisitorItems) -> bool: return item not in self.received_items def _player_is_at(self, game_location: str) -> bool: - return self.game_state_manager.game_location == game_location + return self.game_location == game_location + + def _player_is_at_for_at_least(self, game_location: str, milliseconds: int) -> bool: + return self.game_location == game_location and ( + int(time.time() * 1000) - self.game_location_since >= milliseconds + ) + + def _player_is_at_starting_location(self) -> bool: + if self.option_starting_location == ZorkGrandInquisitorStartingLocations.PORT_FOOZLE: + return self._player_is_at("ps10") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.CROSSROADS: + return self._player_is_at("uc10") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.DM_LAIR: + return self._player_is_at("dg10") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: + return self._player_is_at("dv10") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.GUE_TECH: + return self._player_is_at("tr20") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.SPELL_LAB: + return self._player_is_at("tp20") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.HADES_SHORE: + return self._player_is_at("uh10") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.SUBWAY_FLOOD_CONTROL_DAM: + return self._player_is_at("ue10") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.MONASTERY: + return self._player_is_at("mt20") + elif self.option_starting_location == ZorkGrandInquisitorStartingLocations.MONASTERY_EXHIBIT: + return self._player_is_at("me10") def _player_is_afgncaap(self) -> bool: return self._read_game_state_value_for(1596) == 1 diff --git a/worlds/zork_grand_inquisitor/game_state_manager.py b/worlds/zork_grand_inquisitor/game_state_manager.py index 25b35969bf5e..31a168404be7 100644 --- a/worlds/zork_grand_inquisitor/game_state_manager.py +++ b/worlds/zork_grand_inquisitor/game_state_manager.py @@ -82,6 +82,14 @@ def next_location_address(self) -> int: def next_location_offset_address(self) -> int: return self.script_manager_struct_address + 0x40C + @property + def zvision_address(self) -> int: + return self.render_manager_struct_address + 0x0 + + @property + def render_type_address(self) -> int: + return self.render_manager_struct_address + 0x10 + @property def panorama_reversed_address(self) -> int: return self.render_manager_struct_address + 0x1C @@ -250,6 +258,22 @@ def set_game_location(self, game_location: str, offset: int) -> Optional[bool]: return None + def set_zvision(self, is_zvision: bool) -> Optional[bool]: + if self.is_process_running: + self.process.write_int(self.zvision_address, 320 if is_zvision else 640) + + return True + + return None + + def set_render_type(self, render_type: int) -> Optional[bool]: + if self.is_process_running: + self.process.write_int(self.render_type_address, render_type) + + return True + + return None + def set_panorama_reversed(self, is_reversed: bool) -> Optional[bool]: if self.is_process_running: self.process.write_int(self.panorama_reversed_address, 1 if is_reversed else 0) diff --git a/worlds/zork_grand_inquisitor/options.py b/worlds/zork_grand_inquisitor/options.py index f06415199934..5adbf20a6eeb 100644 --- a/worlds/zork_grand_inquisitor/options.py +++ b/worlds/zork_grand_inquisitor/options.py @@ -1,61 +1,416 @@ +from typing import List + from dataclasses import dataclass -from Options import Choice, DefaultOnToggle, PerGameCommonOptions, Toggle +from Options import ( + Choice, + DeathLinkMixin, + DefaultOnToggle, + OptionGroup, + PerGameCommonOptions, + Range, + StartInventoryPool, + Toggle, +) class Goal(Choice): """ - Determines the victory condition + Determines the victory condition. - Three Artifacts: Retrieve the three artifacts of magic and place them in the walking castle + Three Artifacts: Retrieve the Coconut of Quendor, the Cube of Foundation and the Skull of Yoruk + Artifact of Magic Hunt: Retrieve X artifacts of magic and bring them to the walking castle + Spell Heist: Acquire all spells and travel to the Port Foozle signpost + Zork Tour: Visit all 20 landmarks and travel to the Port Foozle signpost + Grim Journey: Experience all 22 player deaths and go beyond the gates of Hades """ display_name: str = "Goal" - default: int = 0 option_three_artifacts: int = 0 + option_artifact_of_magic_hunt: int = 1 + option_spell_heist: int = 2 + option_zork_tour: int = 3 + option_grim_journey: int = 4 + + default = 0 + + +class ArtifactsOfMagicTotal(Range): + """ + Determines how many Artifacts of Magic are in the item pool. + + Only relevant if the selected goal is Artifact of Magic Hunt. + """ + + display_name = "Artifacts of Magic Total" + + range_start = 5 + range_end = 15 + default = 15 -class QuickPortFoozle(DefaultOnToggle): - """If true, the items needed to go down the well will be found in early locations for a smoother early game""" - display_name: str = "Quick Port Foozle" +class ArtifactsOfMagicRequired(Range): + """ + Determines how many Artifacts of Magic are required to win. + + Only relevant if the selected goal is Artifact of Magic Hunt. + """ + + display_name = "Artifacts of Magic Required" + + range_start = 5 + range_end = 15 + default = 10 -class StartWithHotspotItems(DefaultOnToggle): + +class LandmarksRequired(Range): """ - If true, the player will be given all the hotspot items at the start of the game, effectively removing the need - to enable the important hotspots in the game before interacting with them. Recommended for beginners + Determines how many Landmarks are required to win. - Note: The spots these hotspot items would have occupied in the item pool will instead be filled with junk items. - Expect a higher volume of filler items if you enable this option + Only relevant if the selected goal is Zork Tour. """ - display_name: str = "Start with Hotspot Items" + display_name = "Landmarks Required" + + range_start = 10 + range_end = 20 + + default = 20 + + +class DeathsRequired(Range): + """ + Determines how many Deaths are required to win. + + Only relevant if the selected goal is Grim Journey. + """ + + display_name = "Deaths Required" + + range_start = 10 + range_end = 22 + + default = 22 + + +class StartingLocation(Choice): + """ + Determines the in-game location the player will start at. + + The player always starts with VOXAM, which can be used to teleport back to the starting location at any time. + Depending on the starting location, the player may also be given a starter kit of items to help them get going. + + Note: VOXAM will only work from the spell quickbar. It will have no effect from the spellbook. + """ + + display_name: str = "Starting Location" + + option_port_foozle: int = 0 + option_crossroads: int = 1 + option_dm_lair: int = 2 + option_dm_lair_house: int = 3 + option_gue_tech: int = 4 + option_spell_lab: int = 5 + option_hades_shore: int = 6 + option_flood_control_dam_3: int = 7 + option_monastery_totemizer: int = 8 + option_monastery_exhibit: int = 9 + + default = 0 + + +class Hotspots(Choice): + """ + Determines the behavior of hotspots (interactable areas of the screen) in the game. + + Enabled: All hotspots will be enabled at the start of the game + Require Item per Region: An item will enable all hotspots for a given region (e.g. Hotspots: Crossroads) + Require Item per Hotspot: An item will enable a specific hotspot (e.g. Hotspot: Subway Token Slot) + """ + + display_name: str = "Hotspots" + + option_enabled: int = 0 + option_require_item_per_region: int = 1 + option_require_item_per_hotspot: int = 2 + + default = 0 + + +class CraftableSpells(Choice): + """ + Determines the behavior when craftable spells (BEBURTT, OBIDIL, SNAVIG, YASTARD) are obtained. + Spells in a starting location's starter kit always have precedence over this option. + + Vanilla: After crafting a spell, the player will be given that exact spell + Any Spell: After crafting a spell, the player will be given a random spell + Anything: After crafting a spell, a random item from the multiworld will be unlocked + """ + + display_name: str = "Craftable Spells" + + option_vanilla: int = 0 + option_any_spell: int = 1 + option_anything: int = 2 + + default = 2 + + +class WildVoxam(Toggle): + """ + If true, casting VOXAM will have a small chance to teleport the player to a different location. + + This option can enable small stretches of out-of-logic gameplay in the early game, with strong diminishing returns + as the game progresses. + """ + + display_name: str = "Wild VOXAM" + + +class WildVoxamChance(Range): + """ + Determines the percentage chance that a VOXAM cast will be wild. + """ + + display_name = "Wild VOXAM Chance %" + + range_start = 1 + range_end = 10 + + default = 5 class Deathsanity(Toggle): - """If true, adds 16 player death locations to the world""" + """ + If true, adds 22 unique player death locations to the world. + + This option will be forced on if your goal is Grim Journey. + """ display_name: str = "Deathsanity" +class Landmarksanity(DefaultOnToggle): + """ + If true, adds 20 landmark locations to the world. + + This option will be forced on if your goal is Zork Tour. + """ + + display_name: str = "Landmarksanity" + + +class EntranceRandomizer(Choice): + """ + Determines the behavior of entrances in the game. + + Disabled: Entrances are not randomized and lead to their vanilla areas + Coupled: Entrances are randomized. Going back through an entrance will lead back to the previous area + Uncoupled: Entrances are randomized. Going back through an entrance can lead to a different area + """ + + display_name: str = "Entrance Randomizer" + + option_disabled: int = 0 + option_coupled: int = 1 + option_uncoupled: int = 2 + + default = 0 + + +class EntranceRandomizerIncludeSubwayDestinations(Toggle): + """ + If true, the entrance randomizer will include subway destinations in the randomization pool. + + Only relevant if the entrance randomizer is enabled. + """ + + display_name: str = "Entrance Randomizer - Include Subway Destinations" + + +class TrapPercentage(Range): + """ + Determines the percentage chance that a trap will replace a filler item. + + Possible traps are: + - Infinite Corridor Trap: The player is teleported to a random depth in the Infinite Corridor + - Reverse Controls Trap: The player's panorama controls are reversed for 30 seconds + - Teleport Trap: The player is teleported to a random location + - ZVision Trap: The player's vision is obscured for 30 seconds + """ + + display_name = "Trap Percentage" + + range_start = 0 + range_end = 100 + + default = 0 + + +class InfiniteCorridorTrapWeight(Range): + """ + Determines the weight of the Infinite Corridor Trap. + + The higher the weight, the more likely this trap will be chosen when a trap is rolled. + """ + + display_name = "Infinite Corridor Trap Weight" + + range_start = 0 + range_end = 100 + + default = 1 + + +class ReverseControlsTrapWeight(Range): + """ + Determines the weight of the Reverse Controls Trap. + + The higher the weight, the more likely this trap will be chosen when a trap is rolled. + """ + + display_name = "Reverse Controls Trap Weight" + + range_start = 0 + range_end = 100 + + default = 1 + + +class TeleportTrapWeight(Range): + """ + Determines the weight of the Teleport Trap. + + The higher the weight, the more likely this trap will be chosen when a trap is rolled. + """ + + display_name = "Teleport Trap Weight" + + range_start = 0 + range_end = 100 + + default = 1 + + +class ZVisionTrapWeight(Range): + """ + Determines the weight of the ZVision Trap. + + The higher the weight, the more likely this trap will be chosen when a trap is rolled. + """ + + display_name = "ZVision Trap Weight" + + range_start = 0 + range_end = 100 + + default = 1 + + class GrantMissableLocationChecks(Toggle): """ If true, performing an irreversible action will grant the locations checks that would have become unobtainable as a - result of that action when you meet the item requirements + result of that action when you meet the item requirements. Otherwise, the player is expected to potentially have to use the save system to reach those location checks. If you don't like the idea of rarely having to reload an earlier save to get a location check, make sure this option is - enabled + enabled. + + Note: This option is incompatible with the entrance randomizer and will be forced off in the scenario where + entrances are randomized. """ display_name: str = "Grant Missable Checks" +class ClientSeedInformation(Choice): + """ + Determines what information about the seed the client will reveal after using the /zork command. + + Reveal Nothing: No information about the seed is displayed + Reveal Goal: Only the goal of the seed is displayed + Reveal Goal and Options: Both the goal and the options of the seed are displayed + """ + + display_name: str = "Client Seed Information" + + option_reveal_nothing: int = 0 + option_reveal_goal: int = 1 + option_reveal_goal_and_options: int = 2 + + default = 2 + + @dataclass -class ZorkGrandInquisitorOptions(PerGameCommonOptions): +class ZorkGrandInquisitorOptions(PerGameCommonOptions, DeathLinkMixin): + start_inventory_from_pool: StartInventoryPool goal: Goal - quick_port_foozle: QuickPortFoozle - start_with_hotspot_items: StartWithHotspotItems + artifacts_of_magic_total: ArtifactsOfMagicTotal + artifacts_of_magic_required: ArtifactsOfMagicRequired + landmarks_required: LandmarksRequired + deaths_required: DeathsRequired + starting_location: StartingLocation + hotspots: Hotspots + craftable_spells: CraftableSpells + wild_voxam: WildVoxam + wild_voxam_chance: WildVoxamChance deathsanity: Deathsanity + landmarksanity: Landmarksanity + entrance_randomizer: EntranceRandomizer + entrance_randomizer_include_subway_destinations: EntranceRandomizerIncludeSubwayDestinations + trap_percentage: TrapPercentage + infinite_corridor_trap_weight: InfiniteCorridorTrapWeight + reverse_controls_trap_weight: ReverseControlsTrapWeight + teleport_trap_weight: TeleportTrapWeight + zvision_trap_weight: ZVisionTrapWeight grant_missable_location_checks: GrantMissableLocationChecks + client_seed_information: ClientSeedInformation + + +# Option presets here... + +option_groups: List[OptionGroup] = [ + OptionGroup( + "Goal Options", + [ + Goal, + ArtifactsOfMagicTotal, + ArtifactsOfMagicRequired, + LandmarksRequired, + DeathsRequired, + ], + ), + OptionGroup( + "Gameplay Options", + [ + StartingLocation, + Hotspots, + CraftableSpells, + WildVoxam, + WildVoxamChance, + Deathsanity, + Landmarksanity, + EntranceRandomizer, + EntranceRandomizerIncludeSubwayDestinations, + ], + ), + OptionGroup( + "Trap Options", + [ + TrapPercentage, + InfiniteCorridorTrapWeight, + ReverseControlsTrapWeight, + TeleportTrapWeight, + ZVisionTrapWeight, + ], + ), + OptionGroup( + "Client Options", + [ + GrantMissableLocationChecks, + ClientSeedInformation, + ], + ), +] diff --git a/worlds/zork_grand_inquisitor/test/test_access.py b/worlds/zork_grand_inquisitor/test/skip_test_access.py similarity index 100% rename from worlds/zork_grand_inquisitor/test/test_access.py rename to worlds/zork_grand_inquisitor/test/skip_test_access.py diff --git a/worlds/zork_grand_inquisitor/test/test_data_funcs.py b/worlds/zork_grand_inquisitor/test/skip_test_data_funcs.py similarity index 100% rename from worlds/zork_grand_inquisitor/test/test_data_funcs.py rename to worlds/zork_grand_inquisitor/test/skip_test_data_funcs.py diff --git a/worlds/zork_grand_inquisitor/test/test_locations.py b/worlds/zork_grand_inquisitor/test/skip_test_locations.py similarity index 100% rename from worlds/zork_grand_inquisitor/test/test_locations.py rename to worlds/zork_grand_inquisitor/test/skip_test_locations.py diff --git a/worlds/zork_grand_inquisitor/world.py b/worlds/zork_grand_inquisitor/world.py index 3698ad7f8960..d6d64cd9d2bd 100644 --- a/worlds/zork_grand_inquisitor/world.py +++ b/worlds/zork_grand_inquisitor/world.py @@ -1,34 +1,76 @@ -from typing import Any, Dict, List, Tuple +import logging -from BaseClasses import Item, ItemClassification, Location, Region, Tutorial +from typing import Any, Dict, List, Set, TextIO, Tuple, Union +from BaseClasses import Entrance, EntranceType, Item, ItemClassification, Location, Region, Tutorial +from Options import OptionError +from Utils import visualize_regions + +from entrance_rando import disconnect_entrance_for_randomization, randomize_entrances from worlds.AutoWorld import WebWorld, World -from .data.item_data import item_data, ZorkGrandInquisitorItemData -from .data.location_data import location_data, ZorkGrandInquisitorLocationData -from .data.region_data import region_data +from .data.entrance_data import Entrance, EntranceRuleData, entrance_rule_data + +from .data.entrance_randomizer_data import ( + entrances_to_game_location_teleports, + entrances_to_game_locations, + randomizable_entrances, + randomizable_entrances_subway, +) + +from .data.item_data import ZorkGrandInquisitorItemData, item_data +from .data.location_data import ZorkGrandInquisitorLocationData + +from .data.mapping_data import ( + early_items_for_starting_location, + endgame_connecting_regions_for_goal, + entrance_names, + entrance_names_reverse, + starter_kit_for_entrance_randomizer, + starter_kits_for_starting_location, + starting_location_to_region, +) from .data_funcs import ( item_names_to_id, item_names_to_item, location_names_to_id, + id_to_craftable_spell_behaviors, + id_to_deathsanity, + id_to_entrance_randomizer, + id_to_goals, + id_to_hotspots, + id_to_landmarksanity, + id_to_starting_locations, item_groups, items_with_tag, location_groups, - locations_by_region, + locations_by_region_for_world, + locations_with_tag, + prepare_item_data, + prepare_location_data, location_access_rule_for, + entrances_by_region_for_world, entrance_access_rule_for, + goal_access_rule_for, ) from .enums import ( + ZorkGrandInquisitorCraftableSpellBehaviors, + ZorkGrandInquisitorDeathsanity, + ZorkGrandInquisitorEntranceRandomizer, ZorkGrandInquisitorEvents, + ZorkGrandInquisitorGoals, + ZorkGrandInquisitorHotspots, ZorkGrandInquisitorItems, + ZorkGrandInquisitorLandmarksanity, ZorkGrandInquisitorLocations, ZorkGrandInquisitorRegions, + ZorkGrandInquisitorStartingLocations, ZorkGrandInquisitorTags, ) -from .options import ZorkGrandInquisitorOptions +from .options import ZorkGrandInquisitorOptions, option_groups class ZorkGrandInquisitorItem(Item): @@ -53,6 +95,9 @@ class ZorkGrandInquisitorWebWorld(WebWorld): ) ] + # Option presets here... + option_groups = option_groups + class ZorkGrandInquisitorWorld(World): """ @@ -74,24 +119,184 @@ class ZorkGrandInquisitorWorld(World): item_name_groups = item_groups() location_name_groups = location_groups() - required_client_version: Tuple[int, int, int] = (0, 4, 4) + required_client_version: Tuple[int, int, int] = (0, 6, 0) web = ZorkGrandInquisitorWebWorld() + artifacts_of_magic_required: int + artifacts_of_magic_total: int + craftable_spells: ZorkGrandInquisitorCraftableSpellBehaviors + deaths_required: int + deathsanity: ZorkGrandInquisitorDeathsanity + early_items: Tuple[ZorkGrandInquisitorItems, ...] + entrance_randomizer: ZorkGrandInquisitorEntranceRandomizer + entrance_randomizer_include_subway_destinations: bool + + entrance_randomizer_pairings: Dict[ + Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions], + Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions], + ] + + entrance_randomizer_pairings_by_name: Dict[str, str] + entrance_rule_data: EntranceRuleData filler_item_names: List[str] = item_groups()["Filler"] + goal: ZorkGrandInquisitorGoals + grant_missable_location_checks: bool + hotspots: ZorkGrandInquisitorHotspots + initial_totemizer_destination: ZorkGrandInquisitorItems + item_data: Dict[ZorkGrandInquisitorItems, ZorkGrandInquisitorItemData] item_name_to_item: Dict[str, ZorkGrandInquisitorItems] = item_names_to_item() + landmarks_required: int + landmarksanity: ZorkGrandInquisitorLandmarksanity + location_data: Dict[Union[ZorkGrandInquisitorLocations, ZorkGrandInquisitorEvents], ZorkGrandInquisitorLocationData] + locked_items: Dict[ZorkGrandInquisitorLocations, ZorkGrandInquisitorItems] + starter_kit: Tuple[ZorkGrandInquisitorItems, ...] + starting_location: ZorkGrandInquisitorStartingLocations + trap_percentage: int + trap_weights: Tuple[int, ...] + + @property + def is_universal_tracker(self) -> bool: + return hasattr(self.multiworld, "re_gen_passthrough") + + def generate_early(self) -> None: + self.goal = id_to_goals()[self.options.goal.value] + + self.artifacts_of_magic_required = self.options.artifacts_of_magic_required.value + self.artifacts_of_magic_total = self.options.artifacts_of_magic_total.value + + if self.artifacts_of_magic_required > self.artifacts_of_magic_total: + self.artifacts_of_magic_total = self.artifacts_of_magic_required + + if self.goal == ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: + logging.warning( + f"Zork Grand Inquisitor: {self.player_name} has more required artifacts than " + "total artifacts. Using required artifacts as total artifacts..." + ) + + self.landmarks_required = self.options.landmarks_required.value + self.deaths_required = self.options.deaths_required.value + + self.starting_location = id_to_starting_locations()[self.options.starting_location.value] + + self.entrance_randomizer = id_to_entrance_randomizer()[self.options.entrance_randomizer.value] + + if self.entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + self.topology_present = True + + self.entrance_randomizer_include_subway_destinations = bool( + self.options.entrance_randomizer_include_subway_destinations + ) + + self.starter_kit = tuple() + + if starter_kits_for_starting_location[self.starting_location] is not None: + self.starter_kit = self.random.choice( + starter_kits_for_starting_location[self.starting_location] + ) + + if self.entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + starter_kit_extended: List[ZorkGrandInquisitorItems] = list(self.starter_kit) + + item: ZorkGrandInquisitorItems + for item in starter_kit_for_entrance_randomizer: + if item not in starter_kit_extended: + starter_kit_extended.append(item) + + self.starter_kit = tuple(starter_kit_extended) + + self.early_items = tuple() + + if early_items_for_starting_location[self.starting_location] is not None: + self.early_items = self.random.choice( + early_items_for_starting_location[self.starting_location] + ) + + self.craftable_spells = id_to_craftable_spell_behaviors()[self.options.craftable_spells.value] + self.hotspots = id_to_hotspots()[self.options.hotspots] + + self.deathsanity = id_to_deathsanity()[self.options.deathsanity] + + if self.goal == ZorkGrandInquisitorGoals.GRIM_JOURNEY and ( + self.deathsanity == ZorkGrandInquisitorDeathsanity.OFF + ): + self.deathsanity = ZorkGrandInquisitorDeathsanity.ON + + self.landmarksanity = id_to_landmarksanity()[self.options.landmarksanity] + + if self.goal == ZorkGrandInquisitorGoals.ZORK_TOUR and ( + self.landmarksanity == ZorkGrandInquisitorLandmarksanity.OFF + ): + self.landmarksanity = ZorkGrandInquisitorLandmarksanity.ON + + self.grant_missable_location_checks = bool(self.options.grant_missable_location_checks) + + if self.grant_missable_location_checks: + if self.entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + self.grant_missable_location_checks = False + + logging.warning( + f"Zork Grand Inquisitor: {self.player_name} wants to grant missable location checks but " + "has the entrance randomizer enabled. Disabling the grantinmg of missable location checks..." + ) + + self.entrance_randomizer_pairings = dict() + self.entrance_randomizer_pairings_by_name = dict() + + self.entrance_rule_data = entrance_rule_data + + self.item_data = prepare_item_data( + self.starting_location, + self.goal, + self.deathsanity, + self.landmarksanity, + self.entrance_randomizer, + ) + + self.location_data = prepare_location_data( + self.starting_location, + self.goal, + self.deathsanity, + self.landmarksanity, + ) + + self.locked_items = self._prepare_locked_items() + + self.initial_totemizer_destination = self._select_initial_totemizer_destination() + + self.trap_percentage = self.options.trap_percentage.value / 100 + + self.trap_weights = ( + self.options.infinite_corridor_trap_weight.value, + self.options.reverse_controls_trap_weight.value, + self.options.teleport_trap_weight.value, + self.options.zvision_trap_weight.value, + ) + + if self.trap_percentage and not any(self.trap_weights): + raise OptionError( + f"Zork Grand Inquisitor: {self.player_name} has traps enabled but all traps are weighted at 0." + ) + + # Universal Tracker Support + if self.is_universal_tracker: + self._apply_universal_tracker_passthrough() def create_regions(self) -> None: - deathsanity: bool = bool(self.options.deathsanity) + entrances_by_region: Dict[ZorkGrandInquisitorRegions, List[Entrance]] = entrances_by_region_for_world( + self.entrance_rule_data + ) region_mapping: Dict[ZorkGrandInquisitorRegions, Region] = dict() region_enum_item: ZorkGrandInquisitorRegions - for region_enum_item in region_data.keys(): + for region_enum_item in entrances_by_region.keys(): region_mapping[region_enum_item] = Region(region_enum_item.value, self.player, self.multiworld) region_locations_mapping: Dict[ZorkGrandInquisitorRegions, List[ZorkGrandInquisitorLocations]] - region_locations_mapping = locations_by_region(include_deathsanity=deathsanity) + region_locations_mapping = locations_by_region_for_world(self.location_data) + + region_connecting_endgame: ZorkGrandInquisitorRegions = endgame_connecting_regions_for_goal[self.goal] region_enum_item: ZorkGrandInquisitorRegions region: Region @@ -101,7 +306,7 @@ def create_regions(self) -> None: # Locations location_enum_item: ZorkGrandInquisitorLocations for location_enum_item in regions_locations: - data: ZorkGrandInquisitorLocationData = location_data[location_enum_item] + data: ZorkGrandInquisitorLocationData = self.location_data[location_enum_item] location: ZorkGrandInquisitorLocation = ZorkGrandInquisitorLocation( self.player, @@ -110,7 +315,10 @@ def create_regions(self) -> None: region_mapping[data.region], ) - if isinstance(location_enum_item, ZorkGrandInquisitorEvents): + # Locked Items + if location_enum_item in self.locked_items: + location.place_locked_item(self.create_item(self.locked_items[location_enum_item].value)) + elif isinstance(location_enum_item, ZorkGrandInquisitorEvents): location.place_locked_item( ZorkGrandInquisitorItem( data.event_item_name, @@ -120,6 +328,7 @@ def create_regions(self) -> None: ) ) + # Access Rules location_access_rule: str = location_access_rule_for(location_enum_item, self.player) if location_access_rule != "lambda state: True": @@ -129,58 +338,149 @@ def create_regions(self) -> None: # Connections region_exit: ZorkGrandInquisitorRegions - for region_exit in region_data[region_enum_item].exits or tuple(): - entrance_access_rule: str = entrance_access_rule_for(region_enum_item, region_exit, self.player) + for _, region_exit in entrances_by_region[region_enum_item]: + connection_tuple: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] = ( + region_enum_item, + region_exit, + ) + + entrance_access_rule: str = entrance_access_rule_for( + region_enum_item, + region_exit, + self.player, + dataset=self.entrance_rule_data, + ) + + entrance: Entrance if entrance_access_rule == "lambda state: True": - region.connect(region_mapping[region_exit]) + entrance = region.connect(region_mapping[region_exit]) else: - region.connect(region_mapping[region_exit], rule=eval(entrance_access_rule)) + entrance = region.connect(region_mapping[region_exit], rule=eval(entrance_access_rule)) + + entrance.name = entrance_names.get(connection_tuple, entrance.name) + + if region_enum_item == region_connecting_endgame: + goal_access_rule: str = goal_access_rule_for( + region_enum_item, + self.goal, + self.player, + self.artifacts_of_magic_required, + self.landmarks_required, + self.deaths_required, + ) + + region.connect(region_mapping[ZorkGrandInquisitorRegions.ENDGAME], rule=eval(goal_access_rule)) self.multiworld.regions.append(region) + # Connect "Menu" region to starting location and to endgame when applicable + region_menu: Region = Region("Menu", self.player, self.multiworld) + region_starting_location: ZorkGrandInquisitorRegions = starting_location_to_region[self.starting_location] + + region_menu.connect(region_mapping[ZorkGrandInquisitorRegions.ANYWHERE]) + region_menu.connect(region_mapping[region_starting_location]) + + if region_connecting_endgame == ZorkGrandInquisitorRegions.MENU: + goal_access_rule: str = goal_access_rule_for( + ZorkGrandInquisitorRegions.MENU, + self.goal, + self.player, + self.artifacts_of_magic_required, + self.landmarks_required, + self.deaths_required, + ) + + region_menu.connect(region_mapping[ZorkGrandInquisitorRegions.ENDGAME], rule=eval(goal_access_rule)) + + self.multiworld.regions.append(region_menu) + def create_items(self) -> None: - quick_port_foozle: bool = bool(self.options.quick_port_foozle) - start_with_hotspot_items: bool = bool(self.options.start_with_hotspot_items) + # Populate Items to Ignore and Precollect + items_to_ignore: Set[ZorkGrandInquisitorItems] = ( + items_with_tag(ZorkGrandInquisitorTags.FILLER) + | items_with_tag(ZorkGrandInquisitorTags.TRAP) + | items_with_tag(ZorkGrandInquisitorTags.GOAL_THREE_ARTIFACTS) + | items_with_tag(ZorkGrandInquisitorTags.GOAL_ZORK_TOUR) + | items_with_tag(ZorkGrandInquisitorTags.GOAL_GRIM_JOURNEY) + | set(self.locked_items.values()) + ) + if self.goal != ZorkGrandInquisitorGoals.ARTIFACT_OF_MAGIC_HUNT: + items_to_ignore |= items_with_tag(ZorkGrandInquisitorTags.GOAL_ARTIFACT_OF_MAGIC_HUNT) + + items_to_precollect: Set[ZorkGrandInquisitorItems] = ( + set(self.starter_kit) + | {self.initial_totemizer_destination} + ) + + hotspot_items: Set[ZorkGrandInquisitorItems] = items_with_tag(ZorkGrandInquisitorTags.HOTSPOT) + + hotspot_regional_items: Set[ZorkGrandInquisitorItems] = items_with_tag( + ZorkGrandInquisitorTags.HOTSPOT_REGIONAL + ) + + if self.hotspots == ZorkGrandInquisitorHotspots.ENABLED: + items_to_ignore |= hotspot_items + items_to_precollect |= hotspot_regional_items + elif self.hotspots == ZorkGrandInquisitorHotspots.REQUIRE_ITEM_PER_REGION: + items_to_ignore |= hotspot_items + elif self.hotspots == ZorkGrandInquisitorHotspots.REQUIRE_ITEM_PER_HOTSPOT: + items_to_ignore |= hotspot_regional_items + + if self.starting_location != ZorkGrandInquisitorStartingLocations.DM_LAIR_INTERIOR: + items_to_precollect.add(ZorkGrandInquisitorItems.HOTSPOT_DUNGEON_MASTERS_HOUSE_EXIT) + + if self.starting_location != ZorkGrandInquisitorStartingLocations.SPELL_LAB: + items_to_precollect.add(ZorkGrandInquisitorItems.HOTSPOT_SPELL_LAB_BRIDGE_EXIT) + + items_to_ignore |= items_to_precollect + + # Create Item Pool item_pool: List[ZorkGrandInquisitorItem] = list() - item: ZorkGrandInquisitorItems data: ZorkGrandInquisitorItemData - for item, data in item_data.items(): - tags: Tuple[ZorkGrandInquisitorTags, ...] = data.tags or tuple() - - if ZorkGrandInquisitorTags.FILLER in tags: - continue - elif ZorkGrandInquisitorTags.HOTSPOT in tags and start_with_hotspot_items: + for item, data in self.item_data.items(): + if item in items_to_ignore: continue - item_pool.append(self.create_item(item.value)) + if item == ZorkGrandInquisitorItems.ARTIFACT_OF_MAGIC: + for _ in range(self.artifacts_of_magic_total): + item_pool.append(self.create_item(item.value)) + else: + item_pool.append(self.create_item(item.value)) - total_locations: int = len(self.multiworld.get_unfilled_locations(self.player)) - item_pool += [self.create_filler() for _ in range(total_locations - len(item_pool))] + total_location_count: int = len(self.multiworld.get_unfilled_locations(self.player)) + to_fill_location_count: int = total_location_count - len(item_pool) - self.multiworld.itempool += item_pool + trap_count: int = int(round(to_fill_location_count * self.trap_percentage)) + + if trap_count: + item_pool += [ + self.create_item(trap.value) for trap in self._sample_trap_items(trap_count) + ] - if quick_port_foozle: - self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.ROPE.value] = 1 - self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.LANTERN.value] = 1 + item_pool += [ + self.create_filler() for _ in range(to_fill_location_count - trap_count) + ] - if not start_with_hotspot_items: - self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.HOTSPOT_WELL.value] = 1 - self.multiworld.early_items[self.player][ZorkGrandInquisitorItems.HOTSPOT_JACKS_DOOR.value] = 1 + self.multiworld.itempool += item_pool - self.multiworld.early_items[self.player][ - ZorkGrandInquisitorItems.HOTSPOT_GRAND_INQUISITOR_DOLL.value - ] = 1 + # Precollect Items + for item in items_to_precollect: + self.multiworld.push_precollected(self.create_item(item.value)) - if start_with_hotspot_items: - item: ZorkGrandInquisitorItems - for item in sorted(items_with_tag(ZorkGrandInquisitorTags.HOTSPOT), key=lambda item: item.name): - self.multiworld.push_precollected(self.create_item(item.value)) + # Define Early Items + items_to_place_early: Set[ZorkGrandInquisitorItems] = ( + set(self.early_items) - items_to_ignore + ) + + if len(items_to_place_early): + for item in items_to_place_early: + self.multiworld.early_items[self.player][item.value] = 1 def create_item(self, name: str) -> ZorkGrandInquisitorItem: - data: ZorkGrandInquisitorItemData = item_data[self.item_name_to_item[name]] + data: ZorkGrandInquisitorItemData = (self.item_data or item_data)[self.item_name_to_item[name]] return ZorkGrandInquisitorItem( name, @@ -189,17 +489,395 @@ def create_item(self, name: str) -> ZorkGrandInquisitorItem: self.player, ) + def connect_entrances(self) -> None: + if self.entrance_randomizer == ZorkGrandInquisitorEntranceRandomizer.DISABLED: + visualize_regions(self.multiworld.get_region("Menu", self.player), "zgi-vanilla.puml") + return + + randomization_pool: List[Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions]] = list( + randomizable_entrances + ) + + if self.entrance_randomizer_include_subway_destinations: + randomization_pool.extend(randomizable_entrances_subway) + + entrance: Entrance + for entrance in self.get_entrances(): + entrance_region_tuple: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] = ( + ZorkGrandInquisitorRegions(entrance.parent_region.name), + ZorkGrandInquisitorRegions(entrance.connected_region.name), + ) + + if entrance_region_tuple in randomization_pool: + entrance.randomization_type = EntranceType.TWO_WAY + disconnect_entrance_for_randomization(entrance) + + if self.is_universal_tracker: + self._reconnect_entrances_for_universal_tracker() + visualize_regions(self.multiworld.get_region("Menu", self.player), "zgi-er-ut.puml") + + return + + entrance_randomizer_pairings: List[Tuple[str, str]] = randomize_entrances( + self, + self.entrance_randomizer == ZorkGrandInquisitorEntranceRandomizer.COUPLED, + {0: [0]}, + ).pairings + + self.entrance_randomizer_pairings = self._process_entrance_randomizer_pairings(entrance_randomizer_pairings) + + visualize_regions(self.multiworld.get_region("Menu", self.player), "zgi-er.puml") + def generate_basic(self) -> None: self.multiworld.completion_condition[self.player] = lambda state: state.has("Victory", self.player) def fill_slot_data(self) -> Dict[str, Any]: - return self.options.as_dict( + slot_data: Dict[str, Any] = self.options.as_dict( "goal", - "quick_port_foozle", - "start_with_hotspot_items", + "artifacts_of_magic_required", + "artifacts_of_magic_total", + "landmarks_required", + "deaths_required", + "starting_location", + "hotspots", + "craftable_spells", + "wild_voxam", + "wild_voxam_chance", "deathsanity", + "landmarksanity", + "entrance_randomizer", + "entrance_randomizer_include_subway_destinations", + "trap_percentage", "grant_missable_location_checks", + "client_seed_information", + "death_link", + ) + + slot_data["starter_kit"] = sorted([item.value for item in self.starter_kit]) + slot_data["initial_totemizer_destination"] = self.initial_totemizer_destination.value + + slot_data["trap_weights"] = self.trap_weights + + slot_data["save_ids"] = ( + self.random.randint(1, 65365), + self.random.randint(1, 65365), + self.random.randint(1, 65365), ) + # Relay generate_early Overrides + if slot_data["artifacts_of_magic_total"] != self.artifacts_of_magic_total: + slot_data["artifacts_of_magic_total"] = self.artifacts_of_magic_total + + if slot_data["deathsanity"] != self.deathsanity.value: + slot_data["deathsanity"] = self.deathsanity.value + + if slot_data["landmarksanity"] != self.landmarksanity.value: + slot_data["landmarksanity"] = self.landmarksanity.value + + if slot_data["grant_missable_location_checks"] != self.grant_missable_location_checks: + slot_data["grant_missable_location_checks"] = self.grant_missable_location_checks + + slot_data["entrance_randomizer_data"] = dict() + slot_data["entrance_randomizer_data_by_name"] = dict() + + if self.entrance_randomizer != ZorkGrandInquisitorEntranceRandomizer.DISABLED: + slot_data["entrance_randomizer_data"] = self._prepare_entrance_randomizer_slot_data() + slot_data["entrance_randomizer_data_by_name"] = self._prepare_entrance_randomizer_slot_data_by_name() + + return slot_data + + def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]) -> None: + if self.entrance_randomizer == ZorkGrandInquisitorEntranceRandomizer.DISABLED: + return + + entrance_name_cache: Dict[ZorkGrandInquisitorRegions, List[str]] = dict() + + entrance_from: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + entrance_to: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + for entrance_from, entrance_to in self.entrance_randomizer_pairings.items(): + if entrance_to[1] not in entrance_name_cache: + entrance_name_cache[entrance_to[1]] = list() + + entrance_name_cache[entrance_to[1]].append( + f"{entrance_from[0].value}: {entrance_names[entrance_from]}" + ) + + data: Dict[int, str] = dict() + + location_enum_item: Union[ZorkGrandInquisitorLocations, ZorkGrandInquisitorEvents] + location_data: ZorkGrandInquisitorLocationData + for location_enum_item, location_data in self.location_data.items(): + if isinstance(location_enum_item, ZorkGrandInquisitorEvents): + continue + elif location_data.region not in entrance_name_cache: + continue + + data[location_data.archipelago_id] = " OR ".join(entrance_name_cache[location_data.region]) + + hint_data[self.player] = data + + def write_spoiler_header(self, spoiler_handle: TextIO) -> None: + if self.entrance_randomizer == ZorkGrandInquisitorEntranceRandomizer.DISABLED: + return + + spoiler_handle.write("\nRandomized Entrances:") + + spoiler_lines: List[str] = list() + + entrance_from: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + entrance_to: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + for entrance_from, entrance_to in self.entrance_randomizer_pairings.items(): + spoiler_lines.append( + f"\n {entrance_from[0].value}: {entrance_names[entrance_from]} -> " + f"{entrance_to[1].value} (Emerging out of: {entrance_names[entrance_to]})" + ) + + spoiler_line: str + for spoiler_line in sorted(spoiler_lines): + spoiler_handle.write(spoiler_line) + def get_filler_item_name(self) -> str: return self.random.choice(self.filler_item_names) + + # Universal Tracker Support + @staticmethod + def interpret_slot_data(slot_data: Dict[str, Any]) -> Dict[str, Any]: + slot_data["goal"] = id_to_goals()[slot_data["goal"]] + slot_data["starting_location"] = id_to_starting_locations()[slot_data["starting_location"]] + slot_data["hotspots"] = id_to_hotspots()[slot_data["hotspots"]] + slot_data["craftable_spells"] = id_to_craftable_spell_behaviors()[slot_data["craftable_spells"]] + slot_data["deathsanity"] = id_to_deathsanity()[slot_data["deathsanity"]] + slot_data["landmarksanity"] = id_to_landmarksanity()[slot_data["landmarksanity"]] + slot_data["entrance_randomizer"] = id_to_entrance_randomizer()[slot_data["entrance_randomizer"]] + slot_data["entrance_randomizer_include_subway_destinations"] = bool(slot_data["entrance_randomizer"]) + + slot_data["starter_kit"] = tuple([ZorkGrandInquisitorItems(item) for item in slot_data["starter_kit"]]) + + slot_data["initial_totemizer_destination"] = ZorkGrandInquisitorItems( + slot_data["initial_totemizer_destination"] + ) + + return slot_data + + def _apply_universal_tracker_passthrough(self) -> None: + if "Zork Grand Inquisitor" in self.multiworld.re_gen_passthrough: + passthrough: Dict[str, Any] = self.multiworld.re_gen_passthrough["Zork Grand Inquisitor"] + + self.goal = passthrough["goal"] + self.artifacts_of_magic_required = passthrough["artifacts_of_magic_required"] + self.artifacts_of_magic_total = passthrough["artifacts_of_magic_total"] + self.landmarks_required = passthrough["landmarks_required"] + self.deaths_required = passthrough["deaths_required"] + self.starting_location = passthrough["starting_location"] + self.starter_kit = passthrough["starter_kit"] + self.craftable_spells = passthrough["craftable_spells"] + self.hotspots = passthrough["hotspots"] + self.deathsanity = passthrough["deathsanity"] + self.landmarksanity = passthrough["landmarksanity"] + self.entrance_randomizer = passthrough["entrance_randomizer"] + + self.entrance_randomizer_include_subway_destinations = passthrough[ + "entrance_randomizer_include_subway_destinations" + ] + + self.entrance_randomizer_pairings_by_name = passthrough["entrance_randomizer_data_by_name"] + + self.item_data = prepare_item_data( + self.starting_location, + self.goal, + self.deathsanity, + self.landmarksanity, + self.entrance_randomizer, + ) + + self.location_data = prepare_location_data( + self.starting_location, + self.goal, + self.deathsanity, + self.landmarksanity, + ) + + self.locked_items = self._prepare_locked_items() + self.initial_totemizer_destination = passthrough["initial_totemizer_destination"] + self.trap_percentage = passthrough["trap_percentage"] / 100 + self.trap_weights = passthrough["trap_weights"] + + def _prepare_entrance_randomizer_slot_data(self) -> Dict[str, str]: + entrance_randomizer_slot_data: Dict[str, str] = dict() + + entrance_from: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + entrance_to: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + for entrance_from, entrance_to in self.entrance_randomizer_pairings.items(): + game_locations_pair: Tuple[str, str] + for game_locations_pair in entrances_to_game_locations[entrance_from]: + entrance_randomizer_slot_data["-".join(game_locations_pair)] = ( + " ".join(entrances_to_game_location_teleports[entrance_to]) + ) + + return entrance_randomizer_slot_data + + def _prepare_entrance_randomizer_slot_data_by_name(self) -> Dict[str, str]: + entrance_randomizer_slot_data_by_name: Dict[str, str] = dict() + + entrance_from: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + entrance_to: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] + for entrance_from, entrance_to in self.entrance_randomizer_pairings.items(): + entrance_randomizer_slot_data_by_name[entrance_names[entrance_from]] = entrance_names[entrance_to] + + return entrance_randomizer_slot_data_by_name + + def _prepare_locked_items( + self, + ) -> Dict[ZorkGrandInquisitorLocations, ZorkGrandInquisitorItems]: + locked_items: Dict[ZorkGrandInquisitorLocations, ZorkGrandInquisitorItems] = dict() + + # Goal Items + if self.goal == ZorkGrandInquisitorGoals.THREE_ARTIFACTS: + locked_items[ + ZorkGrandInquisitorLocations.COME_TO_PAPA_YOU_NUT + ] = ZorkGrandInquisitorItems.COCONUT_OF_QUENDOR + + locked_items[ + ZorkGrandInquisitorLocations.GOOD_PUZZLE_SMART_BROG + ] = ZorkGrandInquisitorItems.SKULL_OF_YORUK + + locked_items[ + ZorkGrandInquisitorLocations.YOU_LOSE_MUFFET_ANTE_UP + ] = ZorkGrandInquisitorItems.CUBE_OF_FOUNDATION + elif self.goal == ZorkGrandInquisitorGoals.ZORK_TOUR: + landmarksanity_locations: Set[ZorkGrandInquisitorLocations] = locations_with_tag( + ZorkGrandInquisitorTags.LANDMARKSANITY + ) + + location: ZorkGrandInquisitorLocations + for location in landmarksanity_locations: + locked_items[location] = ZorkGrandInquisitorItems.LANDMARK + elif self.goal == ZorkGrandInquisitorGoals.GRIM_JOURNEY: + deathsanity_locations: Set[ZorkGrandInquisitorLocations] = locations_with_tag( + ZorkGrandInquisitorTags.DEATHSANITY + ) + + location: ZorkGrandInquisitorLocations + for location in deathsanity_locations: + locked_items[location] = ZorkGrandInquisitorItems.DEATH + + # Craftable Spells + if self.craftable_spells == ZorkGrandInquisitorCraftableSpellBehaviors.VANILLA: + if ZorkGrandInquisitorItems.SPELL_BEBURTT not in self.starter_kit: + locked_items[ + ZorkGrandInquisitorLocations.IMBUE_BEBURTT + ] = ZorkGrandInquisitorItems.SPELL_BEBURTT + + if ZorkGrandInquisitorItems.SPELL_OBIDIL not in self.starter_kit: + locked_items[ + ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP + ] = ZorkGrandInquisitorItems.SPELL_OBIDIL + + if ZorkGrandInquisitorItems.SPELL_SNAVIG not in self.starter_kit: + locked_items[ + ZorkGrandInquisitorLocations.SNAVIG_REPAIRED + ] = ZorkGrandInquisitorItems.SPELL_SNAVIG + + if ZorkGrandInquisitorItems.SPELL_YASTARD not in self.starter_kit: + locked_items[ + ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU + ] = ZorkGrandInquisitorItems.SPELL_YASTARD + elif self.craftable_spells == ZorkGrandInquisitorCraftableSpellBehaviors.ANY_SPELL: + allowable_spells: Set[ZorkGrandInquisitorItems] = { + ZorkGrandInquisitorItems.SPELL_BEBURTT, + ZorkGrandInquisitorItems.SPELL_GLORF, + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ZorkGrandInquisitorItems.SPELL_IGRAM, + ZorkGrandInquisitorItems.SPELL_KENDALL, + ZorkGrandInquisitorItems.SPELL_OBIDIL, + ZorkGrandInquisitorItems.SPELL_NARWILE, + ZorkGrandInquisitorItems.SPELL_REZROV, + ZorkGrandInquisitorItems.SPELL_SNAVIG, + ZorkGrandInquisitorItems.SPELL_THROCK, + ZorkGrandInquisitorItems.SPELL_YASTARD, + } + + allowable_spells -= set(self.starter_kit) + + allowable_spells_yastard: List[str] = sorted([item.value for item in allowable_spells]) + + spell_yastard: ZorkGrandInquisitorItems = self.item_name_to_item[ + self.random.choice(allowable_spells_yastard) + ] + + locked_items[ZorkGrandInquisitorLocations.OH_WOW_TALK_ABOUT_DEJA_VU] = spell_yastard + + allowable_spells -= {spell_yastard} + + if self.starting_location != ZorkGrandInquisitorStartingLocations.SPELL_LAB: + allowable_spells -= { + ZorkGrandInquisitorItems.SPELL_GOLGATEM, + ZorkGrandInquisitorItems.SPELL_REZROV, + } + + allowable_spells_spell_lab: List[str] = sorted( + [item.value for item in allowable_spells] + ) + + spells_to_lock: List[ZorkGrandInquisitorItems] = [ + self.item_name_to_item[item] + for item in self.random.sample(allowable_spells_spell_lab, 3) + ] + + locked_items[ZorkGrandInquisitorLocations.IMBUE_BEBURTT] = spells_to_lock[0] + locked_items[ZorkGrandInquisitorLocations.OBIDIL_DRIED_UP] = spells_to_lock[1] + locked_items[ZorkGrandInquisitorLocations.SNAVIG_REPAIRED] = spells_to_lock[2] + + return locked_items + + @staticmethod + def _process_entrance_randomizer_pairings(pairings: List[Tuple[str, str]]) -> Dict[ + Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions], + Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions], + ]: + entrance_randomizer_pairings: Dict[ + Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions], + Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions], + ] = dict() + + entrance_from: str + entrance_to: str + for entrance_from, entrance_to in pairings: + entrance_randomizer_pairings[entrance_names_reverse[entrance_from]] = tuple( + reversed(entrance_names_reverse[entrance_to]) + ) + + return entrance_randomizer_pairings + + def _reconnect_entrances_for_universal_tracker(self) -> None: + entrance_from: Entrance + for entrance_from in self.get_entrances(): + if entrance_from.name in self.entrance_randomizer_pairings_by_name: + entrance_to_name: str = self.entrance_randomizer_pairings_by_name[entrance_from.name] + + entrance_to_region_pair: Tuple[ZorkGrandInquisitorRegions, ZorkGrandInquisitorRegions] = ( + entrance_names_reverse[entrance_to_name] + ) + + entrance_from.connect(self.get_region(entrance_to_region_pair[1].value)) + + def _select_initial_totemizer_destination(self) -> ZorkGrandInquisitorItems: + return self.random.choice(( + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_HALL_OF_INQUISITION, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_SURFACE_OF_MERZ, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_NEWARK_NEW_JERSEY, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_INFINITY, + ZorkGrandInquisitorItems.TOTEMIZER_DESTINATION_STRAIGHT_TO_HELL, + )) + + def _sample_trap_items(self, count: int) -> List[ZorkGrandInquisitorItems]: + return self.random.choices( + ( + ZorkGrandInquisitorItems.TRAP_INFINITE_CORRIDOR, + ZorkGrandInquisitorItems.TRAP_REVERSE_CONTROLS, + ZorkGrandInquisitorItems.TRAP_TELEPORT, + ZorkGrandInquisitorItems.TRAP_ZVISION, + ), + weights=self.trap_weights, + k=count, + ) diff --git a/worlds/zork_grand_inquisitor/zork_grand_inquisitor.png b/worlds/zork_grand_inquisitor/zork_grand_inquisitor.png new file mode 100644 index 000000000000..141376eab43e Binary files /dev/null and b/worlds/zork_grand_inquisitor/zork_grand_inquisitor.png differ