//ESM detection calculator: http://www.y1pwe.co.uk/AppletCD/Prop2.htm public override bool AttemptDetectUnit(BaseUnit unit, double distanceM) { if (!IsAbleToDetectUnit(unit, distanceM)) { return(false); } if ((double)OwnerUnit.Position.HeightOverSeaLevelM < GameConstants.DEPTH_PERISCOPE_MIN_M || (double)unit.Position.HeightOverSeaLevelM < GameConstants.DEPTH_PERISCOPE_MIN_M) //under sea level { return(false); } //TODO: Take into account over-the horizon capabilities of some radars double sensorHeightOverSeaLevelM = (double)OwnerUnit.Position.HeightOverSeaLevelM + OwnerUnit.UnitClass.HeightM; double targetMaxHeightOverSeaLevelM = (double)unit.Position.HeightOverSeaLevelM + unit.UnitClass.HeightM; double LineOfSightM = MapHelper.CalculateMaxRadarLineOfSightM(sensorHeightOverSeaLevelM, targetMaxHeightOverSeaLevelM); TerrainLineSummary terrainLineSummary = null; if (IsActive) { if (distanceM <= LineOfSightM) { GameConstants.DirectionCardinalPoints direction = MapHelper.CalculateBearingDegrees(OwnerUnit.Position.Coordinate, unit.Position.Coordinate).ToCardinalMark(); double targetApparentSizeArcSec = unit.CalculateRadarCorrectedSizeArcSec(direction, distanceM); double minDetectableSizeArcSec = SensorClass.MinimumTargetSurfaceSizeArcSec; double degradationPercent = 0; if (targetMaxHeightOverSeaLevelM > 90) { minDetectableSizeArcSec = SensorClass.MinimumTargetAirSizeArcSec; } else { degradationPercent = GameManager.Instance.GetRadarDegradationFromSeaStatePercent( OwnerUnit.GetEffectiveSeaState()); } terrainLineSummary = GetTerrainHeightSummaryToTarget(unit); if (sensorHeightOverSeaLevelM < GameConstants.MAX_HEIGHT_TERRAIN_M) { if (sensorHeightOverSeaLevelM < terrainLineSummary.MaxHeightM && targetMaxHeightOverSeaLevelM < terrainLineSummary.MaxHeightM) { return(false); //no radar detection through terrain } if (targetMaxHeightOverSeaLevelM < terrainLineSummary.MaxHeightM * 1.2 && terrainLineSummary.HeightVarianceM > 0) { degradationPercent += 10; } } if (targetMaxHeightOverSeaLevelM < terrainLineSummary.MaxHeightBehindM) { degradationPercent += 75; } if (OwnerUnit.Position.HeightOverSeaLevelM < GameConstants.HEIGHT_MEDIUM_MIN_M || unit.Position.HeightOverSeaLevelM < GameConstants.HEIGHT_MEDIUM_MIN_M) //only if any below cloud cover { degradationPercent += GameManager.Instance.GetRadarDegradationFromWeatherPercent( unit.GetWeatherSystem()); } degradationPercent += OwnerUnit.GetDegradationFromJammingPercent(this.SensorClass.SensorType); if (unit.UnitClass.UnitType == GameConstants.UnitType.Missile && unit.Position.HeightOverSeaLevelM < 20) { degradationPercent += 40; //make sea skimming missiles much harder to detect } if (degradationPercent > 100) { degradationPercent = 100; } var degradationFactor = (1.0 - (degradationPercent / 100.0)); if (degradationFactor < 0.001) { degradationFactor = 0.001; } minDetectableSizeArcSec = minDetectableSizeArcSec * degradationFactor; if (targetApparentSizeArcSec >= minDetectableSizeArcSec) { double detectionStrength = targetApparentSizeArcSec / minDetectableSizeArcSec; //(SensorClass.MaxRangeM - DistanceM) / SensorClass.MaxRangeM; DetectedUnit detect = CreateOrUpdateDetectionReport(unit, detectionStrength, distanceM, targetApparentSizeArcSec, minDetectableSizeArcSec); if (detect != null) { return(true); } } } } //Even if active detection attempt has been made and failed, test for esm (passive) if (SensorClass.IsEsmDetector) { GameConstants.EsmRadiationLevel esmRad = unit.GetCurrentEsmRadiation(); if (esmRad == GameConstants.EsmRadiationLevel.EsmNone) { return(false); } else { LineOfSightM = (LineOfSightM * SensorClass.EsmDetectionOverHorizonPercent) / 100.0; if (esmRad == GameConstants.EsmRadiationLevel.EsmLow) { LineOfSightM *= 0.1; } else if (esmRad == GameConstants.EsmRadiationLevel.EsmMedium) { LineOfSightM *= 0.25; } if (sensorHeightOverSeaLevelM < GameConstants.MAX_HEIGHT_TERRAIN_M) { // Get terrain summary if not already gotten if (terrainLineSummary == null) { terrainLineSummary = GetTerrainHeightSummaryToTarget(unit); } if (sensorHeightOverSeaLevelM < terrainLineSummary.MaxHeightM && targetMaxHeightOverSeaLevelM < terrainLineSummary.MaxHeightM) { return(false); //no radar detection through terrain } if (targetMaxHeightOverSeaLevelM < terrainLineSummary.MaxHeightM * 1.2 && terrainLineSummary.HeightVarianceM > 0) { LineOfSightM *= 0.75; } } if (unit.UnitClass.UnitType == GameConstants.UnitType.Missile && unit.Position.HeightOverSeaLevelM < 20) //sea skimmers { LineOfSightM *= 0.25; } var degrJammingPercent = OwnerUnit.GetDegradationFromJammingPercent(this.SensorClass.SensorType); LineOfSightM *= (100.0 - degrJammingPercent); if (distanceM <= LineOfSightM) { DetectedUnit detect = CreateOrUpdateDetectionReport(unit, (LineOfSightM / distanceM), distanceM, 3600, 60); return(detect != null); } } } return(false); }
//public virtual void Sweep() //{ // if (!IsReady) // { // return; // } // if (OwnerUnit.CarriedByUnit != null) // { // return; // } // System.Diagnostics.Debug.Assert(OwnerPlayer != null, "BaseSensor.Sweep: Sensor OwnerPlayer should never be null."); // System.Diagnostics.Debug.Assert(OwnerUnit != null, "BaseSensor.Sweep: Sensor OwnerUnit should never be null."); // System.Diagnostics.Debug.Assert(SensorClass != null, "BaseSensor.Sweep: Sensor SensorClass should never be null."); //} public virtual bool AttemptDetectUnit(BaseUnit unit, double distanceM) { //TODO: Hit test on detection if (!IsAbleToDetectUnit(unit, distanceM)) { return(false); } if (SensorClass.MaxHeightDeployedM > 0 && OwnerUnit.ActualHeightOverSeaLevelM > SensorClass.MaxHeightDeployedM) { ReadyInSec = 120; return(false); } if (SensorClass.MaxSpeedDeployedKph > 0 && OwnerUnit.ActualSpeedKph > SensorClass.MaxSpeedDeployedKph) { ReadyInSec = 120; return(false); } //if (!OwnerPlayer.IsComputerPlayer) //{ // GameManager.Instance.Log.LogDebug(string.Format( // "PLAYER {0}, UNIT {1}, SENSOR {2} attemtping to detect unit {3}.", // OwnerPlayer.ToString(), OwnerUnit.ToString(), this.ToString(), unit.ToString())); //} GameConstants.DirectionCardinalPoints DirectionToTarget = MapHelper.CalculateBearingDegrees(OwnerUnit.Position.Coordinate, unit.Position.Coordinate).ToCardinalMark(); TerrainLineSummary terrainLineSummary = GetTerrainHeightSummaryToTarget(unit); //No sensor can see through terrain if (terrainLineSummary.MaxHeightM > OwnerUnit.Position.HeightOverSeaLevelM && terrainLineSummary.MaxHeightM > unit.Position.HeightOverSeaLevelM) { return(false); } double targetApparentSizeArcSec = unit.CalculateRadarCorrectedSizeArcSec(DirectionToTarget, distanceM); if (SensorClass.SensorType == GameConstants.SensorType.Visual) { if ((double)OwnerUnit.Position.HeightOverSeaLevelM < GameConstants.DEPTH_PERISCOPE_MIN_M || (double)unit.Position.HeightOverSeaLevelM < GameConstants.DEPTH_SHALLOW_MIN_M) //under sea level { return(false); } double lineOfSightM = MapHelper.CalculateMaxLineOfSightM( (double)OwnerUnit.Position.HeightOverSeaLevelM + OwnerUnit.UnitClass.HeightM, (double)unit.Position.HeightOverSeaLevelM + unit.UnitClass.HeightM); double minimumTargetSizeArcSec = SensorClass.MinimumTargetSurfaceSizeArcSec; if (unit.Position.HeightOverSeaLevelM > 10) { minimumTargetSizeArcSec = SensorClass.MinimumTargetAirSizeArcSec; } if (distanceM <= lineOfSightM && targetApparentSizeArcSec >= minimumTargetSizeArcSec) { double degradationPercent = 0; WeatherSystem wsystem = OwnerUnit.GetWeatherSystem(); if (wsystem != null && SensorClass.SensorType == GameConstants.SensorType.Visual) { degradationPercent = 100 - wsystem.TotalLightPercent; } if (OwnerUnit.Position.HeightOverSeaLevelM < GameConstants.HEIGHT_MEDIUM_MIN_M || unit.Position.HeightOverSeaLevelM < GameConstants.HEIGHT_MEDIUM_MIN_M) //only if any below cloud cover { degradationPercent += GameManager.Instance.GetRadarDegradationFromWeatherPercent(wsystem); } //double MinDetectableSizeArcSec = SensorClass.MinimumTargetSurfaceSizeArcSec; minimumTargetSizeArcSec = minimumTargetSizeArcSec * (1.0 + (degradationPercent / 100)); double DetectionStrength = targetApparentSizeArcSec / minimumTargetSizeArcSec; //(LineOfSightM - DistanceM) / LineOfSightM; DetectedUnit detect = CreateOrUpdateDetectionReport(unit, DetectionStrength, distanceM, targetApparentSizeArcSec, minimumTargetSizeArcSec); return(detect != null); } else { return(false); //does not detect it } } else if (SensorClass.SensorType == GameConstants.SensorType.Infrared) { if ((double)OwnerUnit.Position.HeightOverSeaLevelM < GameConstants.DEPTH_PERISCOPE_MIN_M || (double)unit.Position.HeightOverSeaLevelM < GameConstants.DEPTH_SHALLOW_MIN_M) //under sea level { return(false); } double irDegradationPercent = 0; WeatherSystem weatherSystem = OwnerUnit.GetWeatherSystem(); if (OwnerUnit.Position.HeightOverSeaLevelM < GameConstants.HEIGHT_MEDIUM_MIN_M || unit.Position.HeightOverSeaLevelM < GameConstants.HEIGHT_MEDIUM_MIN_M) //only if any below cloud cover { irDegradationPercent += GameManager.Instance.GetIRDegradationFromWeatherPercent(weatherSystem); } double maxTargetDetectionDistanceM = unit.GetMaxIrDetectionDistanceM(); double LineOfSightM = MapHelper.CalculateMaxLineOfSightM( (double)OwnerUnit.Position.HeightOverSeaLevelM + OwnerUnit.UnitClass.HeightM, (double)unit.Position.HeightOverSeaLevelM) + unit.UnitClass.HeightM; if (maxTargetDetectionDistanceM > LineOfSightM) { maxTargetDetectionDistanceM = LineOfSightM; } double detectionStrength = distanceM / maxTargetDetectionDistanceM; if (detectionStrength > 1.0) { DetectedUnit detect = CreateOrUpdateDetectionReport(unit, detectionStrength, distanceM, 0, 0); return(detect != null); } else { return(false); } } else if (SensorClass.SensorType == GameConstants.SensorType.MAD) { if (unit.Position == null || !unit.Position.HasHeightOverSeaLevel || unit.Position.HeightOverSeaLevelM >= GameConstants.DEPTH_SHALLOW_MIN_M) { return(false); } var unitDepthM = (double)unit.Position.HeightOverSeaLevelM; if (OwnerUnit.Position == null || !OwnerUnit.Position.HasHeightOverSeaLevel || OwnerUnit.Position.HeightOverSeaLevelM >= GameConstants.HEIGHT_MEDIUM_MIN_M) { return(false); } if (unitDepthM <= GameConstants.DEPTH_DEEP_MIN_M) { return(false); } double maxDetectionDistanceM = 1000.0; if (unitDepthM <= GameConstants.DEPTH_MEDIUM_MIN_M) { maxDetectionDistanceM = 500; } double detectionStrength = distanceM / maxDetectionDistanceM; if (detectionStrength > 1.0) { DetectedUnit detect = CreateOrUpdateDetectionReport(unit, detectionStrength, distanceM, 0, 0); return(detect != null); } else { return(false); } } return(false); }