""" 命令解析器模块 处理用户输入的命令并执行相应操作 """ from typing import List, Tuple, Optional, Dict, Any from kards_battle.core.battle_engine import BattleEngine from kards_battle.core.enums import LineType from kards_battle.units.unit_loader import load_unit, list_all_units class CommandParser: """命令解析器""" def __init__(self, engine: BattleEngine): self.engine = engine self.commands = { # 显示命令 'show': self.cmd_show, 's': self.cmd_show, 'list': self.cmd_list, 'ls': self.cmd_list, 'info': self.cmd_info, 'i': self.cmd_info, # 单位操作 'deploy': self.cmd_deploy, 'd': self.cmd_deploy, 'dp': self.cmd_deploy, 'move': self.cmd_move, 'm': self.cmd_move, 'mv': self.cmd_move, 'attack': self.cmd_attack, 'a': self.cmd_attack, 'at': self.cmd_attack, # 游戏控制 'endturn': self.cmd_endturn, 'end': self.cmd_endturn, 'reset': self.cmd_reset, 'switch': self.cmd_switch, # 资源管理 'kredits': self.cmd_kredits, 'k': self.cmd_kredits, 'setk': self.cmd_set_kredits, 'sets': self.cmd_set_slot, # 帮助 'help': self.cmd_help, 'h': self.cmd_help, '?': self.cmd_help, } # 命令历史 self.history = [] self.last_result = None def parse(self, input_str: str) -> Tuple[bool, str]: """解析并执行命令,返回(成功, 消息)""" input_str = input_str.strip() if not input_str: return True, "" # 保存到历史 self.history.append(input_str) # 分割命令和参数 parts = input_str.split() cmd = parts[0].lower() args = parts[1:] if len(parts) > 1 else [] # 查找并执行命令 if cmd in self.commands: try: success, message = self.commands[cmd](args) self.last_result = (success, message) return success, message except Exception as e: return False, f"命令执行错误: {str(e)}" else: return False, f"未知命令: {cmd}. 输入 'help' 查看帮助" def cmd_show(self, args: List[str]) -> Tuple[bool, str]: """显示战场状态""" detailed = 'detailed' in args or 'd' in args return True, f"detailed={detailed}" def cmd_list(self, args: List[str]) -> Tuple[bool, str]: """列出可用单位""" if not args: nation = 'all' else: nation = args[0].lower() all_units = list_all_units() if nation == 'germany' or nation == 'ger': units = [u for u in all_units if u.startswith('ger_')] title = "德军单位" elif nation == 'usa' or nation == 'us': units = [u for u in all_units if u.startswith('usa_')] title = "美军单位" else: units = all_units title = "所有单位" result = f"\n=== {title} ({len(units)}个) ===\n" for unit_id in sorted(units): unit = load_unit(unit_id) result += f" {unit_id:30} - {unit.name} ({unit.unit_type.name}) {unit.stats.attack}/{unit.stats.defense}\n" return True, result def cmd_info(self, args: List[str]) -> Tuple[bool, str]: """查看单位详细信息""" if not args: return False, "用法: info " try: index = int(args[0]) # 查找单位 all_units = [] for unit in self.engine.battlefield.player1_support.get_all_units(): if unit: all_units.append(unit) for unit in self.engine.battlefield.front_line.get_all_units(): if unit: all_units.append(unit) for unit in self.engine.battlefield.player2_support.get_all_units(): if unit: all_units.append(unit) if index >= len(all_units): return False, f"单位索引 {index} 不存在" return True, f"unit_info:{index}" except ValueError: return False, "索引必须是数字" def cmd_deploy(self, args: List[str]) -> Tuple[bool, str]: """部署单位""" if not args: return False, "用法: deploy [position]" unit_id = args[0] position = int(args[1]) if len(args) > 1 else None try: # 加载单位 unit = load_unit(unit_id) # 部署到当前玩家的支援线 player_id = self.engine.active_player result = self.engine.deploy_unit_to_support(unit, player_id, position) if result['success']: return True, f"✅ 成功部署 {unit.name} 到位置 {result['position']}" else: return False, f"❌ 部署失败: {result['reason']}" except ValueError as e: return False, f"无法加载单位 {unit_id}: {str(e)}" def cmd_move(self, args: List[str]) -> Tuple[bool, str]: """移动单位 - 从己方支援线移动到前线""" if len(args) < 2: return False, "用法: move (从支援线位置移动到前线位置)" try: from_pos = int(args[0]) target_pos = int(args[1]) # 获取己方支援线上的单位 player_name = self.engine.player_names[self.engine.active_player] if self.engine.active_player == 0: support_line = self.engine.battlefield.player1_support else: support_line = self.engine.battlefield.player2_support # 找到要移动的单位 unit = support_line.get_unit_at_index(from_pos) if not unit: return False, f"在支援线位置 {from_pos} 没有找到单位" # 检查是否是单位(而不是HQ或其他对象) from kards_battle.units.unit import Unit if not isinstance(unit, Unit): return False, f"只有单位才能移动,{type(unit).__name__}无法移动" # 检查单位所有权 if unit.owner != player_name: return False, f"单位 {unit.name} 不属于你" # 目标始终是前线 line_type = LineType.FRONT # 执行移动 result = self.engine.move_unit(unit.id, (line_type, target_pos), self.engine.active_player) if result['success']: return True, f"✅ 成功移动 {unit.name} 到前线[{target_pos}]" else: return False, f"❌ 移动失败: {result['reason']}" except (ValueError, IndexError) as e: return False, f"参数错误: {str(e)}" def _parse_position(self, pos_str: str) -> Tuple[str, int]: """解析位置字符串,支持 F0, S1, FRONT, SUPPORT 等格式""" pos_str = pos_str.upper() # 简化格式: F0, S1, F2, S3 等 if len(pos_str) >= 2 and pos_str[0] in ['F', 'S'] and pos_str[1:].isdigit(): line = 'FRONT' if pos_str[0] == 'F' else 'SUPPORT' pos = int(pos_str[1:]) return line, pos # 完整格式处理会在调用方进行 raise ValueError(f"无效的位置格式: {pos_str}") def _convert_to_line_type(self, line: str, pos: int, is_source: bool = True): """将字符串战线转换为LineType枚举""" from kards_battle.core.enums import LineType if line == 'FRONT': return (LineType.FRONT, pos) elif line == 'SUPPORT': # 根据攻击方向确定具体的支援线 if is_source: # 攻击来源:己方支援线 if self.engine.active_player == 0: return (LineType.PLAYER1_SUPPORT, pos) else: return (LineType.PLAYER2_SUPPORT, pos) else: # 攻击目标:敌方支援线 if self.engine.active_player == 0: return (LineType.PLAYER2_SUPPORT, pos) else: return (LineType.PLAYER1_SUPPORT, pos) else: raise ValueError(f"未知的战线类型: {line}") def cmd_attack(self, args: List[str]) -> Tuple[bool, str]: """攻击目标 - 支持简化格式 F0, S1 等""" if len(args) < 2: return False, "用法: attack (如: attack F0 S1 或 attack FRONT 0 SUPPORT 1)" try: if len(args) == 2: # 简化格式: attack F0 S1 from_line, from_pos = self._parse_position(args[0]) to_line, to_pos = self._parse_position(args[1]) elif len(args) == 4: # 完整格式: attack FRONT 0 SUPPORT 1 from_line = args[0].upper() from_pos = int(args[1]) to_line = args[2].upper() to_pos = int(args[3]) # 验证完整格式 if from_line not in ['SUPPORT', 'FRONT'] or to_line not in ['SUPPORT', 'FRONT']: return False, "战线必须是 SUPPORT/S 或 FRONT/F" else: return False, "用法: attack (如: F0 S1) 或 attack " # 获取攻击者 from_location = self._convert_to_line_type(from_line, from_pos, is_source=True) attacker = None player_name = self.engine.player_names[self.engine.active_player] # 使用玩家名称 for unit in self.engine.battlefield.get_all_units(player_name): if unit.position == from_location: attacker = unit break if not attacker: return False, f"在 {from_line}:{from_pos} 没有找到己方单位" # 获取目标 - 需要检查敌方单位 enemy_player_name = self.engine.player_names[1 - self.engine.active_player] # 使用敌方玩家名称 to_location = self._convert_to_line_type(to_line, to_pos, is_source=False) target = None # 先检查敌方单位 for unit in self.engine.battlefield.get_all_units(enemy_player_name): if unit.position == to_location: target = unit break # 如果没有找到敌方单位,检查是否是HQ目标 target_id = None if not target: if to_line == 'SUPPORT': enemy_player_index = 1 - self.engine.active_player if enemy_player_index == 0: hq = self.engine.battlefield.player1_hq target_id = "hq1" else: hq = self.engine.battlefield.player2_hq target_id = "hq2" # 检查HQ位置 if hasattr(hq, 'position_index') and hq.position_index == to_pos: target = hq if not target: return False, f"在 {to_line}:{to_pos} 没有找到敌方目标" # 执行攻击 - HQ使用字符串ID,单位使用UUID if target_id: result = self.engine.attack_target(attacker.id, target_id, self.engine.active_player) else: result = self.engine.attack_target(attacker.id, target.id, self.engine.active_player) if result['success']: damage = result.get('damage_dealt', result.get('damage', 0)) target_name = getattr(target, 'name', 'HQ') message = f"✅ {attacker.name} 攻击 {target_name},造成 {damage} 点伤害" # 检查游戏是否结束 if result.get('game_over', False): winner_name = result.get('winner_name', '未知') message += f"\n\n🎉 游戏结束! {winner_name} 获得胜利!" return True, message else: return False, f"❌ 攻击失败: {result['reason']}" except (ValueError, IndexError) as e: return False, f"参数错误: {str(e)}" def cmd_endturn(self, args: List[str]) -> Tuple[bool, str]: """结束回合""" result = self.engine.end_turn() msg = f"回合结束!\n" msg += f"新回合: {result['turn_number']}.{result['turn_phase']}\n" msg += f"当前玩家: {result['new_active_player']}\n" msg += f"Kredits: {result['kredits']}/{result['kredits_slot']}" if result['is_new_round']: msg += f"\n🆕 新的完整回合开始!" return True, msg def cmd_reset(self, args: List[str]) -> Tuple[bool, str]: """重置战场""" # 需要创建新的引擎实例 return True, "RESET_ENGINE" def cmd_switch(self, args: List[str]) -> Tuple[bool, str]: """切换活动玩家(调试用)""" self.engine.active_player = 1 - self.engine.active_player new_player = self.engine.player_names[self.engine.active_player] return True, f"切换到玩家: {new_player}" def cmd_kredits(self, args: List[str]) -> Tuple[bool, str]: """查看资源状态""" p1_k = self.engine.player1_kredits p1_s = self.engine.player1_kredits_slot p2_k = self.engine.player2_kredits p2_s = self.engine.player2_kredits_slot msg = f"\n资源状态:\n" msg += f" {self.engine.player_names[0]}: {p1_k}/{p1_s} Kredits\n" msg += f" {self.engine.player_names[1]}: {p2_k}/{p2_s} Kredits" return True, msg def cmd_set_kredits(self, args: List[str]) -> Tuple[bool, str]: """设置Kredits""" if len(args) < 2: return False, "用法: setk " try: player_id = int(args[0]) amount = int(args[1]) if player_id not in [0, 1]: return False, "玩家ID必须是0或1" self.engine.debug_set_kredits(player_id, kredits=amount) return True, f"设置玩家{player_id} Kredits为 {amount}" except ValueError: return False, "参数必须是数字" def cmd_set_slot(self, args: List[str]) -> Tuple[bool, str]: """设置Kredits Slot""" if len(args) < 2: return False, "用法: sets " try: player_id = int(args[0]) amount = int(args[1]) if player_id not in [0, 1]: return False, "玩家ID必须是0或1" self.engine.debug_set_kredits(player_id, kredits_slot=amount) return True, f"设置玩家{player_id} Kredits Slot为 {amount}" except ValueError: return False, "参数必须是数字" def cmd_help(self, args: List[str]) -> Tuple[bool, str]: """显示帮助""" return True, "SHOW_HELP" def _get_unit_by_index(self, index: int) -> Optional[Any]: """根据全局索引获取单位""" current_index = 0 # 检查Player1支援线 for unit in self.engine.battlefield.player1_support.get_all_units(): if unit: if current_index == index: return unit current_index += 1 # 检查前线 for unit in self.engine.battlefield.front_line.get_all_units(): if unit: if current_index == index: return unit current_index += 1 # 检查Player2支援线 for unit in self.engine.battlefield.player2_support.get_all_units(): if unit: if current_index == index: return unit current_index += 1 return None def get_command_suggestions(self, partial: str) -> List[str]: """获取命令建议(用于自动补全)""" if not partial: return list(self.commands.keys()) suggestions = [] for cmd in self.commands.keys(): if cmd.startswith(partial.lower()): suggestions.append(cmd) return suggestions