339 lines
12 KiB
Python
339 lines
12 KiB
Python
|
|
"""
|
|||
|
|
卡牌基类和各种卡牌类型定义
|
|||
|
|
"""
|
|||
|
|
from typing import Optional, Set, List, Dict, Any
|
|||
|
|
from dataclasses import dataclass
|
|||
|
|
from uuid import uuid4, UUID
|
|||
|
|
from abc import ABC, abstractmethod
|
|||
|
|
|
|||
|
|
from ..core.enums import Nation, Rarity, CardType, UnitType
|
|||
|
|
from ..units.unit import Unit
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class CardStats:
|
|||
|
|
"""卡牌基础属性"""
|
|||
|
|
cost: int # 使用花费
|
|||
|
|
nation: Nation # 所属国家
|
|||
|
|
rarity: Rarity # 稀有度
|
|||
|
|
exile_nations: Optional[Set[Nation]] = None # 流亡国家(可选)
|
|||
|
|
|
|||
|
|
def can_be_used_by_nation(self, nation: Nation) -> bool:
|
|||
|
|
"""判断该卡是否可以被指定国家使用(考虑流亡效果)"""
|
|||
|
|
if self.nation == nation:
|
|||
|
|
return True
|
|||
|
|
if self.exile_nations and nation in self.exile_nations:
|
|||
|
|
return True
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
class BaseCard(ABC):
|
|||
|
|
"""所有卡牌的基类"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
name: str,
|
|||
|
|
card_type: CardType,
|
|||
|
|
cost: int,
|
|||
|
|
nation: Nation,
|
|||
|
|
rarity: Rarity,
|
|||
|
|
exile_nations: Optional[Set[Nation]] = None,
|
|||
|
|
description: str = "",
|
|||
|
|
parameters: Optional[List] = None
|
|||
|
|
):
|
|||
|
|
self.id: UUID = uuid4()
|
|||
|
|
self.name = name
|
|||
|
|
self.card_type = card_type
|
|||
|
|
self.description = description
|
|||
|
|
self.stats = CardStats(cost, nation, rarity, exile_nations)
|
|||
|
|
self.parameters = parameters or [] # 参数定义列表
|
|||
|
|
|
|||
|
|
def __str__(self) -> str:
|
|||
|
|
exile_str = ""
|
|||
|
|
if self.stats.exile_nations:
|
|||
|
|
exile_nations = [str(n) for n in self.stats.exile_nations]
|
|||
|
|
exile_str = f" (流亡: {', '.join(exile_nations)})"
|
|||
|
|
|
|||
|
|
return f"{self.name} [{self.card_type}] - {self.stats.cost}费 {self.stats.nation}{exile_str} {self.stats.rarity}"
|
|||
|
|
|
|||
|
|
@abstractmethod
|
|||
|
|
def can_be_played(self, game_state: Any) -> bool:
|
|||
|
|
"""检查该卡牌是否可以被打出"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
@abstractmethod
|
|||
|
|
def play_effect(self, game_state: Any, target: Any = None, parameters: Optional[Dict[str, Any]] = None) -> bool:
|
|||
|
|
"""执行卡牌效果,返回是否成功"""
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def has_parameters(self) -> bool:
|
|||
|
|
"""检查卡牌是否有参数"""
|
|||
|
|
return len(self.parameters) > 0
|
|||
|
|
|
|||
|
|
def get_required_parameters(self) -> List:
|
|||
|
|
"""获取必需参数列表"""
|
|||
|
|
from .card_params import ParameterDefinition
|
|||
|
|
return [p for p in self.parameters if isinstance(p, ParameterDefinition) and p.required]
|
|||
|
|
|
|||
|
|
def validate_parameters(self, parameters: Dict[str, Any], game_state: Any = None) -> Dict[str, str]:
|
|||
|
|
"""验证参数,返回错误信息字典"""
|
|||
|
|
from .card_params import CardParameterValidator
|
|||
|
|
return CardParameterValidator.validate_card_parameters(self.parameters, parameters, game_state)
|
|||
|
|
|
|||
|
|
|
|||
|
|
class UnitCard(BaseCard):
|
|||
|
|
"""单位卡牌"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
name: str,
|
|||
|
|
cost: int,
|
|||
|
|
nation: Nation,
|
|||
|
|
rarity: Rarity,
|
|||
|
|
unit_definition_id: str, # 对应的单位定义ID
|
|||
|
|
exile_nations: Optional[Set[Nation]] = None,
|
|||
|
|
description: str = "",
|
|||
|
|
card_id: Optional[str] = None, # YAML文件中的卡牌ID
|
|||
|
|
parameters: Optional[List] = None # 参数定义列表
|
|||
|
|
):
|
|||
|
|
# 根据单位定义确定卡牌类型
|
|||
|
|
card_type = self._determine_card_type_from_unit_id(unit_definition_id)
|
|||
|
|
super().__init__(name, card_type, cost, nation, rarity, exile_nations, description, parameters)
|
|||
|
|
|
|||
|
|
self.unit_definition_id = unit_definition_id
|
|||
|
|
self.card_id = card_id # 存储YAML中的卡牌ID
|
|||
|
|
|
|||
|
|
def _determine_card_type_from_unit_id(self, unit_id: str) -> CardType:
|
|||
|
|
"""根据单位ID推断卡牌类型"""
|
|||
|
|
# 简单的ID到卡牌类型映射,后续可以优化
|
|||
|
|
if "infantry" in unit_id.lower():
|
|||
|
|
return CardType.UNIT_INFANTRY
|
|||
|
|
elif "tank" in unit_id.lower():
|
|||
|
|
return CardType.UNIT_TANK
|
|||
|
|
elif "artillery" in unit_id.lower():
|
|||
|
|
return CardType.UNIT_ARTILLERY
|
|||
|
|
elif "fighter" in unit_id.lower():
|
|||
|
|
return CardType.UNIT_FIGHTER
|
|||
|
|
elif "bomber" in unit_id.lower():
|
|||
|
|
return CardType.UNIT_BOMBER
|
|||
|
|
else:
|
|||
|
|
return CardType.UNIT_INFANTRY # 默认为步兵
|
|||
|
|
|
|||
|
|
def can_be_played(self, game_state: Any) -> bool:
|
|||
|
|
"""检查单位卡是否可以被打出"""
|
|||
|
|
# 基础检查:玩家是否有足够的kredits
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
if current_player.kredits < self.stats.cost:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 检查是否有空间部署单位
|
|||
|
|
battlefield = game_state.get_battlefield()
|
|||
|
|
# 这里需要根据具体的部署规则来实现
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def play_effect(self, game_state: Any, target: Any = None, parameters: Optional[Dict[str, Any]] = None) -> bool:
|
|||
|
|
"""部署单位到战场"""
|
|||
|
|
try:
|
|||
|
|
from ..units.unit_loader import load_unit
|
|||
|
|
|
|||
|
|
# 验证参数
|
|||
|
|
if parameters is None:
|
|||
|
|
parameters = {}
|
|||
|
|
|
|||
|
|
validation_errors = self.validate_parameters(parameters, game_state)
|
|||
|
|
if validation_errors:
|
|||
|
|
print(f"参数验证失败: {validation_errors}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 加载单位
|
|||
|
|
unit = load_unit(self.unit_definition_id)
|
|||
|
|
if not unit:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 部署到战场
|
|||
|
|
battle_engine = game_state.get_battle_engine()
|
|||
|
|
current_player_id = game_state.get_current_player_id()
|
|||
|
|
|
|||
|
|
# 检查参数中是否指定了位置
|
|||
|
|
deploy_position = parameters.get('position')
|
|||
|
|
if deploy_position is not None:
|
|||
|
|
# 部署到指定槽位
|
|||
|
|
result = battle_engine.deploy_unit_to_support(unit, current_player_id, deploy_position)
|
|||
|
|
elif target:
|
|||
|
|
# 兼容旧接口
|
|||
|
|
line_type, position = target
|
|||
|
|
result = battle_engine.deploy_unit_to_line(unit, line_type, position, current_player_id)
|
|||
|
|
else:
|
|||
|
|
# 部署到支援线(默认)
|
|||
|
|
result = battle_engine.deploy_unit_to_support(unit, current_player_id)
|
|||
|
|
|
|||
|
|
if hasattr(result, 'success') and result.success:
|
|||
|
|
# 扣除费用
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
current_player.kredits -= self.stats.cost
|
|||
|
|
return True
|
|||
|
|
elif isinstance(result, bool) and result:
|
|||
|
|
# 如果result直接是bool类型
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
current_player.kredits -= self.stats.cost
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"部署单位失败: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
class OrderCard(BaseCard):
|
|||
|
|
"""指令卡牌"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
name: str,
|
|||
|
|
cost: int,
|
|||
|
|
nation: Nation,
|
|||
|
|
rarity: Rarity,
|
|||
|
|
effect_callback: callable, # 效果回调函数
|
|||
|
|
exile_nations: Optional[Set[Nation]] = None,
|
|||
|
|
description: str = "",
|
|||
|
|
target_required: bool = False,
|
|||
|
|
parameters: Optional[List] = None
|
|||
|
|
):
|
|||
|
|
super().__init__(name, CardType.ORDER, cost, nation, rarity, exile_nations, description, parameters)
|
|||
|
|
self.effect_callback = effect_callback
|
|||
|
|
self.target_required = target_required
|
|||
|
|
|
|||
|
|
def can_be_played(self, game_state: Any) -> bool:
|
|||
|
|
"""检查指令卡是否可以被打出"""
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
if current_player.kredits < self.stats.cost:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 如果需要目标,检查是否有有效目标
|
|||
|
|
if self.target_required:
|
|||
|
|
# 这里需要根据具体的指令效果来检查
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def play_effect(self, game_state: Any, target: Any = None, parameters: Optional[Dict[str, Any]] = None) -> bool:
|
|||
|
|
"""执行指令效果"""
|
|||
|
|
try:
|
|||
|
|
# 验证参数
|
|||
|
|
if parameters is None:
|
|||
|
|
parameters = {}
|
|||
|
|
|
|||
|
|
validation_errors = self.validate_parameters(parameters, game_state)
|
|||
|
|
if validation_errors:
|
|||
|
|
print(f"参数验证失败: {validation_errors}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 执行效果回调,传入参数
|
|||
|
|
success = self.effect_callback(game_state, target, parameters)
|
|||
|
|
|
|||
|
|
if success:
|
|||
|
|
# 扣除费用
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
current_player.kredits -= self.stats.cost
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"指令卡效果执行失败: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
|
|||
|
|
class CounterCard(BaseCard):
|
|||
|
|
"""反制卡牌(陷阱卡)"""
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
name: str,
|
|||
|
|
cost: int,
|
|||
|
|
nation: Nation,
|
|||
|
|
rarity: Rarity,
|
|||
|
|
trigger_condition: str, # 触发条件描述
|
|||
|
|
counter_effect: callable, # 反制效果回调
|
|||
|
|
exile_nations: Optional[Set[Nation]] = None,
|
|||
|
|
description: str = "",
|
|||
|
|
parameters: Optional[List] = None
|
|||
|
|
):
|
|||
|
|
super().__init__(name, CardType.COUNTER, cost, nation, rarity, exile_nations, description, parameters)
|
|||
|
|
self.trigger_condition = trigger_condition
|
|||
|
|
self.counter_effect = counter_effect
|
|||
|
|
self.is_set = False # 是否已经设置到场上
|
|||
|
|
|
|||
|
|
def can_be_played(self, game_state: Any) -> bool:
|
|||
|
|
"""检查反制卡是否可以被打出(设置到场上)"""
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
if current_player.kredits < self.stats.cost:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 检查是否已经设置了太多反制卡
|
|||
|
|
# 这里可以根据游戏规则限制同时设置的反制卡数量
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def play_effect(self, game_state: Any, target: Any = None, parameters: Optional[Dict[str, Any]] = None) -> bool:
|
|||
|
|
"""设置反制卡到场上"""
|
|||
|
|
try:
|
|||
|
|
# 验证参数
|
|||
|
|
if parameters is None:
|
|||
|
|
parameters = {}
|
|||
|
|
|
|||
|
|
validation_errors = self.validate_parameters(parameters, game_state)
|
|||
|
|
if validation_errors:
|
|||
|
|
print(f"参数验证失败: {validation_errors}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 将反制卡设置为激活状态
|
|||
|
|
self.is_set = True
|
|||
|
|
|
|||
|
|
# 存储设置时的参数,触发时可能需要用到
|
|||
|
|
self.set_parameters = parameters
|
|||
|
|
|
|||
|
|
# 扣除费用
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
current_player.kredits -= self.stats.cost
|
|||
|
|
|
|||
|
|
# 将卡牌添加到玩家的已设置反制卡列表
|
|||
|
|
if hasattr(current_player, 'set_counters'):
|
|||
|
|
current_player.set_counters.append(self)
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"设置反制卡失败: {e}")
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def can_trigger(self, game_state: Any, trigger_event: Any) -> bool:
|
|||
|
|
"""检查是否可以触发反制效果"""
|
|||
|
|
if not self.is_set:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 这里需要根据trigger_condition和trigger_event来判断
|
|||
|
|
# 暂时返回True,具体实现取决于事件系统
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def trigger_effect(self, game_state: Any, trigger_event: Any) -> bool:
|
|||
|
|
"""触发反制效果"""
|
|||
|
|
try:
|
|||
|
|
success = self.counter_effect(game_state, trigger_event)
|
|||
|
|
|
|||
|
|
if success:
|
|||
|
|
# 反制卡使用后移除
|
|||
|
|
self.is_set = False
|
|||
|
|
current_player = game_state.get_current_player()
|
|||
|
|
if self in current_player.set_counters:
|
|||
|
|
current_player.set_counters.remove(self)
|
|||
|
|
|
|||
|
|
# 将反制卡移到弃牌堆
|
|||
|
|
current_player.card_manager.discard_pile.append(self)
|
|||
|
|
|
|||
|
|
return success
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
print(f"反制效果执行失败: {e}")
|
|||
|
|
return False
|