"""三国杀游戏主控制器""" import logging from typing import Optional, Tuple from games.base import BaseGame from games.sgs_game import GameState, get_game_manager from games.sgs_core import Phase, Role from utils.parser import CommandParser from core.database import get_db logger = logging.getLogger(__name__) class SanguoshaGame(BaseGame): """三国杀游戏""" def __init__(self): """初始化游戏""" super().__init__() self.manager = get_game_manager() async def handle(self, command: str, chat_id: int, user_id: int) -> str: """处理游戏指令 Args: command: 指令 chat_id: 会话ID user_id: 用户ID Returns: 回复消息 """ try: # 提取指令和参数 cmd, args = CommandParser.extract_command_args(command) args = args.strip().lower() # 获取用户信息 user = self.db.get_or_create_user(user_id) username = user.get('username') or f'用户{user_id}' # 路由指令 if not args or args == 'help': return self.get_help() elif args == 'create' or args == '创建': return await self._handle_create(chat_id, user_id, username) elif args == 'join' or args == '加入': return await self._handle_join(chat_id, user_id, username) elif args == 'leave' or args == '离开': return await self._handle_leave(chat_id, user_id) elif args == 'start' or args == '开始': return await self._handle_start(chat_id, user_id) elif args == 'status' or args == '状态': return await self._handle_status(chat_id, user_id) elif args == 'players' or args == '玩家': return await self._handle_players(chat_id) elif args == 'hand' or args == '手牌': return await self._handle_hand(chat_id, user_id) elif args == 'next' or args == '下一阶段': return await self._handle_next_phase(chat_id, user_id) elif args.startswith('play ') or args.startswith('出牌 '): card_name = args.split(maxsplit=1)[1] return await self._handle_play_card(chat_id, user_id, card_name) elif args.startswith('use ') or args.startswith('使用 '): card_name = args.split(maxsplit=1)[1] return await self._handle_use_card(chat_id, user_id, card_name) elif args == 'draw' or args == '摸牌': return await self._handle_draw(chat_id, user_id) elif args == 'discard' or args == '弃牌': return await self._handle_discard(chat_id, user_id) elif args == 'cancel' or args == '取消': return await self._handle_cancel(chat_id, user_id) elif args == 'stats' or args == '战绩': return await self._handle_stats(user_id) else: return f"❌ 未知指令: {args}\n\n使用 `.sgs help` 查看帮助" except Exception as e: logger.error(f"处理三国杀指令错误: {e}", exc_info=True) return f"❌ 处理指令出错: {str(e)}" async def _handle_create(self, chat_id: int, user_id: int, username: str) -> str: """创建游戏""" # 检查是否已有游戏 if self.manager.has_active_game(chat_id): return "❌ 当前已有进行中的游戏" # 创建游戏 game = self.manager.create_game(chat_id, user_id, username) return f"""✅ 三国杀游戏已创建! **房主**: {username} **游戏ID**: {game.game_id} 📝 其他玩家使用 `.sgs join` 加入游戏 📝 人数达到 {game.min_players}-{game.max_players} 人后,房主使用 `.sgs start` 开始游戏 当前玩家: 1/{game.max_players}""" async def _handle_join(self, chat_id: int, user_id: int, username: str) -> str: """加入游戏""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏,使用 `.sgs create` 创建游戏" if game.is_started: return "❌ 游戏已经开始,无法加入" if game.get_player_by_id(user_id): return "❌ 你已经在游戏中了" if not game.add_player(user_id, username): return f"❌ 加入失败,游戏已满({game.max_players}人)" return f"""✅ {username} 加入游戏! 当前玩家: {len(game.players)}/{game.max_players} 玩家列表: {', '.join(p.username for p in game.players)} {f'📝 人数已满,房主可以使用 `.sgs start` 开始游戏' if len(game.players) >= game.min_players else f'📝 还需要 {game.min_players - len(game.players)} 人才能开始'}""" async def _handle_leave(self, chat_id: int, user_id: int) -> str: """离开游戏""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if game.is_started: return "❌ 游戏已经开始,无法离开" player = game.get_player_by_id(user_id) if not player: return "❌ 你不在游戏中" if not game.remove_player(user_id): return "❌ 离开失败" # 如果房主离开且还有其他玩家,转移房主 if user_id == game.host_id and game.players: game.host_id = game.players[0].user_id return f"✅ {player.username} 离开游戏,房主已转移给 {game.players[0].username}" # 如果没有玩家了,删除游戏 if not game.players: self.manager.remove_game(chat_id) return f"✅ {player.username} 离开游戏,游戏已解散" return f"✅ {player.username} 离开游戏\n\n当前玩家: {len(game.players)}/{game.max_players}" async def _handle_start(self, chat_id: int, user_id: int) -> str: """开始游戏""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if user_id != game.host_id: return "❌ 只有房主可以开始游戏" success, message = game.start_game() if not success: return f"❌ {message}" # 构建游戏开始消息 result = "## 🎮 三国杀游戏开始!\n\n" # 显示主公信息 lord = game.lord_player if lord: result += f"### 👑 主公\n" result += f"**{lord.username}** - {lord.general.name}({lord.general.kingdom})\n" result += f"体力: {lord.hp}/{lord.general.max_hp}\n" result += f"技能: {', '.join(s.name for s in lord.general.skills)}\n\n" # 显示其他玩家信息(不显示身份) result += f"### 👥 玩家列表\n" for idx, player in enumerate(game.players, 1): if player.role != Role.LORD: result += f"{idx}. **{player.username}** - {player.general.name}({player.general.kingdom})\n" result += f" 体力: {player.hp}/{player.general.max_hp}\n" result += f"\n### 📋 游戏信息\n" result += f"- 玩家数: {len(game.players)}\n" result += f"- 当前回合: {game.current_player.username}\n" result += f"- 当前阶段: {game.current_phase.value}\n\n" result += "💡 使用 `.sgs status` 查看游戏状态\n" result += "💡 使用 `.sgs hand` 查看手牌\n" result += "💡 使用 `.sgs next` 进入下一阶段" return result async def _handle_status(self, chat_id: int, user_id: int) -> str: """查看游戏状态""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if not game.is_started: return f"""## 📋 游戏状态 (准备中) **房主**: {game.players[0].username if game.players else '无'} **玩家数**: {len(game.players)}/{game.max_players} **玩家列表**: {', '.join(p.username for p in game.players)} 📝 使用 `.sgs start` 开始游戏""" # 游戏进行中 result = f"## 📋 游戏状态\n\n" result += f"### 🕒 当前回合\n" result += f"- 玩家: **{game.current_player.username}**\n" result += f"- 阶段: **{game.current_phase.value}**\n" result += f"- 回合数: {game.round_number}\n\n" result += f"### 👥 玩家状态\n" for idx, player in enumerate(game.players, 1): status = "💀" if not player.is_alive else "✅" role_display = player.role.value if player.role == Role.LORD or not player.is_alive else "???" result += f"{idx}. {status} **{player.username}** ({role_display})\n" result += f" 武将: {player.general.name}({player.general.kingdom})\n" result += f" 体力: {'❤️' * player.hp}{'🖤' * (player.general.max_hp - player.hp)} {player.hp}/{player.general.max_hp}\n" result += f" 手牌: {player.hand_count}张\n" if player.equipment: equip_list = ', '.join(f"{k}:{v.name}" for k, v in player.equipment.items()) result += f" 装备: {equip_list}\n" result += "\n" return result async def _handle_players(self, chat_id: int) -> str: """查看玩家列表""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" result = f"## 👥 玩家列表\n\n" for idx, player in enumerate(game.players, 1): result += f"{idx}. **{player.username}**" if game.is_started: result += f" - {player.general.name}" if player.role == Role.LORD or not player.is_alive: result += f"({player.role.value})" result += "\n" return result async def _handle_hand(self, chat_id: int, user_id: int) -> str: """查看手牌""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if not game.is_started: return "❌ 游戏还未开始" player = game.get_player_by_id(user_id) if not player: return "❌ 你不在游戏中" if not player.is_alive: return "❌ 你已经阵亡" if not player.hand_cards: return "📋 你的手牌为空" result = f"## 🃏 你的手牌({len(player.hand_cards)}张)\n\n" # 按类型分组 basic_cards = [c for c in player.hand_cards if c.card_type.value == "基本牌"] trick_cards = [c for c in player.hand_cards if c.card_type.value == "锦囊牌"] equip_cards = [c for c in player.hand_cards if c.card_type.value == "装备牌"] if basic_cards: result += "### 基本牌\n" for idx, card in enumerate(basic_cards, 1): result += f"{idx}. {card}\n" result += "\n" if trick_cards: result += "### 锦囊牌\n" for idx, card in enumerate(trick_cards, 1): result += f"{idx}. {card}\n" result += "\n" if equip_cards: result += "### 装备牌\n" for idx, card in enumerate(equip_cards, 1): result += f"{idx}. {card}\n" result += "\n💡 使用 `.sgs play 卡牌名` 出牌" return result async def _handle_next_phase(self, chat_id: int, user_id: int) -> str: """进入下一阶段""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if not game.is_started: return "❌ 游戏还未开始" if game.current_player.user_id != user_id: return "❌ 不是你的回合" # 执行阶段转换 new_phase, current_player = game.next_phase() # 检查游戏是否结束 is_over, winner_role = game.check_game_over() if is_over: game.is_finished = True game.winner_role = winner_role return await self._handle_game_over(game) result = f"✅ 进入下一阶段\n\n" result += f"**当前玩家**: {current_player.username}\n" result += f"**当前阶段**: {new_phase.value}\n\n" # 阶段提示 if new_phase == Phase.DRAW: result += "💡 摸牌阶段,使用 `.sgs draw` 摸牌" elif new_phase == Phase.PLAY: result += "💡 出牌阶段,使用 `.sgs play 卡牌名` 出牌" elif new_phase == Phase.DISCARD: result += "💡 弃牌阶段,使用 `.sgs discard` 弃牌" else: result += "请使用正确的语法" return result async def _handle_draw(self, chat_id: int, user_id: int) -> str: """摸牌""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if game.current_player.user_id != user_id: return "❌ 不是你的回合" if game.current_phase != Phase.DRAW: return f"❌ 当前不是摸牌阶段(当前: {game.current_phase.value})" # 摸2张牌 cards = game.deck.draw(2) game.current_player.hand_cards.extend(cards) result = f"✅ 摸牌成功!\n\n" result += f"摸到: {', '.join(str(c) for c in cards)}\n" result += f"当前手牌数: {game.current_player.hand_count}\n\n" result += "💡 使用 `.sgs next` 进入出牌阶段" return result async def _handle_play_card(self, chat_id: int, user_id: int, card_name: str) -> str: """出牌""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if game.current_player.user_id != user_id: return "❌ 不是你的回合" if game.current_phase != Phase.PLAY: return f"❌ 当前不是出牌阶段(当前: {game.current_phase.value})" player = game.current_player # 查找卡牌 card = None for c in player.hand_cards: if c.name == card_name: card = c break if not card: return f"❌ 你没有【{card_name}】这张牌" # 简化处理:直接打出(实际游戏需要选择目标等) player.remove_card(card) game.deck.discard([card]) return f"✅ 使用了【{card}】\n\n💡 继续出牌或使用 `.sgs next` 进入弃牌阶段" async def _handle_use_card(self, chat_id: int, user_id: int, card_name: str) -> str: """使用卡牌(同play_card)""" return await self._handle_play_card(chat_id, user_id, card_name) async def _handle_discard(self, chat_id: int, user_id: int) -> str: """弃牌""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if game.current_player.user_id != user_id: return "❌ 不是你的回合" if game.current_phase != Phase.DISCARD: return f"❌ 当前不是弃牌阶段(当前: {game.current_phase.value})" player = game.current_player # 检查是否需要弃牌 max_hand = player.hp if player.hand_count <= max_hand: return f"✅ 手牌数({player.hand_count})未超过体力值({max_hand}),无需弃牌\n\n💡 使用 `.sgs next` 结束回合" # 简化处理:自动弃掉多余的牌 discard_count = player.hand_count - max_hand discarded = player.hand_cards[:discard_count] player.hand_cards = player.hand_cards[discard_count:] game.deck.discard(discarded) return f"✅ 弃置了 {discard_count} 张牌\n\n💡 使用 `.sgs next` 结束回合" async def _handle_cancel(self, chat_id: int, user_id: int) -> str: """取消游戏""" game = self.manager.get_game(chat_id) if not game: return "❌ 当前没有游戏" if user_id != game.host_id: return "❌ 只有房主可以取消游戏" self.manager.remove_game(chat_id) return "✅ 游戏已取消" async def _handle_stats(self, user_id: int) -> str: """查看战绩""" stats = self.db.get_game_stats(user_id, 'sanguosha') if stats['total_plays'] == 0: return "三国杀游戏记录" win_rate = (stats['wins'] / stats['total_plays'] * 100) if stats['total_plays'] > 0 else 0 result ="战绩\n\n" result += f"- 总局数: {stats['total_plays']}\n" result += f"- 胜利: {stats['wins']} 次\n" result += f"- 失败: {stats['losses']} 次\n" result += f"- 胜率: {win_rate:.1f}%\n" return result async def _handle_game_over(self, game: GameState) -> str: """处理游戏结束""" result = "## 🎉 游戏结束!\n\n" if game.winner_role == Role.LORD: result += "## 🎉公和忠臣获胜!\n\n" winners = [p for p in game.players if p.role in [Role.LORD, Role.LOYAL]] losers = [p for p in game.players if p.role in [Role.REBEL, Role.SPY]] else: result += "### ⚔️ 反贼获胜!\n\n" winners = [p for p in game.players if p.role == Role.REBEL] losers = [p for p in game.players if p.role in [Role.LORD, Role.LOYAL, Role.SPY]] result += "**获胜方**:\n" for player in winners: result += f"- {player.username} ({player.role.value}) - {player.general.name}\n" # 更新战绩 self.db.update_game_stats(player.user_id, 'sanguosha', win=True) result += "\n**失败方**:\n" for player in losers: result += f"- {player.username} ({player.role.value}) - {player.general.name}\n" # 更新战绩 self.db.update_game_stats(player.user_id, 'sanguosha', loss=True) result += f"\n游戏时长: {game.round_number} 回合" # 清理游戏 self.manager.remove_game(game.chat_id) return result def get_help(self) -> str: """获取帮助信息""" return """## 🎮 三国杀游戏帮助 ### 游戏准备 - `.sgs create` - 创建游戏房间 - `.sgs join` - 加入游戏 - `.sgs leave` - 离开游戏 - `.sgs start` - 开始游戏(房主) - `.sgs cancel` - 取消游戏(房主) ### 游戏中 - `.sgs status` - 查看游戏状态 - `.sgs players` - 查看玩家列表 - `.sgs hand` - 查看手牌 - `.sgs next` - 进入下一阶段 - `.sgs draw` - 摸牌(摸牌阶段) - `.sgs play 卡牌名` - 出牌(出牌阶段) - `.sgs discard` - 弃牌(弃牌阶段) ### 其他 - `.sgs stats` - 查看个人战绩 - `.sgs help` - 显示帮助 ### 游戏规则 1. **身份**: 主公、忠臣、反贼、内奸 2. **胜利条件**: - 主公+忠臣: 消灭所有反贼和内奸 - 反贼: 击杀主公 - 内奸: 成为最后存活的人 3. **回合流程**: - 准备阶段 → 判定阶段 → 摸牌阶段 → 出牌阶段 → 弃牌阶段 → 结束阶段 ### 卡牌类型 - **基本牌**: 杀、闪、桃 - **锦囊牌**: 决斗、过河拆桥、顺手牵羊、南蛮入侵、万箭齐发等 - **装备牌**: 武器、防具、马 --- 💡 提示:游戏支持 2-8 人,建议 5-8 人游戏体验最佳 """