""" 关键词系统 实现KARDS中的各种关键词效果 """ from abc import ABC, abstractmethod from typing import Any, Dict, List, Optional, TYPE_CHECKING from enum import Enum if TYPE_CHECKING: from ..units.unit import Unit from ..battlefield.battlefield import Battlefield class TriggerTiming(Enum): """触发时机枚举""" ON_DEPLOY = "on_deploy" # 部署时 ON_ATTACK = "on_attack" # 攻击时 ON_DEFEND = "on_defend" # 防御时 ON_DAMAGE_TAKEN = "on_damage_taken" # 受到伤害时 ON_DEATH = "on_death" # 死亡时 ON_TURN_START = "on_turn_start" # 回合开始时 ON_TURN_END = "on_turn_end" # 回合结束时 PASSIVE = "passive" # 被动效果 BEFORE_COMBAT = "before_combat" # 战斗前 AFTER_COMBAT = "after_combat" # 战斗后 class KeywordEffect(ABC): """关键词效果基类""" def __init__(self, name: str): self.name = name @abstractmethod def get_trigger_timing(self) -> List[TriggerTiming]: """获取触发时机""" pass def can_trigger(self, timing: TriggerTiming, context: Dict[str, Any]) -> bool: """检查是否可以在指定时机触发""" return timing in self.get_trigger_timing() def apply_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: """应用关键词效果""" if self.can_trigger(timing, context): self._execute_effect(owner, battlefield, timing, context) @abstractmethod def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: """执行具体效果(子类实现)""" pass def get_description(self) -> str: """获取关键词描述""" return f"{self.name}: {self._get_description_text()}" @abstractmethod def _get_description_text(self) -> str: """获取描述文本(子类实现)""" pass class BlitzKeyword(KeywordEffect): """闪击关键词 - 单位可以在部署回合立即行动""" def __init__(self): super().__init__("BLITZ") def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.ON_DEPLOY] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: if timing == TriggerTiming.ON_DEPLOY: owner.can_operate_immediately = True def _get_description_text(self) -> str: return "单位可以在部署回合立即行动" class GuardKeyword(KeywordEffect): """守护关键词 - 相邻单位只能被轰炸机和火炮攻击""" def __init__(self): super().__init__("GUARD") def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.PASSIVE] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: # 守护是被动效果,在攻击检查时处理 pass def _get_description_text(self) -> str: return "相邻单位只能被轰炸机和火炮攻击" class SmokescreenKeyword(KeywordEffect): """烟幕关键词 - 单位不能被攻击,移动或攻击后失效""" def __init__(self): super().__init__("SMOKESCREEN") def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.PASSIVE, TriggerTiming.ON_ATTACK] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: if timing == TriggerTiming.ON_ATTACK: # 攻击后移除烟幕 owner.remove_keyword("SMOKESCREEN") def _get_description_text(self) -> str: return "单位不能被攻击,移动或攻击后失效" class HeavyArmorKeyword(KeywordEffect): """重甲关键词 - 受到伤害减少指定数值""" def __init__(self, armor_value: int = 1): super().__init__(f"HEAVY_ARMOR_{armor_value}") self.armor_value = armor_value def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.ON_DAMAGE_TAKEN] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: if timing == TriggerTiming.ON_DAMAGE_TAKEN: damage = context.get('damage', 0) reduced_damage = max(0, damage - self.armor_value) context['damage'] = reduced_damage def _get_description_text(self) -> str: return f"受到伤害减少{self.armor_value}点" class AmbushKeyword(KeywordEffect): """伏击关键词 - 每回合首次被攻击时先造成伤害""" def __init__(self): super().__init__("AMBUSH") self.has_ambushed_this_turn = False def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.ON_DEFEND, TriggerTiming.ON_TURN_START] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: if timing == TriggerTiming.ON_TURN_START: self.has_ambushed_this_turn = False elif timing == TriggerTiming.ON_DEFEND and not self.has_ambushed_this_turn: attacker = context.get('attacker') if attacker: # 先造成伤害 damage = owner.get_effective_attack() actual_damage = attacker.stats.take_damage(damage) self.has_ambushed_this_turn = True # 如果摧毁了攻击者,则自己不受伤害 if not attacker.stats.is_alive(): context['skip_damage'] = True def _get_description_text(self) -> str: return "每回合首次被攻击时先造成伤害,若摧毁攻击者则不受伤害" class FuryKeyword(KeywordEffect): """奋战关键词 - 每回合可以攻击两次""" def __init__(self): super().__init__("FURY") self.attacks_this_turn = 0 def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.PASSIVE, TriggerTiming.ON_TURN_START, TriggerTiming.ON_ATTACK] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: if timing == TriggerTiming.ON_TURN_START: self.attacks_this_turn = 0 elif timing == TriggerTiming.ON_ATTACK: self.attacks_this_turn += 1 def can_attack_again(self) -> bool: """检查是否还能再次攻击""" return self.attacks_this_turn < 2 def _get_description_text(self) -> str: return "每回合可以攻击两次" class MobilizeKeyword(KeywordEffect): """动员关键词 - 回合开始时+1/+1,受伤后失效""" def __init__(self): super().__init__("MOBILIZE") def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.ON_TURN_START, TriggerTiming.ON_DAMAGE_TAKEN] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: if timing == TriggerTiming.ON_TURN_START: # 增加1点攻击和1点防御 owner.stats.attack += 1 owner.stats.defense += 1 owner.stats.current_defense += 1 elif timing == TriggerTiming.ON_DAMAGE_TAKEN: # 受伤后失去动员 owner.remove_keyword("MOBILIZE") def _get_description_text(self) -> str: return "回合开始时+1/+1,受伤后失效" class PinnedKeyword(KeywordEffect): """压制关键词 - 单位无法移动或攻击直到下回合结束""" def __init__(self, duration: int = 1): super().__init__("PINNED") self.duration = duration def get_trigger_timing(self) -> List[TriggerTiming]: return [TriggerTiming.PASSIVE, TriggerTiming.ON_TURN_END] def _execute_effect(self, owner: 'Unit', battlefield: 'Battlefield', timing: TriggerTiming, context: Dict[str, Any]) -> None: if timing == TriggerTiming.ON_TURN_END: self.duration -= 1 if self.duration <= 0: owner.remove_keyword("PINNED") def _get_description_text(self) -> str: return f"无法移动或攻击{self.duration}回合" class KeywordRegistry: """关键词注册表""" _keywords: Dict[str, type] = { "BLITZ": BlitzKeyword, "GUARD": GuardKeyword, "SMOKESCREEN": SmokescreenKeyword, "HEAVY_ARMOR": HeavyArmorKeyword, "AMBUSH": AmbushKeyword, "FURY": FuryKeyword, "MOBILIZE": MobilizeKeyword, "PINNED": PinnedKeyword, } @classmethod def create_keyword(cls, keyword_name: str, **kwargs) -> Optional[KeywordEffect]: """创建关键词实例""" if keyword_name.startswith("HEAVY_ARMOR_"): # 处理带数值的重甲关键词 parts = keyword_name.split("_") if len(parts) == 3 and parts[2].isdigit(): return HeavyArmorKeyword(int(parts[2])) keyword_class = cls._keywords.get(keyword_name) if keyword_class: return keyword_class(**kwargs) return None @classmethod def get_all_keywords(cls) -> List[str]: """获取所有注册的关键词名称""" return list(cls._keywords.keys()) @classmethod def register_keyword(cls, name: str, keyword_class: type) -> None: """注册新的关键词""" cls._keywords[name] = keyword_class @classmethod def get_keyword_description(cls, keyword_name: str) -> Optional[str]: """获取关键词描述""" keyword = cls.create_keyword(keyword_name) return keyword.get_description() if keyword else None