283 lines
10 KiB
Python
283 lines
10 KiB
Python
"""
|
||
关键词系统
|
||
实现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 |