using System; using CounterDrone.Core; using CounterDrone.Core.Algorithms; using CounterDrone.Core.Models; namespace CounterDrone.Core.Simulation { public enum PlatformState { Idle, FlyingToTarget, ReadyToRelease } public class PlatformEntity { public string Id { get; } public EquipmentRole Role { get; } public PlatformType? PlatformType { get; } public float PosX { get; private set; } public float PosY { get; private set; } public float PosZ { get; private set; } public AerosolType? AerosolType { get; } public int MunitionCount { get; private set; } public float Cooldown { get; } public float CooldownRemaining { get; private set; } public PlatformState State { get; private set; } = PlatformState.Idle; // 空基平台 public float CruiseSpeed { get; } public float PatrolX { get; } public float PatrolY { get; } public float PatrolZ { get; } public float ReleaseAltitude { get; } private float _targetX, _targetY, _targetZ; private float _flightDistance; private float _flownDistance; private float _driftDistance; /// 空基平台精确投放时刻(计算值) public float ExactReleaseTime => _exactReleaseTime; private float _exactReleaseTime; public float TargetX => _targetX; public float TargetY => _targetY; public float TargetZ => _targetZ; public float FlightDistance => _flightDistance; public float FlownDistance => _flownDistance; // 地基平台 public float MuzzleVelocity { get; } public string? ModelId { get; } /// Ready 包含:冷却就绪 + 有弹药 + 非飞行中 public bool Ready => CooldownRemaining <= 0 && MunitionCount > 0 && State != PlatformState.FlyingToTarget; public PlatformEntity(string id, FireUnitSpec spec, ScenarioUnit deployment) { Id = id; Role = (EquipmentRole)deployment.EquipmentRole; PlatformType = (PlatformType?)spec.PlatformType; PosX = (float)deployment.PositionX; PosY = (float)deployment.PositionY; PosZ = (float)deployment.PositionZ; AerosolType = deployment.AerosolType.HasValue ? (AerosolType?)deployment.AerosolType.Value : null; MunitionCount = deployment.MunitionCount ?? 1; Cooldown = (float)spec.Cooldown; MuzzleVelocity = (float)spec.MuzzleVelocity; CruiseSpeed = (float)spec.CruiseSpeed; ReleaseAltitude = (float)spec.ReleaseAltitude; ModelId = string.IsNullOrEmpty(spec.ModelId) ? null : spec.ModelId; PatrolX = PosX; PatrolY = PosY; PatrolZ = PosZ; } public void Update(float deltaTime) { if (CooldownRemaining > 0) CooldownRemaining -= deltaTime; if (State != PlatformState.FlyingToTarget) return; float step = CruiseSpeed * deltaTime; _flownDistance += step; if (_flownDistance >= _flightDistance - _driftDistance - 0.01f || _flightDistance < 0.01f) { // 在投放点(非终点),保留插值位置 float rt = _flightDistance > 0.01f ? Math.Min(1f, _flownDistance / _flightDistance) : 1f; PosX = PatrolX + (_targetX - PatrolX) * rt; PosY = PatrolY + (_targetY - PatrolY) * rt; PosZ = PatrolZ + (_targetZ - PatrolZ) * rt; State = PlatformState.ReadyToRelease; return; } float t = _flownDistance / _flightDistance; PosX = PatrolX + (_targetX - PatrolX) * t; PosY = PatrolY + (_targetY - PatrolY) * t; PosZ = PatrolZ + (_targetZ - PatrolZ) * t; } /// 下令空基平台飞往投放点 public void CommandFlyTo(float targetX, float releaseAlt, float targetZ, float fireTime, float disperseHeight) { if (PlatformType != Models.PlatformType.AirBased) return; _targetX = targetX; _targetY = releaseAlt; _targetZ = targetZ; float distToCloud = Kinematics.Distance3D(PosX, PosY, PosZ, targetX, releaseAlt, targetZ); float fallTime = Kinematics.AirDropFallTime(releaseAlt, disperseHeight); float driftDist = CruiseSpeed * fallTime; _flightDistance = distToCloud; _flownDistance = 0; _driftDistance = driftDist; float flightDist = Math.Max(0f, distToCloud - driftDist); float flightTime = CruiseSpeed > 0 ? flightDist / CruiseSpeed : 0f; _exactReleaseTime = fireTime + flightTime; State = PlatformState.FlyingToTarget; } /// 地基:直接发射;空基:投弹后冷却 public void Release() { MunitionCount--; CooldownRemaining = Cooldown; State = PlatformState.Idle; } /// 空基平台到达投放点后可投弹 public bool CanRelease => State == PlatformState.ReadyToRelease && CooldownRemaining <= 0 && MunitionCount > 0; /// 当前速度矢量(空基平台:飞行方向 × 巡航速度;地基:零矢量) public (float Vx, float Vy, float Vz) CurrentVelocity { get { if (PlatformType != Models.PlatformType.AirBased) return (0, 0, 0); return Kinematics.DirectionVelocity(PatrolX, PatrolY, PatrolZ, _targetX, _targetY, _targetZ, CruiseSpeed); } } } }