mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
a0b5f9f2d5
* Workaround for laser max range. Made laser hud seeker use ammo values. * Nested ifs for optimization of check
203 lines
8.0 KiB
Plaintext
203 lines
8.0 KiB
Plaintext
/*
|
|
* Author: Nou
|
|
* Searches for a laser spot given a seekers params.
|
|
* Provides the interface for Missile Guidance
|
|
*
|
|
* Arguments:
|
|
* 0: Position of seeker (ASL) <ARRAY>
|
|
* 1: Direction vector (will be normalized) <ARRAY>
|
|
* 2: Seeker FOV in degrees <NUMBER>
|
|
* 3: Seeker max distance in meters <NUMBER>
|
|
* 4: Seeker wavelength sensitivity range, [1550,1550] is common eye safe. <ARRAY>
|
|
* 5: Seeker laser code. <NUMBER>
|
|
* 6: Ignore 1 (e.g. Player's vehicle) <OPTIONAL><OBJECT>
|
|
*
|
|
* Return Value:
|
|
* [Strongest compatible laser spot ASL pos, owner object] Nil array values if nothing found. <ARRAY>
|
|
*
|
|
* Example:
|
|
* [getPosASL player, [0,1,0], 90, [1500, 1500], 1111, player] call ace_laser_fnc_seekerFindLaserSpot;
|
|
*
|
|
* Public: No
|
|
*/
|
|
// #define DEBUG_MODE_FULL
|
|
#include "script_component.hpp"
|
|
|
|
BEGIN_COUNTER(seekerFindLaserSpot);
|
|
|
|
params ["_posASL", "_dir", "_seekerFov", "_seekerMaxDistance", "_seekerWavelengths", "_seekerCode", ["_ignoreObj1", objNull]];
|
|
|
|
_dir = vectorNormalized _dir;
|
|
_seekerWavelengths params ["_seekerWavelengthMin", "_seekerWavelengthMax"];
|
|
|
|
private _seekerCos = cos _seekerFov;
|
|
private _seekerMaxDistSq = _seekerMaxDistance ^ 2;
|
|
|
|
TRACE_6("",_posASL,_dir,_seekerFov,_seekerMaxDistance,_seekerWavelengths,_seekerCode);
|
|
|
|
private _spots = [];
|
|
private _finalPos = nil;
|
|
private _finalOwner = objNull;
|
|
|
|
// Go through all lasers in GVAR(laserEmitters)
|
|
{
|
|
_x params ["_obj", "_owner", "_laserMethod", "_emitterWavelength", "_laserCode", "_divergence"];
|
|
TRACE_6("laser",_obj,_owner,_laserMethod,_emitterWavelength,_laserCode,_divergence);
|
|
|
|
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, []]);
|
|
} else {
|
|
if (IS_CODE(_laserMethod)) then {
|
|
_laser = _x call _laserMethod;
|
|
} else {
|
|
|
|
if (IS_ARRAY(_laserMethod)) then {
|
|
if (count _laserMethod == 2) then { // [modelPosition, weaponName] for _obj
|
|
_laser = [AGLtoASL (_obj modelToWorldVisual (_laserMethod select 0)), _obj weaponDirection (_laserMethod select 1)];
|
|
} else {
|
|
if (count _laserMethod == 3) then {
|
|
_laser = [AGLtoASL (_obj modelToWorldVisual (_laserMethod select 0)), (AGLtoASL (_obj modelToWorldVisual (_laserMethod select 1))) vectorFromTo (AGLtoASL (_obj modelToWorldVisual (_laserMethod select 2)))];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
TRACE_1("",_laser);
|
|
//Handle Weird Data Return - skips over this laser in the for loop
|
|
if ((_laser isEqualTo []) || {_laser isEqualTo [-1, -1]}) exitWith {WARNING_1("Bad Laser Return",_laser);};
|
|
_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"];
|
|
{
|
|
private _testPoint = _x select 0;
|
|
private _testPointVector = _posASL vectorFromTo _testPoint;
|
|
private _testDotProduct = _dir vectorDotProduct _testPointVector;
|
|
if ((_testDotProduct > _seekerCos) && {(_testPoint vectorDistanceSqr _posASL) < _seekerMaxDistSq}) then {
|
|
_spots pushBack [_testPoint, _owner];
|
|
};
|
|
} 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;
|
|
if ((_testDotProduct > _seekerCos) && {(_resultPos vectorDistanceSqr _posASL) < _seekerMaxDistSq}) then {
|
|
_spots pushBack [_resultPos, _owner];
|
|
};
|
|
};
|
|
};
|
|
};
|
|
} forEach (GVAR(laserEmitters) select 2); // Go through all values in hash
|
|
|
|
TRACE_2("",count _spots, _spots);
|
|
|
|
if ((count _spots) > 0) then {
|
|
private _bucketList = nil;
|
|
private _bucketPos = nil;
|
|
private _c = 0;
|
|
private _buckets = [];
|
|
private _excludes = [];
|
|
private _bucketIndex = 0;
|
|
|
|
// Put close points together into buckets
|
|
while { count(_spots) != count(_excludes) && _c < (count _spots) } do {
|
|
scopeName "mainSearch";
|
|
{
|
|
if (!(_forEachIndex in _excludes)) then {
|
|
private _index = _buckets pushBack [_x, [_x]];
|
|
_excludes pushBack _forEachIndex;
|
|
_bucketPos = _x select 0;
|
|
_bucketList = (_buckets select _index) select 1;
|
|
breakTo "mainSearch";
|
|
};
|
|
} forEach _spots;
|
|
{
|
|
if (!(_forEachIndex in _excludes)) then {
|
|
private _testPos = (_x select 0);
|
|
if ((_testPos vectorDistanceSqr _bucketPos) <= 100) then {
|
|
_bucketList pushBack _x;
|
|
_excludes pushBack _forEachIndex;
|
|
};
|
|
};
|
|
} forEach _spots;
|
|
_c = _c + 1;
|
|
};
|
|
|
|
TRACE_1("",_buckets);
|
|
|
|
private _finalBuckets = [];
|
|
private _largest = -1;
|
|
private _largestIndex = 0;
|
|
{
|
|
// find bucket with largest number of points we can see
|
|
private _index = _finalBuckets pushBack [];
|
|
_bucketList = _finalBuckets select _index;
|
|
{
|
|
private _testPos = (_x select 0) vectorAdd [0,0,0.05];
|
|
private _testIntersections = lineIntersectsSurfaces [_posASL, _testPos, _ignoreObj1];
|
|
if ([] isEqualTo _testIntersections) then {
|
|
_bucketList pushBack _x;
|
|
};
|
|
} forEach (_x select 1);
|
|
if ((count _bucketList) > _largest) then {
|
|
_largest = (count _bucketList);
|
|
_largestIndex = _index;
|
|
};
|
|
} forEach _buckets;
|
|
|
|
private _finalBucket = _finalBuckets select _largestIndex;
|
|
private _ownersHash = [] call CBA_fnc_hashCreate;
|
|
|
|
TRACE_2("",_finalBucket,_finalBuckets);
|
|
|
|
if (count _finalBucket > 0) then {
|
|
// merge all points in the best bucket into an average point and find effective owner
|
|
_finalPos = [0,0,0];
|
|
{
|
|
_x params ["_xPos", "_owner"];
|
|
_finalPos = _finalPos vectorAdd _xPos;
|
|
if ([_ownersHash, _owner] call CBA_fnc_hashHasKey) then {
|
|
private _count = [_ownersHash, _owner] call CBA_fnc_hashGet;
|
|
[_ownersHash, _owner, _count + 1] call CBA_fnc_hashSet;
|
|
} else {
|
|
[_ownersHash, _owner, 1] call CBA_fnc_hashSet;
|
|
};
|
|
} forEach _finalBucket;
|
|
|
|
_finalPos = _finalPos vectorMultiply (1 / (count _finalBucket));
|
|
|
|
private _maxOwnerCount = -1;
|
|
|
|
[_ownersHash, {
|
|
//IGNORE_PRIVATE_WARNING ["_key", "_value"];
|
|
if (_value > _maxOwnerCount) then {
|
|
_finalOwner = _key;
|
|
};
|
|
}] call CBA_fnc_hashEachPair;
|
|
};
|
|
};
|
|
|
|
END_COUNTER(seekerFindLaserSpot);
|
|
|
|
#ifdef DRAW_LASER_INFO
|
|
if (isNil "_finalPos") then {
|
|
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"];
|
|
} else {
|
|
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"];
|
|
drawLine3D [ASLtoAGL _posASL, ASLtoAGL _finalPos, [0.5,1,0,1]];
|
|
};
|
|
#endif
|
|
|
|
TRACE_2("return",_finalPos,_finalOwner);
|
|
if (isNil "_finalPos") exitWith {[nil, _finalOwner]};
|
|
[_finalPos, _finalOwner];
|