Isolate MANPAD and IR

This commit is contained in:
Bailey Danyluk 2024-05-27 21:04:00 -06:00
parent bcb6ed7c68
commit 7659e834e0
12 changed files with 503 additions and 0 deletions

View File

@ -0,0 +1 @@
z\ace\addons\manpad

90
addons/manpad/CfgAmmo.hpp Normal file
View File

@ -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"};
};
};
};

View File

@ -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);
};
};

View File

@ -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)};
};
};
};
};

View File

@ -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)};
};
};

12
addons/manpad/README.md Normal file
View File

@ -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)

21
addons/manpad/config.cpp Normal file
View File

@ -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"

View File

@ -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"

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ACE">
<Package name="MANPAD">
<Key ID="STR_ACE_MANPAD_stinger">
<English>FIM-92 Stinger [ACE]</English>
</Key>
<Key ID="STR_ACE_MANPAD_rim116">
<English>RIM-116 [ACE]</English>
</Key>
<Key ID="STR_ACE_MANPAD_rim116_21x">
<English>21x RIM-116 [ACE]</English>
</Key>
<Key ID="STR_ACE_MANPAD_stinger_4x">
<English>4x FIM-92 Stinger [ACE]</English>
</Key>
</Package>
</Project>

View File

@ -88,6 +88,14 @@ class GVAR(SeekerTypes) {
functionName = QFUNC(seekerType_MWR); functionName = QFUNC(seekerType_MWR);
onFired = QFUNC(mwr_onFired); onFired = QFUNC(mwr_onFired);
}; };
class IR {
name = "";
visualName = "";
description = "";
functionName = QFUNC(seekerType_IR);
onFired = QFUNC(IR_onFired);
};
}; };
class GVAR(NavigationTypes) { class GVAR(NavigationTypes) {

View File

@ -0,0 +1,29 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Sets up IR state arrays (called from missileGuidance's onFired).
*
* Arguments:
* Guidance Arg Array <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];

View File

@ -0,0 +1,154 @@
#include "..\script_component.hpp"
/*
* Author: tcvm
* Infrared seeker. Checks if flares are popped
*
* Arguments:
* 1: Guidance Arg Array <ARRAY>
* 2: Seeker State <ARRAY>
*
* Return Value:
* Position of wanted missile pos relative to the camera direction <ARRAY>
*
* 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