""" Unit基类定义 KARDS游戏中的单位基础实现 """ from typing import Set, List, Optional, Dict, Any from dataclasses import dataclass, field from uuid import uuid4, UUID from ..core.enums import UnitType, LineType @dataclass class UnitStats: """单位属性数据类""" attack: int defense: int current_defense: int operation_cost: int def __post_init__(self): if self.current_defense > self.defense: self.current_defense = self.defense def take_damage(self, damage: int) -> int: """受到伤害,返回实际受到的伤害""" if damage <= 0: return 0 # 记录伤害前的血量 old_defense = self.current_defense # 直接扣除伤害,血量可以降到0或更低 self.current_defense -= damage # 但不能低于0 if self.current_defense < 0: self.current_defense = 0 # 返回实际造成的伤害 return old_defense - self.current_defense def heal(self, amount: int) -> int: """恢复防御值,返回实际恢复的量""" max_heal = self.defense - self.current_defense actual_heal = min(amount, max_heal) self.current_defense += actual_heal return actual_heal def is_alive(self) -> bool: """判断单位是否存活""" return self.current_defense > 0 class Unit: """KARDS单位基类""" def __init__( self, name: str, unit_type: UnitType, attack: int, defense: int, operation_cost: int, keywords: Optional[Set[str]] = None, abilities: Optional[List] = None ): self.id: UUID = uuid4() self.name = name self.unit_type = unit_type self.stats = UnitStats(attack, defense, defense, operation_cost) # 关键词集合 self.keywords: Set[str] = keywords or set() # 能力列表(稍后实现具体的Ability类) self.abilities: List = abilities or [] # 单位状态 self.position: Optional[tuple] = None # (line_type, index) self.owner: Optional[str] = None # 玩家ID # 回合制行动限制 self.deployed_this_turn: bool = True # 刚部署的单位当回合不能行动 self.has_moved_this_turn: bool = False # 本回合是否已移动 self.has_attacked_this_turn: bool = False # 本回合是否已攻击 self.attacks_this_turn: int = 0 # 本回合攻击次数 self.max_attacks_per_turn: int = 1 # 每回合最大攻击次数 # 特殊能力标记 self.can_operate_immediately: bool = False # Blitz关键词 - 部署当回合就能行动 self.can_move_and_attack: bool = False # Tank关键词 - 可以移动后攻击 self.can_attack_multiple_times: bool = False # Fury等关键词 # YAML加载的额外属性 self.nation: Optional[str] = None # 单位所属国家 self.definition_id: Optional[str] = None # YAML定义中的ID # 临时效果和修正器 self.temporary_modifiers: Dict[str, Any] = {} self.status_effects: Set[str] = set() # 如"Pinned"等状态 # 应用关键词能力 self.apply_keyword_abilities() def has_keyword(self, keyword: str) -> bool: """检查单位是否具有特定关键词""" return keyword in self.keywords def add_keyword(self, keyword: str) -> None: """添加关键词""" self.keywords.add(keyword) # 添加守护关键词时移除烟幕 if keyword == "GUARD" and self.has_keyword("SMOKESCREEN"): self.remove_keyword("SMOKESCREEN") def remove_keyword(self, keyword: str) -> None: """移除关键词""" self.keywords.discard(keyword) def check_smokescreen_conditions(self) -> None: """检查并移除不符合条件的烟幕""" if not self.has_keyword("SMOKESCREEN"): return # 检查是否具有守护关键词 if self.has_keyword("GUARD"): self.remove_keyword("SMOKESCREEN") return # 检查是否在前线(前线单位自动失去烟幕) if self.position and self.position[0] == LineType.FRONT: self.remove_keyword("SMOKESCREEN") return # 检查是否不在己方支援线 if self.position and self.owner: current_line = self.position[0] # 如果不在支援线,失去烟幕 if current_line != LineType.PLAYER1_SUPPORT and current_line != LineType.PLAYER2_SUPPORT: self.remove_keyword("SMOKESCREEN") return def get_effective_attack(self) -> int: """获取有效攻击力(包含修正器)""" base_attack = self.stats.attack # TODO: 应用临时修正器 return base_attack def get_effective_defense(self) -> int: """获取有效防御力上限(包含修正器)""" base_defense = self.stats.defense # TODO: 应用临时修正器 return base_defense def can_move(self) -> bool: """检查单位是否可以移动""" # 刚部署的单位不能移动(除非有Blitz) if self.deployed_this_turn and not self.can_operate_immediately: return False # 已经移动过的单位不能再移动 if self.has_moved_this_turn: return False # 已经攻击过的单位不能移动(除非有Tank能力) if self.has_attacked_this_turn and not self.can_move_and_attack: return False return True def can_attack(self) -> bool: """检查单位是否可以攻击""" # 被压制的单位不能主动攻击 if self.has_keyword("PINNED"): return False # 刚部署的单位不能攻击(除非有Blitz) if self.deployed_this_turn and not self.can_operate_immediately: return False # 已经达到最大攻击次数 if self.attacks_this_turn >= self.max_attacks_per_turn: return False # 已经移动过的单位不能攻击(除非有Tank能力) if self.has_moved_this_turn and not self.can_move_and_attack: return False return True def perform_move(self): """执行移动行动""" self.has_moved_this_turn = True # 移动后失去烟幕 if self.has_keyword("SMOKESCREEN"): self.remove_keyword("SMOKESCREEN") def perform_attack(self): """执行攻击行动""" self.has_attacked_this_turn = True self.attacks_this_turn += 1 # 攻击后失去烟幕 if self.has_keyword("SMOKESCREEN"): self.remove_keyword("SMOKESCREEN") def start_new_turn(self): """开始新回合时重置状态""" self.deployed_this_turn = False self.has_moved_this_turn = False self.has_attacked_this_turn = False self.attacks_this_turn = 0 def apply_keyword_abilities(self): """根据关键词设置特殊能力""" if self.has_keyword("BLITZ"): self.can_operate_immediately = True if self.has_keyword("TANK") or self.unit_type == UnitType.TANK: self.can_move_and_attack = True if self.has_keyword("FURY"): self.can_attack_multiple_times = True self.max_attacks_per_turn = 2 def can_attack_target(self, target: 'Unit', battlefield) -> bool: """判断是否能攻击目标单位""" # 基础规则:前线只能攻击对方前线,除非有特殊能力 if self.position is None or target.position is None: return False my_line, my_pos = self.position target_line, target_pos = target.position # 特殊单位类型的攻击规则 if self.unit_type == UnitType.ARTILLERY: # 火炮可以跨战线攻击 return True elif self.unit_type == UnitType.FIGHTER: # 战斗机可以攻击任意位置,但不能攻击被守护的目标 return not self._is_target_guarded(target, battlefield) elif self.unit_type == UnitType.BOMBER: # 轰炸机可以攻击任意位置,但不能攻击与战斗机同排的目标 return not self._is_target_protected_by_fighter(target, battlefield) else: # 常规单位攻击规则:支援线攻击敌方前线,前线攻击敌方支援线 if my_line == LineType.FRONT: # 前线单位攻击敌方支援线 return target_line in [LineType.PLAYER1_SUPPORT, LineType.PLAYER2_SUPPORT] elif my_line in [LineType.PLAYER1_SUPPORT, LineType.PLAYER2_SUPPORT]: # 支援线单位攻击敌方前线 return target_line == LineType.FRONT else: return False def _is_target_guarded(self, target: 'Unit', battlefield) -> bool: """检查目标是否被守护""" # TODO: 实现守护逻辑检查 return False def _is_target_protected_by_fighter(self, target: 'Unit', battlefield) -> bool: """检查目标是否被战斗机保护""" # TODO: 实现战斗机保护逻辑 return False def can_be_attacked_by(self, attacker: 'Unit', battlefield) -> bool: """判断是否能被指定单位攻击""" # 烟幕保护 if self.has_keyword("SMOKESCREEN"): return False # 守护保护 if self._is_guarded(battlefield): # 只有轰炸机和火炮能攻击被守护的单位 return attacker.unit_type in [UnitType.BOMBER, UnitType.ARTILLERY] return True def _is_guarded(self, battlefield) -> bool: """检查自身是否被守护""" # TODO: 实现守护检查逻辑 return False def reset_operation_status(self) -> None: """重置行动状态(回合开始时调用)""" # 已移除 has_operated_this_turn,使用新的细分状态 # 移除Blitz状态(如果是临时的) if "BLITZ" in self.temporary_modifiers: self.can_operate_immediately = False def __str__(self) -> str: keywords_str = f" [{', '.join(self.keywords)}]" if self.keywords else "" nation_str = f" ({self.nation})" if self.nation else "" return f"{self.name}{nation_str} ({self.unit_type}) {self.stats.attack}/{self.stats.current_defense}{keywords_str}" def __repr__(self) -> str: return f"Unit(id={self.id.hex[:8]}, name='{self.name}', type={self.unit_type}, stats={self.stats.attack}/{self.stats.current_defense})"