Add Iron Dome interceptor API

This commit is contained in:
Brandon Danyluk 2021-05-18 00:32:55 -06:00
parent b82484b73c
commit 980c3d0546
15 changed files with 415 additions and 5 deletions

View File

@ -0,0 +1 @@
z\ace\addons\iron_dome

View File

@ -0,0 +1,11 @@
class Extended_PreStart_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preStart));
};
};
class Extended_PreInit_EventHandlers {
class ADDON {
init = QUOTE(call COMPILE_FILE(XEH_preInit));
};
};

View File

@ -0,0 +1,11 @@
ace_iron_dome
===================
Adds an Iron Dome projectile interceptor system
## Maintainers
The people responsible for merging changes to this component or answering potential questions.
- [Brandon (TCVM)](https://github.com/TheCandianVendingMachine)

View File

@ -0,0 +1,2 @@
PREP(projectileTrackerPFH);
PREP(proximityFusePFH);

View File

@ -0,0 +1,79 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
ADDON = true;
// Server handles the tracking of all projectiles. It dispatches events to launchers to fire at specific targets
if (isServer) then {
GVAR(trackers) = [];
GVAR(launchers) = [];
GVAR(nonTrackingProjectiles) = [];
GVAR(trackingProjectiles) = [];
GVAR(interceptors) = [];
GVAR(toBeShot) = [];
[QGVAR(track), {
params ["_projectile"];
GVAR(nonTrackingProjectiles) pushBack _projectile;
}] call CBA_fnc_addEventHandler;
[QGVAR(registerLauncher), {
params ["_launcher"];
GVAR(launchers) pushBackUnique _launcher;
_launcher setVariable [QGVAR(targetList), []];
_launcher setVariable [QGVAR(launchState), LAUNCH_STATE_IDLE];
_launcher setVariable [QGVAR(lastLaunchTime), 0];
_launcher setVariable [QGVAR(engagedTargets), [[], objNull] call CBA_fnc_hashCreate];
_launcher setVariable [QEGVAR(missileguidance,target), objNull];
}] call CBA_fnc_addEventHandler;
[QGVAR(registerTracker), {
params ["_tracker", "_range"];
GVAR(trackers) pushBack [_tracker, _range];
}] call CBA_fnc_addEventHandler;
[QGVAR(registerInterceptor), {
params ["_interceptor", "_target"];
GVAR(interceptors) pushBack [_interceptor, _target, getPosASLVisual _interceptor];
GVAR(toBeShot) deleteAt (GVAR(toBeShot) find _target);
}] call CBA_fnc_addEventHandler;
[LINKFUNC(projectileTrackerPFH)] call CBA_fnc_addPerFrameHandler;
[LINKFUNC(proximityFusePFH)] call CBA_fnc_addPerFrameHandler;
};
// duplicate event to add event handler
[QGVAR(registerLauncher), {
params ["_launcher"];
if !(local _launcher) exitWith {};
_launcher addEventHandler ["Fired", {
params ["_launcher", "", "", "", "", "", "_projectile"];
private _target = _launcher getVariable [QEGVAR(missileguidance,target), objNull];
if !(isNull _target) then {
[QGVAR(registerInterceptor), [_projectile, _target]] call CBA_fnc_serverEvent;
};
}];
}] call CBA_fnc_addEventHandler;
// When something is fired, determine if we want to track it. If so, send it to the server for processing
GVAR(projectilesToIntercept) = [];
[QGVAR(addProjectileToIntercept), {
params ["_type"];
GVAR(projectilesToIntercept) pushBackUnique _type;
}] call CBA_fnc_addEventHandler;
["All", "fired", {
params ["", "", "", "", "", "", "_projectile"];
if (local _projectile && { (typeOf _projectile) in GVAR(projectilesToIntercept) }) then {
[QGVAR(track), [_projectile]] call CBA_fnc_serverEvent;
};
}] call CBA_fnc_addClassEventHandler;

View File

@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

View File

@ -0,0 +1,17 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_common"};
author = ECSTRING(common,ACETeam);
authors[] = {"Brandon (TCVM)"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};
#include "CfgEventHandlers.hpp"

View File

@ -0,0 +1,194 @@
#include "script_component.hpp"
/*
* Author: Brandon (TCVM)
* Handles tracking of incoming projectiles per frame
*
* Arguments:
* 0: Args <ARRAY>
* 1: Handle <NUMBER>
*
* Return Value:
* None
*
* Example:
* [ace_iron_dome_projectileTrackerPFH] call CBA_fnc_addPerFrameHandler
*
* Public: No
*/
GVAR(trackers) = GVAR(trackers) select {
_x params ["_tracker"];
alive _tracker
};
GVAR(launchers) = GVAR(launchers) select {
alive _x
};
GVAR(toBeShot) = GVAR(toBeShot) select {
_x params ["", "_shotTime"];
(CBA_missionTime - _shotTime) < RECYCLE_TIME
};
GVAR(nonTrackingProjectiles) = GVAR(nonTrackingProjectiles) select {
private _projectile = _x;
private _keep = true;
private _bestRange = 1e10;
{
_x params ["_tracker", "_range"];
_bestRange = _bestRange min (_projectile distanceSqr _tracker);
if (_projectile distanceSqr _tracker <= _range * _range) exitWith {
GVAR(trackingProjectiles) pushBack [_projectile, 0];
_keep = false;
};
} forEach GVAR(trackers);
#ifdef DRAW_TRACKING_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], getPos _projectile, 0.75, 0.75, 0, format ["%1 %2m", typeOf _projectile, sqrt _bestRange], 1, 0.025, "TahomaB"];
#endif
_keep
};
GVAR(trackingProjectiles) = GVAR(trackingProjectiles) select {
_x params ["_projectile", "_lastFired"];
private _keep = false;
if (alive _projectile) then {
{
_x params ["_tracker", "_range"];
private _withinRange = _projectile distanceSqr _tracker <= _range * _range;
if (_withinRange) exitWith {
_keep = true;
};
} forEach GVAR(trackers);
if !(_keep) then {
GVAR(nonTrackingProjectiles) pushBack _projectile;
} else {
private _bestLauncher = objNull;
private _bestAmmo = 0;
private _engagedFuture = GVAR(toBeShot) findIf {
_x params ["_target", "_timeShot"];
_projectile isEqualTo _target
};
private _engagedPast = GVAR(interceptors) findIf {
_x params ["", "_target"];
_projectile isEqualTo _target;
};
private _engaged = (_engagedFuture != -1) || (_engagedPast != -1);
if !(_engaged) then {
// launch a missile
{
// try to get a launcher with the most ammo that is the closest to the incoming projectile
private _state = _x getVariable QGVAR(launchState);
private _ammo = parseNumber (((currentMagazineDetail _x) splitString "([ ]/:)") select 3);
if (_state == LAUNCH_STATE_IDLE && { _ammo >= _bestAmmo }) then {
_bestAmmo = _ammo;
_bestLauncher = _x;
};
} forEach GVAR(launchers);
private _targetList = _bestLauncher getVariable QGVAR(targetList);
_targetList pushBackUnique _projectile;
_bestLauncher setVariable [QGVAR(targetList), _targetList];
GVAR(toBeShot) pushBackUnique [_projectile, CBA_missionTime];
};
#ifdef DRAW_TRACKING_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,1,0,1], getPos _projectile, 0.75, 0.75, 0, format ["%1 %2m %3s", typeOf _projectile, _bestLauncher distance _projectile], 1, 0.025, "TahomaB"];
#endif
};
};
_keep
};
{
private _launcher = _x;
private _state = _launcher getVariable QGVAR(launchState);
switch (_state) do {
case LAUNCH_STATE_IDLE: {
private _targetList = _x getVariable QGVAR(targetList);
private _engagedTargets = _x getVariable QGVAR(engagedTargets);
_targetList = _targetList select {
private _timeFiredAt = [_engagedTargets, _x, 0] call CBA_fnc_hashGet;
alive _x && { (CBA_missionTime - _timeFiredAt) >= RE_ENGAGEMENT_TIME }
};
private _bestTarget = objNull;
private _bestDistance = 1e10;
{
if (_x distanceSqr _launcher < _bestDistance) then {
_bestTarget = _x;
_bestDistance = _x distanceSqr _launcher;
};
} forEach _targetList;
if !(isNull _bestTarget) then {
_launcher setVariable [QEGVAR(missileguidance,target), _bestTarget];
_launcher setVariable [QGVAR(launchState), LAUNCH_STATE_TRACKING];
};
#ifdef DRAW_TRACKING_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1, 1, 1, 1], getPos _launcher, 0.75, 0.75, 0, format ["IDLE"], 1, 0.025, "TahomaB"];
#endif
};
case LAUNCH_STATE_TRACKING: {
private _target = _launcher getVariable QEGVAR(missileguidance,target);
_launcher lookAt getPosVisual _target;
if (isNull _target) then {
_launcher setVariable [QGVAR(launchState), LAUNCH_STATE_IDLE];
} else {
private _directionToTarget = (getPosASLVisual _launcher) vectorFromTo (getPosASLVisual _target);
private _turretDirection = _launcher weaponDirection currentWeapon _launcher;
private _angle = acos (_turretDirection vectorCos _directionToTarget);
if (_angle <= LAUNCH_ACCEPTABLE_ANGLE) then {
_launcher setVariable [QGVAR(launchState), LAUNCH_STATE_FIRING];
};
#ifdef DRAW_TRACKING_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0, 0, 1, 1], getPos _launcher, 0.75, 0.75, 0, format ["TRACKING: %1", _angle], 1, 0.025, "TahomaB"];
drawLine3D [getPos _launcher, getPos _target, [0, 0, 1, 1]];
#endif
};
};
case LAUNCH_STATE_FIRING: {
private _turret = [_launcher, (crew _launcher) select 0] call CBA_fnc_turretPath;
[_launcher, _launcher currentWeaponTurret _turret] call BIS_fnc_fire;
_launcher setVariable [QGVAR(lastLaunchTime), CBA_missionTime];
_launcher setVariable [QGVAR(launchState), LAUNCH_STATE_COOLDOWN];
private _target = _launcher getVariable QEGVAR(missileguidance,target);
private _engagedTargets = _x getVariable QGVAR(engagedTargets);
[_engagedTargets, _target, CBA_missionTime] call CBA_fnc_hashSet;
_x setVariable [QGVAR(engagedTargets), _engagedTargets];
#ifdef DRAW_TRACKING_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1, 0, 0, 1], getPos _launcher, 0.75, 0.75, 0, format ["FIRING"], 1, 0.025, "TahomaB"];
#endif
};
case LAUNCH_STATE_COOLDOWN: {
private _lastLaunchTime = _launcher getVariable QGVAR(lastLaunchTime);
if (CBA_missionTime - _lastLaunchTime >= TIME_BETWEEN_LAUNCHES) then {
_launcher setVariable [QGVAR(launchState), LAUNCH_STATE_IDLE];
};
#ifdef DRAW_TRACKING_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0, 0, 1, 1], getPos _launcher, 0.75, 0.75, 0, format ["COOLDOWN %1", CBA_missionTime - _lastLaunchTime], 1, 0.025, "TahomaB"];
#endif
};
};
} forEach GVAR(launchers);

View File

@ -0,0 +1,49 @@
#include "script_component.hpp"
/*
* Author: Brandon (TCVM)
* Handles the fusing and detonation of any and all interceptors in the air
*
* Arguments:
* None
*
* Return Value:
* None
*
* Example:
* [ace_iron_dome_proximityFusePFH] call CBA_fnc_addPerFrameHandler
*
* Public: No
*/
GVAR(interceptors) = GVAR(interceptors) select {
_x params ["_projectile", "_target", "_lastPosition"];
// Sweep along path to ensure we don't overshoot target
private _minDistance = 0;
private _currentPosition = getPosASLVisual _projectile;
private _targetPosition = getPosASLVisual _target;
private _posDiff = (_currentPosition vectorDiff _lastPosition);
private _lengthSqr = _posDiff vectorDotProduct _posDiff;
if (_lengthSqr - 0.001 <= 0) then {
_minDistance = _lastPosition vectorDistance _targetPosition
} else {
private _d = (_targetPosition vectorDiff _lastPosition) vectorDotProduct (_currentPosition vectorDiff _lastPosition);
private _t = 0 max (1 min (_d / _lengthSqr));
private _projection = _lastPosition vectorAdd ((_currentPosition vectorDiff _lastPosition) vectorMultiply _t);
_minDistance = _projection vectorDistance _targetPosition;
};
_x set [2, _currentPosition];
#ifdef DRAW_TRACKING_INFO
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,0,1,1], (getPos _target) vectorAdd [0, 0, 0.5], 0.75, 0.75, 0, format ["%1m", _minDistance], 1, 0.025, "TahomaB"];
#endif
if (!alive _target || { _minDistance <= PROX_RANGE }) then {
triggerAmmo _projectile;
deleteVehicle _target;
false
} else {
true
}
};

View File

@ -0,0 +1 @@
#include "\z\ace\addons\iron_dome\script_component.hpp"

View File

@ -0,0 +1,35 @@
#define COMPONENT iron_dome
#define COMPONENT_BEAUTIFIED Iron Dome
#include "\z\ace\addons\main\script_mod.hpp"
// #define DRAW_TRACKING_INFO
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_IRON_DOME
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_IRON_DOME
#define DEBUG_SETTINGS DEBUG_SETTINGS_IRON_DOME
#endif
#define LAUNCH_STATE_IDLE 0
#define LAUNCH_STATE_TRACKING 1
#define LAUNCH_STATE_FIRING 2
#define LAUNCH_STATE_COOLDOWN 3
// How long it takes to recycle a target
#define RECYCLE_TIME 5
// how many error degrees the launcher has to be pointing toward the target
#define LAUNCH_ACCEPTABLE_ANGLE 30
// How fast the launcher launches
#define TIME_BETWEEN_LAUNCHES 1
// how many seconds does a launcher have to wait before re-engaging the same target
#define RE_ENGAGEMENT_TIME 5
// Proximity fuse range
#define PROX_RANGE 10
#include "\z\ace\addons\main\script_macros.hpp"

View File

@ -14,14 +14,16 @@
*
* Public: No
*/
params ["_firedEH", "", "", "", "_stateParams"];
params ["_firedEH", "_launchParams", "", "", "_stateParams"];
_firedEH params ["_shooter","_weapon","","","","","_projectile"];
_stateParams params ["", "_seekerStateParams"];
_launchParams params ["", "_targetLaunchParams"];
_targetLaunchParams params ["_target"];
private _flareDistanceFilter = getNumber (configOf _projectile >> QUOTE(ADDON) >> "flareDistanceFilter");
private _flareAngleFilter = getNumber (configOf _projectile >> QUOTE(ADDON) >> "flareAngleFilter");
_seekerStateParams set [0, _flareDistanceFilter];
_seekerStateParams set [1, _flareAngleFilter];
_seekerStateParams set [2, missileTarget _projectile];
_seekerStateParams set [2, _target];

View File

@ -24,7 +24,8 @@ _target = missileTarget _projectile;
if (isNull _target && isVehicleRadarOn vehicle _shooter) then {
_target = cursorTarget;
};
if !(_target isKindOf "AllVehicles") then {
// always allow tracking of projectiles
if !(_target isKindOf "AllVehicles" || { _target isKindOf ["Default", configFile >> "CfgAmmo"] }) then {
_target = nil;
};
_launchParams set [0, _target];

View File

@ -2,10 +2,10 @@
#define COMPONENT_BEAUTIFIED Missile Guidance
#include "\z\ace\addons\main\script_mod.hpp"
#define DRAW_GUIDANCE_INFO
// #define DRAW_GUIDANCE_INFO
// #define ENABLE_PROJECTILE_CAMERA
// #define DEBUG_MODE_FULL
#define DISABLE_COMPILE_CACHE
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_MISSILEGUIDANCE

View File

@ -91,3 +91,7 @@ General To-Do:
X Fix GBU drag
X Make sure all applicable pylons can hold all applicable weapons
X NLAW is busted: figure out PLOS navigation system
Add 9m14 textures
Add 9m14 animation state where missile doesn't exist
Add 9m14 joystick view rotation (+- 5 deg vertical as well)
Add 9m14 proxy