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; } // --- 结束新增干扰事件处理逻辑 --- } }