410 lines
14 KiB
Python
410 lines
14 KiB
Python
|
|
"""
|
|||
|
|
重新设计的战场系统
|
|||
|
|
符合KARDS真实的3条战线布局:玩家1支援线 - 共用前线 - 玩家2支援线
|
|||
|
|
"""
|
|||
|
|
from typing import List, Optional, Dict, Tuple, Union
|
|||
|
|
from dataclasses import dataclass
|
|||
|
|
from uuid import UUID
|
|||
|
|
|
|||
|
|
from ..core.enums import LineType
|
|||
|
|
from ..units.unit import Unit
|
|||
|
|
|
|||
|
|
|
|||
|
|
@dataclass
|
|||
|
|
class HQ:
|
|||
|
|
"""总部类 - 作为支援线上的特殊目标"""
|
|||
|
|
defense: int = 20
|
|||
|
|
current_defense: int = 20
|
|||
|
|
player_id: str = ""
|
|||
|
|
position_index: int = 2 # HQ默认在支援线中间位置
|
|||
|
|
|
|||
|
|
def take_damage(self, damage: int) -> int:
|
|||
|
|
"""总部受到伤害"""
|
|||
|
|
actual_damage = min(damage, self.current_defense)
|
|||
|
|
self.current_defense -= actual_damage
|
|||
|
|
return actual_damage
|
|||
|
|
|
|||
|
|
def is_destroyed(self) -> bool:
|
|||
|
|
"""判断总部是否被摧毁"""
|
|||
|
|
return self.current_defense <= 0
|
|||
|
|
|
|||
|
|
def __str__(self) -> str:
|
|||
|
|
return f"HQ ({self.current_defense}/{self.defense})"
|
|||
|
|
|
|||
|
|
|
|||
|
|
class BattleLine:
|
|||
|
|
"""战线类 - 支持单位和HQ的紧凑排列"""
|
|||
|
|
|
|||
|
|
def __init__(self, line_type: LineType, max_objectives: int = 5, hq: Optional[HQ] = None):
|
|||
|
|
self.line_type = line_type
|
|||
|
|
self.max_objectives = max_objectives
|
|||
|
|
self.objectives: List[Optional[Union[Unit, HQ]]] = [] # 紧凑排列的目标
|
|||
|
|
|
|||
|
|
# 如果有HQ,放在中间位置
|
|||
|
|
if hq:
|
|||
|
|
self.objectives.append(hq)
|
|||
|
|
hq.position_index = 0
|
|||
|
|
|
|||
|
|
def add_unit(self, unit: Unit) -> bool:
|
|||
|
|
"""添加单位到战线(自动紧凑排列)"""
|
|||
|
|
if len(self.objectives) >= self.max_objectives:
|
|||
|
|
return False # 战线已满
|
|||
|
|
|
|||
|
|
self.objectives.append(unit)
|
|||
|
|
unit.position = (self.line_type, len(self.objectives) - 1)
|
|||
|
|
|
|||
|
|
# 检查烟幕条件
|
|||
|
|
unit.check_smokescreen_conditions()
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
def get_hq(self) -> Optional[HQ]:
|
|||
|
|
"""获取战线上的HQ"""
|
|||
|
|
for obj in self.objectives:
|
|||
|
|
if isinstance(obj, HQ):
|
|||
|
|
return obj
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def get_units(self) -> List[Unit]:
|
|||
|
|
"""获取战线上的所有单位(不包括HQ)"""
|
|||
|
|
units = []
|
|||
|
|
for obj in self.objectives:
|
|||
|
|
if isinstance(obj, Unit):
|
|||
|
|
units.append(obj)
|
|||
|
|
return units
|
|||
|
|
|
|||
|
|
def remove_unit(self, unit: Unit) -> bool:
|
|||
|
|
"""移除单位并重新排列"""
|
|||
|
|
try:
|
|||
|
|
index = self.objectives.index(unit)
|
|||
|
|
self.objectives.pop(index)
|
|||
|
|
unit.position = None
|
|||
|
|
|
|||
|
|
# 重新分配位置索引
|
|||
|
|
self._reindex_objectives()
|
|||
|
|
return True
|
|||
|
|
except ValueError:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def _reindex_objectives(self) -> None:
|
|||
|
|
"""重新分配所有目标的位置索引"""
|
|||
|
|
for i, obj in enumerate(self.objectives):
|
|||
|
|
if isinstance(obj, Unit):
|
|||
|
|
obj.position = (self.line_type, i)
|
|||
|
|
# 重新索引后检查烟幕条件
|
|||
|
|
obj.check_smokescreen_conditions()
|
|||
|
|
elif isinstance(obj, HQ):
|
|||
|
|
obj.position_index = i
|
|||
|
|
|
|||
|
|
def get_unit_at_index(self, index: int) -> Optional[Union[Unit, HQ]]:
|
|||
|
|
"""获取指定索引位置的目标"""
|
|||
|
|
if 0 <= index < len(self.objectives):
|
|||
|
|
return self.objectives[index]
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def get_all_units(self) -> List[Unit]:
|
|||
|
|
"""获取战线上所有单位(不包括HQ)"""
|
|||
|
|
return [obj for obj in self.objectives if isinstance(obj, Unit)]
|
|||
|
|
|
|||
|
|
def get_all_objectives(self) -> List[Union[Unit, HQ]]:
|
|||
|
|
"""获取战线上所有目标(包括单位和HQ)"""
|
|||
|
|
return self.objectives.copy()
|
|||
|
|
|
|||
|
|
def find_unit_index(self, unit: Unit) -> Optional[int]:
|
|||
|
|
"""查找单位的索引位置"""
|
|||
|
|
try:
|
|||
|
|
return self.objectives.index(unit)
|
|||
|
|
except ValueError:
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def get_adjacent_indices(self, index: int) -> List[int]:
|
|||
|
|
"""获取相邻位置的索引"""
|
|||
|
|
adjacent = []
|
|||
|
|
if index > 0:
|
|||
|
|
adjacent.append(index - 1)
|
|||
|
|
if index < len(self.objectives) - 1:
|
|||
|
|
adjacent.append(index + 1)
|
|||
|
|
return adjacent
|
|||
|
|
|
|||
|
|
def get_adjacent_objectives(self, index: int) -> List[Union[Unit, HQ]]:
|
|||
|
|
"""获取相邻的目标"""
|
|||
|
|
adjacent_indices = self.get_adjacent_indices(index)
|
|||
|
|
return [self.objectives[i] for i in adjacent_indices]
|
|||
|
|
|
|||
|
|
def is_full(self) -> bool:
|
|||
|
|
"""检查战线是否已满"""
|
|||
|
|
return len(self.objectives) >= self.max_objectives
|
|||
|
|
|
|||
|
|
def is_empty(self) -> bool:
|
|||
|
|
"""检查战线是否为空"""
|
|||
|
|
return len(self.objectives) == 0
|
|||
|
|
|
|||
|
|
def get_objective_count(self) -> int:
|
|||
|
|
"""获取目标数量"""
|
|||
|
|
return len(self.objectives)
|
|||
|
|
|
|||
|
|
def __str__(self) -> str:
|
|||
|
|
if not self.objectives:
|
|||
|
|
return f"{self.line_type}: [空]"
|
|||
|
|
|
|||
|
|
obj_strs = []
|
|||
|
|
for i, obj in enumerate(self.objectives):
|
|||
|
|
if isinstance(obj, Unit):
|
|||
|
|
obj_strs.append(f"{i}:{obj.name}")
|
|||
|
|
elif isinstance(obj, HQ):
|
|||
|
|
obj_strs.append(f"{i}:HQ")
|
|||
|
|
else:
|
|||
|
|
obj_strs.append(f"{i}:未知")
|
|||
|
|
|
|||
|
|
return f"{self.line_type}: [{', '.join(obj_strs)}]"
|
|||
|
|
|
|||
|
|
|
|||
|
|
class Battlefield:
|
|||
|
|
"""重新设计的战场类 - 3条战线布局"""
|
|||
|
|
|
|||
|
|
def __init__(self, player1_id: str, player2_id: str):
|
|||
|
|
# 玩家信息
|
|||
|
|
self.player1_id = player1_id
|
|||
|
|
self.player2_id = player2_id
|
|||
|
|
|
|||
|
|
# 创建HQ
|
|||
|
|
self.player1_hq = HQ(player_id=player1_id)
|
|||
|
|
self.player2_hq = HQ(player_id=player2_id)
|
|||
|
|
|
|||
|
|
# 创建3条战线
|
|||
|
|
self.player1_support = BattleLine(LineType.PLAYER1_SUPPORT, hq=self.player1_hq)
|
|||
|
|
self.front_line = BattleLine(LineType.FRONT) # 共用前线,无HQ
|
|||
|
|
self.player2_support = BattleLine(LineType.PLAYER2_SUPPORT, hq=self.player2_hq)
|
|||
|
|
|
|||
|
|
# 单位快速查找索引
|
|||
|
|
self.unit_registry: Dict[UUID, Unit] = {}
|
|||
|
|
|
|||
|
|
# 前线控制权(None表示无人控制)
|
|||
|
|
self.front_line_controller: Optional[str] = None
|
|||
|
|
|
|||
|
|
def get_player_hq(self, player_id: str) -> Optional[HQ]:
|
|||
|
|
"""获取玩家的HQ"""
|
|||
|
|
if player_id == self.player1_id:
|
|||
|
|
return self.player1_hq
|
|||
|
|
elif player_id == self.player2_id:
|
|||
|
|
return self.player2_hq
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def get_player_support_line(self, player_id: str) -> Optional[BattleLine]:
|
|||
|
|
"""获取玩家的支援线"""
|
|||
|
|
if player_id == self.player1_id:
|
|||
|
|
return self.player1_support
|
|||
|
|
elif player_id == self.player2_id:
|
|||
|
|
return self.player2_support
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def get_line_by_type(self, line_type: LineType) -> Optional[BattleLine]:
|
|||
|
|
"""根据类型获取战线"""
|
|||
|
|
if line_type == LineType.PLAYER1_SUPPORT:
|
|||
|
|
return self.player1_support
|
|||
|
|
elif line_type == LineType.FRONT:
|
|||
|
|
return self.front_line
|
|||
|
|
elif line_type == LineType.PLAYER2_SUPPORT:
|
|||
|
|
return self.player2_support
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def deploy_unit_to_support(self, unit: Unit, player_id: str) -> bool:
|
|||
|
|
"""部署单位到玩家支援线"""
|
|||
|
|
support_line = self.get_player_support_line(player_id)
|
|||
|
|
if not support_line:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
if support_line.add_unit(unit):
|
|||
|
|
unit.owner = player_id
|
|||
|
|
self.unit_registry[unit.id] = unit
|
|||
|
|
return True
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def deploy_unit_to_front(self, unit: Unit, player_id: str) -> bool:
|
|||
|
|
"""部署单位到前线"""
|
|||
|
|
if self.front_line.add_unit(unit):
|
|||
|
|
unit.owner = player_id
|
|||
|
|
self.unit_registry[unit.id] = unit
|
|||
|
|
|
|||
|
|
# 更新前线控制权
|
|||
|
|
self._update_front_line_control()
|
|||
|
|
return True
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def _update_front_line_control(self) -> None:
|
|||
|
|
"""更新前线控制权"""
|
|||
|
|
units = self.front_line.get_all_units()
|
|||
|
|
if not units:
|
|||
|
|
self.front_line_controller = None
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 检查前线上的单位是否都属于同一玩家
|
|||
|
|
owners = set(unit.owner for unit in units)
|
|||
|
|
if len(owners) == 1:
|
|||
|
|
self.front_line_controller = list(owners)[0]
|
|||
|
|
else:
|
|||
|
|
self.front_line_controller = None # 混合控制或争夺中
|
|||
|
|
|
|||
|
|
def remove_unit(self, unit: Unit) -> bool:
|
|||
|
|
"""从战场移除单位"""
|
|||
|
|
if not unit.position:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
line_type, _ = unit.position
|
|||
|
|
battle_line = self.get_line_by_type(line_type)
|
|||
|
|
|
|||
|
|
if battle_line and battle_line.remove_unit(unit):
|
|||
|
|
if unit.id in self.unit_registry:
|
|||
|
|
del self.unit_registry[unit.id]
|
|||
|
|
|
|||
|
|
# 如果是前线单位,更新控制权
|
|||
|
|
if line_type == LineType.FRONT:
|
|||
|
|
self._update_front_line_control()
|
|||
|
|
|
|||
|
|
return True
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def get_all_units(self, player_id: Optional[str] = None) -> List[Unit]:
|
|||
|
|
"""获取所有单位,可按玩家筛选"""
|
|||
|
|
all_units = []
|
|||
|
|
|
|||
|
|
# 收集所有战线的单位
|
|||
|
|
for line in [self.player1_support, self.front_line, self.player2_support]:
|
|||
|
|
all_units.extend(line.get_all_units())
|
|||
|
|
|
|||
|
|
# 按玩家筛选
|
|||
|
|
if player_id:
|
|||
|
|
all_units = [unit for unit in all_units if unit.owner == player_id]
|
|||
|
|
|
|||
|
|
return all_units
|
|||
|
|
|
|||
|
|
def find_unit(self, unit_id: UUID) -> Optional[Unit]:
|
|||
|
|
"""通过ID查找单位"""
|
|||
|
|
return self.unit_registry.get(unit_id)
|
|||
|
|
|
|||
|
|
def get_units_with_keyword(self, keyword: str, player_id: Optional[str] = None) -> List[Unit]:
|
|||
|
|
"""获取具有特定关键词的所有单位"""
|
|||
|
|
units = self.get_all_units(player_id)
|
|||
|
|
return [unit for unit in units if unit.has_keyword(keyword)]
|
|||
|
|
|
|||
|
|
def is_game_over(self) -> Tuple[bool, Optional[str]]:
|
|||
|
|
"""检查游戏是否结束,返回(是否结束, 胜利者ID)"""
|
|||
|
|
if self.player1_hq.is_destroyed():
|
|||
|
|
return True, self.player2_id
|
|||
|
|
elif self.player2_hq.is_destroyed():
|
|||
|
|
return True, self.player1_id
|
|||
|
|
return False, None
|
|||
|
|
|
|||
|
|
def get_valid_attack_targets_for_unit(self, attacker: Unit) -> List[Union[Unit, HQ]]:
|
|||
|
|
"""获取单位可以攻击的所有目标"""
|
|||
|
|
if not attacker.owner or not attacker.position:
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
valid_targets = []
|
|||
|
|
attacker_line, attacker_index = attacker.position
|
|||
|
|
|
|||
|
|
# 根据单位类型和位置确定攻击规则
|
|||
|
|
if attacker.unit_type.name == "ARTILLERY":
|
|||
|
|
# 火炮可以攻击任意敌方目标
|
|||
|
|
valid_targets = self._get_all_enemy_objectives(attacker.owner)
|
|||
|
|
|
|||
|
|
elif attacker.unit_type.name in ["FIGHTER", "BOMBER"]:
|
|||
|
|
# 飞机可以攻击任意敌方目标
|
|||
|
|
valid_targets = self._get_all_enemy_objectives(attacker.owner)
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
# 常规单位攻击规则
|
|||
|
|
if attacker_line == LineType.FRONT:
|
|||
|
|
# 前线单位可以攻击敌方支援线
|
|||
|
|
enemy_id = self.get_enemy_player_id(attacker.owner)
|
|||
|
|
enemy_support = self.get_player_support_line(enemy_id)
|
|||
|
|
if enemy_support:
|
|||
|
|
valid_targets = enemy_support.get_all_objectives()
|
|||
|
|
|
|||
|
|
else:
|
|||
|
|
# 支援线单位只能攻击前线单位
|
|||
|
|
front_units = self.front_line.get_all_units()
|
|||
|
|
enemy_units = [u for u in front_units if u.owner != attacker.owner]
|
|||
|
|
valid_targets = enemy_units
|
|||
|
|
|
|||
|
|
# 应用守护等规则过滤目标
|
|||
|
|
return self._filter_protected_targets(valid_targets, attacker)
|
|||
|
|
|
|||
|
|
def _get_all_enemy_objectives(self, player_id: str) -> List[Union[Unit, HQ]]:
|
|||
|
|
"""获取所有敌方目标(单位+HQ)"""
|
|||
|
|
enemy_id = self.get_enemy_player_id(player_id)
|
|||
|
|
if not enemy_id:
|
|||
|
|
return []
|
|||
|
|
|
|||
|
|
targets = []
|
|||
|
|
|
|||
|
|
# 敌方支援线目标
|
|||
|
|
enemy_support = self.get_player_support_line(enemy_id)
|
|||
|
|
if enemy_support:
|
|||
|
|
targets.extend(enemy_support.get_all_objectives())
|
|||
|
|
|
|||
|
|
# 前线敌方单位
|
|||
|
|
front_units = self.front_line.get_all_units()
|
|||
|
|
enemy_front_units = [u for u in front_units if u.owner == enemy_id]
|
|||
|
|
targets.extend(enemy_front_units)
|
|||
|
|
|
|||
|
|
return targets
|
|||
|
|
|
|||
|
|
def _filter_protected_targets(self, targets: List[Union[Unit, HQ]],
|
|||
|
|
attacker: Unit) -> List[Union[Unit, HQ]]:
|
|||
|
|
"""过滤受保护的目标"""
|
|||
|
|
valid_targets = []
|
|||
|
|
|
|||
|
|
for target in targets:
|
|||
|
|
can_attack = True
|
|||
|
|
|
|||
|
|
if isinstance(target, Unit):
|
|||
|
|
# 检查烟幕保护
|
|||
|
|
if target.has_keyword("SMOKESCREEN"):
|
|||
|
|
can_attack = False
|
|||
|
|
|
|||
|
|
# 检查守护保护
|
|||
|
|
if self._is_target_guarded(target) and attacker.unit_type.name not in ["BOMBER", "ARTILLERY"]:
|
|||
|
|
can_attack = False
|
|||
|
|
|
|||
|
|
if can_attack:
|
|||
|
|
valid_targets.append(target)
|
|||
|
|
|
|||
|
|
return valid_targets
|
|||
|
|
|
|||
|
|
def _is_target_guarded(self, target: Union[Unit, HQ]) -> bool:
|
|||
|
|
"""检查目标是否被守护"""
|
|||
|
|
if not isinstance(target, Unit) or not target.position:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
line_type, target_index = target.position
|
|||
|
|
battle_line = self.get_line_by_type(line_type)
|
|||
|
|
if not battle_line:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
# 检查相邻位置是否有守护单位
|
|||
|
|
adjacent_objectives = battle_line.get_adjacent_objectives(target_index)
|
|||
|
|
for obj in adjacent_objectives:
|
|||
|
|
if isinstance(obj, Unit) and obj.has_keyword("GUARD"):
|
|||
|
|
return True
|
|||
|
|
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def get_enemy_player_id(self, player_id: str) -> Optional[str]:
|
|||
|
|
"""获取敌方玩家ID"""
|
|||
|
|
if player_id == self.player1_id:
|
|||
|
|
return self.player2_id
|
|||
|
|
elif player_id == self.player2_id:
|
|||
|
|
return self.player1_id
|
|||
|
|
return None
|
|||
|
|
|
|||
|
|
def __str__(self) -> str:
|
|||
|
|
front_control = f" (控制: {self.front_line_controller})" if self.front_line_controller else " (无人控制)"
|
|||
|
|
|
|||
|
|
return f"""
|
|||
|
|
=== 战场状态 ===
|
|||
|
|
{self.player1_support}
|
|||
|
|
{self.front_line}{front_control}
|
|||
|
|
{self.player2_support}
|
|||
|
|
==================
|
|||
|
|
"""
|