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()
							 |