kards-env/kards_battle/abilities/keywords.py

283 lines
10 KiB
Python
Raw Normal View History

2025-09-05 17:05:43 +08:00
"""
关键词系统
实现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