""" 能力系统 实现KARDS中的各种单位能力 """ from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, TYPE_CHECKING from enum import Enum from .keywords import TriggerTiming if TYPE_CHECKING: from ..units.unit import Unit from ..battlefield.battlefield import Battlefield class AbilityType(Enum): """能力类型枚举""" DEPLOY = "deploy" # 部署能力 TRIGGERED = "triggered" # 触发能力 STATIC = "static" # 静态能力(光环) ACTIVATED = "activated" # 激活能力 DEATH = "death" # 死亡能力 class TargetType(Enum): """目标类型枚举""" SELF = "self" # 自己 FRIENDLY_UNIT = "friendly_unit" # 友方单位 ENEMY_UNIT = "enemy_unit" # 敌方单位 ANY_UNIT = "any_unit" # 任意单位 FRIENDLY_HQ = "friendly_hq" # 友方HQ ENEMY_HQ = "enemy_hq" # 敌方HQ ALL_FRIENDLY = "all_friendly" # 所有友方单位 ALL_ENEMY = "all_enemy" # 所有敌方单位 ADJACENT_FRIENDLY = "adjacent_friendly" # 相邻友方单位 NO_TARGET = "no_target" # 无目标 class Ability(ABC): """能力基类""" def __init__(self, name: str, ability_type: AbilityType, description: str = ""): self.name = name self.ability_type = ability_type self.description = description self.is_used = False # 是否已使用(某些能力只能使用一次) @abstractmethod def can_activate(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> bool: """检查能力是否可以激活""" pass @abstractmethod def get_valid_targets(self, owner: 'Unit', battlefield: 'Battlefield') -> List[Any]: """获取有效目标列表""" pass @abstractmethod def execute(self, owner: 'Unit', battlefield: 'Battlefield', target: Any = None, context: Dict[str, Any] = None) -> Dict[str, Any]: """执行能力,返回执行结果""" pass def get_trigger_timings(self) -> List[TriggerTiming]: """获取能力触发时机""" timing_map = { AbilityType.DEPLOY: [TriggerTiming.ON_DEPLOY], AbilityType.TRIGGERED: [], # 子类指定 AbilityType.STATIC: [TriggerTiming.PASSIVE], AbilityType.ACTIVATED: [], # 主动激活,无固定时机 AbilityType.DEATH: [TriggerTiming.ON_DEATH] } return timing_map.get(self.ability_type, []) def reset(self) -> None: """重置能力状态""" self.is_used = False def __str__(self) -> str: return f"{self.name} ({self.ability_type.value}): {self.description}" class DeployAbility(Ability): """部署能力基类""" def __init__(self, name: str, description: str = ""): super().__init__(name, AbilityType.DEPLOY, description) def can_activate(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> bool: return timing == TriggerTiming.ON_DEPLOY and not self.is_used class TriggeredAbility(Ability): """触发能力基类""" def __init__(self, name: str, trigger_timing: TriggerTiming, trigger_condition: callable = None, description: str = ""): super().__init__(name, AbilityType.TRIGGERED, description) self.trigger_timing = trigger_timing self.trigger_condition = trigger_condition or (lambda *args: True) def can_activate(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> bool: return (timing == self.trigger_timing and self.trigger_condition(owner, battlefield, context)) def get_trigger_timings(self) -> List[TriggerTiming]: return [self.trigger_timing] class StaticAbility(Ability): """静态能力(光环效果)基类""" def __init__(self, name: str, description: str = ""): super().__init__(name, AbilityType.STATIC, description) def can_activate(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> bool: return timing == TriggerTiming.PASSIVE @abstractmethod def apply_static_effect(self, owner: 'Unit', battlefield: 'Battlefield') -> None: """应用静态效果""" pass def execute(self, owner: 'Unit', battlefield: 'Battlefield', target: Any = None, context: Dict[str, Any] = None) -> Dict[str, Any]: self.apply_static_effect(owner, battlefield) return {"success": True, "effect_type": "static"} class ActivatedAbility(Ability): """激活能力(主动使用)基类""" def __init__(self, name: str, cost: int = 0, uses_per_turn: int = 1, description: str = ""): super().__init__(name, AbilityType.ACTIVATED, description) self.cost = cost # 使用成本(如果有) self.uses_per_turn = uses_per_turn self.uses_this_turn = 0 def can_activate(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> bool: return self.uses_this_turn < self.uses_per_turn def reset_turn(self) -> None: """回合重置""" self.uses_this_turn = 0 class DeathAbility(Ability): """死亡能力基类""" def __init__(self, name: str, description: str = ""): super().__init__(name, AbilityType.DEATH, description) def can_activate(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> bool: return timing == TriggerTiming.ON_DEATH and not self.is_used # 具体能力实现示例 class DealDamageOnDeploy(DeployAbility): """部署时造成伤害""" def __init__(self, damage: int, target_type: TargetType = TargetType.ENEMY_UNIT): super().__init__("DEPLOY_DAMAGE", f"部署时对{target_type.value}造成{damage}点伤害") self.damage = damage self.target_type = target_type def get_valid_targets(self, owner: 'Unit', battlefield: 'Battlefield') -> List['Unit']: from ..effects.target_selectors import TargetSelector selector = TargetSelector() return selector.get_targets_by_type(owner, battlefield, self.target_type) def execute(self, owner: 'Unit', battlefield: 'Battlefield', target: 'Unit' = None, context: Dict[str, Any] = None) -> Dict[str, Any]: if not target: return {"success": False, "reason": "No target specified"} actual_damage = target.stats.take_damage(self.damage) self.is_used = True result = { "success": True, "effect_type": "damage", "target": target.id, "damage_dealt": actual_damage, "target_destroyed": not target.stats.is_alive() } # 如果目标被摧毁,从战场移除 if not target.stats.is_alive(): battlefield.remove_unit(target) return result class BuffAlliesStatic(StaticAbility): """静态增益友方单位""" def __init__(self, attack_bonus: int = 0, defense_bonus: int = 0): description = f"友方单位获得+{attack_bonus}/+{defense_bonus}" super().__init__("BUFF_ALLIES", description) self.attack_bonus = attack_bonus self.defense_bonus = defense_bonus def get_valid_targets(self, owner: 'Unit', battlefield: 'Battlefield') -> List['Unit']: return battlefield.get_all_units(owner.owner) def apply_static_effect(self, owner: 'Unit', battlefield: 'Battlefield') -> None: # 静态效果在战斗计算时动态应用,这里仅做标记 pass def get_buff_values(self, target: 'Unit', owner: 'Unit') -> tuple: """获取对目标的增益值""" if target.owner == owner.owner and target.id != owner.id: return (self.attack_bonus, self.defense_bonus) return (0, 0) class SacrificeForDamage(ActivatedAbility): """牺牲自己造成伤害""" def __init__(self, damage: int): super().__init__("SACRIFICE_DAMAGE", 0, 1, f"牺牲自己对敌方单位造成{damage}点伤害") self.damage = damage def get_valid_targets(self, owner: 'Unit', battlefield: 'Battlefield') -> List['Unit']: enemy_id = battlefield.get_enemy_player_id(owner.owner) return battlefield.get_all_units(enemy_id) if enemy_id else [] def execute(self, owner: 'Unit', battlefield: 'Battlefield', target: 'Unit' = None, context: Dict[str, Any] = None) -> Dict[str, Any]: if not target: return {"success": False, "reason": "No target specified"} # 造成伤害 actual_damage = target.stats.take_damage(self.damage) # 牺牲自己 owner.stats.current_defense = 0 battlefield.remove_unit(owner) self.uses_this_turn += 1 result = { "success": True, "effect_type": "sacrifice_damage", "target": target.id, "damage_dealt": actual_damage, "target_destroyed": not target.stats.is_alive(), "owner_sacrificed": True } # 如果目标也被摧毁,从战场移除 if not target.stats.is_alive(): battlefield.remove_unit(target) return result class HealOnDeath(DeathAbility): """死亡时治疗友方单位""" def __init__(self, heal_amount: int): super().__init__("DEATH_HEAL", f"死亡时治疗所有友方单位{heal_amount}点") self.heal_amount = heal_amount def get_valid_targets(self, owner: 'Unit', battlefield: 'Battlefield') -> List['Unit']: return battlefield.get_all_units(owner.owner) def execute(self, owner: 'Unit', battlefield: 'Battlefield', target: Any = None, context: Dict[str, Any] = None) -> Dict[str, Any]: friendly_units = self.get_valid_targets(owner, battlefield) healed_units = [] for unit in friendly_units: if unit.id != owner.id: # 不治疗自己(已死亡) heal_amount = unit.stats.heal(self.heal_amount) if heal_amount > 0: healed_units.append({ "unit_id": unit.id, "heal_amount": heal_amount }) self.is_used = True return { "success": True, "effect_type": "death_heal", "healed_units": healed_units } class AbilityManager: """能力管理器,负责处理能力的触发和执行""" def __init__(self): self.pending_abilities = [] # 待处理的能力 def trigger_abilities(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> List[Dict[str, Any]]: """触发单位的能力""" results = [] for ability in owner.abilities: if ability.can_activate(owner, battlefield, timing, context): # 获取目标 valid_targets = ability.get_valid_targets(owner, battlefield) if ability.ability_type == AbilityType.STATIC: # 静态能力直接执行 result = ability.execute(owner, battlefield, context=context) results.append(result) elif valid_targets or ability.get_valid_targets(owner, battlefield) == []: # 有有效目标或无需目标的能力 if len(valid_targets) == 1: # 单一目标自动选择 target = valid_targets[0] result = ability.execute(owner, battlefield, target, context) results.append(result) elif len(valid_targets) == 0 and ability.ability_type in [AbilityType.DEPLOY, AbilityType.DEATH]: # 无目标能力(如群体效果) result = ability.execute(owner, battlefield, context=context) results.append(result) else: # 多目标需要外部选择,添加到待处理列表 self.pending_abilities.append({ "owner": owner, "ability": ability, "battlefield": battlefield, "valid_targets": valid_targets, "context": context }) return results def execute_pending_ability(self, ability_index: int, target_index: int) -> Optional[Dict[str, Any]]: """执行待处理的能力""" if 0 <= ability_index < len(self.pending_abilities): pending = self.pending_abilities[ability_index] valid_targets = pending["valid_targets"] if 0 <= target_index < len(valid_targets): target = valid_targets[target_index] result = pending["ability"].execute( pending["owner"], pending["battlefield"], target, pending["context"] ) # 移除已处理的能力 self.pending_abilities.pop(ability_index) return result return None def clear_pending(self) -> None: """清除所有待处理的能力""" self.pending_abilities.clear()