""" KARDS游戏引擎 v3 - 真实的拖拽操作系统 玩家只能进行两种操作: 1. 使用卡牌(部署单位/执行指令) 2. 拖动单位(移动/攻击的统一操作) """ from typing import Dict, List, Optional, Any, Union from uuid import UUID from ..battlefield.battlefield_v2 import Battlefield, HQ from ..units.unit import Unit from ..abilities.keywords import TriggerTiming, KeywordRegistry from ..abilities.abilities import AbilityManager from .enums import GamePhase, LineType from .unit_combat_rules import UnitCombatRules class GameEngineV3: """KARDS游戏引擎 v3""" def __init__(self, player1_id: str, player2_id: str): self.battlefield = Battlefield(player1_id, player2_id) self.current_turn = 1 self.active_player = player1_id # 指挥点资源管理 self.player1_command_points = 1 # 第一回合1点 self.player2_command_points = 1 self.max_command_points = 12 # 最大12点 # 事件系统 self.event_history = [] def get_command_points(self, player_id: str) -> int: """获取玩家当前指挥点""" if player_id == self.battlefield.player1_id: return self.player1_command_points elif player_id == self.battlefield.player2_id: return self.player2_command_points return 0 def spend_command_points(self, player_id: str, cost: int) -> bool: """消耗指挥点""" current_points = self.get_command_points(player_id) if current_points >= cost: if player_id == self.battlefield.player1_id: self.player1_command_points -= cost elif player_id == self.battlefield.player2_id: self.player2_command_points -= cost return True return False # ===== 操作1: 使用卡牌 ===== def play_unit_card(self, unit: Unit, player_id: str) -> Dict[str, Any]: """使用单位卡牌 - 部署到支援线空位""" if player_id != self.active_player: return {"success": False, "reason": "Not your turn"} # 检查指挥点 if not self.spend_command_points(player_id, unit.stats.operation_cost): return {"success": False, "reason": "Insufficient command points"} # 部署到支援线 support_line = self.battlefield.get_player_support_line(player_id) if not support_line or support_line.is_full(): return {"success": False, "reason": "Support line is full"} success = support_line.add_unit(unit) if success: unit.owner = player_id self.battlefield.unit_registry[unit.id] = unit # 触发部署效果 self._trigger_deploy_abilities(unit) return { "success": True, "action": "deploy", "unit_id": unit.id, "location": "support" } return {"success": False, "reason": "Failed to deploy"} # ===== 操作2: 拖动单位 ===== def drag_unit(self, unit_id: UUID, target_position: tuple, player_id: str) -> Dict[str, Any]: """ 拖动单位 - 统一的移动/攻击操作 target_position: (line_type, index) 或者 target_unit_id """ if player_id != self.active_player: return {"success": False, "reason": "Not your turn"} unit = self.battlefield.find_unit(unit_id) if not unit or unit.owner != player_id: return {"success": False, "reason": "Unit not found or not yours"} # 检查单位激活点数(油费)- 每个单位必须有此属性 activation_cost = unit.activation_cost if not self.spend_command_points(player_id, activation_cost): return {"success": False, "reason": "Insufficient command points for activation"} # 判断拖拽目标类型 if isinstance(target_position, tuple) and len(target_position) == 2: # 拖拽到位置 - 可能是移动 return self._handle_position_drag(unit, target_position) else: # 拖拽到目标单位/HQ - 攻击 return self._handle_target_drag(unit, target_position) def _handle_position_drag(self, unit: Unit, target_pos: tuple) -> Dict[str, Any]: """处理拖拽到位置的操作(移动)""" target_line_type, target_index = target_pos # 只能从支援线移动到前线 if not unit.position: return {"success": False, "reason": "Unit not on battlefield"} current_line, current_index = unit.position # 检查移动规则:只能从支援线移动到前线 if target_line_type != LineType.FRONT: return {"success": False, "reason": "Can only move to front line"} if current_line == LineType.FRONT: # 前线单位不能移动 return {"success": False, "reason": "Units on front line cannot move"} else: # 从支援线移动到前线(包括挤位置) return self._move_to_front_line(unit, target_index) def _move_to_front_line(self, unit: Unit, target_index: int) -> Dict[str, Any]: """从支援线移动到前线(包括挤位置机制)""" front_line = self.battlefield.front_line # 检查前线控制权 - 必须是空的或己方控制 if (self.battlefield.front_line_controller is not None and self.battlefield.front_line_controller != unit.owner): return {"success": False, "reason": "Front line controlled by enemy"} # 检查是否要挤位置(目标位置非空) if target_index < len(front_line.objectives): # 挤位置:如果前线满了则失败 if front_line.is_full(): return {"success": False, "reason": "Front line is full, cannot squeeze"} else: # 添加到末尾:检查前线是否满 if front_line.is_full(): return {"success": False, "reason": "Front line is full"} # 从支援线移除 support_line = self.battlefield.get_player_support_line(unit.owner) if not support_line.remove_unit(unit): return {"success": False, "reason": "Failed to remove from support line"} # 执行移动 if target_index >= len(front_line.objectives): # 添加到末尾 front_line.add_unit(unit) actual_position = len(front_line.objectives) - 1 else: # 挤位置:插入到指定位置,后续单位顺位移动 front_line.objectives.insert(target_index, unit) front_line._reindex_objectives() actual_position = target_index # 更新前线控制权 self.battlefield._update_front_line_control() return { "success": True, "action": "move_to_front", "unit_id": unit.id, "to_position": actual_position, "front_line_controller": self.battlefield.front_line_controller } def _handle_target_drag(self, unit: Unit, target_id: Union[UUID, str]) -> Dict[str, Any]: """处理拖拽到目标的操作(攻击)""" # 查找目标 target = None if isinstance(target_id, str) and target_id.startswith("hq"): # HQ目标 if target_id == "hq1": target = self.battlefield.player1_hq elif target_id == "hq2": target = self.battlefield.player2_hq else: # 单位目标 target = self.battlefield.find_unit(target_id) if not target: return {"success": False, "reason": "Target not found"} # 检查是否能攻击该目标 if not self._can_attack_target(unit, target): return {"success": False, "reason": "Cannot attack this target"} # 执行攻击 return self._execute_attack(unit, target) def _can_attack_target(self, attacker: Unit, target: Union[Unit, HQ]) -> bool: """检查是否能攻击目标""" # 不能攻击己方目标 if isinstance(target, Unit) and target.owner == attacker.owner: return False elif isinstance(target, HQ) and target.player_id == attacker.owner: return False # 使用详细的战斗规则检查 return UnitCombatRules.can_attack_target(attacker, target, self.battlefield) def _execute_attack(self, attacker: Unit, target: Union[Unit, HQ]) -> Dict[str, Any]: """执行攻击""" # 计算伤害 damage = attacker.get_effective_attack() # 应用伤害 if isinstance(target, Unit): # 单位战斗 return self._unit_combat(attacker, target) else: # 攻击HQ actual_damage = target.take_damage(damage) return { "success": True, "action": "attack_hq", "attacker_id": attacker.id, "target": "HQ", "damage_dealt": actual_damage, "hq_destroyed": target.is_destroyed() } def _unit_combat(self, attacker: Unit, target: Unit) -> Dict[str, Any]: """单位间战斗""" # 处理伏击 if target.has_keyword("AMBUSH"): ambush_damage = target.get_effective_attack() attacker_damage = attacker.stats.take_damage(ambush_damage) if not attacker.stats.is_alive(): self.battlefield.remove_unit(attacker) return { "success": True, "action": "attack_unit", "ambush_triggered": True, "attacker_destroyed": True, "ambush_damage": attacker_damage } # 计算攻击伤害 attack_damage = attacker.get_effective_attack() # 应用重甲减伤 for keyword in target.keywords: if keyword.startswith("HEAVY_ARMOR_"): armor_value = int(keyword.split("_")[-1]) attack_damage = max(0, attack_damage - armor_value) break # 造成伤害 actual_damage = target.stats.take_damage(attack_damage) # 反击(使用详细的反击规则) counter_damage = 0 if target.stats.is_alive() and UnitCombatRules.can_counter_attack(target, attacker): counter_attack = target.get_effective_attack() counter_damage = attacker.stats.take_damage(counter_attack) # 检查单位死亡 units_destroyed = [] if not target.stats.is_alive(): self.battlefield.remove_unit(target) units_destroyed.append(target.id) # 触发死亡能力 self._trigger_death_abilities(target) if not attacker.stats.is_alive(): self.battlefield.remove_unit(attacker) units_destroyed.append(attacker.id) self._trigger_death_abilities(attacker) return { "success": True, "action": "attack_unit", "attacker_id": attacker.id, "target_id": target.id, "damage_dealt": actual_damage, "counter_damage": counter_damage, "units_destroyed": units_destroyed } def _trigger_deploy_abilities(self, unit: Unit) -> None: """触发部署能力""" for ability in unit.abilities: if hasattr(ability, 'ability_type') and ability.ability_type.name == "DEPLOY": # 简化的能力触发 valid_targets = ability.get_valid_targets(unit, self.battlefield) if valid_targets: ability.execute(unit, self.battlefield, valid_targets[0]) def _trigger_death_abilities(self, unit: Unit) -> None: """触发死亡能力""" for ability in unit.abilities: if hasattr(ability, 'ability_type') and ability.ability_type.name == "DEATH": ability.execute(unit, self.battlefield) def end_turn(self) -> Dict[str, Any]: """结束回合""" old_player = self.active_player # 切换玩家 self.active_player = (self.battlefield.player2_id if self.active_player == self.battlefield.player1_id else self.battlefield.player1_id) # 增加指挥点 if self.active_player == self.battlefield.player1_id: self.player1_command_points = min(self.max_command_points, self.current_turn + 1) else: self.player2_command_points = min(self.max_command_points, self.current_turn + 1) # 重置单位状态 for unit in self.battlefield.get_all_units(self.active_player): unit.has_operated_this_turn = False self.current_turn += 1 # 检查游戏是否结束 game_over, winner = self.battlefield.is_game_over() return { "turn_ended": True, "previous_player": old_player, "new_active_player": self.active_player, "turn_number": self.current_turn, "command_points": self.get_command_points(self.active_player), "game_over": game_over, "winner": winner } def get_game_state(self) -> Dict[str, Any]: """获取游戏状态""" return { "turn": self.current_turn, "active_player": self.active_player, "command_points": { "player1": self.player1_command_points, "player2": self.player2_command_points, "active_player": self.get_command_points(self.active_player) }, "battlefield": str(self.battlefield), "player1_hq": self.battlefield.player1_hq.current_defense, "player2_hq": self.battlefield.player2_hq.current_defense, "front_line_controller": self.battlefield.front_line_controller, "units_count": { "player1_total": len(self.battlefield.get_all_units(self.battlefield.player1_id)), "player2_total": len(self.battlefield.get_all_units(self.battlefield.player2_id)), "front_line": len(self.battlefield.front_line.get_all_units()) } }