kards-env/kards_battle/core/unit_combat_rules.py

212 lines
8.8 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
KARDS单位战斗规则实现
完整实现各兵种的特殊攻击和防御规则
"""
from typing import List, Union, Optional
from ..units.unit import Unit
from ..battlefield.battlefield import HQ, Battlefield
from ..core.enums import UnitType, LineType
class UnitCombatRules:
"""单位战斗规则管理器"""
@staticmethod
def can_attack_target(attacker: Unit, target: Union[Unit, HQ], battlefield: Battlefield) -> bool:
"""检查攻击者是否能攻击目标"""
if not attacker.owner or not attacker.position:
return False
# 不能攻击己方目标
if isinstance(target, Unit) and target.owner == attacker.owner:
return False
elif isinstance(target, HQ) and target.player_id == attacker.owner:
return False
# 根据攻击者类型应用规则
if attacker.unit_type == UnitType.INFANTRY:
return UnitCombatRules._infantry_can_attack(attacker, target, battlefield)
elif attacker.unit_type == UnitType.TANK:
return UnitCombatRules._tank_can_attack(attacker, target, battlefield)
elif attacker.unit_type == UnitType.ARTILLERY:
return UnitCombatRules._artillery_can_attack(attacker, target, battlefield)
elif attacker.unit_type == UnitType.FIGHTER:
return UnitCombatRules._fighter_can_attack(attacker, target, battlefield)
elif attacker.unit_type == UnitType.BOMBER:
return UnitCombatRules._bomber_can_attack(attacker, target, battlefield)
return False
@staticmethod
def _infantry_can_attack(attacker: Unit, target: Union[Unit, HQ], battlefield: Battlefield) -> bool:
"""步兵攻击规则:只能攻击相邻阵线"""
attacker_line, _ = attacker.position
if isinstance(target, HQ):
# 步兵只能从前线攻击敌方支援线的HQ
enemy_support_line = battlefield.get_player_support_line(
battlefield.get_enemy_player_id(attacker.owner)
)
return (attacker_line == LineType.FRONT and
enemy_support_line and target in enemy_support_line.get_all_objectives())
if not target.position:
return False
target_line, _ = target.position
# 前线攻击敌方支援线,支援线攻击前线
if attacker_line == LineType.FRONT:
# 前线单位可以攻击任一敌方支援线
return target_line in [LineType.PLAYER1_SUPPORT, LineType.PLAYER2_SUPPORT]
else:
# 支援线只能攻击前线
return target_line == LineType.FRONT
@staticmethod
def _tank_can_attack(attacker: Unit, target: Union[Unit, HQ], battlefield: Battlefield) -> bool:
"""坦克攻击规则:与步兵相同的距离限制,但可以一回合内移动+攻击"""
return UnitCombatRules._infantry_can_attack(attacker, target, battlefield)
@staticmethod
def _artillery_can_attack(attacker: Unit, target: Union[Unit, HQ], battlefield: Battlefield) -> bool:
"""火炮攻击规则:无视攻击距离,无视守护,不受反击"""
# 火炮可以攻击任意敌方目标
if isinstance(target, HQ):
return target.player_id != attacker.owner
else:
return target.owner != attacker.owner
@staticmethod
def _fighter_can_attack(attacker: Unit, target: Union[Unit, HQ], battlefield: Battlefield) -> bool:
"""战斗机攻击规则:无视攻击距离"""
# 战斗机可以攻击任意敌方目标
if isinstance(target, HQ):
return target.player_id != attacker.owner
else:
return target.owner != attacker.owner
@staticmethod
def _bomber_can_attack(attacker: Unit, target: Union[Unit, HQ], battlefield: Battlefield) -> bool:
"""轰炸机攻击规则:无视攻击距离,无视守护,但有战斗机保护限制"""
if isinstance(target, HQ):
return target.player_id != attacker.owner
if target.owner == attacker.owner:
return False
# 检查目标所在阵线是否有战斗机保护
if isinstance(target, Unit) and target.position:
target_line_type, _ = target.position
target_line = battlefield.get_line_by_type(target_line_type)
if target_line:
# 检查同阵线是否有战斗机
for obj in target_line.get_all_objectives():
if (isinstance(obj, Unit) and
obj.unit_type == UnitType.FIGHTER and
obj.owner == target.owner):
# 如果目标不是战斗机,且同阵线有战斗机,则不能攻击
return target.unit_type == UnitType.FIGHTER
return True
@staticmethod
def can_counter_attack(defender: Unit, attacker: Unit) -> bool:
"""检查防御者是否可以反击"""
# 被压制的单位可以反击,只是不能主动攻击
# PINNED不影响反击能力
# 轰炸机和火炮攻击不受反击
if attacker.unit_type in [UnitType.BOMBER, UnitType.ARTILLERY]:
return False
# 轰炸机被攻击时不会反击(除非攻击者是战斗机)
if defender.unit_type == UnitType.BOMBER:
return attacker.unit_type == UnitType.FIGHTER
return True
@staticmethod
def is_protected_by_guard(target: Union[Unit, HQ], battlefield: Battlefield) -> bool:
"""检查目标是否被守护保护"""
if isinstance(target, HQ):
# HQ的守护检查
hq_player = target.player_id
support_line = battlefield.get_player_support_line(hq_player)
if not support_line:
return False
hq_index = None
objectives = support_line.get_all_objectives()
for i, obj in enumerate(objectives):
if obj == target:
hq_index = i
break
if hq_index is None:
return False
# 检查相邻位置是否有守护单位
adjacent_indices = support_line.get_adjacent_indices(hq_index)
for idx in adjacent_indices:
if idx < len(objectives):
obj = objectives[idx]
if isinstance(obj, Unit) and obj.has_keyword("GUARD"):
return True
return False
elif isinstance(target, Unit) and target.position:
target_line_type, target_index = target.position
target_line = battlefield.get_line_by_type(target_line_type)
if not target_line:
return False
# 检查相邻位置是否有守护单位
adjacent_objectives = target_line.get_adjacent_objectives(target_index)
for obj in adjacent_objectives:
if isinstance(obj, Unit) and obj.has_keyword("GUARD"):
return True
return False
@staticmethod
def can_ignore_guard(attacker: Unit) -> bool:
"""检查攻击者是否可以无视守护"""
# 火炮和轰炸机可以无视守护
return attacker.unit_type in [UnitType.ARTILLERY, UnitType.BOMBER]
@staticmethod
def get_valid_targets(attacker: Unit, battlefield: Battlefield) -> List[Union[Unit, HQ]]:
"""获取攻击者的所有有效目标"""
if not attacker.owner:
return []
all_targets = []
enemy_id = battlefield.get_enemy_player_id(attacker.owner)
if enemy_id:
# 获取敌方所有目标
enemy_support = battlefield.get_player_support_line(enemy_id)
if enemy_support:
all_targets.extend(enemy_support.get_all_objectives())
# 前线敌方单位
front_units = battlefield.front_line.get_all_units()
enemy_front_units = [u for u in front_units if u.owner == enemy_id]
all_targets.extend(enemy_front_units)
# 过滤出可攻击的目标
valid_targets = []
for target in all_targets:
if UnitCombatRules.can_attack_target(attacker, target, battlefield):
# 检查守护保护
if UnitCombatRules.is_protected_by_guard(target, battlefield):
if UnitCombatRules.can_ignore_guard(attacker):
valid_targets.append(target)
# 被守护保护且攻击者不能无视守护
else:
valid_targets.append(target)
return valid_targets