""" Game Facade - Simplified interface for complex game operations. """ import time from typing import Dict, Any, Optional, List, Tuple from ..core.game_engine import get_game_engine from ..core.player import Player class GameFacade: """Facade providing simplified access to game functionality.""" def __init__(self): self.engine = get_game_engine() def start_game(self) -> bool: """Start the game engine and all services.""" return self.engine.start() def stop_game(self) -> bool: """Stop the game engine and all services.""" return self.engine.stop() # Player Operations def join_game(self, name: str, player_type: str = "human") -> Optional[str]: """Join a player to the game.""" try: player_service = self.engine.get_player_service() # Create player player = player_service.create_player(name, player_type) # Add to world success = player_service.add_player_to_world(player) if success: # Notify plugins self.engine.get_plugin_service().call_plugin_event('on_player_join', player.id) return player.id return None except Exception as e: print(f"[GameFacade] Error joining game: {e}") return None def leave_game(self, player_id: str) -> bool: """Remove a player from the game.""" try: player_service = self.engine.get_player_service() # Notify plugins before removal self.engine.get_plugin_service().call_plugin_event('on_player_leave', player_id) return player_service.remove_player_from_world(player_id) except Exception as e: print(f"[GameFacade] Error leaving game: {e}") return False def move_player(self, player_id: str, direction: str) -> Tuple[bool, Dict[str, int], List[str]]: """Move a player in specified direction, returning success, new position, and triggered events.""" from math import sqrt success = self.engine.get_player_service().move_player(player_id, direction) new_position = {} events = [] if success: # Get updated player position player = self.engine.get_player_service().get_player(player_id) new_position = {'x': player.x, 'y': player.y} # Gather world events since movement world_events = self.engine.get_world().get_world_events() # Extract only new events messages events = [ev.get('event') for ev in world_events] return success, new_position, events def get_player_stats(self, player_id: str) -> Dict: """Get player statistics.""" return self.engine.get_player_service().get_player_stats(player_id) def handle_action(self, player_id: str) -> str: """Handle special action: interact with nearest NPC if in range.""" # Determine proximity prox = self.get_proximity_info(player_id) nearby = prox.get('nearby_entities', []) if nearby: npc_id = nearby[0]['id'] # Interact with NPC via default empty message return self.interact_with_npc(player_id, npc_id, "") return "No entity nearby to interact." def get_proximity_info(self, player_id: str) -> Dict[str, Any]: """Get nearby entities (NPCs and other players) within interaction range.""" import math result = {'nearby_entities': []} # Get player position player = self.engine.get_player_service().get_player(player_id) if not player: return result # Check NPCs world = self.engine.get_world() for npc_id, npc_data in world.get_all_npcs().items(): dx = player.x - npc_data.get('x', 0) dy = player.y - npc_data.get('y', 0) distance = math.hypot(dx, dy) if distance <= 50: result['nearby_entities'].append({'id': npc_id, 'name': npc_data.get('name')}) # Optionally include other players for other_id, other in world.get_all_players().items(): if other_id == player_id: continue dx = player.x - other.x dy = player.y - other.y if math.hypot(dx, dy) <= 50: result['nearby_entities'].append({'id': other_id, 'name': other.name}) return result # Chat Operations def send_chat_message(self, player_id: str, message: str) -> bool: """Send a public chat message.""" try: chat_service = self.engine.get_chat_service() success = chat_service.send_public_message(player_id, message) if success: # Notify plugins self.engine.get_plugin_service().call_plugin_event( 'on_chat_message', player_id, message, 'public' ) return success except Exception as e: print(f"[GameFacade] Error sending chat: {e}") return False def get_chat_history(self, limit: int = 10) -> List[Dict]: """Get recent chat messages.""" return self.engine.get_chat_service().get_chat_history(limit) # NPC Operations def interact_with_npc(self, player_id: str, npc_id: str, message: str) -> str: """Interact with an NPC.""" return self.engine.get_npc_service().get_npc_response(npc_id, message, player_id) def get_npc_data(self, npc_id: str) -> Optional[Dict]: """Get NPC information.""" return self.engine.get_npc_service().get_npc(npc_id) # World State def get_game_state(self) -> Dict: """Get current game state.""" return self.engine.get_world().get_world_state() def get_all_players(self) -> Dict[str, Player]: """Get all players in the game.""" return self.engine.get_player_service().get_all_players() def get_all_npcs(self) -> Dict[str, Dict]: """Get all NPCs in the game.""" return self.engine.get_npc_service().get_all_npcs() # MCP Operations def register_ai_agent(self, agent_id: str, name: str) -> str: """Register an AI agent via MCP.""" return self.engine.get_mcp_service().register_ai_agent(agent_id, name) def move_ai_agent(self, agent_id: str, direction: str) -> Dict: """Move an AI agent.""" return self.engine.get_mcp_service().move_agent(agent_id, direction) def ai_agent_chat(self, agent_id: str, message: str) -> Dict: """Send chat message from AI agent.""" return self.engine.get_mcp_service().send_chat(agent_id, message) def get_available_npcs(self) -> List[Dict[str, Any]]: """Get list of available NPCs with their data for MCP integration.""" try: npcs = self.get_all_npcs() available_npcs = [] for npc_id, npc_data in npcs.items(): available_npcs.append({ 'id': npc_id, 'name': npc_data.get('name', 'Unknown NPC'), 'type': npc_data.get('type', 'npc'), 'position': { 'x': npc_data.get('x', 0), 'y': npc_data.get('y', 0) }, 'description': npc_data.get('description', ''), 'dialogue_count': len(npc_data.get('dialogue_history', [])), 'active': npc_data.get('active', True) }) return available_npcs except Exception as e: print(f"[GameFacade] Error getting available NPCs: {e}") return [] def get_available_players(self) -> List[Dict[str, Any]]: """Get list of available players with their data for MCP integration.""" try: players = self.get_all_players() available_players = [] for player_id, player in players.items(): available_players.append({ 'id': player.id, 'name': player.name, 'type': player.type, 'position': { 'x': player.x, 'y': player.y }, 'level': player.level, 'hp': f"{player.hp}/{player.max_hp}", 'gold': player.gold, 'experience': player.experience, 'last_active': player.last_active, 'session_hash': getattr(player, 'session_hash', None) }) return available_players except Exception as e: print(f"[GameFacade] Error getting available players: {e}") return [] def get_raw_world_data(self) -> Dict[str, Any]: """Get comprehensive raw world data without HTML parsing for MCP integration.""" try: return { 'players': self.get_available_players(), 'npcs': self.get_available_npcs(), 'world_state': self.get_game_state(), 'world_events': self.get_world_events(), 'chat_history': self.get_chat_history(50), # More chat history for context 'system_status': self.get_system_status(), 'loaded_plugins': self.get_loaded_plugins(), 'timestamp': time.time() } except Exception as e: print(f"[GameFacade] Error getting raw world data: {e}") return { 'error': f"Failed to get raw world data: {str(e)}", 'timestamp': time.time() } def get_recent_chat_messages(self, limit: int = 20) -> List[Dict]: """Get recent chat messages for MCP integration.""" return self.get_chat_history(limit) def get_recent_world_events(self, limit: int = 10) -> List[Dict[str, Any]]: """Get recent world events for MCP integration.""" try: all_events = self.get_world_events() return all_events[-limit:] if len(all_events) > limit else all_events except Exception as e: print(f"[GameFacade] Error getting recent world events: {e}") return [] # Plugin Operations def reload_plugin(self, plugin_id: str) -> bool: """Reload a plugin (hot-reload).""" return self.engine.get_plugin_service().reload_plugin(plugin_id) def get_loaded_plugins(self) -> List[str]: """Get list of loaded plugins.""" return self.engine.get_plugin_service().get_loaded_plugins() def get_plugin_status(self, plugin_id: str) -> Optional[Dict]: """Get status of a specific plugin.""" return self.engine.get_plugin_service().get_plugin_status(plugin_id) # World Events def get_world_events(self) -> List[Dict[str, Any]]: """Get all world events.""" try: world = self.engine.get_world() return world.get_world_events() except Exception as e: print(f"[GameFacade] Error getting world events: {e}") return [] # System Operations def get_system_status(self) -> Dict[str, Any]: """Get comprehensive system status.""" return self.engine.get_engine_status() def cleanup_inactive_players(self, timeout_seconds: int = 300) -> int: """Remove inactive players.""" return self.engine.get_player_service().cleanup_inactive_players(timeout_seconds) def is_running(self) -> bool: """Check if game is running.""" return self.engine.is_running() # Private Messaging def send_private_message(self, sender_id: str, sender_name: str, recipient_id: str, message: str) -> bool: """Send a private message between players or to NPCs.""" success, _ = self.engine.get_chat_service().send_private_message(sender_id, recipient_id, message) if success: self.engine.get_plugin_service().call_plugin_event('on_chat_message', sender_id, message, 'private') return success def get_private_messages(self, player_id: str, entity_id: str) -> List[Dict]: """Get private messages between player and entity.""" history = self.engine.get_chat_service().get_chat_history() private_msgs = [] for msg in history: msg_type = msg.get('type', '') # Check for private messages between player and entity (using backup version logic) if msg_type in ['private_to_npc', 'private_from_npc', 'private_to_player', 'private_from_player']: # Message is to/from this specific entity and player if ((msg.get('sender_id') == player_id and msg.get('target') == entity_id) or (msg.get('sender_id') == entity_id and msg.get('target') == player_id)): private_msgs.append({ 'timestamp': msg.get('timestamp', ''), 'sender': msg.get('sender', 'Unknown'), 'sender_id': msg.get('sender_id', ''), 'message': msg.get('message', ''), 'type': msg_type, 'target': msg.get('target', '') }) print(f"[GameFacade] Found {len(private_msgs)} private messages between {player_id} and {entity_id}") return private_msgs # Utility methods for UI def get_world_html(self) -> str: """Get HTML representation of the game world (for UI).""" try: players = self.get_all_players() npcs = self.get_all_npcs() # This would be implemented based on the original HTML generation logic # For now, return a placeholder return f"
Game World - {len(players)} players, {len(npcs)} NPCs
" except Exception as e: return f"
Error loading world: {e}
" def get_player_from_session(self, session_hash: str) -> Optional[Player]: """Get player by session hash.""" try: all_players = self.get_all_players() for player in all_players.values(): if player.session_hash == session_hash: return player return None except Exception as e: print(f"[GameFacade] Error getting player from session: {e}") return None def update_player_session(self, player_id: str, session_hash: str) -> bool: """Update player session hash.""" try: player = self.engine.get_player_service().get_player(player_id) if player: player.session_hash = session_hash return self.engine.get_player_service().update_player(player) return False except Exception as e: print(f"[GameFacade] Error updating player session: {e}") return False