diff --git a/addons/artillerytables/functions/fnc_firedEH.sqf b/addons/artillerytables/functions/fnc_firedEH.sqf index 5aa46c69690..853dd39debb 100644 --- a/addons/artillerytables/functions/fnc_firedEH.sqf +++ b/addons/artillerytables/functions/fnc_firedEH.sqf @@ -62,6 +62,9 @@ if (_newMuzzleVelocityCoefficent != 1) then { if (_airFriction > 0) exitWith {}; // positive value indicates it has vanilla airFriction, so we can just exit +// For compatiblity with other addons +_projectile setVariable [QGVAR(kFactor), _kFactor]; + [{ params ["_projectile", "_kFactor", "_time"]; diff --git a/addons/missile_clgp/$PBOPREFIX$ b/addons/missile_clgp/$PBOPREFIX$ new file mode 100644 index 00000000000..433858d3351 --- /dev/null +++ b/addons/missile_clgp/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\missile_clgp diff --git a/addons/missile_clgp/ACE_GuidanceConfig.hpp b/addons/missile_clgp/ACE_GuidanceConfig.hpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/missile_clgp/CfgAmmo.hpp b/addons/missile_clgp/CfgAmmo.hpp new file mode 100644 index 00000000000..12b153dccc7 --- /dev/null +++ b/addons/missile_clgp/CfgAmmo.hpp @@ -0,0 +1,89 @@ +class CfgAmmo { + class SubmunitionBase; + + + + class GVAR(pike_launch): SubmunitionBase { + model = QPATHTOF(data\ace_pike_ammo.p3d); + submunitionAmmo = QGVAR(pike_guidance); + submunitionCount = 1; + submunitionConeAngle = 0; + triggerTime = 0.1; + class Eventhandlers { + fired = QUOTE(call FUNC(submunition_ammoFired)); + }; + }; + + class MissileBase; + class GVAR(pike_guidance): MissileBase { + irLock = 0; + laserLock = 0; + airLock = 0; + manualControl = 0; + timeToLive = 22; + + model = QPATHTOF(data\ace_pike_ammo.p3d); + maxSpeed = 150; // ~2km in 15sec + thrust = 19; + thrustTime = 14; + initTime = 0; + airFriction = 0.1; + sideAirFriction = 0.1; + + hit = 120; + indirectHit = 10; + indirectHitRange = 10; + warheadName = "HE"; + + EGVAR(frag,enabled) = 1; + EGVAR(frag,force) = 1; + EGVAR(frag,classes)[] = {QEGVAR(frag,tiny_HD)}; + EGVAR(frag,metal) = 200; + EGVAR(frag,charge) = 270; // ~8x a normal 40mm + EGVAR(frag,gurney_c) = 2700; + EGVAR(frag,gurney_k) = "1/2"; + + CraterEffects="ExploAmmoCrater"; + explosionEffects="ExploAmmoExplosion"; + effectsMissileInit = ""; + effectsMissile = "missile2"; + + SoundSetExplosion[] = {"GrenadeHe_Exp_SoundSet", "GrenadeHe_Tail_SoundSet", "Explosion_Debris_SoundSet"}; + soundHit1[] = { "A3\Sounds_F\arsenal\explosives\Grenades\Explosion_gng_grenades_01", 3.1622777, 1, 1500}; + soundHit2[] = { "A3\Sounds_F\arsenal\explosives\Grenades\Explosion_gng_grenades_02", 3.1622777, 1, 1500}; + soundHit3[] = { "A3\Sounds_F\arsenal\explosives\Grenades\Explosion_gng_grenades_03", 3.1622777, 1, 1500}; + soundHit4[] = { "A3\Sounds_F\arsenal\explosives\Grenades\Explosion_gng_grenades_04", 3.1622777, 1, 1500}; + multiSoundHit[] = {"soundHit1", 0.25, "soundHit2", 0.25, "soundHit3", 0.25, "soundHit4", 0.25}; + whistleDist=16; + + // Begin ACE guidance Configs + class ace_missileguidance { + enabled = 2; + + pitchRate = 15; + yawRate = 20; + + canVanillaLock = 0; // Can this default vanilla lock? Only applicable to non-cadet mode + + // Guidance type for munitions + defaultSeekerType = "SALH"; + seekerTypes[] = {"SALH"}; + + defaultSeekerLockMode = "LOAL"; + seekerLockModes[] = {"LOAL"}; + + defaultNavigationType = "Direct"; + navigationTypes[] = { "Direct" }; + + seekerAngle = 90; // Angle in front of the missile which can be searched + seekerAccuracy = 1; // seeker accuracy multiplier + + seekerMinRange = 1; + seekerMaxRange = 3000; // Range from the missile which the seeker can visually search + + // Attack profile type selection + defaultAttackProfile = "LIN"; + attackProfiles[] = {"LIN"}; + }; + }; +}; diff --git a/addons/missile_clgp/CfgEventhandlers.hpp b/addons/missile_clgp/CfgEventhandlers.hpp new file mode 100644 index 00000000000..2a3f71f852d --- /dev/null +++ b/addons/missile_clgp/CfgEventhandlers.hpp @@ -0,0 +1,15 @@ +class Extended_PreStart_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); + }; +}; +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preInit)); + }; +}; +class Extended_PostInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_postInit)); + }; +}; diff --git a/addons/missile_clgp/CfgMagazineWells.hpp b/addons/missile_clgp/CfgMagazineWells.hpp new file mode 100644 index 00000000000..e5ccc8b0749 --- /dev/null +++ b/addons/missile_clgp/CfgMagazineWells.hpp @@ -0,0 +1,5 @@ +class CfgMagazineWells { + class CBA_40mm_EGLM { // for longer grenades that can only fit side breech-loading + ADDON[] = {"ACE_40mm_Pike"}; + }; +}; diff --git a/addons/missile_clgp/CfgMagazines.hpp b/addons/missile_clgp/CfgMagazines.hpp new file mode 100644 index 00000000000..99886a2a938 --- /dev/null +++ b/addons/missile_clgp/CfgMagazines.hpp @@ -0,0 +1,15 @@ +class CfgMagazines { + class 1Rnd_HE_Grenade_shell; + class ACE_40mm_Pike: 1Rnd_HE_Grenade_shell { + author = ECSTRING(common,ACETeam); + displayName = CSTRING(pike_magazine_displayName); + displayNameShort = CSTRING(pike_magazine_displayNameShort); + descriptionShort = CSTRING(pike_magazine_descriptionShort); + picture = QPATHTOF(ui\gear_pike_ca.paa); + ammo = QGVAR(pike_launch); + count = 1; + // model = QPATHTOF(data\ace_pike_ammo.p3d); // just use default pouch + initSpeed = 50; + mass = 12; // ~1.7 Pounds, but high density + }; +}; diff --git a/addons/missile_clgp/CfgWeapons.hpp b/addons/missile_clgp/CfgWeapons.hpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/missile_clgp/GUI.hpp b/addons/missile_clgp/GUI.hpp new file mode 100644 index 00000000000..e69de29bb2d diff --git a/addons/missile_clgp/README.md b/addons/missile_clgp/README.md new file mode 100644 index 00000000000..9e1065a9f00 --- /dev/null +++ b/addons/missile_clgp/README.md @@ -0,0 +1,4 @@ +ace_missile_clgp +=================== + +Cannon Launched Guided Projectiles diff --git a/addons/missile_clgp/XEH_PREP.hpp b/addons/missile_clgp/XEH_PREP.hpp new file mode 100644 index 00000000000..c6d509b520a --- /dev/null +++ b/addons/missile_clgp/XEH_PREP.hpp @@ -0,0 +1,5 @@ +LOG("prep"); + +PREP(submunition_ammoFired); +PREP(submunition_applyDrag); +PREP(submunition_submunitionCreated); diff --git a/addons/missile_clgp/XEH_postInit.sqf b/addons/missile_clgp/XEH_postInit.sqf new file mode 100644 index 00000000000..603af8fcd11 --- /dev/null +++ b/addons/missile_clgp/XEH_postInit.sqf @@ -0,0 +1,5 @@ +#include "script_component.hpp" + +#ifdef ENABLE_QUICK_TESTING +call compileScript [QPATHTOF(dev\quickTesting.sqf)]; +#endif diff --git a/addons/missile_clgp/XEH_preInit.sqf b/addons/missile_clgp/XEH_preInit.sqf new file mode 100644 index 00000000000..b47cf6628db --- /dev/null +++ b/addons/missile_clgp/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/missile_clgp/XEH_preStart.sqf b/addons/missile_clgp/XEH_preStart.sqf new file mode 100644 index 00000000000..022888575ed --- /dev/null +++ b/addons/missile_clgp/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/missile_clgp/config.cpp b/addons/missile_clgp/config.cpp new file mode 100644 index 00000000000..9221b0b9c99 --- /dev/null +++ b/addons/missile_clgp/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 "ACE_GuidanceConfig.hpp" +#include "CfgAmmo.hpp" +#include "CfgEventhandlers.hpp" +#include "CfgMagazines.hpp" +#include "CfgMagazineWells.hpp" +#include "CfgWeapons.hpp" +#include "GUI.hpp" diff --git a/addons/missile_clgp/data/ace_pike_ammo.p3d b/addons/missile_clgp/data/ace_pike_ammo.p3d new file mode 100644 index 00000000000..9a60996295d Binary files /dev/null and b/addons/missile_clgp/data/ace_pike_ammo.p3d differ diff --git a/addons/missile_clgp/dev/quickTesting.sqf b/addons/missile_clgp/dev/quickTesting.sqf new file mode 100644 index 00000000000..0bb998648c2 --- /dev/null +++ b/addons/missile_clgp/dev/quickTesting.sqf @@ -0,0 +1,77 @@ +#include "..\script_component.hpp" + +["recompile", "recompile", "recompile", { + private _start = diag_tickTime; + [] call ACE_PREP_RECOMPILE; + [] call ace_common_fnc_dumpPerformanceCounters; + private _end = diag_tickTime; + systemChat format ["recompile took [%1 ms]", (1000 * (_end - _start)) toFixed 1]; + + if (productVersion #4 == "Diag") then { + call compile "diag_mergeConfigFile ['P:\z\ace\addons\missile_clgp\config.cpp']"; + }; + false +}, {false}, [59, [false, false, false]], false] call CBA_fnc_addKeybind; // F1 Key + +DFUNC(dev_trackShell) = { + params ["_projectile"]; + [{ + params ["_projectile", "_time", "_lastVel"]; + if (isNull _projectile) exitWith { true }; + + private _ammo = typeOf _projectile; + if (_ammo isKindOf "SubmunitionBase") then { + private _projectilePos = getPosASL _projectile; + private _forwardPosition = _projectilePos vectorAdd ((vectorNormalized (velocity _projectile)) vectorMultiply 15); + drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [1,0,0,1], ASLToAGL _projectilePos, 0.75, 0.75, 0, _ammo, 1, 0.025, "TahomaB"]; + drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", [0.5,0,1,1], ASLToAGL _forwardPosition, 0.75, 0.75, 0, + format ["V: %1 (%2)", round vectorMagnitude velocity _projectile, round ((velocity _projectile) # 2)], 1, 0.025, "TahomaB"]; + drawLine3D [ASLToAGL _projectilePos, ASLToAGL _forwardPosition, [0.5,0,1,1]]; + }; + + private _deltaT = CBA_missionTime - _time; + if (_deltaT < 0.25) exitWith { false }; + + private _curVelocity = velocity _projectile; + private _avgSpeed = 0.5 * vectorMagnitude (_curVelocity vectorAdd _lastVel); + systemChat format ["Vel %1 (%2)", _curVelocity, _avgSpeed]; + + private _diff = _curVelocity vectorDiff _lastVel; + _diff = _diff vectorMultiply (1/_deltaT); + _diff = _diff vectorAdd [0,0,GRAVITY]; + private _drag = (vectorMagnitude _diff) / ((_avgSpeed max 1) ^ 2); + systemChat format ["Drag %1", (100000 * _drag) toFixed 2]; + + // systemChat format ["E %1", (GRAVITY * ((getposasl _projectile) # 2)) + (_avgSpeed ^2)]; + + _this set[1, CBA_missionTime]; + _this set[2, _curVelocity]; + false + }, {}, [_projectile, CBA_missionTime, velocity _projectile]] call CBA_fnc_waitUntilAndExecute; +}; + + +// ["turret", { +// params ["_player", "_turret"]; +// private _veh = vehicle _player; +// if (currentWeapon _veh == "mortar_155mm_AMOS" && {_veh getVariable [QGVAR(needSetup), true]}) then { +// _veh setVariable [QGVAR(needSetup), false]; +// systemChat "Setting up..."; +// for "_i" from 0 to 9 do { _veh addMagazineTurret ["ace_1rnd_155mm_m712", [0], 1]; }; +// _veh loadMagazine [[0], "mortar_155mm_AMOS", "ace_1rnd_155mm_m712"]; +// }; +// }, true] call CBA_fnc_addPlayerEventHandler; + +{ + _x addEventHandler ["Fired", { + params ["", "", "", "", "", "", "_projectile"]; + _projectile call FUNC(dev_trackShell); + _projectile addEventHandler ["SubmunitionCreated", { + params ["_projectile", "_submunitionProjectile"]; + hint format ["sub\n%1\n=%2 %3\n\n%4\n%5 %6", + velocity _projectile, vectorMagnitude velocity _projectile, local _projectile, + velocity _submunitionProjectile, vectorMagnitude velocity _submunitionProjectile, local _submunitionProjectile]; + _submunitionProjectile call FUNC(dev_trackShell); + }]; + }]; +} forEach (vehicles + [player]); diff --git a/addons/missile_clgp/functions/fnc_submunition_ammoFired.sqf b/addons/missile_clgp/functions/fnc_submunition_ammoFired.sqf new file mode 100644 index 00000000000..39aa6b97182 --- /dev/null +++ b/addons/missile_clgp/functions/fnc_submunition_ammoFired.sqf @@ -0,0 +1,49 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Generic handler for firing a clgp submuntion ammo. Called from the ammo's fired EH. + * + * Arguments: + * FiredEH + * + * Return Value: + * Nothing + * + * Example: + * [] call ace_missile_clgp_fnc_submunition_ammoFired + * + * Public: No + */ + +params ["_unit", "", "", "", "_ammo", "", "_projectile", "_gunner"]; +if (!local _gunner) exitWith {}; +if (isNull _projectile) exitWith {}; +TRACE_4("ammoFired local",typeOf _unit,_ammo,_projectile,_gunner); +private _ammoCfg = configOf _projectile; + +// Get MissileGuidance args now +private _firedEH = +_this; +// Inject the submunition ammo into guidance args +_firedEH set [4, getText (_ammoCfg >> "submunitionAmmo")]; +private _guidanceArgs = _firedEH call EFUNC(missileguidance,onFiredGetArgs); +if (_guidanceArgs isEqualTo []) then { WARNING_1("no args %1",_projectile); }; +_projectile setVariable [QGVAR(guidanceArgs), _guidanceArgs]; + +// On missile deploy start guidance +_projectile addEventHandler ["SubmunitionCreated", LINKFUNC(submunition_submunitionCreated)]; + +// Trigger ammo when conditions are met +private _deployCondition = getText (_ammoCfg >> QGVAR(deployCondition)); +if (_deployCondition != "") then { + _deployCondition = missionNamespace getVariable [_deployCondition, { ERROR("bad func"); true} ]; + [{ + params ["_projectile", "", "_deployCondition"]; + if (isNull _projectile) exitWith { true }; + _this call _deployCondition + }, { + params ["_projectile"]; + if (isNull _projectile) exitWith { TRACE_1("null at trigger condition %1",_projectile); }; + TRACE_1("triggerAmmo",_projectile); + triggerAmmo _projectile; + }, [_projectile, _guidanceArgs, _deployCondition]] call CBA_fnc_waitUntilAndExecute; +}; diff --git a/addons/missile_clgp/functions/fnc_submunition_applyDrag.sqf b/addons/missile_clgp/functions/fnc_submunition_applyDrag.sqf new file mode 100644 index 00000000000..b0f6b3303cd --- /dev/null +++ b/addons/missile_clgp/functions/fnc_submunition_applyDrag.sqf @@ -0,0 +1,39 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Applies drag to a missile (based on ace_artillerytables_fnc_firedEH) + * + * Arguments: + * 0: Projectile + * 1: Drag + * 2: Last time ran (modified via _this array) + * + * Return Value: + * None + * + * Example: + * [x, 2, 2] call ace_missile_clgp_fnc_submunition_applyDrag + * + * Public: No + */ + +params ["_projectile", "_kFactor", "_time"]; + +private _bulletVelocity = velocity _projectile; +private _deltaT = CBA_missionTime - _time; +_this set [2, CBA_missionTime]; + +if (_kFactor != 0) then { + private _trueVelocity = _bulletVelocity vectorDiff wind; + private _trueSpeed = vectorMagnitude _trueVelocity; + + private _drag = _deltaT * _kFactor * _trueSpeed; + private _accel = _trueVelocity vectorMultiply _drag; + _bulletVelocity = _bulletVelocity vectorAdd _accel; + + _projectile setVelocity _bulletVelocity; +}; + +private _dir = vectorNormalized _bulletVelocity; +_projectile setVectorDirAndUp [_dir, _dir vectorCrossProduct vectorSide _projectile]; +TRACE_2("setVectorDirAndUp",_projectile,_dir); diff --git a/addons/missile_clgp/functions/fnc_submunition_submunitionCreated.sqf b/addons/missile_clgp/functions/fnc_submunition_submunitionCreated.sqf new file mode 100644 index 00000000000..8c1be94a802 --- /dev/null +++ b/addons/missile_clgp/functions/fnc_submunition_submunitionCreated.sqf @@ -0,0 +1,42 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Generic handler for submunitionCreated + * + * Arguments: + * 0: Old Projectile + * 1: New Projectile + * + * Return Value: + * Nothing + * + * Example: + * [a,b] call ace_missile_clgp_fnc_submunition_submunitionCreated + * + * Public: No + */ + +params ["_projectile", "_submunitionProjectile"]; + +private _guidanceArgs = _projectile getVariable [QGVAR(guidanceArgs), []]; +TRACE_2("submunitionCreated",typeOf _projectile,typeOf _submunitionProjectile); +if (isNull _submunitionProjectile) exitWith { WARNING_1("null %1",_this); }; + +if (_guidanceArgs isNotEqualTo []) then { // Inject the submunition projectile and time into guidance args + _guidanceArgs params ["_firedEH", "", "", "", "_stateParams"]; + _firedEH set [6, _submunitionProjectile]; // _firedEH params ["","","","","","","_projectile"]; + _stateParams set [0, diag_tickTime]; // _stateParams params ["_lastRunTime"] + TRACE_2("-Starting missileGuidance",_submunitionProjectile,count _guidanceArgs); + [EFUNC(missileguidance,guidancePFH), 0, _guidanceArgs] call CBA_fnc_addPerFrameHandler; +}; + +private _deployArtilleryDragConfig = (configOf _projectile) >> QGVAR(artilleryDrag); +if (isNumber _deployArtilleryDragConfig) then { + private _deployArtilleryDrag = getNumber _deployArtilleryDragConfig; + if (_deployArtilleryDrag < 0) exitWith { TRACE_1("-Ignoring Drag",_deployArtilleryDrag); }; + private _kFactor = _projectile getVariable [QEGVAR(artillerytables,kFactor), 0]; + TRACE_2("-Drag",_deployArtilleryDrag,_kFactor); + _kFactor = _kFactor * _deployArtilleryDrag; // Can be 0 from either source (0 is valid and will keep nose pointed) + private _dragArray = [_submunitionProjectile, _kFactor, CBA_missionTime]; + _submunitionProjectile setVariable [QGVAR(dragArray), _dragArray]; +}; diff --git a/addons/missile_clgp/script_component.hpp b/addons/missile_clgp/script_component.hpp new file mode 100644 index 00000000000..2d2043b2a12 --- /dev/null +++ b/addons/missile_clgp/script_component.hpp @@ -0,0 +1,12 @@ +#define COMPONENT missile_clgp +#define COMPONENT_BEAUTIFIED Cannon Launched Guided Projectiles +#include "\z\ace\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_QUICK_TESTING +// #define ENABLE_PERFORMANCE_COUNTERS + +#include "\z\ace\addons\main\script_macros.hpp" + +#define COPPERHEAD_DEFUALT_SETTINGS [ARR_5(1,1,1,1,1)] diff --git a/addons/missile_clgp/stringtable.xml b/addons/missile_clgp/stringtable.xml new file mode 100644 index 00000000000..3c6d47beb55 --- /dev/null +++ b/addons/missile_clgp/stringtable.xml @@ -0,0 +1,14 @@ + + + + + Semi-Active Laser Guided 40mm Grenade + + + 40mm Pike + + + Pike SALH + + + diff --git a/addons/missile_clgp/ui/gear_pike_ca.paa b/addons/missile_clgp/ui/gear_pike_ca.paa new file mode 100644 index 00000000000..1b0ecb38078 Binary files /dev/null and b/addons/missile_clgp/ui/gear_pike_ca.paa differ diff --git a/addons/missileguidance/XEH_PREP.hpp b/addons/missileguidance/XEH_PREP.hpp index afdaba260a0..3705fe34999 100644 --- a/addons/missileguidance/XEH_PREP.hpp +++ b/addons/missileguidance/XEH_PREP.hpp @@ -9,6 +9,7 @@ PREP(checkLos); PREP(dev_ProjectileCamera); PREP(onFired); +PREP(onFiredGetArgs); PREP(onIncomingMissile); PREP(guidancePFH); diff --git a/addons/missileguidance/functions/fnc_onFired.sqf b/addons/missileguidance/functions/fnc_onFired.sqf index 150b2fd5f06..f4a1250087b 100644 --- a/addons/missileguidance/functions/fnc_onFired.sqf +++ b/addons/missileguidance/functions/fnc_onFired.sqf @@ -5,6 +5,8 @@ * * Arguments: * 0: Shooter (Man/Vehicle) + * 1: Weapon + * 3: Mode * 4: Ammo * 6: Projectile * @@ -30,212 +32,11 @@ if ( !isPlayer _shooter && { GVAR(enabled) < 2 } ) exitWith {}; // Verify ammo has explicity added guidance config (ignore inheritances) private _configs = configProperties [(configFile >> "CfgAmmo" >> _ammo), QUOTE(configName _x == QUOTE(QUOTE(ADDON))), false]; -if ((count _configs) < 1) exitWith {}; +if (_configs isEqualTo []) exitWith {}; -// MissileGuidance is enabled for this shot -TRACE_4("enabled",_shooter,_ammo,_projectile,typeOf _shooter); - -private _config = configFile >> "CfgAmmo" >> _ammo >> QUOTE(ADDON); - -private _target = _shooter getVariable [QGVAR(target), nil]; -private _targetPos = _shooter getVariable [QGVAR(targetPosition), nil]; -private _seekerType = _shooter getVariable [QGVAR(seekerType), nil]; -private _attackProfile = _shooter getVariable [QGVAR(attackProfile), nil]; -private _navigationType = _shooter getVariable [QGVAR(navigationType), nil]; -if ((getNumber (configFile >> "CfgAmmo" >> _ammo >> QUOTE(ADDON) >> "useModeForAttackProfile")) == 1) then { - _attackProfile = getText (configFile >> "CfgWeapons" >> _weapon >> _mode >> QGVAR(attackProfile)) -}; -private _lockMode = _shooter getVariable [QGVAR(lockMode), nil]; - -private _laserCode = _shooter getVariable [QEGVAR(laser,code), ACE_DEFAULT_LASER_CODE]; -private _laserInfo = [_laserCode, ACE_DEFAULT_LASER_WAVELENGTH, ACE_DEFAULT_LASER_WAVELENGTH]; - -TRACE_7("getVars",_target,_targetPos,_seekerType,_attackProfile,_lockMode,_laserCode,_navigationType); - -private _launchPos = getPosASL (vehicle _shooter); - -if (isNil "_seekerType" || {!(_seekerType in (getArray (_config >> "seekerTypes")))}) then { - _seekerType = getText (_config >> "defaultSeekerType"); -}; -if (isNil "_attackProfile" || {!(_attackProfile in (getArray (_config >> "attackProfiles")))}) then { - _attackProfile = getText (_config >> "defaultAttackProfile"); -}; -if (isNil "_lockMode" || {!(_lockMode in (getArray (_config >> "seekerLockModes")))}) then { - _lockMode = getText (_config >> "defaultSeekerLockMode"); -}; -if (isNil "_navigationType" || {!(_navigationType in (getArray (_config >> "navigationTypes")))}) then { - _navigationType = getText (_config >> "defaultNavigationType"); -}; - -if (isNil "_navigationType" || _navigationType isEqualTo "") then { - // most missiles use ProNav by default - _navigationType = "ProportionalNavigation"; -}; - -// If we didn't get a target, try to fall back on tab locking -if (isNil "_target") then { - if (!isPlayer _shooter) then { - // This was an AI shot, lets still guide it on the AI target - _target = _shooter getVariable [QGVAR(vanilla_target), nil]; - TRACE_1("Detected AI Shooter!",_target); - } else { - private _canUseLock = getNumber (_config >> "canVanillaLock"); - // @TODO: Get vanilla target - if (_canUseLock > 0 || difficulty < 1) then { - private _vanillaTarget = missileTarget _projectile; - - TRACE_1("Using Vanilla Locking",_vanillaTarget); - if (!isNil "_vanillaTarget") then { - _target = _vanillaTarget; - }; - }; - }; -}; -_targetPos = getPosASLVisual _target; - -// Array for seek last target position -private _seekLastTargetPos = (getNumber ( _config >> "seekLastTargetPos")) == 1; -private _lastKnownPosState = [_seekLastTargetPos]; -if (_seekLastTargetPos && {!isNil "_target"}) then { - _lastKnownPosState set [1, (getPosASL _target)]; -} else { - _lastKnownPosState set [1, [0,0,0]]; -}; - -private _navigationParameters = [ - // set up in navigation type onFired function -]; - -// default config values to make sure there is backwards compat -private _pitchRate = 30; -private _yawRate = 30; -private _bangBang = false; -if (isNumber (_config >> "pitchRate")) then { - _pitchRate = getNumber ( _config >> "pitchRate" ); - _yawRate = getNumber ( _config >> "yawRate" ); - _bangBang = (1 == getNumber (_config >> "bangBangGuidance")); -}; - -// How much this projectile likes to stay toward current velocity -private _stabilityCoefficient = getNumber (_config >> "stabilityCoefficient"); - -// show a light trail in flight -private _showTrail = (1 == getNumber (_config >> "showTrail")); - -private _navigationStateSubclass = _config >> "navigationStates"; -private _states = getArray (_navigationStateSubclass >> "states"); - -private _navigationStateData = []; - -if (_states isNotEqualTo []) then { - { - private _stateClass = _navigationStateSubclass >> _x; - _navigationStateData pushBack [ - getText (_stateClass >> "transitionCondition"), - getText (_stateClass >> "navigationType"), - [] - ]; - } forEach _states; -}; - -private _initialRoll = getNumber (_config >> "initialRoll"); -private _initialYaw = getNumber (_config >> "initialYaw"); -private _initialPitch = getNumber (_config >> "initialPitch"); - -private _yawRollPitch = (vectorDir _projectile) call CBA_fnc_vect2Polar; - -TRACE_5("Beginning ACE guidance system",_target,_ammo,_seekerType,_attackProfile,_navigationType); -private _args = [_this, - [ _shooter, - [_target, _targetPos, _launchPos, vectorDirVisual vehicle _shooter, CBA_missionTime], - _seekerType, - _attackProfile, - _lockMode, - _laserInfo, - _navigationType - ], - [ - _pitchRate, - _yawRate, - _bangBang, - _stabilityCoefficient, - _showTrail - ], - [ - getNumber ( _config >> "seekerAngle" ), - getNumber ( _config >> "seekerAccuracy" ), - getNumber ( _config >> "seekerMaxRange" ), - getNumber ( _config >> "seekerMinRange" ) - ], - [ diag_tickTime, [], [], _lastKnownPosState, _navigationParameters, [_initialYaw + (_yawRollPitch select 1), _initialRoll, _initialPitch + (_yawRollPitch select 2)]], - [ - // target data from missile. Must be filled by seeker for navigation to work - [0, 0, 0], // direction to target - [0, 0, 0], // direction to attack profile - 0, // range to target - [0, 0, 0], // target velocity - [0, 0, 0] // target acceleration - ], - [0, _navigationStateData] - ]; - -private _onFiredFunc = getText (configFile >> QGVAR(SeekerTypes) >> _seekerType >> "onFired"); -TRACE_1("seeker on fired",_onFiredFunc); -if (_onFiredFunc != "") then { - _args call (missionNamespace getVariable _onFiredFunc); -}; - -_onFiredFunc = getText (configFile >> QGVAR(AttackProfiles) >> _attackProfile >> "onFired"); -TRACE_1("attack on fired",_onFiredFunc); -if (_onFiredFunc != "") then { - _args call (missionNamespace getVariable _onFiredFunc); -}; - -if (_states isEqualTo []) then { - _onFiredFunc = getText (configFile >> QGVAR(NavigationTypes) >> _navigationType >> "onFired"); - TRACE_1("navigation on fired",_onFiredFunc); - if (_onFiredFunc != "") then { - private _navState = (_args call (missionNamespace getVariable _onFiredFunc)); - (_args select 4) set [4, _navState]; - }; -} else { - { - _onFiredFunc = getText (configFile >> QGVAR(NavigationTypes) >> _x >> "onFired"); - TRACE_1("navigation on fired",_onFiredFunc); - if (_onFiredFunc != "") then { - private _navState = (_args call (missionNamespace getVariable _onFiredFunc)); - (_navigationStateData select _forEachIndex) set [2, _navState]; - }; - } forEach getArray (_config >> "navigationTypes"); -}; - -// Run the "onFired" function passing the full guidance args array -_onFiredFunc = getText (_config >> "onFired"); -TRACE_1("general on fired",_onFiredFunc); -if (_onFiredFunc != "") then { - _args call (missionNamespace getVariable _onFiredFunc); -}; - -// Reverse: -// _args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams", "_targetData", "_navigationStateData"]; -// _firedEH params ["_shooter","","","","_ammo","","_projectile"]; -// _launchParams params ["_shooter","_targetLaunchParams","_seekerType","_attackProfile","_lockMode","_laserInfo","_navigationType"]; -// _targetLaunchParams params ["_target", "_targetPos", "_launchPos", "_launchDir", "_launchTime"]; -// _flightParams params ["_pitchRate", "_yawRate", "_isBangBangGuidance"]; -// _stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState", "_navigationParams", "_guidanceParameters"]; -// _seekerParams params ["_seekerAngle", "_seekerAccuracy", "_seekerMaxRange", "_seekerMinRange"]; -// _targetData params ["_targetDirection", "_attackProfileDirection", "_targetRange", "_targetVelocity", "_targetAcceleration"]; - -[LINKFUNC(guidancePFH),0, _args ] call CBA_fnc_addPerFrameHandler; +private _args = call FUNC(onFiredGetArgs); +[LINKFUNC(guidancePFH),0, _args] call CBA_fnc_addPerFrameHandler; if (GVAR(debug_enableMissileCamera)) then { [_projectile] call FUNC(dev_ProjectileCamera); }; - - -/* Clears locking settings -(vehicle _shooter) setVariable [QGVAR(target), nil]; -(vehicle _shooter) setVariable [QGVAR(seekerType), nil]; -(vehicle _shooter) setVariable [QGVAR(attackProfile), nil]; -(vehicle _shooter) setVariable [QGVAR(lockMode), nil]; - */ diff --git a/addons/missileguidance/functions/fnc_onFiredGetArgs.sqf b/addons/missileguidance/functions/fnc_onFiredGetArgs.sqf new file mode 100644 index 00000000000..93bd58e4c31 --- /dev/null +++ b/addons/missileguidance/functions/fnc_onFiredGetArgs.sqf @@ -0,0 +1,218 @@ +#include "..\script_component.hpp" +/* + * Author: jaynus, nou, TCVM, PabstMirror + * Gets guidance args for a fired missile, running fired EHs and returning the full guidance arg array + * + * Arguments: + * 0: Shooter (Man/Vehicle) + * 1: Weapon + * 3: Mode + * 4: Ammo (in the future) + * 6: Projectile + * + * Return Value: + * + * + * Example: + * [player, "", "", "", "ACE_Javelin_FGM148", "", theMissile] call ace_missileguidance_fnc_onFiredGetArgs + * + * Public: No + */ + +params ["_shooter","_weapon","","_mode","_ammo","","_projectile"]; + +// MissileGuidance is enabled for this shot +TRACE_4("enabled",_shooter,_ammo,_projectile,typeOf _shooter); + +private _config = configFile >> "CfgAmmo" >> _ammo >> QUOTE(ADDON); + +private _target = _shooter getVariable [QGVAR(target), nil]; +private _targetPos = _shooter getVariable [QGVAR(targetPosition), nil]; +private _seekerType = _shooter getVariable [QGVAR(seekerType), nil]; +private _attackProfile = _shooter getVariable [QGVAR(attackProfile), nil]; +private _navigationType = _shooter getVariable [QGVAR(navigationType), nil]; +if ((getNumber (configFile >> "CfgAmmo" >> _ammo >> QUOTE(ADDON) >> "useModeForAttackProfile")) == 1) then { + _attackProfile = getText (configFile >> "CfgWeapons" >> _weapon >> _mode >> QGVAR(attackProfile)) +}; +private _lockMode = _shooter getVariable [QGVAR(lockMode), nil]; + +private _laserCode = _shooter getVariable [QEGVAR(laser,code), ACE_DEFAULT_LASER_CODE]; +private _laserInfo = [_laserCode, ACE_DEFAULT_LASER_WAVELENGTH, ACE_DEFAULT_LASER_WAVELENGTH]; + +TRACE_7("onFiredGetArgs",_target,_targetPos,_seekerType,_attackProfile,_lockMode,_laserCode,_navigationType); + +private _launchPos = getPosASL (vehicle _shooter); + +if (isNil "_seekerType" || {!(_seekerType in (getArray (_config >> "seekerTypes")))}) then { + _seekerType = getText (_config >> "defaultSeekerType"); +}; +if (isNil "_attackProfile" || {!(_attackProfile in (getArray (_config >> "attackProfiles")))}) then { + _attackProfile = getText (_config >> "defaultAttackProfile"); +}; +if (isNil "_lockMode" || {!(_lockMode in (getArray (_config >> "seekerLockModes")))}) then { + _lockMode = getText (_config >> "defaultSeekerLockMode"); +}; +if (isNil "_navigationType" || {!(_navigationType in (getArray (_config >> "navigationTypes")))}) then { + _navigationType = getText (_config >> "defaultNavigationType"); +}; + +if (isNil "_navigationType" || _navigationType isEqualTo "") then { + // most missiles use ProNav by default + _navigationType = "ProportionalNavigation"; +}; + +// If we didn't get a target, try to fall back on tab locking +if (isNil "_target") then { + if (!isPlayer _shooter) then { + // This was an AI shot, lets still guide it on the AI target + _target = _shooter getVariable [QGVAR(vanilla_target), nil]; + TRACE_1("Detected AI Shooter!",_target); + } else { + private _canUseLock = getNumber (_config >> "canVanillaLock"); + // @TODO: Get vanilla target + if (_canUseLock > 0 || difficulty < 1) then { + private _vanillaTarget = missileTarget _projectile; + + TRACE_1("Using Vanilla Locking",_vanillaTarget); + if (!isNil "_vanillaTarget") then { + _target = _vanillaTarget; + }; + }; + }; +}; +_targetPos = getPosASLVisual _target; + +// Array for seek last target position +private _seekLastTargetPos = (getNumber ( _config >> "seekLastTargetPos")) == 1; +private _lastKnownPosState = [_seekLastTargetPos]; +if (_seekLastTargetPos && {!isNil "_target"}) then { + _lastKnownPosState set [1, (getPosASL _target)]; +} else { + _lastKnownPosState set [1, [0,0,0]]; +}; + +private _navigationParameters = [ + // set up in navigation type onFired function +]; + +// default config values to make sure there is backwards compat +private _pitchRate = 30; +private _yawRate = 30; +private _bangBang = false; +if (isNumber (_config >> "pitchRate")) then { + _pitchRate = getNumber ( _config >> "pitchRate" ); + _yawRate = getNumber ( _config >> "yawRate" ); + _bangBang = (1 == getNumber (_config >> "bangBangGuidance")); +}; + +// How much this projectile likes to stay toward current velocity +private _stabilityCoefficient = getNumber (_config >> "stabilityCoefficient"); + +// show a light trail in flight +private _showTrail = (1 == getNumber (_config >> "showTrail")); + +private _navigationStateSubclass = _config >> "navigationStates"; +private _states = getArray (_navigationStateSubclass >> "states"); + +private _navigationStateData = []; + +if (_states isNotEqualTo []) then { + { + private _stateClass = _navigationStateSubclass >> _x; + _navigationStateData pushBack [ + getText (_stateClass >> "transitionCondition"), + getText (_stateClass >> "navigationType"), + [] + ]; + } forEach _states; +}; + +private _initialRoll = getNumber (_config >> "initialRoll"); +private _initialYaw = getNumber (_config >> "initialYaw"); +private _initialPitch = getNumber (_config >> "initialPitch"); + +private _yawRollPitch = (vectorDir _projectile) call CBA_fnc_vect2Polar; + +TRACE_5("Beginning ACE guidance system",_target,_ammo,_seekerType,_attackProfile,_navigationType); +private _args = [_this, + [ _shooter, + [_target, _targetPos, _launchPos, vectorDirVisual vehicle _shooter, CBA_missionTime], + _seekerType, + _attackProfile, + _lockMode, + _laserInfo, + _navigationType + ], + [ + _pitchRate, + _yawRate, + _bangBang, + _stabilityCoefficient, + _showTrail + ], + [ + getNumber ( _config >> "seekerAngle" ), + getNumber ( _config >> "seekerAccuracy" ), + getNumber ( _config >> "seekerMaxRange" ), + getNumber ( _config >> "seekerMinRange" ) + ], + [ diag_tickTime, [], [], _lastKnownPosState, _navigationParameters, [_initialYaw + (_yawRollPitch select 1), _initialRoll, _initialPitch + (_yawRollPitch select 2)]], + [ + // target data from missile. Must be filled by seeker for navigation to work + [0, 0, 0], // direction to target + [0, 0, 0], // direction to attack profile + 0, // range to target + [0, 0, 0], // target velocity + [0, 0, 0] // target acceleration + ], + [0, _navigationStateData] + ]; + +private _onFiredFunc = getText (configFile >> QGVAR(SeekerTypes) >> _seekerType >> "onFired"); +TRACE_1("seeker on fired",_onFiredFunc); +if (_onFiredFunc != "") then { + _args call (missionNamespace getVariable _onFiredFunc); +}; + +_onFiredFunc = getText (configFile >> QGVAR(AttackProfiles) >> _attackProfile >> "onFired"); +TRACE_1("attack on fired",_onFiredFunc); +if (_onFiredFunc != "") then { + _args call (missionNamespace getVariable _onFiredFunc); +}; + +if (_states isEqualTo []) then { + _onFiredFunc = getText (configFile >> QGVAR(NavigationTypes) >> _navigationType >> "onFired"); + TRACE_1("navigation on fired",_onFiredFunc); + if (_onFiredFunc != "") then { + private _navState = (_args call (missionNamespace getVariable _onFiredFunc)); + (_args select 4) set [4, _navState]; + }; +} else { + { + _onFiredFunc = getText (configFile >> QGVAR(NavigationTypes) >> _x >> "onFired"); + TRACE_1("navigation on fired",_onFiredFunc); + if (_onFiredFunc != "") then { + private _navState = (_args call (missionNamespace getVariable _onFiredFunc)); + (_navigationStateData select _forEachIndex) set [2, _navState]; + }; + } forEach getArray (_config >> "navigationTypes"); +}; + +// Run the "onFired" function passing the full guidance args array +_onFiredFunc = getText (_config >> "onFired"); +TRACE_1("general on fired",_onFiredFunc); +if (_onFiredFunc != "") then { + _args call (missionNamespace getVariable _onFiredFunc); +}; + +// Reverse: +// _args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams", "_targetData", "_navigationStateData"]; +// _firedEH params ["_shooter","","","","_ammo","","_projectile"]; +// _launchParams params ["_shooter","_targetLaunchParams","_seekerType","_attackProfile","_lockMode","_laserInfo","_navigationType"]; +// _targetLaunchParams params ["_target", "_targetPos", "_launchPos", "_launchDir", "_launchTime"]; +// _flightParams params ["_pitchRate", "_yawRate", "_isBangBangGuidance"]; +// _stateParams params ["_lastRunTime", "_seekerStateParams", "_attackProfileStateParams", "_lastKnownPosState", "_navigationParams", "_guidanceParameters"]; +// _seekerParams params ["_seekerAngle", "_seekerAccuracy", "_seekerMaxRange", "_seekerMinRange"]; +// _targetData params ["_targetDirection", "_attackProfileDirection", "_targetRange", "_targetVelocity", "_targetAcceleration"]; + +_args diff --git a/docs/wiki/framework/clgp-framework.md b/docs/wiki/framework/clgp-framework.md new file mode 100644 index 00000000000..8d8dc6dda43 --- /dev/null +++ b/docs/wiki/framework/clgp-framework.md @@ -0,0 +1,56 @@ +--- +layout: wiki +title: Missile CLGP Framework +description: Explains how to set-up ammo for the cannon-launched-guided-projectile system. +group: framework +parent: wiki +order: 7 +mod: ace +version: + major: 3 + minor: 0 + patch: 0 +--- + +## 1. Overview + +CLGP allows firing submunition shells that will deploy into missiles that can use the Missile Guidance framework. + +## 2. Config Values + +### 2.1 Example Config +```cpp +class CfgAmmo { + class x_submunition: SubmunitionBase { // this ammo can be fired normally + ace_missile_clgp_deployCondition = "your_fnc"; // function that returns true when it should be triggered + ace_missile_clgp_artilleryDrag = 1; + submunitionAmmo = "x_missle"; + class Eventhandlers { + fired = "call ace_missile_clgp_fnc_submunition_ammoFired"; // need to add the EH yourself to all ammos + }; + }; + + class x_missle: MissileBase { + class ace_missileguidance { + enabled = 2; + // rest of normal ace missile guidance config +``` + +### 2.2 Configs + +`ace_missile_clgp_deployCondition` +- A function that will trigger the ammo when returning true, passed `[_projectile, _guidanceArgs]` +- Is optional, can just use normal submuntion triggers + +`ace_missile_clgp_artilleryDrag` +- Optional (Leave undefined (or set to -1) to ignore) +- Used as a coefficient on drag from ace_artilleryTables's advancedCorrections +- Value of 0 is valid and will not apply drag but still keep nose pointed correctly (for unpowered missiles) + +## 3. Note on adding magazines via mission + +```cpp +if (local turretLocal [0]) then { + this addMagazineTurret ["ace_1rnd_155mm_m712", [0]]; +}; +```