189 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			189 lines
		
	
	
		
			6.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | import asyncio | ||
|  | 
 | ||
|  | import CommonClient | ||
|  | import NetUtils | ||
|  | import Utils | ||
|  | 
 | ||
|  | from typing import Any, Dict, List, Optional, Set, Tuple | ||
|  | 
 | ||
|  | 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): | ||
|  |     def _cmd_zork(self) -> None: | ||
|  |         """Attach to an open Zork Grand Inquisitor process.""" | ||
|  |         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.") | ||
|  |         else: | ||
|  |             self.output("Failed to attach to Zork Grand Inquisitor process.") | ||
|  | 
 | ||
|  |     def _cmd_brog(self) -> None: | ||
|  |         """List received Brog items.""" | ||
|  |         self.ctx.game_controller.list_received_brog_items() | ||
|  | 
 | ||
|  |     def _cmd_griff(self) -> None: | ||
|  |         """List received Griff items.""" | ||
|  |         self.ctx.game_controller.list_received_griff_items() | ||
|  | 
 | ||
|  |     def _cmd_lucy(self) -> None: | ||
|  |         """List received Lucy items.""" | ||
|  |         self.ctx.game_controller.list_received_lucy_items() | ||
|  | 
 | ||
|  |     def _cmd_hotspots(self) -> None: | ||
|  |         """List received Hotspots.""" | ||
|  |         self.ctx.game_controller.list_received_hotspots() | ||
|  | 
 | ||
|  | 
 | ||
|  | class ZorkGrandInquisitorContext(CommonClient.CommonContext): | ||
|  |     tags: Set[str] = {"AP"} | ||
|  |     game: str = "Zork Grand Inquisitor" | ||
|  |     command_processor: CommonClient.ClientCommandProcessor = ZorkGrandInquisitorCommandProcessor | ||
|  |     items_handling: int = 0b111 | ||
|  |     want_slot_data: bool = True | ||
|  | 
 | ||
|  |     item_name_to_id: Dict[str, int] = item_names_to_id() | ||
|  |     location_name_to_id: Dict[str, int] = location_names_to_id() | ||
|  | 
 | ||
|  |     id_to_items: Dict[int, ZorkGrandInquisitorItems] = id_to_items() | ||
|  |     id_to_locations: Dict[int, ZorkGrandInquisitorLocations] = id_to_locations() | ||
|  | 
 | ||
|  |     game_controller: GameController | ||
|  | 
 | ||
|  |     controller_task: Optional[asyncio.Task] | ||
|  | 
 | ||
|  |     process_attached_at_least_once: bool | ||
|  |     can_display_process_message: bool | ||
|  | 
 | ||
|  |     def __init__(self, server_address: Optional[str], password: Optional[str]) -> None: | ||
|  |         super().__init__(server_address, password) | ||
|  | 
 | ||
|  |         self.game_controller = GameController(logger=CommonClient.logger) | ||
|  | 
 | ||
|  |         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") | ||
|  | 
 | ||
|  |     async def server_auth(self, password_requested: bool = False): | ||
|  |         if password_requested and not self.password: | ||
|  |             await super().server_auth(password_requested) | ||
|  | 
 | ||
|  |         await self.get_username() | ||
|  |         await self.send_connect() | ||
|  | 
 | ||
|  |     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_grant_missable_location_checks = ( | ||
|  |                 _args["slot_data"]["grant_missable_location_checks"] == 1 | ||
|  |             ) | ||
|  | 
 | ||
|  |     async def controller(self): | ||
|  |         while not self.exit_event.is_set(): | ||
|  |             await asyncio.sleep(0.1) | ||
|  | 
 | ||
|  |             # Enqueue Received Item Delta | ||
|  |             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 not in self.game_controller.received_items_queue: | ||
|  |                         self.game_controller.received_items_queue.append(item) | ||
|  | 
 | ||
|  |             # Game Controller Update | ||
|  |             if self.game_controller.is_process_running(): | ||
|  |                 self.game_controller.update() | ||
|  |                 self.can_display_process_message = True | ||
|  |             else: | ||
|  |                 process_message: str | ||
|  | 
 | ||
|  |                 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." | ||
|  |                     ) | ||
|  |                 else: | ||
|  |                     process_message = ( | ||
|  |                         "Please use the /zork command to attach to a running 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() | ||
|  | 
 | ||
|  |             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) | ||
|  | 
 | ||
|  |             await self.send_msgs([ | ||
|  |                 { | ||
|  |                     "cmd": "LocationChecks", | ||
|  |                     "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 | ||
|  |                     } | ||
|  |                 ]) | ||
|  | 
 | ||
|  | 
 | ||
|  | def main() -> None: | ||
|  |     Utils.init_logging("ZorkGrandInquisitorClient", exception_logger="Client") | ||
|  | 
 | ||
|  |     async def _main(): | ||
|  |         ctx: ZorkGrandInquisitorContext = ZorkGrandInquisitorContext(None, None) | ||
|  | 
 | ||
|  |         ctx.server_task = asyncio.create_task(CommonClient.server_loop(ctx), name="server loop") | ||
|  |         ctx.controller_task = asyncio.create_task(ctx.controller(), name="ZorkGrandInquisitorController") | ||
|  | 
 | ||
|  |         if CommonClient.gui_enabled: | ||
|  |             ctx.run_gui() | ||
|  | 
 | ||
|  |         ctx.run_cli() | ||
|  | 
 | ||
|  |         await ctx.exit_event.wait() | ||
|  |         await ctx.shutdown() | ||
|  | 
 | ||
|  |     import colorama | ||
|  | 
 | ||
|  |     colorama.init() | ||
|  | 
 | ||
|  |     asyncio.run(_main()) | ||
|  | 
 | ||
|  |     colorama.deinit() | ||
|  | 
 | ||
|  | 
 | ||
|  | if __name__ == "__main__": | ||
|  |     main() |