using ThreatSource.Utils;
using ThreatSource.Jammer;
using ThreatSource.Simulation;
using ThreatSource.Jammable;
using ThreatSource.Equipment;
using System.Diagnostics;
namespace ThreatSource.Indicator
{
///
/// 指示器抽象基类,实现IIndicator接口的基本功能
///
///
/// 该类提供了指示器的通用实现:
/// - 位置和姿态管理
/// - 激活状态控制
/// - 干扰处理机制
/// 是具体指示器类的实现基础
///
public abstract class BaseIndicator : SimulationElement, IIndicator, IJammable
{
///
/// 视觉遮挡阈值 (0.0 - 1.0)
///
protected const double TargetObscurationThreshold = 0.8;
///
/// 干扰处理组件
///
protected readonly JammableComponent _jammingComponent;
///
/// 获取目标是否被烟幕遮挡 (由任何烟幕引起)
///
protected bool IsTargetObscured { get; private set; } = false;
///
/// 最后一次成功获取的目标位置
///
protected Vector3D? _lastKnownTargetPosition = null;
///
/// 最后一次成功获取的目标朝向
///
protected Orientation? _lastKnownTargetOrientation = null;
///
/// 获取设备支持的干扰类型
///
///
/// 定义指示器可以被哪些类型的干扰影响
/// 子类需要重写此属性以指定支持的干扰类型
///
public abstract IEnumerable SupportedJammingTypes { get; }
///
/// 获取设备当前是否处于被干扰状态
///
///
/// true表示指示器当前受到干扰影响
/// false表示指示器正常工作
///
public bool IsJammed => _jammingComponent.IsJammed;
///
/// 获取或设置目标ID
///
public string? TargetId { get; protected set; }
///
/// 获取或设置导弹ID
///
public string? MissileId { get; protected set; }
///
/// 初始化指示器基类的新实例
///
/// 指示器ID
/// 初始运动参数
/// 仿真管理器实例
///
/// 构造过程:
/// - 调用基类构造器
/// - 初始化基本属性
/// - 初始化干扰处理组件
///
protected BaseIndicator(string id, MotionParameters motionParameters, ISimulationManager manager)
: base(id, motionParameters, manager)
{
_jammingComponent = new JammableComponent(
positionProvider: () => base.Position
);
_jammingComponent.JammingApplied += HandleJammingApplied;
_jammingComponent.JammingCleared += HandleJammingCleared;
}
///
/// 初始化干扰阈值和支持的干扰类型
///
/// 干扰抗性阈值,单位:瓦特
/// 支持的干扰类型
///
/// 使用此方法初始化指示器的干扰处理参数:
/// - 设置干扰抗性阈值
/// - 配置支持的干扰类型
/// 子类应在构造函数中调用此方法
///
protected void InitializeJamming(double jammingResistanceThreshold, IEnumerable supportedTypes)
{
_jammingComponent.LoadJammingConfigFromThreshold(jammingResistanceThreshold, supportedTypes);
}
///
/// 应用干扰
///
/// 干扰参数
///
/// 处理对指示器的干扰影响
/// 只有支持的干扰类型才会被处理
///
public virtual void ApplyJamming(JammingParameters parameters)
{
_jammingComponent.ApplyJamming(parameters);
}
///
/// 清除干扰
///
/// 干扰参数
///
/// 移除特定类型的干扰影响
/// 恢复指示器的正常工作状态
///
public virtual void ClearJamming(JammingParameters parameters)
{
_jammingComponent.ClearJamming(parameters);
}
///
/// 处理指示器被干扰的事件
///
/// 干扰参数
protected virtual void HandleJammingApplied(JammingParameters parameters)
{
// 如果是烟幕干扰,重新计算遮挡状态
if (parameters.Type == JammingType.SmokeGrenade)
{
RecalculateObscurationStatus();
Debug.WriteLine($"[BaseIndicator] 烟幕状态更新,IsTargetObscured: {IsTargetObscured}", "Jamming");
}
else // 其他干扰类型打印通用消息
{
// 子类可以重写此方法以实现特定的干扰响应
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} 受到 {parameters.Type} 类型干扰,功率:{parameters.Power}W", "Jamming");
}
}
///
/// 处理指示器干扰被清除的事件
///
/// 干扰参数
protected virtual void HandleJammingCleared(JammingParameters parameters)
{
// 如果是烟幕干扰,重新计算遮挡状态
if (parameters.Type == JammingType.SmokeGrenade)
{
RecalculateObscurationStatus();
Debug.WriteLine($"[BaseIndicator] 烟幕状态更新,IsTargetObscured: {IsTargetObscured}", "Jamming");
}
else // 其他干扰类型打印通用消息
{
// 子类可以重写此方法以实现干扰清除后的特定行为
Debug.WriteLine($"[BaseIndicator] {this.GetType().Name} {Id} {parameters.Type} 类型干扰已清除", "Jamming");
}
}
///
/// 激活指示器 (基类统一处理干扰事件订阅)
///
public override void Activate()
{
if (!IsActive)
{
IsActive = true;
// 统一订阅 JammingEvent
SimulationManager.SubscribeToEvent(HandleJammingEvent);
// 子类仍需在此处订阅其他特定事件(如导弹热源事件)
}
base.Activate(); // 调用 SimulationElement 的基类 Activate
// 子类需要在此之后调用 base.Activate() 并订阅烟幕事件 <--- 旧注释,移除或更新
// 现在基类处理通用 JammingEvent,子类只需要处理非干扰事件
RecalculateObscurationStatus(); // 激活时检查一次初始遮挡状态
}
///
/// 停用指示器 (基类统一处理干扰事件取消订阅)
///
public override void Deactivate()
{
if (IsActive)
{
IsActive = false;
// 统一取消订阅 JammingEvent
SimulationManager.UnsubscribeFromEvent(HandleJammingEvent);
// 子类仍需在此处取消订阅其他特定事件
}
// 子类需要在此之前取消订阅烟幕事件 <--- 旧注释,移除或更新
IsTargetObscured = false;
// 重置最后目标状态
_lastKnownTargetPosition = null;
_lastKnownTargetOrientation = null;
base.Deactivate(); // 调用 SimulationElement 的基类 Deactivate
}
///
/// 更新指示器状态
///
public override void Update(double deltaTime)
{
_jammingComponent.UpdateJammingStatus(deltaTime); // 更新干扰状态(包括持续时间等)
if (IsActive)
{
// 只有未被电子干扰时才更新指示器特定功能
// 烟幕遮挡 (IsTargetObscured) 在 UpdateIndicator 内部处理
if (!IsJammed)
{
UpdateIndicator(deltaTime);
}
}
}
///
/// 更新指示器特定功能
///
/// 时间步长,单位:秒
///
/// 子类需要重写此方法以实现特定的更新逻辑
/// 只有在未被干扰时才应执行正常的功能逻辑
///
protected virtual void UpdateIndicator(double deltaTime)
{
// 子类中实现具体的指示器更新逻辑
}
///
/// 获取指示器运行状态
///
/// 包含指示器完整状态信息的结构体
///
/// 子类需要重写此方法以提供完整的状态信息
/// 包括位置、姿态、目标关联和干扰状态等
///
public abstract IndicatorRunningState GetRunningState();
///
/// 获取指示器状态信息
///
/// 包含指示器状态信息的字符串
public override string GetStatus()
{
return $"指示器 {Id} 当前状态: 上次目标位置: {_lastKnownTargetPosition}, 上次目标朝向: {_lastKnownTargetOrientation}";
}
///
/// 重新计算目标是否被任何活动的烟幕遮挡,并更新 IsTargetObscured 状态。
/// 子类应在处理烟幕相关事件时调用此方法。
///
protected void RecalculateObscurationStatus()
{
bool currentlyObscured = CheckIfTargetIsObscured(out BaseEquipment? currentTarget);
if (IsTargetObscured != currentlyObscured)
{
IsTargetObscured = currentlyObscured;
}
// 仅当目标被看到 (未遮挡) 且目标有效时,才更新最后已知状态
if (!currentlyObscured && currentTarget != null)
{
_lastKnownTargetPosition = currentTarget.Position;
_lastKnownTargetOrientation = currentTarget.Orientation;
}
}
///
/// 检查当前目标是否被活动烟幕遮挡。
///
/// 如果成功找到目标实体,则输出该实体。
/// 如果目标被遮挡,则返回 true;否则返回 false。
private bool CheckIfTargetIsObscured(out BaseEquipment? targetFound)
{
targetFound = null;
// 1. 检查指示器状态和目标ID
if (!IsActive || string.IsNullOrEmpty(TargetId))
{
return false; // 未激活或无目标,视为未遮挡
}
// 2. 尝试获取目标实体
targetFound = SimulationManager.GetEntityById(TargetId) as BaseEquipment;
if (targetFound == null)
{
return false; // 找不到目标实体,视为未遮挡
}
// 3. 获取活动的烟幕
var activeSmokeGrenades = SimulationManager.GetEntitiesByType()
.Where(sg => sg != null && sg.IsActive)
.ToList(); // 获取列表以便检查是否为空
if (!activeSmokeGrenades.Any())
{
return false; // 没有活动的烟幕,视为未遮挡
}
// 4. 准备计算所需数据
Vector3D observerPos = Position;
Vector3D targetCenter = targetFound.Position;
Orientation targetOrient = targetFound.Orientation;
Vector3D targetDims = new(targetFound.Properties.Width, targetFound.Properties.Height, targetFound.Properties.Length);
// 5. 遍历烟幕检查遮挡
foreach (var smokeGrenade in activeSmokeGrenades)
{
try
{
Vector3D smokeCenter = smokeGrenade.Position;
Orientation smokeOrient = smokeGrenade.Orientation;
Vector3D smokeDims;
if (smokeGrenade.config.SmokeType == SmokeScreenType.Cloud)
{
smokeDims = new(smokeGrenade.config.Thickness, smokeGrenade.config.CloudDiameter, smokeGrenade.config.CloudDiameter);
}
else // Wall
{
smokeDims = new(smokeGrenade.config.Thickness, smokeGrenade.config.WallHeight, smokeGrenade.config.WallWidth);
}
double ratio = ObscurationUtils.CalculateProjectedOverlapRatio(
observerPos,
smokeCenter, smokeDims, smokeOrient,
targetCenter, targetDims, targetOrient
);
// 只要有一个烟幕满足遮挡条件,即可判定为遮挡
if (ratio >= TargetObscurationThreshold)
{
return true; // 被遮挡
}
}
catch (Exception ex)
{
Debug.WriteLine($"检查烟幕遮挡状态时发生错误: {ex.Message}");
// 在发生错误时,可以选择继续检查其他烟幕,或者直接返回一个默认值(例如 false)
}
}
// 6. 如果遍历完所有烟幕都没有达到遮挡阈值
return false; // 未被遮挡
}
// --- 新增干扰事件处理逻辑 ---
///
/// 统一处理干扰事件
///
/// 干扰事件
protected virtual void HandleJammingEvent(JammingEvent evt)
{
if (evt == null) return;
// 在应用干扰前检查是否应该处理此干扰
if (!ShouldHandleJamming(evt.Parameters))
{
// Debug.WriteLine($"[BaseIndicator] {Id} 忽略干扰事件,类型: {evt.Parameters.Type}");
return;
}
// 如果应该处理,则应用干扰
ApplyJamming(evt.Parameters);
}
///
/// 判断是否应该处理传入的干扰参数(可被子类重写以添加特定检查)
///
/// 干扰参数
/// 如果应该处理则返回 true,否则返回 false
protected virtual bool ShouldHandleJamming(JammingParameters parameters)
{
// 基类默认处理所有受支持类型的干扰 (包括烟幕)
// 子类可以重写此方法添加额外检查,如波段匹配
// 注意:烟幕干扰虽然通过这里,但主要效果通过 IsTargetObscured 和 RecalculateObscurationStatus 体现
bool supported = SupportedJammingTypes.Contains(parameters.Type);
// if(!supported) Debug.WriteLine($"[BaseIndicator] {Id} 不支持干扰类型: {parameters.Type}");
return supported;
}
// --- 结束新增干扰事件处理逻辑 ---
}
}