diff --git a/addons/manpad/$PBOPREFIX$ b/addons/manpad/$PBOPREFIX$ new file mode 100644 index 0000000000..4c93d594fc --- /dev/null +++ b/addons/manpad/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\manpad \ No newline at end of file diff --git a/addons/manpad/CfgAmmo.hpp b/addons/manpad/CfgAmmo.hpp new file mode 100644 index 0000000000..cf59197990 --- /dev/null +++ b/addons/manpad/CfgAmmo.hpp @@ -0,0 +1,90 @@ +class CfgAmmo { + class ammo_Missile_rim116; + class GVAR(rim116): ammo_Missile_rim116 { + maneuvrability = 0; + missileLockMaxSpeed = 2000; + + missileLockCone = 3; // caged lock + missileKeepLockedCone = 45; + missileLockMaxDistance = 5000; + missileLockMinDistance = 250; + + class ace_missileguidance { + enabled = 1; + + pitchRate = 50; // Minium flap deflection for guidance + yawRate = 50; // Maximum flap deflection for guidance + + canVanillaLock = 1; // Can this default vanilla lock? Only applicable to non-cadet mode + + // Guidance type for munitions + defaultSeekerType = "IR"; + seekerTypes[] = { "IR" }; + + flareDistanceFilter = 100; + flareAngleFilter = 0.5; // can filter out flares that are >= flareAngleFilter to known target velocity + + defaultSeekerLockMode = "LOBL"; + seekerLockModes[] = { "LOBL" }; + + defaultNavigationType = "ZeroEffortMiss"; + navigationTypes[] = { "ZeroEffortMiss" }; + + seekLastTargetPos = 0; // seek last target position [if seeker loses LOS of target, continue to last known pos] + seekerAngle = 45; // Angle from the shooter's view that can track the missile + seekerAccuracy = 0.8; // seeker accuracy multiplier + + seekerMinRange = 75; + seekerMaxRange = 5000; // Range from the missile which the seeker can visually search + + // Attack profile type selection + defaultAttackProfile = "DIR"; + attackProfiles[] = {"DIR"}; + }; + }; + + class M_70mm_SAAMI; + class GVAR(stinger): M_70mm_SAAMI { + maneuvrability = 0; + missileLockMaxSpeed = 2000; + + missileLockCone = 3; // caged lock + missileKeepLockedCone = 45; + missileLockMaxDistance = 5000; + missileLockMinDistance = 250; + + class ace_missileguidance { + enabled = 1; + + pitchRate = 42; // Minium flap deflection for guidance + yawRate = 42; // Maximum flap deflection for guidance + + canVanillaLock = 1; // Can this default vanilla lock? Only applicable to non-cadet mode + + // Guidance type for munitions + defaultSeekerType = "IR"; + seekerTypes[] = { "IR" }; + + flareDistanceFilter = 100; + flareAngleFilter = 1.3; // can filter out flares that are >= flareAngleFilter to known target velocity + + defaultSeekerLockMode = "LOBL"; + seekerLockModes[] = { "LOBL" }; + + defaultNavigationType = "AugmentedProportionalNavigation"; + navigationTypes[] = { "AugmentedProportionalNavigation" }; + + seekLastTargetPos = 0; // seek last target position [if seeker loses LOS of target, continue to last known pos] + seekerAngle = 45; // Angle from the shooter's view that can track the missile + seekerAccuracy = 0.8; // seeker accuracy multiplier + + seekerMinRange = 75; + seekerMaxRange = 5000; // Range from the missile which the seeker can visually search + + // Attack profile type selection + defaultAttackProfile = "DIR"; + attackProfiles[] = {"DIR"}; + }; + }; +}; + diff --git a/addons/manpad/CfgMagazines.hpp b/addons/manpad/CfgMagazines.hpp new file mode 100644 index 0000000000..22f8054998 --- /dev/null +++ b/addons/manpad/CfgMagazines.hpp @@ -0,0 +1,22 @@ +class CfgMagazines { + class magazine_Missile_rim116_x21; + class GVAR(rim116): magazine_Missile_rim116_x21 { + author = "Dani (TCVM)"; + displayName = CSTRING(rim116_21x); + ammo = QGVAR(rim116); + }; + + class 4Rnd_70mm_SAAMI_missiles; + class GVAR(stinger): 4Rnd_70mm_SAAMI_missiles { + author = "Dani (TCVM)"; + displayName = CSTRING(stinger_4x); + ammo = QGVAR(stinger); + }; + + class Titan_AA; + class GVAR(stinger_man): Titan_AA { + author = "Dani (TCVM)"; + displayName = CSTRING(stinger); + ammo = QGVAR(stinger); + }; +}; diff --git a/addons/manpad/CfgVehicles.hpp b/addons/manpad/CfgVehicles.hpp new file mode 100644 index 0000000000..7bbc7fb182 --- /dev/null +++ b/addons/manpad/CfgVehicles.hpp @@ -0,0 +1,108 @@ +class CfgVehicles { + class LandVehicle; + class StaticWeapon: LandVehicle { + class Turrets; + }; + class StaticMGWeapon: StaticWeapon { + class Turrets: Turrets { + class MainTurret; + }; + }; + + class SAM_System_01_base_F: StaticMGWeapon { + class AnimationSources { + class Missiles_revolving { + source = "revolving"; + weapon = QGVAR(rim116); + }; + }; + + class Turrets: Turrets { + class MainTurret: MainTurret { + weapons[] = {QGVAR(rim116)}; + magazines[] = {QGVAR(rim116)}; + }; + }; + }; + + class Tank; + class Tank_F: Tank { + class Turrets { + class MainTurret; + }; + }; + class LT_01_base_F: Tank_F { + class AnimationSources; + class Turrets: Turrets {}; + }; + + class LT_01_AA_base_F: LT_01_base_F { + class AnimationSources: AnimationSources { + class Missiles_revolving { + source = "revolving"; + weapon = QGVAR(FIM92); + }; + }; + + class Turrets: Turrets { + class MainTurret: MainTurret { + weapons[] = {"SmokeLauncher", QGVAR(FIM92), "HMG_127"}; + magazines[] = { + "SmokeLauncherMag", + QGVAR(stinger), + QGVAR(stinger), + "100Rnd_127x99_mag_Tracer_Red", + "100Rnd_127x99_mag_Tracer_Red", + "100Rnd_127x99_mag_Tracer_Red", + "100Rnd_127x99_mag_Tracer_Red" + }; + }; + }; + }; + + class APC_Tracked_01_base_F: Tank_F { + class Turrets: Turrets { + class MainTurret: MainTurret {}; + }; + }; + class B_APC_Tracked_01_base_F: APC_Tracked_01_base_F { + class AnimationSources; + }; + class B_APC_Tracked_01_AA_F: B_APC_Tracked_01_base_F { + class AnimationSources: AnimationSources { + class Missiles_revolving { + source = "revolving"; + weapon = QGVAR(FIM92); + }; + }; + class Turrets: Turrets { + class MainTurret: MainTurret { + weapons[] = {"autocannon_35mm", QGVAR(FIM92)}; + magazines[] = {"680Rnd_35mm_AA_shells_Tracer_Red", QGVAR(stinger), QGVAR(stinger)}; + }; + }; + }; + + class APC_Tracked_02_base_F: Tank_F { + class Turrets: Turrets { + class MainTurret: MainTurret {}; + }; + }; + class O_APC_Tracked_02_base_F: APC_Tracked_02_base_F { + class AnimationSources; + }; + class O_APC_Tracked_02_AA_F: O_APC_Tracked_02_base_F { + class AnimationSources: AnimationSources { + class Missiles_revolving { + source = "revolving"; + weapon = QGVAR(FIM92); + }; + }; + class Turrets: Turrets { + class MainTurret: MainTurret { + weapons[] = {"autocannon_35mm", QGVAR(FIM92)}; + magazines[] = {"680Rnd_35mm_AA_shells_Tracer_Green", QGVAR(stinger), QGVAR(stinger)}; + }; + }; + }; +}; diff --git a/addons/manpad/CfgWeapons.hpp b/addons/manpad/CfgWeapons.hpp new file mode 100644 index 0000000000..adba9137d2 --- /dev/null +++ b/addons/manpad/CfgWeapons.hpp @@ -0,0 +1,23 @@ +class CfgWeapons { + class weapon_rim116Launcher; + class GVAR(rim116): weapon_rim116Launcher { + author = "Dani (TCVM)"; + displayName = CSTRING(rim116); + magazines[] = { QGVAR(rim116) }; + weaponLockDelay = 0.5; + }; + + class missiles_SAAMI; + class GVAR(FIM92): missiles_SAAMI { + author = "Dani (TCVM)"; + displayName = CSTRING(stinger); + magazines[] = { QGVAR(stinger) }; + weaponLockDelay = 0.5; + }; + + class Launcher_Base_F; + class launch_Titan_base: Launcher_Base_F { + magazines[] = {QGVAR(stinger_man)}; + }; +}; + diff --git a/addons/manpad/README.md b/addons/manpad/README.md new file mode 100644 index 0000000000..dedbe7ba36 --- /dev/null +++ b/addons/manpad/README.md @@ -0,0 +1,12 @@ +ace_manpad +=================== + +Adds missile guidance to all vanilla MANPAD systems + + +## Maintainers + +The people responsible for merging changes to this component or answering potential questions. + +- [Dani-TCVM](https://github.com/TheCandianVendingMachine) + diff --git a/addons/manpad/config.cpp b/addons/manpad/config.cpp new file mode 100644 index 0000000000..d285b08cfc --- /dev/null +++ b/addons/manpad/config.cpp @@ -0,0 +1,21 @@ +#include "script_component.hpp" + +class CfgPatches { + class ADDON { + name = COMPONENT_NAME; + units[] = {}; + weapons[] = {}; + requiredVersion = REQUIRED_VERSION; + requiredAddons[] = {"ace_common","ace_missileguidance"}; + author = ECSTRING(common,ACETeam); + authors[] = {"Dani (TCVM)"}; + url = ECSTRING(main,URL); + VERSION_CONFIG; + }; +}; + +#include "CfgAmmo.hpp" +#include "CfgMagazines.hpp" +#include "CfgWeapons.hpp" +#include "CfgVehicles.hpp" + diff --git a/addons/manpad/script_component.hpp b/addons/manpad/script_component.hpp new file mode 100644 index 0000000000..d1269fc368 --- /dev/null +++ b/addons/manpad/script_component.hpp @@ -0,0 +1,18 @@ +#define COMPONENT manpad +#define COMPONENT_BEAUTIFIED MANPAD +#include "\z\ace\addons\main\script_mod.hpp" + +// #define DEBUG_MODE_FULL +// #define DISABLE_COMPILE_CACHE +// #define ENABLE_PERFORMANCE_COUNTERS + +#ifdef DEBUG_ENABLED_MANPAD + #define DEBUG_MODE_FULL +#endif + +#ifdef DEBUG_SETTINGS_MANPAD + #define DEBUG_SETTINGS DEBUG_SETTINGS_MANPAD +#endif + +#include "\z\ace\addons\main\script_macros.hpp" + diff --git a/addons/manpad/stringtable.xml b/addons/manpad/stringtable.xml new file mode 100644 index 0000000000..5d6475b394 --- /dev/null +++ b/addons/manpad/stringtable.xml @@ -0,0 +1,17 @@ + + + + + FIM-92 Stinger [ACE] + + + RIM-116 [ACE] + + + 21x RIM-116 [ACE] + + + 4x FIM-92 Stinger [ACE] + + + diff --git a/addons/missileguidance/ACE_GuidanceConfig.hpp b/addons/missileguidance/ACE_GuidanceConfig.hpp index 131beee339..c91fbf2306 100644 --- a/addons/missileguidance/ACE_GuidanceConfig.hpp +++ b/addons/missileguidance/ACE_GuidanceConfig.hpp @@ -88,6 +88,14 @@ class GVAR(SeekerTypes) { functionName = QFUNC(seekerType_MWR); onFired = QFUNC(mwr_onFired); }; + class IR { + name = ""; + visualName = ""; + description = ""; + + functionName = QFUNC(seekerType_IR); + onFired = QFUNC(IR_onFired); + }; }; class GVAR(NavigationTypes) { diff --git a/addons/missileguidance/functions/fnc_IR_onFired.sqf b/addons/missileguidance/functions/fnc_IR_onFired.sqf new file mode 100644 index 0000000000..14dfe45f18 --- /dev/null +++ b/addons/missileguidance/functions/fnc_IR_onFired.sqf @@ -0,0 +1,29 @@ +#include "..\script_component.hpp" +/* + * Author: tcvm + * Sets up IR state arrays (called from missileGuidance's onFired). + * + * Arguments: + * Guidance Arg Array + * + * Return Value: + * None + * + * Example: + * [] call ace_missileguidance_fnc_IR_onFired + * + * Public: No + */ +params ["_firedEH", "_launchParams", "", "", "_stateParams"]; +_firedEH params ["_shooter","_weapon","","","","","_projectile"]; +_stateParams params ["", "_seekerStateParams"]; +_launchParams params ["", "_targetLaunchParams"]; +_targetLaunchParams params ["_target"]; + +private _flareDistanceFilter = getNumber (configOf _projectile >> QUOTE(ADDON) >> "flareDistanceFilter"); +private _flareAngleFilter = getNumber (configOf _projectile >> QUOTE(ADDON) >> "flareAngleFilter"); + +_seekerStateParams set [0, _flareDistanceFilter]; +_seekerStateParams set [1, _flareAngleFilter]; +_seekerStateParams set [2, _target]; + diff --git a/addons/missileguidance/functions/fnc_seekerType_IR.sqf b/addons/missileguidance/functions/fnc_seekerType_IR.sqf new file mode 100644 index 0000000000..7c057cd9e7 --- /dev/null +++ b/addons/missileguidance/functions/fnc_seekerType_IR.sqf @@ -0,0 +1,154 @@ +#include "..\script_component.hpp" +/* + * Author: tcvm + * Infrared seeker. Checks if flares are popped + * + * Arguments: + * 1: Guidance Arg Array + * 2: Seeker State + * + * Return Value: + * Position of wanted missile pos relative to the camera direction + * + * Example: + * [] call ace_missileguidance_fnc_seekerType_IR + * + * Public: No + */ +#ifdef DEBUG_MODE_FULL +#define TRACK_ON_PAUSE true +#else +#define TRACK_ON_PAUSE false +#endif + +_args params ["_firedEH", "_launchParams", "_flightParams", "_seekerParams", "_stateParams", "_targetData"]; +_firedEH params ["_shooter","","","","_ammo","","_projectile"]; +_launchParams params ["_shooter","_targetLaunchParams","_seekerType","_attackProfile","_lockMode","_laserInfo","_navigationType"]; +_targetLaunchParams params ["_target", "", "_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"]; + +_seekerStateParams params ["_flareDistanceFilter", "_flareAngleFilter", "_trackingTarget"]; + +private _distanceFromLaunch = _launchPos distanceSqr getPosASLVisual _projectile; +if (_distanceFromLaunch <= _seekerMinRange * _seekerMinRange) exitWith { + private _dir = _launchPos vectorFromTo getPosASLVisual _projectile; + _dir vectorAdd getPosASLVisual _projectile +}; + +private _withinView = [_projectile, getPosASLVisual _trackingTarget, _seekerAngle] call FUNC(checkSeekerAngle); +private _canSee = [_projectile, _trackingTarget, false] call FUNC(checkLos); +if (_trackingTarget isNotEqualTo objNull && ({ !_withinView || !_canSee })) then { + _trackingTarget = objNull; +}; +if (isNull _trackingTarget) then { + // find any target within seeker range + private _potentialTargets = _projectile nearEntities ["Air", _seekerMaxRange]; + private _bestAngle = 90; + { + private _withinView = [_projectile, getPosASLVisual _x, _seekerAngle] call FUNC(checkSeekerAngle); + private _canSee = [_projectile, _x, false] call FUNC(checkLos); + + if (_withinView && _canSee) then { + private _los = (getPosASLVisual _projectile) vectorFromTo (getPosASLVisual _x); + private _losAngle = (_los#2 atan2 _los#0); + if (_losAngle < _bestAngle) then { + _trackingTarget = _x; + _bestAngle = _losAngle; + }; + }; + } forEach _potentialTargets; +}; + +if (true || {accTime > 0 && !isGamePaused}) then { + // If there are flares nearby, check if they will confuse missile + private _nearby = _trackingTarget nearObjects _flareDistanceFilter; + _nearby = _nearby select { + // 2 = IR blocking + private _blocking = configOf _x >> "weaponLockSystem"; + private _isFlare = false; + if (isNumber _blocking) then { + _isFlare = (2 == getNumber _blocking); + }; + + if (isText _blocking) then { + _isFlare = ("2" in getText _blocking); + }; + + private _withinView = [_projectile, getPosASLVisual _x, _seekerAngle] call FUNC(checkSeekerAngle); + private _canSee = [_projectile, _x, false] call FUNC(checkLos); + + (_x isEqualTo _target && _trackingTarget isNotEqualTo _target) || { (_withinView && _canSee && _isFlare) } + }; + + private _frontAspectMultiplier = 1; + if (_trackingTarget isKindOf "Air") then { + private _targetVelocity = velocity _trackingTarget; + + private _directionToTarget = (getPosASLVisual _projectile) vectorFromTo getPosASLVisual _trackingTarget; + private _angle = acos (_directionToTarget vectorCos _targetVelocity); + + _frontAspectMultiplier = (((_angle / 60) min 1) max 0.3); + }; + + private _relativeTargetVelocity = _projectile vectorWorldToModelVisual velocity _trackingTarget; + _relativeTargetVelocity set [1, 0]; + private _foundDecoy = false; + { + if (_trackingTarget isNotEqualTo _x) then { + private _considering = false; + + private _flareRelativeVelocity = _projectile vectorWorldToModelVisual velocity _x; + _flareRelativeVelocity set [1, 0]; + private _angleBetweenVelocities = acos (_relativeTargetVelocity vectorCos _flareRelativeVelocity); + // further away targets are filtered out by assumption that target cant move instantenously + private _chanceToDecoy = 1 - (_trackingTarget distance _x) / (_flareDistanceFilter * _frontAspectMultiplier); + if !(_foundDecoy) then { + if (_angleBetweenVelocities <= _flareAngleFilter) then { + _considering = true; + if (_seekerAccuracy <= random _chanceToDecoy) then { + _trackingTarget = _x; + _foundDecoy = true; + }; + }; + }; + + if (GVAR(debug_drawGuidanceInfo)) then { + private _flarePos = ASLToAGL getPosASLVisual _x; + private _colour = [1, 0, 0, 1]; + if (_considering) then { + _colour = [0, 1, 0, 1]; + }; + if (_trackingTarget isEqualTo _x) then { + _colour = [0, 0, 1, 1]; + }; + drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\selectover_ca.paa", _colour, _flarePos, 0.75, 0.75, 0, format ["F %1 C %2", _angleBetweenVelocities, _chanceToDecoy], 1, 0.025, "TahomaB"]; + }; + }; + } forEach _nearby; + + _seekerStateParams set [2, _trackingTarget]; + +}; + +private _targetPosition = _trackingTarget modelToWorldVisualWorld getCenterOfMass _trackingTarget; + +if (GVAR(debug_drawGuidanceInfo) && { _targetPosition isNotEqualTo [0, 0, 0] }) then { + if (!isGamePaused && accTime > 0) then { + private _ps = "#particlesource" createVehicleLocal (ASLtoAGL _targetPosition); + _PS setParticleParams [["\A3\Data_f\cl_basic", 8, 3, 1], "", "Billboard", 1, 3.0141, [0, 0, 0], [0, 0, 0], 1, 1.275, 1, 0, [1, 1], [[0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 1, 1]], [1], 1, 0, "", "", nil]; + _PS setDropInterval 1.0; + }; +}; + +_targetData set [0, (getPosASL _projectile) vectorFromTo _targetPosition]; +_targetData set [2, 0]; +_targetData set [3, velocity _trackingTarget]; + +if (_targetPosition isEqualTo [0, 0, 0]) then { + _targetPosition = (velocity _projectile) vectorAdd getPosASLVisual _projectile +}; + +_targetPosition