363 lines
14 KiB
Python
363 lines
14 KiB
Python
|
|
"""
|
||
|
|
能力系统
|
||
|
|
实现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()
|