From 8318b6098228b82dd3d177db76569966b205d56f Mon Sep 17 00:00:00 2001 From: Brandon Danyluk Date: Thu, 15 Apr 2021 22:29:02 -0600 Subject: [PATCH] Improve radar simulation --- addons/aim120/CfgAmmo.hpp | 8 +- addons/hellfire/CfgAmmo.hpp | 4 +- addons/missileguidance/ACE_GuidanceConfig.hpp | 14 +- addons/missileguidance/XEH_PREP.hpp | 8 +- .../functions/fnc_doppler_onFired.sqf | 76 +++++++++ ...nc_ahr_onFired.sqf => fnc_mwr_onFired.sqf} | 4 +- .../functions/fnc_seekerType_Doppler.sqf | 160 ++++++++++++++++++ ...kerType_ARH.sqf => fnc_seekerType_MWR.sqf} | 12 +- .../functions/fnc_shouldFilterRadarHit.sqf | 141 +++++++++++++++ 9 files changed, 410 insertions(+), 17 deletions(-) create mode 100644 addons/missileguidance/functions/fnc_doppler_onFired.sqf rename addons/missileguidance/functions/{fnc_ahr_onFired.sqf => fnc_mwr_onFired.sqf} (95%) create mode 100644 addons/missileguidance/functions/fnc_seekerType_Doppler.sqf rename addons/missileguidance/functions/{fnc_seekerType_ARH.sqf => fnc_seekerType_MWR.sqf} (94%) create mode 100644 addons/missileguidance/functions/fnc_shouldFilterRadarHit.sqf diff --git a/addons/aim120/CfgAmmo.hpp b/addons/aim120/CfgAmmo.hpp index a7c1a00959..48dfefdfb6 100644 --- a/addons/aim120/CfgAmmo.hpp +++ b/addons/aim120/CfgAmmo.hpp @@ -15,8 +15,8 @@ class CfgAmmo { canVanillaLock = 1; // Can this default vanilla lock? Only applicable to non-cadet mode // Guidance type for munitions - defaultSeekerType = "ARH"; - seekerTypes[] = { "ARH" }; + defaultSeekerType = "DopplerRadar"; + seekerTypes[] = { "DopplerRadar" }; lockableTypes[] = {"Air"}; defaultSeekerLockMode = "LOAL"; @@ -55,8 +55,8 @@ class CfgAmmo { canVanillaLock = 1; // Can this default vanilla lock? Only applicable to non-cadet mode // Guidance type for munitions - defaultSeekerType = "ARH"; - seekerTypes[] = { "ARH" }; + defaultSeekerType = "DopplerRadar"; + seekerTypes[] = { "DopplerRadar" }; lockableTypes[] = {"Air"}; defaultSeekerLockMode = "LOAL"; diff --git a/addons/hellfire/CfgAmmo.hpp b/addons/hellfire/CfgAmmo.hpp index 98c835104a..afb6b99175 100644 --- a/addons/hellfire/CfgAmmo.hpp +++ b/addons/hellfire/CfgAmmo.hpp @@ -76,8 +76,8 @@ class CfgAmmo { canVanillaLock = 1; enabled = 1; // Missile Guidance must be explicitly enabled seekLastTargetPos = 0; - defaultSeekerType = "ARH"; - seekerTypes[] = { "ARH" }; + defaultSeekerType = "MillimeterWaveRadar"; + seekerTypes[] = { "MillimeterWaveRadar" }; defaultSeekerLockMode = "LOBL"; seekerLockModes[] = { "LOBL" }; diff --git a/addons/missileguidance/ACE_GuidanceConfig.hpp b/addons/missileguidance/ACE_GuidanceConfig.hpp index f05c5527b0..0960e7fab2 100644 --- a/addons/missileguidance/ACE_GuidanceConfig.hpp +++ b/addons/missileguidance/ACE_GuidanceConfig.hpp @@ -80,13 +80,21 @@ class GVAR(SeekerTypes) { functionName = QFUNC(seekerType_SACLOS); onFired = QFUNC(SACLOS_onFired); }; - class ARH { + class MillimeterWaveRadar { name = ""; visualName = ""; description = ""; - functionName = QFUNC(seekerType_ARH); - onFired = QFUNC(ahr_onFired); + functionName = QFUNC(seekerType_MWR); + onFired = QFUNC(mwr_onFired); + }; + class DopplerRadar { + name = ""; + visualName = ""; + description = ""; + + functionName = QFUNC(seekerType_Doppler); + onFired = QFUNC(doppler_onFired); }; class IR { name = ""; diff --git a/addons/missileguidance/XEH_PREP.hpp b/addons/missileguidance/XEH_PREP.hpp index ca0948e1fd..8206cf49f7 100644 --- a/addons/missileguidance/XEH_PREP.hpp +++ b/addons/missileguidance/XEH_PREP.hpp @@ -16,6 +16,8 @@ PREP(doSeekerSearch); PREP(doHandoff); PREP(handleHandoff); +PREP(shouldFilterRadarHit); + // Attack Profiles PREP(attackProfile_AIR); PREP(attackProfile_DIR); @@ -38,7 +40,8 @@ PREP(navigationType_lineOfSight); PREP(seekerType_SALH); PREP(seekerType_Optic); PREP(seekerType_SACLOS); -PREP(seekerType_ARH); +PREP(seekerType_Doppler); +PREP(seekerType_MWR); PREP(seekerType_IR); // Attack Profiles OnFired @@ -46,7 +49,8 @@ PREP(wire_onFired); // Seeker OnFired PREP(SACLOS_onFired); -PREP(ahr_onFired); +PREP(doppler_onFired); +PREP(mwr_onFired); PREP(IR_onFired); // Navigation OnFired diff --git a/addons/missileguidance/functions/fnc_doppler_onFired.sqf b/addons/missileguidance/functions/fnc_doppler_onFired.sqf new file mode 100644 index 0000000000..e50b66e3f3 --- /dev/null +++ b/addons/missileguidance/functions/fnc_doppler_onFired.sqf @@ -0,0 +1,76 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Sets up doppler radar state arrays (called from missileGuidance's onFired). + * + * Arguments: + * Guidance Arg Array + * + * Return Value: + * None + * + * Example: + * [] call ace_missileguidance_fnc_mwr_onFired + * + * Public: No + */ +params ["_firedEH", "_launchParams", "", "", "_stateParams"]; +_firedEH params ["_shooter","","","","","","_projectile"]; +_stateParams params ["", "_seekerStateParams"]; +_launchParams params ["","_targetLaunchParams"]; +_targetLaunchParams params ["_target"]; + +_target = missileTarget _projectile; +if (isNull _target && isVehicleRadarOn vehicle _shooter) then { + _target = cursorTarget; +}; +if !(_target isKindOf "AllVehicles") then { + _target = nil; +}; +_launchParams set [0, _target]; +_projectile setMissileTarget objNull; // to emulate a no launch warning + +private _projectileConfig = configOf _projectile; +private _config = _projectileConfig >> "ace_missileguidance"; + +private _isActive = false; +private _activeRadarDistance = [_config >> "activeRadarEngageDistance", "NUMBER", 500] call CBA_fnc_getConfigEntry; +private _projectileThrust = [_projectileConfig >> "thrust", "NUMBER", 0] call CBA_fnc_getConfigEntry; +private _projectileThrustTime = [_projectileConfig >> "thrustTime", "NUMBER", 0] call CBA_fnc_getConfigEntry; + +private _lockTypes = [_config >> "lockableTypes", "ARRAY", ["Air", "LandVehicle", "Ship"]] call CBA_fnc_getConfigEntry; + +private _velocityAtImpact = _projectileThrust * _projectileThrustTime; +private _timeToActive = 0; +if (!isNil "_target" && _velocityAtImpact > 0) then { + private _distanceUntilActive = (((getPosASL _shooter) vectorDistance (getPosASL _target)) - _activeRadarDistance); + _timeToActive = 0 max (_distanceUntilActive / _velocityAtImpact); +}; + +if (isNil "_target") then { + _timeToActive = 0; + _isActive = true; + _target = objNull; +}; + +private _shooterHasActiveRadar = { + if ("ActiveRadarSensorComponent" in _x) exitWith { true }; + false +} forEach listVehicleSensors vehicle _shooter; + +if !(isVehicleRadarOn vehicle _shooter) then { + _isActive = true; +}; + +_seekerStateParams set [0, _isActive]; +_seekerStateParams set [1, _activeRadarDistance]; +_seekerStateParams set [2, CBA_missionTime + _timeToActive]; +_seekerStateParams set [3, getPosASL _target]; +_seekerStateParams set [4, CBA_missionTime]; +_seekerStateParams set [5, _shooterHasActiveRadar]; +_seekerStateParams set [6, false]; +_seekerStateParams set [7, [0, 0, 0]]; +_seekerStateParams set [8, CBA_missionTime]; +_seekerStateParams set [9, isNull _target]; +_seekerStateParams set [10, _lockTypes]; + diff --git a/addons/missileguidance/functions/fnc_ahr_onFired.sqf b/addons/missileguidance/functions/fnc_mwr_onFired.sqf similarity index 95% rename from addons/missileguidance/functions/fnc_ahr_onFired.sqf rename to addons/missileguidance/functions/fnc_mwr_onFired.sqf index 3b0183d70f..d9d11d9814 100644 --- a/addons/missileguidance/functions/fnc_ahr_onFired.sqf +++ b/addons/missileguidance/functions/fnc_mwr_onFired.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Author: Brandon (TCVM) - * Sets up Active Radar state arrays (called from missileGuidance's onFired). + * Sets up MWR state arrays (called from missileGuidance's onFired). * * Arguments: * Guidance Arg Array @@ -10,7 +10,7 @@ * None * * Example: - * [] call ace_missileguidance_fnc_ahr_onFired + * [] call ace_missileguidance_fnc_mwr_onFired * * Public: No */ diff --git a/addons/missileguidance/functions/fnc_seekerType_Doppler.sqf b/addons/missileguidance/functions/fnc_seekerType_Doppler.sqf new file mode 100644 index 0000000000..b70bfe38af --- /dev/null +++ b/addons/missileguidance/functions/fnc_seekerType_Doppler.sqf @@ -0,0 +1,160 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * doppler radar seeker + * + * Arguments: + * 1: Guidance Arg Array + * 2: Seeker State + * + * Return Value: + * Seeker Pos + * + * Example: + * [] call call ace_missileguidance_fnc_seekerType_MWR; + * + * Public: No + */ +params ["", "_args", "_seekerStateParams", "", "_timestep"]; +_args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams", "_targetData"]; +_firedEH params ["_shooter","","","","","","_projectile"]; +_launchParams params ["_target","","","",""]; +_seekerParams params ["_seekerAngle", "", "_seekerMaxRange"]; +_seekerStateParams params ["_isActive", "_activeRadarEngageDistance", "_timeWhenActive", "_expectedTargetPos", "_lastTargetPollTime", "_shooterHasRadar", "_wasActive", "_lastKnownVelocity", "_lastTimeSeen", "_doesntHaveTarget", "_lockTypes"]; + +private _minimumFilterSpeed = 25; +private _minimumFilterTime = 1e-4; +private _maxTerrainCheck = 16000; + +if (_isActive || { CBA_missionTime >= _timeWhenActive }) then { + if !(_isActive) then { + _seekerStateParams set [0, true]; + }; + if !(_wasActive) then { + _seekerStateParams set [6, true]; + TRACE_1("Missile Pitbull",_seekerStateParams); + }; + + // Internal radar homing + // For performance reasons only poll for target every so often instead of each frame + if ((_lastTargetPollTime + ACTIVE_RADAR_POLL_FREQUENCY) - CBA_missionTime < 0) then { + private _searchPos = _expectedTargetPos; + if (_searchPos isEqualTo [0, 0, 0] || { _doesntHaveTarget }) then { + _seekerStateParams set [9, true]; + // no target pos - shot without lock. Have the missile's radar search infront of it on the ground + _searchPos = (getPosASL _projectile) vectorAdd (_projectile vectorModelToWorld [0, _seekerMaxRange, -((getPos _projectile)#2)]); + }; + + _target = objNull; + _lastTargetPollTime = CBA_missionTime; + _seekerStateParams set [4, _lastTargetPollTime]; + private _distanceToExpectedTarget = _seekerMaxRange min ((getPosASL _projectile) vectorDistance _searchPos); + + // Simulate how much the seeker can see at the ground + private _projDir = vectorDir _projectile; + private _projYaw = getDir _projectile; + private _rotatedYaw = (+(_projDir select 0) * sin _projYaw) + (+(_projDir select 1) * cos _projYaw); + if (_rotatedYaw isEqualTo 0) then { _rotatedYaw = 0.001 }; + private _projPitch = atan ((_projDir select 2) / _rotatedYaw); + private _a1 = abs _projPitch; + private _a2 = 180 - ((_seekerAngle / 2) + _a1); + private _seekerBaseRadiusAtGround = ACTIVE_RADAR_MINIMUM_SCAN_AREA max (_distanceToExpectedTarget / sin(_a2) * sin(_seekerAngle / 2)); + + private _lastKnownSpeed = if (_lastKnownVelocity isEqualTo [0, 0, 0]) then { + 0 + } else { + vectorMagnitude _lastKnownVelocity; + }; + private _seekerBaseRadiusAdjusted = linearConversion [0, _seekerBaseRadiusAtGround, (CBA_missionTime - _lastTimeSeen) * vectorMagnitude _lastKnownVelocity, ACTIVE_RADAR_MINIMUM_SCAN_AREA, _seekerBaseRadiusAtGround, false]; + if (_doesntHaveTarget) then { + _seekerBaseRadiusAdjusted = _seekerBaseRadiusAtGround; + }; + // Look in front of seeker for any targets + private _nearestObjects = nearestObjects [ASLtoAGL _searchPos, _lockTypes, _seekerBaseRadiusAdjusted, false]; + _nearestObjects = _nearestObjects apply { + // I check both Line of Sight versions to make sure that a single bush doesnt make the target lock dissapear but at the same time ensure that this can see through smoke. Should work 80% of the time + if ([_projectile, getPosASL _x, _seekerAngle] call FUNC(checkSeekerAngle) && { ([_projectile, _x, true] call FUNC(checkLOS)) || { ([_projectile, _x, false] call FUNC(checkLOS)) } }) then { + + if !([_x, _projectile, _minimumFilterSpeed, _minimumFilterTime, _maxTerrainCheck, _seekerAngle] call FUNC(shouldFilterRadarHit)) exitWith { + _x + }; + objNull; + } else { + objNull + }; + }; + _nearestObjects = _nearestObjects select { !isNull _x }; + // Select closest object to the expected position to be the current radar target + if ((count _nearestObjects) <= 0) exitWith { + _projectile setMissileTarget objNull; + _seekerStateParams set [3, _searchPos]; + _searchPos + }; + private _closestDistance = _seekerBaseRadiusAtGround; + { + if ((_x distance2d _searchPos) < _closestDistance) then { + _closestDistance = _x distance2d _searchPos; + _target = _x; + }; + } forEach _nearestObjects; + + _expectedTargetPos = _searchPos; + }; +} else { + #ifdef DRAW_GUIDANCE_INFO + _seekerTypeName = "DOPPLER - EXT"; + #endif + // External radar homing + // if the target is in the remote targets for the side, whoever the donor is will "datalink" the target for the hellfire. + private _remoteTargets = listRemoteTargets side _shooter; + if ((_remoteTargets findIf { (_target in _x) && (_x#1 > 0) }) < 0) then { + // I check both Line of Sight versions to make sure that a single bush doesnt make the target lock dissapear but at the same time ensure that this can see through smoke. Should work 80% of the time + if (!_shooterHasRadar || { !isVehicleRadarOn vehicle _shooter } || { !alive vehicle _shooter } || { !([vehicle _shooter, _target, true] call FUNC(checkLOS)) && { !([vehicle _shooter, _target, false] call FUNC(checkLOS)) } }) then { + _seekerStateParams set [0, true]; + _target = objNull; // set up state for active guidance + }; + }; +}; + +#ifdef DRAW_GUIDANCE_INFO +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], ASLtoAGL _expectedTargetPos, 0.75, 0.75, 0, "expected target pos", 1, 0.025, "TahomaB"]; +#endif + +if !(isNull _target) then { + // we check if the target is moving away from us or perpendicular to see if we maintain lock + private _centerOfObject = getCenterOfMass _target; + private _targetAdjustedPos = _target modelToWorldVisualWorld _centerOfObject; + _expectedTargetPos = _targetAdjustedPos; + + private _filterTarget = [_target, _projectile, _minimumFilterSpeed, _minimumFilterTime, _maxTerrainCheck, _seekerAngle] call FUNC(shouldFilterRadarHit); + if (_filterTarget) then { + // filter out target + _target = objNull; + _seekerStateParams set [9, true]; + } else { + private _lineOfSight = vectorDirVisual _projectile; + private _realTargetVelocity = velocity _target; + private _projectedTargetVelocity = _lineOfSight vectorMultiply (_realTargetVelocity vectorDotProduct _lineOfSight); + private _relativeTargetVelocity = _realTargetVelocity vectorDiff _projectedTargetVelocity; + + _seekerStateParams set [7, _relativeTargetVelocity]; + _seekerStateParams set [8, CBA_missionTime]; + _seekerStateParams set [9, false]; + + _targetData set [2, _projectile distance _target]; + _targetData set [3, _relativeTargetVelocity]; + + if (_timestep != 0) then { + private _acceleration = (_relativeTargetVelocity vectorDiff _lastKnownVelocity) vectorMultiply (1 / _timestep); + _targetData set [4, _acceleration]; + }; + }; + _projectile setMissileTarget _target; +}; + +_targetData set [0, (getPosASLVisual _projectile) vectorFromTo _expectedTargetPos]; + +_seekerStateParams set [3, _expectedTargetPos]; +_launchParams set [0, _target]; +_expectedTargetPos + diff --git a/addons/missileguidance/functions/fnc_seekerType_ARH.sqf b/addons/missileguidance/functions/fnc_seekerType_MWR.sqf similarity index 94% rename from addons/missileguidance/functions/fnc_seekerType_ARH.sqf rename to addons/missileguidance/functions/fnc_seekerType_MWR.sqf index 42fa70680f..90047b2e46 100644 --- a/addons/missileguidance/functions/fnc_seekerType_ARH.sqf +++ b/addons/missileguidance/functions/fnc_seekerType_MWR.sqf @@ -1,7 +1,7 @@ #include "script_component.hpp" /* * Author: Brandon (TCVM) - * Active Radar Homing seeker + * millimeter wave radar seeker * * Arguments: * 1: Guidance Arg Array @@ -11,11 +11,11 @@ * Seeker Pos * * Example: - * [] call call ace_missileguidance_fnc_seekerType_ARH; + * [] call call ace_missileguidance_fnc_seekerType_MWR; * * Public: No */ -params ["", "_args", "_seekerStateParams"]; +params ["", "_args", "_seekerStateParams", "", "_timestep"]; _args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams", "_targetData"]; _firedEH params ["_shooter","","","","","","_projectile"]; _launchParams params ["_target","","","",""]; @@ -119,7 +119,11 @@ if !(isNull _target) then { _targetData set [2, _projectile distance _target]; _targetData set [3, velocity _target]; - _targetData set [4, [0, 0, 0]]; // todo: acceleration + + if (_timestep != 0) then { + private _acceleration = ((velocity _target) vectorDiff _lastKnownVelocity) vectorMultiply (1 / _timestep); + _targetData set [4, _acceleration]; + }; }; _targetData set [0, (getPosASLVisual _projectile) vectorFromTo _expectedTargetPos]; diff --git a/addons/missileguidance/functions/fnc_shouldFilterRadarHit.sqf b/addons/missileguidance/functions/fnc_shouldFilterRadarHit.sqf new file mode 100644 index 0000000000..bf3115aaaa --- /dev/null +++ b/addons/missileguidance/functions/fnc_shouldFilterRadarHit.sqf @@ -0,0 +1,141 @@ +#include "script_component.hpp" +/* + * Author: Brandon (TCVM) + * Whether or not we should filter out this target due to masking. Useful for only doppler radar types + * + * Arguments: + * 0: Target + * 1: Projectile + * 2: Minimum speed for doppler return to filter out + * 3: Minimum time for doppler return to filter out + * 4: Maximum distance to check for terrain + * 5: Radar seeker angle + * + * Return Value: + * Whether or not to filter out this target + * + * Example: + * [] call call ace_missileguidance_fnc_shouldFilterRadarHit; + * + * Public: Yes + */ +// as defined by lineIntersectsSurfaces +#define MAX_LINE_DISTANCE 5000 + +params ["_target", "_projectile", "_minimumSpeed", "_minimumTime", "_maximumTerrainCheck", "_seekerAngle"]; +TRACE_5("should filter target",_projectile,_target,_minimumSpeed,_minimumTime,_maximumTerrainCheck); +// Theory developed from doppler radar visuals at https://www.mudspike.com/dcs-f-15c-combat-guide-for-beginners/ + +// helicopter blades will always produce a doppler shift due to their nature. Don't filter +if (_target isKindOf "Helicopter" && isEngineOn _target) exitWith { + TRACE_2("dont filter helicopters",_target isKindOf "Helicopter",isEngineOn _target); + false +}; + +private _lineOfSight = (getPosASLVisual _projectile) vectorFromTo (getPosASLVisual _target); +private _realTargetVelocity = velocity _target; +private _closingVelocity = _lineOfSight vectorMultiply (_realTargetVelocity vectorDotProduct _lineOfSight); +private _closingSpeed = vectorMagnitude _closingVelocity; + +// if relative target velocity is greather than threshold, we can easily see it. Don't filter +if (_closingSpeed > _minimumSpeed) exitWith { + TRACE_2("dont filter fast objects approaching",_closingSpeed,_minimumSpeed); + false +}; + +private _projectilePos = getPosASLVisual _projectile; + +private _stepDistance = _lineOfSight vectorMultiply (MAX_LINE_DISTANCE min _maximumTerrainCheck); + +private _checkPos = _projectilePos; +private _maskedByGround = false; + +// Check for all surfaces until we reach our max range +for "_i" from 0 to _maximumTerrainCheck step MAX_LINE_DISTANCE do { + // determine if target is masked by ground + private _endPos = _checkPos vectorAdd _stepDistance; + private _groundHit = lineIntersectsSurfaces [_checkPos, _endPos, _projectile, _target]; + + _maskedByGround = _groundHit isNotEqualTo []; + + _checkPos = _endPos; + + if (_maskedByGround || (_checkPos select 2) <= 0) then { + // for ease assume that we can't check underwater + if ((_checkPos select 2) < 0) then { + _checkPos set [2, 0]; + }; + break + }; +}; + +// looking at sky, target is clear as day. Check for chaff before filtering +if !(_maskedByGround) exitWith { + TRACE_1("dont filter stuff in the sky",_maskedByGround); + + // If there is chaff nearby, check if they will confuse missile + private _nearby = _target nearObjects 50; + _nearby = _nearby select { + // 8 = radar blocking + private _blocking = configOf _x >> "weaponLockSystem"; + private _isChaff = false; + if (isNumber _blocking) then { + _isChaff = (8 == getNumber _blocking); + }; + + if (isText _blocking) then { + _isChaff = ("8" in getText _blocking); + }; + + private _withinView = [_projectile, getPosASLVisual _x, _seekerAngle] call FUNC(checkSeekerAngle); + private _canSee = [_projectile, _x, false] call FUNC(checkLos); + + { (_withinView && _canSee && _isChaff) } + }; + + private _foundDecoy = false; + private _projectileVelocity = velocity _projectile; + { + private _considering = false; + + private _distanceToFlare = _target distanceSqr _x; + if !(_foundDecoy) then { + private _chaffRelativeVelocity = (velocity _x) vectorDiff _projectileVelocity; + private _angleBetweenVelocities = acos (_closingVelocity vectorCos _chaffRelativeVelocity); + + if (_angleBetweenVelocities <= 2) then { + _considering = true; + if (0.95 <= random 1) then { + _foundDecoy = true; + }; + }; + }; + + #ifdef DRAW_GUIDANCE_INFO + private _chaffPos = ASLToAGL getPosASLVisual _x; + private _colour = [1, 0, 0, 1]; + if (_considering) then { + _colour = [0, 1, 0, 1]; + }; + if (_foundDecoy) then { + _colour = [0, 0, 1, 1]; + }; + drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", _colour, _chaffPos, 0.75, 0.75, 0, "F", 1, 0.025, "TahomaB"]; + #endif + } forEach _nearby; + + _foundDecoy +}; + +private _distanceToTerrain = _checkPos vectorDistance _projectilePos; +private _checkTime = _distanceToTerrain / 3e8; + +// Time to ground is large enough to know if we are looking at a target, don't filter +if (_checkTime > _minimumTime) exitWith { + TRACE_2("dont filter targets that we can see in ground clutter",_checkTime,_minimumTime); + false +}; + +// filter out the target since we can't reasonably see it +true +