2023-09-12 18:58:10 +00:00
|
|
|
#include "..\script_component.hpp"
|
2016-05-30 16:37:03 +00:00
|
|
|
/*
|
|
|
|
* Author: Nou
|
2016-10-08 10:55:30 +00:00
|
|
|
* Searches for a laser spot given a seekers params.
|
|
|
|
* Provides the interface for Missile Guidance
|
2016-05-30 16:37:03 +00:00
|
|
|
*
|
|
|
|
* Arguments:
|
2016-10-08 10:55:30 +00:00
|
|
|
* 0: Position of seeker (ASL) <ARRAY>
|
|
|
|
* 1: Direction vector (will be normalized) <ARRAY>
|
|
|
|
* 2: Seeker FOV in degrees <NUMBER>
|
2016-10-12 22:35:24 +00:00
|
|
|
* 3: Seeker max distance in meters <NUMBER>
|
2023-11-06 20:16:03 +00:00
|
|
|
* 4: Seeker wavelength sensitivity range, [1550,1550] is common <ARRAY>
|
2016-10-12 22:35:24 +00:00
|
|
|
* 5: Seeker laser code. <NUMBER>
|
2017-09-22 15:33:08 +00:00
|
|
|
* 6: Ignore 1 (e.g. Player's vehicle) <OBJECT> (default: objNull)
|
2023-07-28 04:03:02 +00:00
|
|
|
* 7: Ignore 2 (e.g. Attached object) <OBJECT> (default: objNull)
|
|
|
|
* 8: Owners to ignore (e.g. Player's vehicle) <ARRAY of OBJECT> (default: [])
|
2016-05-30 16:37:03 +00:00
|
|
|
*
|
2016-06-18 09:50:41 +00:00
|
|
|
* Return Value:
|
2017-09-22 15:33:08 +00:00
|
|
|
* [Strongest compatible laser spot ASL pos, owner object] Nil array values if nothing found <ARRAY>
|
2016-10-08 10:55:30 +00:00
|
|
|
*
|
|
|
|
* Example:
|
2023-11-06 20:16:03 +00:00
|
|
|
* [getPosASL player, [0,1,0], 90, [1550, 1550], 1111, player] call ace_laser_fnc_seekerFindLaserSpot
|
2016-10-08 10:55:30 +00:00
|
|
|
*
|
|
|
|
* Public: No
|
2016-05-30 16:37:03 +00:00
|
|
|
*/
|
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
BEGIN_COUNTER(seekerFindLaserSpot);
|
2016-05-30 16:37:03 +00:00
|
|
|
|
2023-07-28 04:03:02 +00:00
|
|
|
params ["_posASL", "_dir", "_seekerFov", "_seekerMaxDistance", "_seekerWavelengths", "_seekerCode", ["_ignoreObj1", objNull], ["_ignoreObj2", objNull], ["_ignoreOwners", []]];
|
2016-05-30 16:37:03 +00:00
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
_dir = vectorNormalized _dir;
|
|
|
|
_seekerWavelengths params ["_seekerWavelengthMin", "_seekerWavelengthMax"];
|
2016-10-12 22:35:24 +00:00
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
private _seekerCos = cos _seekerFov;
|
2017-08-03 17:50:29 +00:00
|
|
|
private _seekerMaxDistSq = _seekerMaxDistance ^ 2;
|
2016-05-30 16:37:03 +00:00
|
|
|
|
2017-08-03 17:50:29 +00:00
|
|
|
TRACE_6("",_posASL,_dir,_seekerFov,_seekerMaxDistance,_seekerWavelengths,_seekerCode);
|
2016-05-30 16:37:03 +00:00
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
private _spots = [];
|
|
|
|
private _finalPos = nil;
|
|
|
|
private _finalOwner = objNull;
|
2016-05-30 16:37:03 +00:00
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
// Go through all lasers in GVAR(laserEmitters)
|
2016-05-30 16:37:03 +00:00
|
|
|
{
|
2016-10-08 10:55:30 +00:00
|
|
|
_x params ["_obj", "_owner", "_laserMethod", "_emitterWavelength", "_laserCode", "_divergence"];
|
|
|
|
TRACE_6("laser",_obj,_owner,_laserMethod,_emitterWavelength,_laserCode,_divergence);
|
|
|
|
|
2023-07-28 04:03:02 +00:00
|
|
|
if (_owner in _ignoreOwners) then {continue};
|
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
if (alive _obj && {_emitterWavelength >= _seekerWavelengthMin} && {_emitterWavelength <= _seekerWavelengthMax} && {_laserCode == _seekerCode}) then {
|
|
|
|
|
|
|
|
private _laser = [];
|
|
|
|
// Find laser pos and dir of the laser depending on type
|
|
|
|
if (IS_STRING(_laserMethod)) then {
|
|
|
|
_laser = _x call (missionNamespace getVariable [_laserMethod, []]);
|
2016-05-30 16:37:03 +00:00
|
|
|
} else {
|
2016-10-08 10:55:30 +00:00
|
|
|
if (IS_CODE(_laserMethod)) then {
|
|
|
|
_laser = _x call _laserMethod;
|
2016-05-30 16:37:03 +00:00
|
|
|
} else {
|
2016-10-08 10:55:30 +00:00
|
|
|
|
|
|
|
if (IS_ARRAY(_laserMethod)) then {
|
|
|
|
if (count _laserMethod == 2) then { // [modelPosition, weaponName] for _obj
|
2021-10-10 13:37:05 +00:00
|
|
|
_laser = [_obj modelToWorldVisualWorld (_laserMethod select 0), _obj weaponDirection (_laserMethod select 1)];
|
2016-05-30 16:37:03 +00:00
|
|
|
} else {
|
2016-10-08 10:55:30 +00:00
|
|
|
if (count _laserMethod == 3) then {
|
2021-10-10 13:37:05 +00:00
|
|
|
_laser = [_obj modelToWorldVisualWorld (_laserMethod select 0), (_obj modelToWorldVisualWorld (_laserMethod select 1)) vectorFromTo (_obj modelToWorldVisualWorld (_laserMethod select 2))];
|
2016-05-30 16:37:03 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
TRACE_1("",_laser);
|
|
|
|
//Handle Weird Data Return - skips over this laser in the for loop
|
2024-08-24 17:55:45 +00:00
|
|
|
if ((_laser isEqualTo []) || {_laser isEqualTo [-1, -1]}) exitWith {WARNING_1("Bad Laser Return %1",_laser);};
|
2016-10-08 10:55:30 +00:00
|
|
|
_laser params [["_laserPos", [], [[]], 3], ["_laserDir", [], [[]], 3]];
|
|
|
|
|
|
|
|
if (GVAR(dispersionCount) > 0) then {
|
|
|
|
// Shoot a cone with dispersion
|
|
|
|
([_laserPos, _laserDir, _divergence, GVAR(dispersionCount), _obj] call FUNC(shootCone)) params ["", "", "_resultPositions"];
|
2016-05-30 16:37:03 +00:00
|
|
|
{
|
2017-05-31 21:09:36 +00:00
|
|
|
private _testPoint = _x select 0;
|
2016-10-08 10:55:30 +00:00
|
|
|
private _testPointVector = _posASL vectorFromTo _testPoint;
|
|
|
|
private _testDotProduct = _dir vectorDotProduct _testPointVector;
|
2016-10-12 22:35:24 +00:00
|
|
|
if ((_testDotProduct > _seekerCos) && {(_testPoint vectorDistanceSqr _posASL) < _seekerMaxDistSq}) then {
|
2016-05-30 16:37:03 +00:00
|
|
|
_spots pushBack [_testPoint, _owner];
|
|
|
|
};
|
2016-10-08 10:55:30 +00:00
|
|
|
} forEach _resultPositions;
|
|
|
|
} else {
|
|
|
|
// Shoot a single perfect ray from source to target (note, increased chance to "miss" on weird objects like bushes / rocks)
|
|
|
|
([_laserPos, _laserDir, _obj] call FUNC(shootRay)) params ["_resultPos", "_distance"];
|
|
|
|
TRACE_2("spot",_resultPos,_distance);
|
|
|
|
if (_distance > 0) then {
|
|
|
|
private _testPointVector = _posASL vectorFromTo _resultPos;
|
|
|
|
private _testDotProduct = _dir vectorDotProduct _testPointVector;
|
2017-05-31 21:09:36 +00:00
|
|
|
if ((_testDotProduct > _seekerCos) && {(_resultPos vectorDistanceSqr _posASL) < _seekerMaxDistSq}) then {
|
2016-10-08 10:55:30 +00:00
|
|
|
_spots pushBack [_resultPos, _owner];
|
|
|
|
};
|
|
|
|
};
|
2016-05-30 16:37:03 +00:00
|
|
|
};
|
|
|
|
};
|
2021-10-16 18:29:57 +00:00
|
|
|
} forEach (values GVAR(laserEmitters)); // Go through all values in hash
|
2016-10-08 10:55:30 +00:00
|
|
|
|
2024-02-05 17:04:24 +00:00
|
|
|
TRACE_2("",count _spots,_spots);
|
2016-10-08 10:55:30 +00:00
|
|
|
|
2024-04-06 18:57:08 +00:00
|
|
|
if (_spots isNotEqualTo []) then {
|
2016-10-08 10:55:30 +00:00
|
|
|
private _bucketList = nil;
|
|
|
|
private _bucketPos = nil;
|
|
|
|
private _c = 0;
|
|
|
|
private _buckets = [];
|
|
|
|
private _excludes = [];
|
|
|
|
private _bucketIndex = 0;
|
2016-05-30 16:37:03 +00:00
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
// Put close points together into buckets
|
2016-05-30 16:37:03 +00:00
|
|
|
while { count(_spots) != count(_excludes) && _c < (count _spots) } do {
|
|
|
|
scopeName "mainSearch";
|
|
|
|
{
|
2024-06-18 14:08:03 +00:00
|
|
|
if !(_forEachIndex in _excludes) then {
|
2016-10-08 10:55:30 +00:00
|
|
|
private _index = _buckets pushBack [_x, [_x]];
|
2016-05-30 16:37:03 +00:00
|
|
|
_excludes pushBack _forEachIndex;
|
|
|
|
_bucketPos = _x select 0;
|
|
|
|
_bucketList = (_buckets select _index) select 1;
|
|
|
|
breakTo "mainSearch";
|
|
|
|
};
|
|
|
|
} forEach _spots;
|
|
|
|
{
|
2024-06-18 14:08:03 +00:00
|
|
|
if !(_forEachIndex in _excludes) then {
|
2016-10-08 10:55:30 +00:00
|
|
|
private _testPos = (_x select 0);
|
|
|
|
if ((_testPos vectorDistanceSqr _bucketPos) <= 100) then {
|
2016-05-30 16:37:03 +00:00
|
|
|
_bucketList pushBack _x;
|
|
|
|
_excludes pushBack _forEachIndex;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
} forEach _spots;
|
|
|
|
_c = _c + 1;
|
|
|
|
};
|
2016-10-08 10:55:30 +00:00
|
|
|
|
|
|
|
TRACE_1("",_buckets);
|
|
|
|
|
|
|
|
private _finalBuckets = [];
|
|
|
|
private _largest = -1;
|
|
|
|
private _largestIndex = 0;
|
2016-05-30 16:37:03 +00:00
|
|
|
{
|
2016-10-08 10:55:30 +00:00
|
|
|
// find bucket with largest number of points we can see
|
|
|
|
private _index = _finalBuckets pushBack [];
|
2016-05-30 16:37:03 +00:00
|
|
|
_bucketList = _finalBuckets select _index;
|
|
|
|
{
|
2016-10-08 10:55:30 +00:00
|
|
|
private _testPos = (_x select 0) vectorAdd [0,0,0.05];
|
2023-07-28 04:03:02 +00:00
|
|
|
private _testIntersections = lineIntersectsSurfaces [_posASL, _testPos, _ignoreObj1, _ignoreObj2];
|
2016-10-08 10:55:30 +00:00
|
|
|
if ([] isEqualTo _testIntersections) then {
|
2016-05-30 16:37:03 +00:00
|
|
|
_bucketList pushBack _x;
|
|
|
|
};
|
|
|
|
} forEach (_x select 1);
|
2016-10-08 10:55:30 +00:00
|
|
|
if ((count _bucketList) > _largest) then {
|
2016-05-30 16:37:03 +00:00
|
|
|
_largest = (count _bucketList);
|
|
|
|
_largestIndex = _index;
|
|
|
|
};
|
|
|
|
} forEach _buckets;
|
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
private _finalBucket = _finalBuckets select _largestIndex;
|
2021-10-10 16:55:14 +00:00
|
|
|
private _ownersHash = createHashMap;
|
2016-10-08 10:55:30 +00:00
|
|
|
|
|
|
|
TRACE_2("",_finalBucket,_finalBuckets);
|
2016-05-30 16:37:03 +00:00
|
|
|
|
2016-10-08 10:55:30 +00:00
|
|
|
if (count _finalBucket > 0) then {
|
|
|
|
// merge all points in the best bucket into an average point and find effective owner
|
|
|
|
_finalPos = [0,0,0];
|
2016-05-30 16:37:03 +00:00
|
|
|
{
|
2016-10-08 10:55:30 +00:00
|
|
|
_x params ["_xPos", "_owner"];
|
|
|
|
_finalPos = _finalPos vectorAdd _xPos;
|
2022-04-06 22:02:39 +00:00
|
|
|
private _value = _ownersHash getOrDefault [hashValue _owner, [0, _owner]];
|
|
|
|
_value set [0, 1 + _value#0];
|
|
|
|
_ownersHash set [hashValue _owner, _value];
|
2016-05-30 16:37:03 +00:00
|
|
|
} forEach _finalBucket;
|
2016-10-08 10:55:30 +00:00
|
|
|
|
|
|
|
_finalPos = _finalPos vectorMultiply (1 / (count _finalBucket));
|
|
|
|
|
|
|
|
private _maxOwnerCount = -1;
|
|
|
|
|
2021-10-10 16:55:14 +00:00
|
|
|
{
|
|
|
|
//IGNORE_PRIVATE_WARNING ["_x", "_y"];
|
2022-04-06 22:02:39 +00:00
|
|
|
_y params ["_count", "_owner"];
|
|
|
|
if (_count > _maxOwnerCount) then {
|
|
|
|
_maxOwnerCount = _count;
|
|
|
|
_finalOwner = _owner;
|
2016-05-30 16:37:03 +00:00
|
|
|
};
|
2021-10-10 16:55:14 +00:00
|
|
|
} forEach _ownersHash;
|
2016-05-30 16:37:03 +00:00
|
|
|
};
|
|
|
|
};
|
2016-10-08 10:55:30 +00:00
|
|
|
|
|
|
|
END_COUNTER(seekerFindLaserSpot);
|
|
|
|
|
|
|
|
#ifdef DRAW_LASER_INFO
|
|
|
|
if (isNil "_finalPos") then {
|
2017-05-31 21:09:36 +00:00
|
|
|
drawIcon3D ["\A3\ui_f\data\map\vehicleicons\iconMan_ca.paa", [0.9,1,0,1], (ASLtoAGL _posASL), 1, 1, 0, format ["Seeker: %1", _seekerCode], 0.5, 0.025, "TahomaB"];
|
2016-10-08 10:55:30 +00:00
|
|
|
} else {
|
2017-05-31 21:09:36 +00:00
|
|
|
drawIcon3D ["\A3\ui_f\data\map\vehicleicons\iconManAT_ca.paa", [0.5,1,0,1], (ASLtoAGL _posASL), 1, 1, 0, format ["Seeker: %1", _seekerCode], 0.5, 0.025, "TahomaB"];
|
2016-10-08 10:55:30 +00:00
|
|
|
drawLine3D [ASLtoAGL _posASL, ASLtoAGL _finalPos, [0.5,1,0,1]];
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TRACE_2("return",_finalPos,_finalOwner);
|
|
|
|
if (isNil "_finalPos") exitWith {[nil, _finalOwner]};
|
|
|
|
[_finalPos, _finalOwner];
|