mirror of
https://github.com/acemod/ACE3.git
synced 2024-08-30 18:23:18 +00:00
NLAW - Predicted Line Of Sight Guidance and Overfly Attack Mode (#4791)
* NLAW Prototype * Make AI Compatible * Add Overfly Top Attack Mode * Limit Max Deflection * Base prediction on AI skill * Generic cycle attack profile key for missile guidance * Add hint for weapons without huds * Configure for attack cycle key * Finish OTA ammo effects * Cleanup * Arm at 20m * Disable Debug * No models for short lived sub-ammos * Fix Korean strings * Change AI randomization to use skillFinal * Add wiki doc for nlaw * Cleanup * Cleanup * Cleanup
This commit is contained in:
parent
4872e186cd
commit
49374feb2a
@ -5,6 +5,7 @@ class CfgWeapons {
|
||||
magazines[] = {"ACE_PreloadedMissileDummy"}; // The dummy magazine
|
||||
};
|
||||
class ACE_launch_NLAW_Used_F: launch_NLAW_F { // the used tube should be a sub class of the disposable launcher
|
||||
EGVAR(nlaw,enabled) = 0; // disable guidance for the disposabled tube
|
||||
scope = 1;
|
||||
ACE_isUsedLauncher = 1;
|
||||
author = ECSTRING(common,ACETeam);
|
||||
|
@ -115,6 +115,15 @@ private _args = [_this,
|
||||
[ diag_tickTime, [], [], _lastKnownPosState]
|
||||
];
|
||||
|
||||
|
||||
// Run the "onFired" function passing the full guidance args array
|
||||
private _onFiredFunc = getText (_config >> "onFired");
|
||||
TRACE_1("",_onFiredFunc);
|
||||
if (_onFiredFunc != "") then {
|
||||
_args call (missionNamespace getVariable _onFiredFunc);
|
||||
};
|
||||
|
||||
|
||||
// Reverse:
|
||||
// _args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"];
|
||||
// _firedEH params ["_shooter","","","","_ammo","","_projectile"];
|
||||
|
1
addons/nlaw/$PBOPREFIX$
Normal file
1
addons/nlaw/$PBOPREFIX$
Normal file
@ -0,0 +1 @@
|
||||
z\ace\addons\nlaw
|
14
addons/nlaw/ACE_GuidanceConfig.hpp
Normal file
14
addons/nlaw/ACE_GuidanceConfig.hpp
Normal file
@ -0,0 +1,14 @@
|
||||
class EGVAR(missileguidance,AttackProfiles) {
|
||||
class GVAR(directAttack) {
|
||||
name = CSTRING(directAttack);
|
||||
functionName = QFUNC(attackProfile);
|
||||
};
|
||||
class GVAR(overflyTopAttack): GVAR(directAttack) {
|
||||
name = CSTRING(overflyTopAttack);
|
||||
};
|
||||
};
|
||||
class EGVAR(missileguidance,SeekerTypes) {
|
||||
class GVAR(seeker) {
|
||||
functionName = QFUNC(seeker);
|
||||
};
|
||||
};
|
55
addons/nlaw/CfgAmmo.hpp
Normal file
55
addons/nlaw/CfgAmmo.hpp
Normal file
@ -0,0 +1,55 @@
|
||||
class CfgAmmo {
|
||||
class M_NLAW_AT_F;
|
||||
class ACE_NLAW: M_NLAW_AT_F {
|
||||
hit = 400; // Default was 500
|
||||
indirectHit = 20; // Default was 15
|
||||
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.
|
||||
|
||||
canVanillaLock = 0; // Can this default vanilla lock? Only applicable to non-cadet mode
|
||||
|
||||
// Guidance type for munitions
|
||||
defaultSeekerType = QGVAR(seeker);
|
||||
seekerTypes[] = {QGVAR(seeker)};
|
||||
|
||||
defaultSeekerLockMode = "LOBL";
|
||||
seekerLockModes[] = {"LOBL"};
|
||||
|
||||
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
|
||||
|
||||
seekerMinRange = 0;
|
||||
seekerMaxRange = 10; // Range from the missile which the seeker can visually search
|
||||
|
||||
// Attack profile type selection
|
||||
defaultAttackProfile = QGVAR(directAttack);
|
||||
attackProfiles[] = {QGVAR(directAttack), QGVAR(overflyTopAttack)};
|
||||
showHintOnCycle = 1;
|
||||
|
||||
// Run once at fired event
|
||||
onFired = QFUNC(onFired);
|
||||
};
|
||||
};
|
||||
|
||||
// Sub ammos used in OTA mode (see fnc_seeker.sqf)
|
||||
class ACE_NLAW_Explosion: ACE_NLAW { // Based on FCS-Airburst, will explode right away
|
||||
timeToLive = 0;
|
||||
model = "";
|
||||
};
|
||||
class ACE_NLAW_ShapedCharge: ACE_NLAW { // Shaped charge from rocket explosion, no effects
|
||||
timeToLive = 1;
|
||||
model = "";
|
||||
hit = 750;
|
||||
indirectHit = 0;
|
||||
indirectHitRange = 0;
|
||||
explosionSoundEffect = "";
|
||||
explosionEffects = "";
|
||||
CraterEffects = "";
|
||||
muzzleEffect = "";
|
||||
};
|
||||
};
|
17
addons/nlaw/CfgEventhandlers.hpp
Normal file
17
addons/nlaw/CfgEventhandlers.hpp
Normal file
@ -0,0 +1,17 @@
|
||||
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));
|
||||
};
|
||||
};
|
||||
|
||||
class Extended_PostInit_EventHandlers {
|
||||
class ADDON {
|
||||
init = QUOTE(call COMPILE_FILE(XEH_postInit));
|
||||
};
|
||||
};
|
6
addons/nlaw/CfgMagazines.hpp
Normal file
6
addons/nlaw/CfgMagazines.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
class CfgMagazines {
|
||||
class CA_LauncherMagazine;
|
||||
class NLAW_F: CA_LauncherMagazine {
|
||||
ammo = "ACE_NLAW";
|
||||
};
|
||||
};
|
13
addons/nlaw/CfgWeapons.hpp
Normal file
13
addons/nlaw/CfgWeapons.hpp
Normal file
@ -0,0 +1,13 @@
|
||||
class CfgWeapons {
|
||||
class Launcher_Base_F;
|
||||
class launch_NLAW_F: Launcher_Base_F {
|
||||
GVAR(enabled) = 1;
|
||||
canLock = 1;
|
||||
class OpticsModes {
|
||||
class optic {
|
||||
distanceZoomMin = 0;
|
||||
distanceZoomMax = 0;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
10
addons/nlaw/README.md
Normal file
10
addons/nlaw/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
ace_nlaw
|
||||
===============
|
||||
|
||||
Adds Predicted Line Of Sight guidance to the NLAW.
|
||||
|
||||
## Maintainers
|
||||
|
||||
The people responsible for merging changes to this component or answering potential questions.
|
||||
|
||||
- [PabstMirror](https://github.com/PabstMirror)
|
6
addons/nlaw/XEH_PREP.hpp
Normal file
6
addons/nlaw/XEH_PREP.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
LOG("prep");
|
||||
|
||||
PREP(attackProfile);
|
||||
PREP(keyDown);
|
||||
PREP(onFired);
|
||||
PREP(seeker);
|
46
addons/nlaw/XEH_postInit.sqf
Normal file
46
addons/nlaw/XEH_postInit.sqf
Normal file
@ -0,0 +1,46 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
if (!hasInterface) exitWith {};
|
||||
|
||||
GVAR(isLockKeyDown) = false;
|
||||
|
||||
// Degrees per second
|
||||
GVAR(yawChange) = 0;
|
||||
GVAR(pitchChange) = 0;
|
||||
|
||||
// Add keybind
|
||||
["ACE3 Weapons", QGVAR(trackTarget), localize LSTRING(trackTarget), {
|
||||
call FUNC(keyDown);
|
||||
false // Return false so it doesn't block the rest weapon action
|
||||
}, {
|
||||
TRACE_1("lock key up",GVAR(isLockKeyDown));
|
||||
GVAR(isLockKeyDown) = false;
|
||||
false
|
||||
}, [15, [false, false, false]], false] call CBA_fnc_addKeybind; //Tab Key
|
||||
|
||||
|
||||
|
||||
// Visual debuging, idealy used with a moving vehicle called "testTarget"
|
||||
#ifdef DRAW_NLAW_INFO
|
||||
addMissionEventHandler ["Draw3d", {
|
||||
// GREEN - Draw an object called "testTarget"'s aim pos and 1 sec aimpos predicted by velocity
|
||||
if ((!isNil "testTarget") && {!isNull testTarget}) then {
|
||||
{
|
||||
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0,1,0,1], ASLtoAGL ((aimPos testTarget) vectorAdd ((velocity testTarget) vectorMultiply _x)), 0.75, 0.75, 0, format ["%1", _x], 1, 0.025, "TahomaB"];
|
||||
} forEach [0, 1, 2, 3];
|
||||
};
|
||||
|
||||
// RED - If lock key is down, draw weapon dir and predicted path at various times
|
||||
if (GVAR(yawChange) != 0) then {
|
||||
{
|
||||
private _viewASL = AGLtoASL positionCameraToWorld [0,0,0];
|
||||
private _viewDir = ACE_player weaponDirection (currentWeapon ACE_player);
|
||||
(_viewDir call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"];
|
||||
private _realYaw = _yaw + GVAR(yawChange) * _x;
|
||||
private _realPitch = _pitch + GVAR(pitchChange) * _x;
|
||||
private _returnTargetPos = _viewASL vectorAdd ([1000, _realYaw, _realPitch] call CBA_fnc_polar2vect);
|
||||
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], ASLtoAGL _returnTargetPos, 0.75, 0.75, 0, format ["%1", _x], 1, 0.025, "TahomaB"];
|
||||
} forEach [0, 1, 2, 3];
|
||||
};
|
||||
}];
|
||||
#endif
|
9
addons/nlaw/XEH_preInit.sqf
Normal file
9
addons/nlaw/XEH_preInit.sqf
Normal file
@ -0,0 +1,9 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
ADDON = false;
|
||||
|
||||
PREP_RECOMPILE_START;
|
||||
#include "XEH_PREP.hpp"
|
||||
PREP_RECOMPILE_END;
|
||||
|
||||
ADDON = true;
|
3
addons/nlaw/XEH_preStart.sqf
Normal file
3
addons/nlaw/XEH_preStart.sqf
Normal file
@ -0,0 +1,3 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
#include "XEH_PREP.hpp"
|
22
addons/nlaw/config.cpp
Normal file
22
addons/nlaw/config.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
#include "script_component.hpp"
|
||||
|
||||
class CfgPatches {
|
||||
class ADDON {
|
||||
name = COMPONENT_NAME;
|
||||
units[] = {};
|
||||
weapons[] = {};
|
||||
requiredVersion = REQUIRED_VERSION;
|
||||
requiredAddons[] = {"ace_missileguidance"};
|
||||
author = ECSTRING(common,ACETeam);
|
||||
url = ECSTRING(main,URL);
|
||||
VERSION_CONFIG;
|
||||
};
|
||||
};
|
||||
|
||||
#include "CfgEventhandlers.hpp"
|
||||
|
||||
#include "CfgAmmo.hpp"
|
||||
#include "CfgMagazines.hpp"
|
||||
#include "CfgWeapons.hpp"
|
||||
|
||||
#include "ACE_GuidanceConfig.hpp"
|
60
addons/nlaw/functions/fnc_attackProfile.sqf
Normal file
60
addons/nlaw/functions/fnc_attackProfile.sqf
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* NLAW missile guidance attack profile.
|
||||
*
|
||||
* Arguments:
|
||||
* 0: Seeker Target PosASL <ARRAY>
|
||||
* 1: Guidance Arg Array <ARRAY>
|
||||
* 2: Attack Profile State <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* Missile Aim PosASL <ARRAY>
|
||||
*
|
||||
* Example:
|
||||
* [[1,2,3], [], []] call ace_nlaw_fnc_attackProfile
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
#include "script_component.hpp"
|
||||
|
||||
params ["_seekerTargetPos", "_args", "_attackProfileStateParams"];
|
||||
_args params ["_firedEH", "_launchParams"];
|
||||
_launchParams params ["","_targetLaunchParams", "", "_attackProfile"];
|
||||
_targetLaunchParams params ["", "", "_launchPos"];
|
||||
_firedEH params ["","","","","","","_projectile"];
|
||||
|
||||
// Use seeker (if terminal)
|
||||
if (!(_seekerTargetPos isEqualTo [0,0,0])) exitWith {_seekerTargetPos};
|
||||
|
||||
_attackProfileStateParams params ["_startTime", "_startLOS", "_yawChange", "_pitchChange"];
|
||||
(_startLOS call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"];
|
||||
|
||||
private _projectilePos = getPosASL _projectile;
|
||||
private _distanceFromLaunch = (_launchPos distance _projectilePos) + 10;
|
||||
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"];
|
||||
drawLine3D [ASLtoAGL _launchPos, ASLtoAGL (_launchPos vectorAdd (_startLOS vectorMultiply (_distanceFromLaunch + 50))), [1,0,0,1]];
|
||||
drawLine3D [ASLtoAGL _launchPos, ASLtoAGL (_launchPos vectorAdd ([_distanceFromLaunch + 50, _realYaw, _realPitch] call CBA_fnc_polar2vect)), [1,1,0,1]];
|
||||
private _test = lineIntersectsSurfaces [_launchPos, _launchPos vectorAdd (_startLOS vectorMultiply 3000), player, _projectile];
|
||||
if ((count _test) > 0) then {
|
||||
private _posAGL = ASLtoAGL ((_test select 0) select 0);
|
||||
drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], _posAGL, 0.75, 0.75, 0, "Original Impact", 1, 0.025, "TahomaB"];
|
||||
};
|
||||
#endif
|
||||
|
||||
// TRACE_1("Adjusted target position", _returnTargetPos);
|
||||
_returnTargetPos;
|
81
addons/nlaw/functions/fnc_keyDown.sqf
Normal file
81
addons/nlaw/functions/fnc_keyDown.sqf
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Handles the track key being held down.
|
||||
* Tracks change in direction of weapon and computes angle change per second.
|
||||
*
|
||||
* Arguments:
|
||||
* None
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call ace_nlaw_fnc_keyDown
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
// #define DEBUG_MODE_FULL
|
||||
#include "script_component.hpp"
|
||||
|
||||
TRACE_1("lock key down",GVAR(isLockKeyDown));
|
||||
|
||||
if (!alive ACE_player) exitWith {};
|
||||
if (!([ACE_player, objNull, ["isNotInside"]] call EFUNC(common,canInteractWith))) exitWith {};
|
||||
if (!(ACE_player call CBA_fnc_canUseWeapon)) exitWith {};
|
||||
if ((getNumber (configFile >> "CfgWeapons" >> (currentWeapon ACE_player) >> QGVAR(enabled))) == 0) exitWith {};
|
||||
if (GVAR(isLockKeyDown)) exitWith {ERROR("already running?");};
|
||||
|
||||
GVAR(isLockKeyDown) = true;
|
||||
playSound "ACE_Sound_Click";
|
||||
|
||||
// Get starting weapon dir
|
||||
((ACE_player weaponDirection (currentWeapon ACE_player)) call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"];
|
||||
|
||||
[{
|
||||
params ["_args", "_pfID"];
|
||||
_args params ["_lastTime", "_lastYaw", "_lastPitch", "_initPhase"];
|
||||
|
||||
if ((!alive ACE_player) ||
|
||||
{!([ACE_player, objNull, ["isNotInside"]] call EFUNC(common,canInteractWith))} ||
|
||||
{!GVAR(isLockKeyDown)} ||
|
||||
{!(ACE_player call CBA_fnc_canUseWeapon)} ||
|
||||
{(getNumber (configFile >> "CfgWeapons" >> (currentWeapon ACE_player) >> QGVAR(enabled))) == 0})
|
||||
exitWith {
|
||||
TRACE_1("ending track",_pfID);
|
||||
[_pfID] call CBA_fnc_removePerFrameHandler;
|
||||
playSound "ACE_Sound_Click";
|
||||
|
||||
[{ // reset gvars after a short delay
|
||||
TRACE_1("reset vars",_this);
|
||||
GVAR(yawChange) = 0;
|
||||
GVAR(pitchChange) = 0;
|
||||
}, [], 0.5] call CBA_fnc_waitAndExecute;
|
||||
};
|
||||
|
||||
private _deltaT = CBA_missionTime - _lastTime;
|
||||
if (_deltaT == 0) exitWith {};
|
||||
if (_initPhase && {_deltaT < 0.75}) exitWith {};
|
||||
|
||||
((ACE_player weaponDirection (currentWeapon ACE_player)) call CBA_fnc_vect2Polar) params ["", "_yaw", "_pitch"];
|
||||
private _yawChange = ([_yaw - _lastYaw] call CBA_fnc_simplifyAngle180) / _deltaT;
|
||||
private _pitchChange = ([_pitch - _lastPitch] call CBA_fnc_simplifyAngle180) / _deltaT;
|
||||
|
||||
if (_initPhase) then { // initial value will use first 0.75 seconds of input
|
||||
GVAR(yawChange) = _yawChange;
|
||||
GVAR(pitchChange) = _pitchChange;
|
||||
_args set [3, false];
|
||||
} else {
|
||||
// smoothing factor alpha - higher values will be more responsive to change, but also spike higher on jerky mouse movmeent
|
||||
private _alpha = _deltaT / 3;
|
||||
GVAR(yawChange) = (_yawChange * _alpha) + GVAR(yawChange) * (1 - _alpha);
|
||||
GVAR(pitchChange) = (_pitchChange * _alpha) + GVAR(pitchChange) * (1 - _alpha);
|
||||
};
|
||||
|
||||
_args set [0, CBA_missionTime];
|
||||
_args set [1, _yaw];
|
||||
_args set [2, _pitch];
|
||||
|
||||
#ifdef DEBUG_MODE_FULL
|
||||
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;
|
62
addons/nlaw/functions/fnc_onFired.sqf
Normal file
62
addons/nlaw/functions/fnc_onFired.sqf
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Sets up missile guidance state arrays (called from missileGuidance's onFired).
|
||||
*
|
||||
* Arguments:
|
||||
* Guidance Arg Array <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* None
|
||||
*
|
||||
* Example:
|
||||
* [] call ace_nlaw_fnc_onFired
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
#include "script_component.hpp"
|
||||
|
||||
params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams"];
|
||||
_firedEH params ["_shooter","","","","","","_projectile"];
|
||||
_launchParams params ["","_targetLaunchParams","","_attackProfile"];
|
||||
_targetLaunchParams params ["_target"];
|
||||
_stateParams params ["", "", "_attackProfileStateParams"];
|
||||
|
||||
// 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;
|
||||
|
||||
TRACE_3("attackProfileStateParams",_firedLOS,_yawChange,_pitchChange);
|
||||
_attackProfileStateParams set [0, CBA_missionTime];
|
||||
_attackProfileStateParams set [1, _firedLOS];
|
||||
_attackProfileStateParams set [2, _yawChange];
|
||||
_attackProfileStateParams set [3, _pitchChange];
|
94
addons/nlaw/functions/fnc_seeker.sqf
Normal file
94
addons/nlaw/functions/fnc_seeker.sqf
Normal file
@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Author: PabstMirror
|
||||
* Handles the top down attack seeker for missile guidance.
|
||||
* Has a very short range (IR/Magnetic?) seeker that will trigger the shaped charge midair above the target.
|
||||
*
|
||||
* Arguments:
|
||||
* 1: Guidance Arg Array <ARRAY>
|
||||
* 2: Seeker State <ARRAY>
|
||||
*
|
||||
* Return Value:
|
||||
* Seeker Pos <ARRAY>
|
||||
*
|
||||
* Example:
|
||||
* [] call ace_nlaw_fnc_seeker
|
||||
*
|
||||
* Public: No
|
||||
*/
|
||||
// #define DEBUG_MODE_FULL
|
||||
#include "script_component.hpp"
|
||||
|
||||
params ["", "_args", "_seekerStateParams"];
|
||||
_args params ["_firedEH", "_launchParams", "", "_seekerParams", "_stateParams"];
|
||||
_firedEH params ["","","","","","","_projectile"];
|
||||
_launchParams params ["", "_targetLaunchParams", "", "_attackProfile"];
|
||||
_targetLaunchParams params ["", "", "_launchPos"];
|
||||
|
||||
if (_attackProfile == QGVAR(directAttack)) exitWith {[0,0,0]};
|
||||
|
||||
private _projPos = getPosASL _projectile;
|
||||
|
||||
// Arm seeker after 20 meters
|
||||
if ((_projPos distance _launchPos) >= 20) then {
|
||||
scopeName "targetScan";
|
||||
BEGIN_COUNTER(targetScan);
|
||||
|
||||
if (_seekerStateParams isEqualTo []) then {
|
||||
TRACE_2("Seeker Armed",_projPos distance _launchPos,diag_fps);
|
||||
_seekerStateParams set [0, _projPos]; // Set _lastPos to current position
|
||||
};
|
||||
|
||||
_seekerStateParams params ["_lastPos", "_terminal"];
|
||||
if (_terminal) exitWith {};
|
||||
|
||||
private _vectorDir = _lastPos vectorFromTo _projPos;
|
||||
private _frameDistance = _lastPos vectorDistance _projPos;
|
||||
|
||||
// Distance traveled depends on velocity and FPS - at 60fps it will be ~4m
|
||||
// Step size will effect accuracy and performance costs
|
||||
for "_stepSize" from 0 to _frameDistance step 0.5 do {
|
||||
// This represents a position that the missile was at between the last frame and now
|
||||
private _virtualPos = _lastPos vectorAdd (_vectorDir vectorMultiply _stepSize);
|
||||
#ifdef DRAW_NLAW_INFO
|
||||
drawLine3D [ASLtoAGL _virtualPos, ASLtoAGL (_virtualPos vectorAdd [0,0,-5]), [1,0,_stepSize/(_frameDistance max 0.1),1]];
|
||||
#endif
|
||||
|
||||
// Limit scan to 5 meters directly down (shaped charge jet has a very limited range)
|
||||
private _res = lineIntersectsSurfaces [_virtualPos, (_virtualPos vectorAdd [0,0,-5]), _projectile];
|
||||
if (!(_res isEqualTo [])) then {
|
||||
(_res select 0) params ["_targetPos", "", "_target"];
|
||||
if ((_target isKindOf "Tank") || {_target isKindOf "Car"} || {_target isKindOf "Air"}) exitWith {
|
||||
TRACE_3("Firing shaped charge down",_target,_targetPos distance _virtualPos,_frameDistance);
|
||||
TRACE_2("",_target worldToModel (ASLtoAGL _virtualPos),boundingBoxReal _target);
|
||||
_virtualPos = _virtualPos vectorAdd (_vectorDir vectorMultiply 1.25);
|
||||
|
||||
deleteVehicle _projectile;
|
||||
|
||||
// Damage and effects of missile exploding (timeToLive is 0 so should happen next frame)
|
||||
private _explosion = "ACE_NLAW_Explosion" createVehicle _virtualPos;
|
||||
_explosion setPosASL _virtualPos;
|
||||
|
||||
// Just damage from shaped charge
|
||||
private _shapedCharage = "ACE_NLAW_ShapedCharge" createVehicle _virtualPos;
|
||||
_shapedCharage setPosASL _virtualPos;
|
||||
_shapedCharage setVectorDirAndUp [[0,0,-1], [1,0,0]];
|
||||
_shapedCharage setVelocity [0,0,-300];
|
||||
|
||||
_seekerStateParams set [1, true];
|
||||
|
||||
END_COUNTER(targetScan);
|
||||
breakOut "targetScan";
|
||||
};
|
||||
};
|
||||
};
|
||||
_seekerStateParams set [0, _projPos];
|
||||
END_COUNTER(targetScan);
|
||||
};
|
||||
|
||||
// Exploded, return dummy value
|
||||
if (_seekerStateParams param [1, false]) exitWith {
|
||||
[0,0,1]
|
||||
};
|
||||
|
||||
// return:
|
||||
[0,0,0]
|
1
addons/nlaw/functions/script_component.hpp
Normal file
1
addons/nlaw/functions/script_component.hpp
Normal file
@ -0,0 +1 @@
|
||||
#include "\z\ace\addons\nlaw\script_component.hpp"
|
18
addons/nlaw/script_component.hpp
Normal file
18
addons/nlaw/script_component.hpp
Normal file
@ -0,0 +1,18 @@
|
||||
#define COMPONENT nlaw
|
||||
#define COMPONENT_BEAUTIFIED NLAW
|
||||
#include "\z\ace\addons\main\script_mod.hpp"
|
||||
|
||||
// #define DRAW_NLAW_INFO
|
||||
// #define DEBUG_MODE_FULL
|
||||
// #define DISABLE_COMPILE_CACHE
|
||||
// #define ENABLE_PERFORMANCE_COUNTERS
|
||||
|
||||
#ifdef DEBUG_ENABLED_NLAW
|
||||
#define DEBUG_MODE_FULL
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_SETTINGS_NLAW
|
||||
#define DEBUG_SETTINGS DEBUG_SETTINGS_NLAW
|
||||
#endif
|
||||
|
||||
#include "\z\ace\addons\main\script_macros.hpp"
|
14
addons/nlaw/stringtable.xml
Normal file
14
addons/nlaw/stringtable.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project name="ACE">
|
||||
<Package name="NLAW">
|
||||
<Key ID="STR_ACE_NLAW_trackTarget">
|
||||
<English>NLAW Track Target (Hold)</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_NLAW_directAttack">
|
||||
<English>Direct Attack</English>
|
||||
</Key>
|
||||
<Key ID="STR_ACE_NLAW_overflyTopAttack">
|
||||
<English>Overfly Top Attack</English>
|
||||
</Key>
|
||||
</Package>
|
||||
</Project>
|
35
docs/wiki/feature/nlaw.md
Normal file
35
docs/wiki/feature/nlaw.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
layout: wiki
|
||||
title: NLAW
|
||||
description: NLAW
|
||||
group: feature
|
||||
category: equipment
|
||||
parent: wiki
|
||||
mod: ace
|
||||
version:
|
||||
major: 3
|
||||
minor: 10
|
||||
patch: 0
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
### 1.1 Guidance
|
||||
NLAW uses Predicted Line Of Sight guidance.
|
||||
Before firing the shooter tracks the targets for several seconds.
|
||||
This programs the missile with the angular rotation and allows it to fly a curved path that will hit the target.
|
||||
It will also correct for gravity drop.
|
||||
|
||||
### 1.2 Attack profiles
|
||||
- Direct - Normal impact fuze for non-armored targets. Note that the missile's shaped charge is aimed downards, so this mode is not recomended against armor.
|
||||
- Overfly Top Attack - Flies high and when sensors detects a target below it triggers the shaped charge to fire downards into the weak top armor.
|
||||
|
||||
## 2. Usage
|
||||
- Cycle attack profiles with the missile guidance "Cycle Fire Mode" keybind (default: <kbd>Ctrl</kbd> + <kbd>Tab</kbd>)
|
||||
- Start tracking by pressing and holding the "NLAW Track Target" keybind (default: <kbd>Tab</kbd>)
|
||||
- While holding the key down track the target for 2-3 seconds and fire.
|
||||
- Can also be fired against static targets without tracking.
|
||||
|
||||
## 3. Dependencies
|
||||
|
||||
{% include dependencies_list.md component="nlaw" %}
|
Loading…
Reference in New Issue
Block a user