diff --git a/addons/disposable/CfgWeapons.hpp b/addons/disposable/CfgWeapons.hpp index 0ca6763d63..731910b704 100644 --- a/addons/disposable/CfgWeapons.hpp +++ b/addons/disposable/CfgWeapons.hpp @@ -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); diff --git a/addons/missileguidance/functions/fnc_onFired.sqf b/addons/missileguidance/functions/fnc_onFired.sqf index 224cfb14ec..bf7d81331e 100644 --- a/addons/missileguidance/functions/fnc_onFired.sqf +++ b/addons/missileguidance/functions/fnc_onFired.sqf @@ -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"]; diff --git a/addons/nlaw/$PBOPREFIX$ b/addons/nlaw/$PBOPREFIX$ new file mode 100644 index 0000000000..cbcdbb46de --- /dev/null +++ b/addons/nlaw/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\nlaw diff --git a/addons/nlaw/ACE_GuidanceConfig.hpp b/addons/nlaw/ACE_GuidanceConfig.hpp new file mode 100644 index 0000000000..e04753d681 --- /dev/null +++ b/addons/nlaw/ACE_GuidanceConfig.hpp @@ -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); + }; +}; diff --git a/addons/nlaw/CfgAmmo.hpp b/addons/nlaw/CfgAmmo.hpp new file mode 100644 index 0000000000..f0649b2ba4 --- /dev/null +++ b/addons/nlaw/CfgAmmo.hpp @@ -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 = ""; + }; +}; diff --git a/addons/nlaw/CfgEventhandlers.hpp b/addons/nlaw/CfgEventhandlers.hpp new file mode 100644 index 0000000000..0d3301d6e0 --- /dev/null +++ b/addons/nlaw/CfgEventhandlers.hpp @@ -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)); + }; +}; diff --git a/addons/nlaw/CfgMagazines.hpp b/addons/nlaw/CfgMagazines.hpp new file mode 100644 index 0000000000..fe2e2ecf8d --- /dev/null +++ b/addons/nlaw/CfgMagazines.hpp @@ -0,0 +1,6 @@ +class CfgMagazines { + class CA_LauncherMagazine; + class NLAW_F: CA_LauncherMagazine { + ammo = "ACE_NLAW"; + }; +}; diff --git a/addons/nlaw/CfgWeapons.hpp b/addons/nlaw/CfgWeapons.hpp new file mode 100644 index 0000000000..35f5b17122 --- /dev/null +++ b/addons/nlaw/CfgWeapons.hpp @@ -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; + }; + }; + }; +}; diff --git a/addons/nlaw/README.md b/addons/nlaw/README.md new file mode 100644 index 0000000000..dd62b36014 --- /dev/null +++ b/addons/nlaw/README.md @@ -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) diff --git a/addons/nlaw/XEH_PREP.hpp b/addons/nlaw/XEH_PREP.hpp new file mode 100644 index 0000000000..aad1e57efb --- /dev/null +++ b/addons/nlaw/XEH_PREP.hpp @@ -0,0 +1,6 @@ +LOG("prep"); + +PREP(attackProfile); +PREP(keyDown); +PREP(onFired); +PREP(seeker); diff --git a/addons/nlaw/XEH_postInit.sqf b/addons/nlaw/XEH_postInit.sqf new file mode 100644 index 0000000000..885e2d5e2f --- /dev/null +++ b/addons/nlaw/XEH_postInit.sqf @@ -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 diff --git a/addons/nlaw/XEH_preInit.sqf b/addons/nlaw/XEH_preInit.sqf new file mode 100644 index 0000000000..b47cf6628d --- /dev/null +++ b/addons/nlaw/XEH_preInit.sqf @@ -0,0 +1,9 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +ADDON = true; diff --git a/addons/nlaw/XEH_preStart.sqf b/addons/nlaw/XEH_preStart.sqf new file mode 100644 index 0000000000..022888575e --- /dev/null +++ b/addons/nlaw/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/nlaw/config.cpp b/addons/nlaw/config.cpp new file mode 100644 index 0000000000..7109459066 --- /dev/null +++ b/addons/nlaw/config.cpp @@ -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" diff --git a/addons/nlaw/functions/fnc_attackProfile.sqf b/addons/nlaw/functions/fnc_attackProfile.sqf new file mode 100644 index 0000000000..778a1924b3 --- /dev/null +++ b/addons/nlaw/functions/fnc_attackProfile.sqf @@ -0,0 +1,60 @@ +/* + * Author: PabstMirror + * NLAW missile guidance attack profile. + * + * Arguments: + * 0: Seeker Target PosASL + * 1: Guidance Arg Array + * 2: Attack Profile State + * + * Return Value: + * Missile Aim PosASL + * + * 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; diff --git a/addons/nlaw/functions/fnc_keyDown.sqf b/addons/nlaw/functions/fnc_keyDown.sqf new file mode 100644 index 0000000000..82dfbbcc2e --- /dev/null +++ b/addons/nlaw/functions/fnc_keyDown.sqf @@ -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; diff --git a/addons/nlaw/functions/fnc_onFired.sqf b/addons/nlaw/functions/fnc_onFired.sqf new file mode 100644 index 0000000000..7049dfb918 --- /dev/null +++ b/addons/nlaw/functions/fnc_onFired.sqf @@ -0,0 +1,62 @@ +/* + * Author: PabstMirror + * Sets up missile guidance state arrays (called from missileGuidance's onFired). + * + * Arguments: + * Guidance Arg 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]; diff --git a/addons/nlaw/functions/fnc_seeker.sqf b/addons/nlaw/functions/fnc_seeker.sqf new file mode 100644 index 0000000000..850c966f56 --- /dev/null +++ b/addons/nlaw/functions/fnc_seeker.sqf @@ -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 + * 2: Seeker State + * + * Return Value: + * Seeker Pos + * + * 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] diff --git a/addons/nlaw/functions/script_component.hpp b/addons/nlaw/functions/script_component.hpp new file mode 100644 index 0000000000..50d8cac4c1 --- /dev/null +++ b/addons/nlaw/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\z\ace\addons\nlaw\script_component.hpp" diff --git a/addons/nlaw/script_component.hpp b/addons/nlaw/script_component.hpp new file mode 100644 index 0000000000..2cabaf7e47 --- /dev/null +++ b/addons/nlaw/script_component.hpp @@ -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" diff --git a/addons/nlaw/stringtable.xml b/addons/nlaw/stringtable.xml new file mode 100644 index 0000000000..14432e4089 --- /dev/null +++ b/addons/nlaw/stringtable.xml @@ -0,0 +1,14 @@ + + + + + NLAW Track Target (Hold) + + + Direct Attack + + + Overfly Top Attack + + + diff --git a/docs/wiki/feature/nlaw.md b/docs/wiki/feature/nlaw.md new file mode 100644 index 0000000000..471a6bdc38 --- /dev/null +++ b/docs/wiki/feature/nlaw.md @@ -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: Ctrl + Tab) +- Start tracking by pressing and holding the "NLAW Track Target" keybind (default: Tab) +- 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" %}