From a1cafef5aea69a0705d6d4382caaf3a11863383a Mon Sep 17 00:00:00 2001 From: Bailey Danyluk Date: Fri, 23 Aug 2024 08:46:01 -0600 Subject: [PATCH] Nlaw - Update to use Missile Guidance Framework (#10034) --- addons/nlaw/ACE_GuidanceConfig.hpp | 6 ++ addons/nlaw/CfgAmmo.hpp | 8 +- addons/nlaw/XEH_PREP.hpp | 2 + addons/nlaw/XEH_postInit.sqf | 4 + addons/nlaw/functions/fnc_attackProfile.sqf | 16 +--- addons/nlaw/functions/fnc_keyDown.sqf | 2 +- addons/nlaw/functions/fnc_navigation.sqf | 78 +++++++++++++++++++ .../nlaw/functions/fnc_navigation_onFired.sqf | 70 +++++++++++++++++ addons/nlaw/functions/fnc_onFired.sqf | 25 +++++- addons/nlaw/functions/fnc_seeker.sqf | 17 +++- 10 files changed, 207 insertions(+), 21 deletions(-) create mode 100644 addons/nlaw/functions/fnc_navigation.sqf create mode 100644 addons/nlaw/functions/fnc_navigation_onFired.sqf diff --git a/addons/nlaw/ACE_GuidanceConfig.hpp b/addons/nlaw/ACE_GuidanceConfig.hpp index e04753d6816..6003586fe86 100644 --- a/addons/nlaw/ACE_GuidanceConfig.hpp +++ b/addons/nlaw/ACE_GuidanceConfig.hpp @@ -12,3 +12,9 @@ class EGVAR(missileguidance,SeekerTypes) { functionName = QFUNC(seeker); }; }; +class EGVAR(missileguidance,NavigationTypes) { + class GVAR(PLOS) { + functionName = QFUNC(navigation); + onFired = QFUNC(navigation_onFired); + }; +}; diff --git a/addons/nlaw/CfgAmmo.hpp b/addons/nlaw/CfgAmmo.hpp index b579ebeb065..f572678d20b 100644 --- a/addons/nlaw/CfgAmmo.hpp +++ b/addons/nlaw/CfgAmmo.hpp @@ -6,9 +6,8 @@ class CfgAmmo { class ace_missileguidance { enabled = 1; - minDeflection = 0.0005; // Minium flap deflection for guidance - maxDeflection = 0.01; // Maximum flap deflection for guidance - incDeflection = 0.0005; // The incrmeent in which deflection adjusts. + pitchRate = 5; // Minium flap deflection for guidance + yawRate = 10; // Maximum flap deflection for guidance canVanillaLock = 0; // Can this default vanilla lock? Only applicable to non-cadet mode @@ -19,6 +18,9 @@ class CfgAmmo { defaultSeekerLockMode = "LOBL"; seekerLockModes[] = {"LOBL"}; + defaultNavigationType = QGVAR(PLOS); + navigationTypes[] = { QGVAR(PLOS) }; + seekLastTargetPos = 0; // seek last target position [if seeker loses LOS of target, continue to last known pos] seekerAngle = 45; // Angle in front of the missile which can be searched seekerAccuracy = 1; // seeker accuracy multiplier diff --git a/addons/nlaw/XEH_PREP.hpp b/addons/nlaw/XEH_PREP.hpp index aad1e57efb2..1cd8dbc3c59 100644 --- a/addons/nlaw/XEH_PREP.hpp +++ b/addons/nlaw/XEH_PREP.hpp @@ -4,3 +4,5 @@ PREP(attackProfile); PREP(keyDown); PREP(onFired); PREP(seeker); +PREP(navigation); +PREP(navigation_onFired); diff --git a/addons/nlaw/XEH_postInit.sqf b/addons/nlaw/XEH_postInit.sqf index 885e2d5e2f2..16be48ce26c 100644 --- a/addons/nlaw/XEH_postInit.sqf +++ b/addons/nlaw/XEH_postInit.sqf @@ -22,7 +22,11 @@ GVAR(pitchChange) = 0; // Visual debuging, idealy used with a moving vehicle called "testTarget" #ifdef DRAW_NLAW_INFO +GVAR(debug_firedPrediction) = []; addMissionEventHandler ["Draw3d", { + // BLACK - On fired path prediction + { drawIcon3D _x; } forEach GVAR(debug_firedPrediction); + // GREEN - Draw an object called "testTarget"'s aim pos and 1 sec aimpos predicted by velocity if ((!isNil "testTarget") && {!isNull testTarget}) then { { diff --git a/addons/nlaw/functions/fnc_attackProfile.sqf b/addons/nlaw/functions/fnc_attackProfile.sqf index 9628974800c..f5277808357 100644 --- a/addons/nlaw/functions/fnc_attackProfile.sqf +++ b/addons/nlaw/functions/fnc_attackProfile.sqf @@ -18,14 +18,13 @@ */ params ["_seekerTargetPos", "_args", "_attackProfileStateParams"]; + +#ifdef DRAW_NLAW_INFO _args params ["_firedEH", "_launchParams"]; _launchParams params ["","_targetLaunchParams", "", "_attackProfile"]; _targetLaunchParams params ["", "", "_launchPos"]; _firedEH params ["","","","","","","_projectile"]; -// Use seeker (if terminal) -if (_seekerTargetPos isNotEqualTo [0,0,0]) exitWith {_seekerTargetPos}; - _attackProfileStateParams params ["_startTime", "_startLOS", "_yawChange", "_pitchChange"]; (_startLOS call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"]; @@ -36,14 +35,6 @@ private _flightTime = CBA_missionTime - _startTime; private _realYaw = _yaw + _yawChange * _flightTime; private _realPitch = _pitch + _pitchChange * _flightTime; -private _returnTargetPos = _launchPos vectorAdd ([_distanceFromLaunch, _realYaw, _realPitch] call CBA_fnc_polar2vect); - -if (_attackProfile == QGVAR(overflyTopAttack)) then { // Add 2m height in OTA attack mode - _returnTargetPos = _returnTargetPos vectorAdd [0,0,2]; -}; - - -#ifdef DRAW_NLAW_INFO drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,1,1], ASLtoAGL _launchPos, 0.75, 0.75, 0, "LAUNCH", 1, 0.025, "TahomaB"]; drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,1,1,1], ASLtoAGL (_launchPos vectorAdd (_startLOS vectorMultiply (_distanceFromLaunch + 50))), 0.75, 0.75, 0, "Original LOS", 1, 0.025, "TahomaB"]; drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,1,0,1], ASLtoAGL (_launchPos vectorAdd ([_distanceFromLaunch + 50, _realYaw, _realPitch] call CBA_fnc_polar2vect)), 0.75, 0.75, 0, format ["Predicted @%1sec",(floor(_flightTime * 10)/10)], 1, 0.025, "TahomaB"]; @@ -56,5 +47,4 @@ if ((count _test) > 0) then { }; #endif -// TRACE_1("Adjusted target position",_returnTargetPos); -_returnTargetPos; +[0, 0, 1] diff --git a/addons/nlaw/functions/fnc_keyDown.sqf b/addons/nlaw/functions/fnc_keyDown.sqf index 5b75c721406..aee9739474b 100644 --- a/addons/nlaw/functions/fnc_keyDown.sqf +++ b/addons/nlaw/functions/fnc_keyDown.sqf @@ -74,7 +74,7 @@ playSound "ACE_Sound_Click"; _args set [1, _yaw]; _args set [2, _pitch]; - #ifdef DEBUG_MODE_FULL + #ifdef DRAW_NLAW_INFO hintSilent format ["Instantaneous\nYaw: %1\n Pitch: %2\nGVAR\nYaw: %3\nPitch: %4", _yawChange, _pitchChange, GVAR(yawChange), GVAR(pitchChange)]; #endif }, .25, [CBA_missionTime, _yaw, _pitch, true]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/nlaw/functions/fnc_navigation.sqf b/addons/nlaw/functions/fnc_navigation.sqf new file mode 100644 index 00000000000..1cfd8f91d56 --- /dev/null +++ b/addons/nlaw/functions/fnc_navigation.sqf @@ -0,0 +1,78 @@ +#include "..\script_component.hpp" +/* + * Author: tcvm + * Attempts to hold angle as fed to by seeker. Does so with a simple proportional controller + * + * Arguments: + * Guidance Arg Array + * + * Return Value: + * Commanded acceleration normal to LOS in world space + * + * Example: + * [] call ace_missileguidance_fnc_navigationType_line + * + * Public: No + */ +// arbitrary constant +#define PROPORTIONALITY_CONSTANT 20 +params ["_args", "_timestep", "_seekerTargetPos", "_profileAdjustedTargetPos", "_targetData", "_navigationParams"]; +_args params ["_firedEH"]; +_firedEH params ["","","","","","","_projectile"]; + +_navigationParams params ["_yawChange", "_pitchChange", "_lastPitch", "_lastYaw", "_initialPitch", "_pitchUp", "_lastYawRateDifference"]; + +// for some reason we need to multiply this. I don't know why, but it just works +_pitchChange = _pitchChange * 1.5; +_yawChange = _yawChange * 1.5; + +((velocity _projectile) call CBA_fnc_vect2polar) params ["", "_currentYaw", "_currentPitch"]; + +private _pitchRate = if (_timestep == 0) then { + 0 +} else { + (_currentPitch - _lastPitch) / _timestep +}; +_navigationParams set [2, _currentPitch]; + +private _pitchModifier = if (_pitchChange == 0) then { + 1 +} else { + abs (_pitchRate / _pitchChange) +}; +private _desiredPitchChange = (_pitchChange - _pitchRate) * PROPORTIONALITY_CONSTANT * _pitchModifier; +_desiredPitchChange = _desiredPitchChange + _pitchUp * (_initialPitch - _currentPitch) * PROPORTIONALITY_CONSTANT * _pitchModifier; + +private _yawRate = if (_timestep == 0) then { + 0 +} else { + (_currentYaw - _lastYaw) / _timestep +}; +_navigationParams set [3, _currentYaw]; + +private _yawModifier = if (_yawChange == 0) then { + 1 +} else { + abs (_yawRate / _yawChange) +}; + +private _yawRateDifference = _yawChange - _yawRate; +private _yawChangeDerivative = if (_timestep == 0) then { + 0 +} else { + (_yawRateDifference - _lastYawRateDifference) / _timestep +}; +_navigationParams set [6, _yawRateDifference]; + +private _desiredYawChange = _yawRateDifference * PROPORTIONALITY_CONSTANT + _yawRateDifference * 2; + +#ifdef DRAW_NLAW_INFO +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,1,1], ASLtoAGL getPosASLVisual _projectile, 0.75, 0.75, 0, format ["dP [%1] dY: [%2]", _desiredPitchChange, _desiredYawChange], 1, 0.025, "TahomaB"]; +drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,1,1], [0, 0, 1] vectorAdd ASLtoAGL getPosASLVisual _projectile, 0.75, 0.75, 0, format ["pitch proportional [%1] yaw proportional [%2]", _pitchModifier, _yawModifier], 1, 0.025, "TahomaB"]; +#endif + +TRACE_4("nlaw pitch/yaw info",_currentPitch,_lastPitch,_currentYaw,_lastYaw); +TRACE_6("nlaw navigation",_yawChange,_desiredYawChange,_pitchChange,_desiredPitchChange,_yawRate,_pitchRate); + +_projectile vectorModelToWorldVisual [_yawChange + _desiredYawChange, 0, _pitchChange + _desiredPitchChange] + diff --git a/addons/nlaw/functions/fnc_navigation_onFired.sqf b/addons/nlaw/functions/fnc_navigation_onFired.sqf new file mode 100644 index 00000000000..895d5ff78b3 --- /dev/null +++ b/addons/nlaw/functions/fnc_navigation_onFired.sqf @@ -0,0 +1,70 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Sets up missile guidance state arrays (called from missileGuidance's onFired). + * + * Arguments: + * Guidance Arg Array + * + * Return Value: + * Navigation Parameters + * + * Example: + * [] call ace_nlaw_fnc_onFired + * + * Public: No + */ + +params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"]; +_firedEH params ["_shooter","","","","","","_projectile"]; +_launchParams params ["","_targetLaunchParams","","_attackProfile"]; +_targetLaunchParams params ["_target"]; +_stateParams params ["", "", "", "", "_navigationParams"]; + +// Reset _launchPos origin as projectile's height instead of player's foot +_targetLaunchParams set [2, getPosASL _projectile]; + +// Get state params: +TRACE_3("start of attack profile",_attackProfile,_shooter,vectorDir _projectile); + +private _firedLOS = _shooter weaponDirection (currentWeapon _shooter); +private _yawChange = 0; +private _pitchChange = 0; + +if (_shooter == ACE_player) then { + TRACE_2("isPlayer",GVAR(yawChange),GVAR(pitchChange)); + _yawChange = GVAR(yawChange); + _pitchChange = GVAR(pitchChange); + TRACE_1("los check",_firedLOS call CBA_fnc_vect2Polar); +} else { + if ((!isNil "_target") && {!isNull _target}) then { + _firedLOS = (getPosASL _projectile) vectorFromTo (aimPos _target); + (((eyePos _shooter) vectorFromTo (aimPos _target)) call CBA_fnc_vect2Polar) params ["", "_startYaw", "_startPitch"]; + // Add some random error to AI's velocity prediction: + private _random = random [(_shooter skillFinal "aimingAccuracy") min 0.9, 1, 2-((_shooter skillFinal "aimingAccuracy") min 0.9)]; + (((eyePos _shooter) vectorFromTo ((aimPos _target) vectorAdd ((velocity _target) vectorMultiply (_random)))) call CBA_fnc_vect2Polar) params ["", "_predictedYaw", "_predictedPitch"]; + _yawChange = ([_predictedYaw - _startYaw] call CBA_fnc_simplifyAngle180); + _pitchChange = ([_predictedPitch - _startPitch] call CBA_fnc_simplifyAngle180); + TRACE_1("AI",_target); + } else { + TRACE_1("AI - no target",_target); + }; +}; + +// Limit Max Deflection +//_yawChange = -10 max _yawChange min 10; +//_pitchChange = -10 max _pitchChange min 10; + +((velocity _projectile) call CBA_fnc_vect2polar) params ["", "_currentYaw", "_currentPitch"]; +((ACE_player weaponDirection (currentWeapon ACE_player)) call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"]; + +TRACE_5("attackProfileStateParams",_firedLOS,_yawChange,_pitchChange,_currentPitch,_currentYaw); +_navigationParams set [0, _yawChange]; +_navigationParams set [1, _pitchChange]; +_navigationParams set [2, _currentPitch]; // last pitch +_navigationParams set [3, _currentYaw]; // last yaw +_navigationParams set [4, _pitch]; // initial pitch +_navigationParams set [5, 0]; // whether or not to zero out the pitch +_navigationParams set [6, 0]; +_navigationParams + diff --git a/addons/nlaw/functions/fnc_onFired.sqf b/addons/nlaw/functions/fnc_onFired.sqf index 11b38a1da03..e8332b8caf9 100644 --- a/addons/nlaw/functions/fnc_onFired.sqf +++ b/addons/nlaw/functions/fnc_onFired.sqf @@ -19,7 +19,7 @@ params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_statePa _firedEH params ["_shooter","","","","","","_projectile"]; _launchParams params ["","_targetLaunchParams","","_attackProfile"]; _targetLaunchParams params ["_target"]; -_stateParams params ["", "", "_attackProfileStateParams"]; +_stateParams params ["", "_seekerStateParams", "_attackProfileStateParams"]; // Reset _launchPos origin as projectile's height instead of player's foot _targetLaunchParams set [2, getPosASL _projectile]; @@ -27,7 +27,7 @@ _targetLaunchParams set [2, getPosASL _projectile]; // Get state params: TRACE_3("start of attack profile",_attackProfile,_shooter,vectorDir _projectile); -private _firedLOS = _shooter weaponDirection (currentWeapon _shooter); +private _firedLOS = vectorDir _projectile; private _yawChange = 0; private _pitchChange = 0; @@ -36,6 +36,23 @@ if (_shooter == ACE_player) then { _yawChange = GVAR(yawChange); _pitchChange = GVAR(pitchChange); TRACE_1("los check",_firedLOS call CBA_fnc_vect2Polar); + + #ifdef DRAW_NLAW_INFO + systemChat format ["YAW [%1]", _yawChange]; + systemChat format ["PITCH [%1]", _pitchChange]; + GVAR(debug_firedPrediction) = []; + private _debugPos = getPosASL _projectile; + ((ACE_player weaponDirection (currentWeapon ACE_player)) call CBA_fnc_vect2Polar) params ["", "_debugYaw", "_debugPitch"]; + private _distance = 0; + for "_x" from 0 to 6 step 0.1 do { + private _debugAproxVel = linearConversion [0, 1, 5, 40, 170, true]; + _distance = _distance + _debugAproxVel * 0.1; + private _debugYaw = _debugYaw + _yawChange * _x; + private _debugPitch = _debugPitch + _pitchChange * _x; + private _debugPos = _debugPos vectorAdd ([_distance, _debugYaw, _debugPitch] call CBA_fnc_polar2vect); + GVAR(debug_firedPrediction) pushBack ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,0,0,1], ASLtoAGL _debugPos, 0.5, 0.5, 0, format ["%1", _x], 1, 0.025, "TahomaB"]; + }; + #endif } else { if ((!isNil "_target") && {!isNull _target}) then { _firedLOS = (getPosASL _projectile) vectorFromTo (aimPos _target); @@ -55,6 +72,10 @@ if (_shooter == ACE_player) then { _yawChange = -10 max _yawChange min 10; _pitchChange = -10 max _pitchChange min 10; +_seekerStateParams set [2, _yawChange]; +_seekerStateParams set [3, _pitchChange]; +_seekerStateParams set [4, CBA_missionTime]; + TRACE_3("attackProfileStateParams",_firedLOS,_yawChange,_pitchChange); _attackProfileStateParams set [0, CBA_missionTime]; _attackProfileStateParams set [1, _firedLOS]; diff --git a/addons/nlaw/functions/fnc_seeker.sqf b/addons/nlaw/functions/fnc_seeker.sqf index e5de6d320ca..a3a60e94a1f 100644 --- a/addons/nlaw/functions/fnc_seeker.sqf +++ b/addons/nlaw/functions/fnc_seeker.sqf @@ -16,14 +16,27 @@ * * Public: No */ +#define PITCH_UP_TIME 1 -params ["", "_args", "_seekerStateParams"]; +params ["", "_args", "_seekerStateParams", "", "", "_targetData"]; _args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams"]; _firedEH params ["","","","","","","_projectile"]; _launchParams params ["", "_targetLaunchParams", "", "_attackProfile"]; _targetLaunchParams params ["", "", "_launchPos"]; +_stateParams params ["", "", "", "", "_navigationParams"]; -if (_attackProfile == QGVAR(directAttack)) exitWith {[0,0,0]}; +if (_attackProfile == QGVAR(directAttack)) exitWith { + _navigationParams set [5, 1]; + [0,0,0] +}; + +_seekerStateParams params ["", "", "", "_originalPitchRate", "_startTime"]; +_navigationParams params ["", "_pitchRate"]; + +// pitch up for the first second of flight to begin an over-fly trajectory +private _pitchChange = linearConversion [0, PITCH_UP_TIME, CBA_missionTime - _startTime, 2, 0, true]; +_navigationParams set [1, _originalPitchRate + _pitchChange]; +_navigationParams set [5, ((CBA_missionTime - _startTime) min PITCH_UP_TIME) / PITCH_UP_TIME]; private _projPos = getPosASL _projectile;