Hellfire missiles (#4679)

* Hellfire

* Cleanup dev macros

* Cleanup some debug

* Add base interaction node for firemode actions

* Handle bad data in attack profile variable

* Skip ammo checks (returns bad data on added weaps)

* Add mags sizes for apache

* Add Hellfire Wiki Doc

* Cleanup doc

* Add pylon support

* Add support for pilot controlled weapons

* Add label to pylon mags

* Cleanup vehicle configs, autoAdd laser des, fix ineractions for driver
This commit is contained in:
PabstMirror 2017-06-02 16:51:38 -05:00 committed by GitHub
parent 49374feb2a
commit 3898202091
28 changed files with 741 additions and 86 deletions

View File

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

View File

@ -0,0 +1,17 @@
class EGVAR(missileguidance,AttackProfiles) {
class hellfire {
// LOBL and LOAL-DIR behaive the same
name = "LOAL-DIR";
nameLocked = "LOBL";
functionName = QFUNC(attackProfile);
GVAR(launchHeightClear) = 0;
};
class hellfire_hi: hellfire {
name = "LOAL-HI";
GVAR(launchHeightClear) = 304.8; // clear 1000 ft by 1500m
};
class hellfire_lo: hellfire_hi {
name = "LOAL-LO";
GVAR(launchHeightClear) = 91.5; // clear 300 ft by 600m
};
};

View File

@ -0,0 +1,57 @@
class CfgAmmo {
class M_PG_AT;
class ACE_Hellfire_AGM114K: M_PG_AT {
displayName = "AGM-114K";
displayNameShort = "AGM-114K";
description = "AGM-114K";
descriptionShort = "AGM-114K";
model = "\A3\Weapons_F\Ammo\Missile_AT_03_fly_F";
proxyShape = "\A3\Weapons_F\Ammo\Missile_AT_03_F";
hit = 1400;
indirectHit = 71;
indirectHitRange = 4.5;
effectsMissile = "missile2";
irLock = 0;
laserLock = 0;
manualControl = 0;
maxSpeed = 450;
thrustTime = 2.5; // motor burn 2-3 sec
thrust = 250;
timeToLive = 40;
EGVAR(rearm,caliber) = 178;
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 = "SALH";
seekerTypes[] = { "SALH", "LIDAR", "SARH", "Optic", "Thermal", "GPS", "SACLOS", "MCLOS" };
defaultSeekerLockMode = "LOAL";
seekerLockModes[] = { "LOAL", "LOBL" };
seekLastTargetPos = 1; // seek last target position [if seeker loses LOS of target, continue to last known pos]
seekerAngle = 70; // Angle in front of the missile which can be searched
seekerAccuracy = 1; // seeker accuracy multiplier
seekerMinRange = 1;
seekerMaxRange = 5000; // Range from the missile which the seeker can visually search
// Attack profile type selection
defaultAttackProfile = "hellfire";
attackProfiles[] = {"hellfire", "hellfire_hi", "hellfire_lo"};
};
};
};

View File

@ -0,0 +1,15 @@
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 {
clientInit = QUOTE(call COMPILE_FILE(XEH_postInit));
};
};

View File

@ -0,0 +1,47 @@
class CfgMagazines {
class 12Rnd_PG_missiles;
class 6Rnd_ACE_Hellfire_AGM114K: 12Rnd_PG_missiles { // Old style vehicle magazine
count = 6;
ammo = "ACE_Hellfire_AGM114K";
displayName = "AGM-114K [ACE]";
displayNameShort = "AGM-114K";
descriptionShort = "AGM-114K";
};
// 1.70 pylon magazines:
class PylonMissile_1Rnd_ACE_Hellfire_AGM114K: 6Rnd_ACE_Hellfire_AGM114K { // Bare missle
displayName = "1x AGM-114K [ACE]";
count = 1;
mass = 70;
pylonWeapon = QGVAR(launcher);
hardpoints[] = {"SCALPEL_1RND"};
model = "\A3\Weapons_F\DynamicLoadout\PylonMissile_1x_Bomb_04_F.p3d";
};
class PylonRack_1Rnd_ACE_Hellfire_AGM114K: 6Rnd_ACE_Hellfire_AGM114K { // 1x Launcher Support Rack
displayName = "1x AGM-114K [ACE]";
count = 1;
mass = 85;
pylonWeapon = QGVAR(launcher);
hardpoints[] = {"B_MISSILE_PYLON", "SCALPEL_1RND_EJECTOR", "B_ASRRAM_EJECTOR", "UNI_SCALPEL"};
model = "\A3\Weapons_F\DynamicLoadout\PylonPod_1x_Missile_AA_04_F.p3d";
};
class PylonRack_3Rnd_ACE_Hellfire_AGM114K: 6Rnd_ACE_Hellfire_AGM114K { // 3x Launcher Support Rack
displayName = "3x AGM-114K [ACE]";
count = 3;
mass = 250;
pylonWeapon = QGVAR(launcher);
hardpoints[] = {"B_MISSILE_PYLON", "UNI_SCALPEL"};
model = "\A3\Weapons_F\DynamicLoadout\PylonPod_3x_Missile_LG_scalpel_F.p3d";
mirrorMissilesIndexes[] = {2, 1, 3};
};
class PylonRack_4Rnd_ACE_Hellfire_AGM114K: 6Rnd_ACE_Hellfire_AGM114K { // 4x Launcher Support Rack
displayName = "4x AGM-114K [ACE]";
count = 4;
mass = 340;
pylonWeapon = QGVAR(launcher);
hardpoints[] = {"UNI_SCALPEL"};
model = "\A3\Weapons_F\DynamicLoadout\PylonPod_4x_Missile_LG_scalpel_F.p3d";
mirrorMissilesIndexes[] = {2, 1, 4, 3};
};
};

View File

@ -0,0 +1,6 @@
class CfgVehicles {
class Heli_Attack_01_base_F;
class Heli_Attack_01_dynamicLoadout_base_F: Heli_Attack_01_base_F {
GVAR(addLaserDesignator) = 1;
};
};

View File

@ -0,0 +1,13 @@
class CfgWeapons {
class missiles_SCALPEL;
class GVAR(launcher): missiles_SCALPEL {
displayName = CSTRING(Hellfire);
GVAR(enabled) = 1; // show attack profile / lock on hud
EGVAR(laser,canSelect) = 1; // can ace_laser lock (allows switching laser code)
canLock = 0;
weaponLockSystem = 0;
magazines[] = {"6Rnd_ACE_Hellfire_AGM114K", "PylonMissile_1Rnd_ACE_Hellfire_AGM114K", "PylonRack_1Rnd_ACE_Hellfire_AGM114K", "PylonRack_3Rnd_ACE_Hellfire_AGM114K", "PylonRack_4Rnd_ACE_Hellfire_AGM114K"};
lockingTargetSound[] = {"",0,1};
lockedTargetSound[] = {"",0,1};
};
};

10
addons/hellfire/README.md Normal file
View File

@ -0,0 +1,10 @@
ace_hellfire
==========
Adds AGM-114K Hellfire missiles.
## Maintainers
The people responsible for merging changes to this component or answering potential questions.
- [PabstMirror](https://github.com/PabstMirror)

View File

@ -0,0 +1,57 @@
class RscControlsGroupNoScrollbars;
class RscPictureKeepAspect;
class RscText;
class RscTitles {
class GVAR(modeDisplay) {
idd = -1;
onLoad = QUOTE(with uiNameSpace do { GVAR(display) = _this select 0 };);
movingEnable = 0;
duration = 60;
fadeIn = "false";
fadeOut = "false";
class controls {
class ModeControlGroup: RscControlsGroupNoScrollbars {
idc = IDC_MODECONTROLGROUP;
x = "3.8 * (((safezoneW / safezoneH) min 1.2) / 40) + (profilenamespace getvariable ['IGUI_GRID_WEAPON_X',((safezoneX + safezoneW) - (10 * (((safezoneW / safezoneH) min 1.2) / 40)) - 4.3 * (((safezoneW / safezoneH) min 1.2) / 40))])";
y = "2.5 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25) + (profilenamespace getVariable ['IGUI_GRID_WEAPON_Y', (safezoneY + 0.5 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25))])";
w = "10 * (((safezoneW / safezoneH) min 1.2) / 40)";
h = "1 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
class controls {
class AttackMode: RscText {
idc = IDC_ATTACKMODE;
colorText[] = {1, 1, 1, 1};
colorBackground[] = {0, 0, 0, 0};
x = "0";
y = "0";
w = "(2.6) * (((safezoneW / safezoneH) min 1.2) / 40)";
h = "1 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
sizeEx = "0.8 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
};
class LaserCode: RscText {
idc = IDC_LASERCODE;
colorText[] = {1, 1, 1, 1};
colorBackground[] = {0, 0, 0, 0};
x = "(3.6) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
y = "0";
w = "(2.5) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
h = "(1) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
sizeEx = "0.8 * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
};
class LaserIcon: RscPictureKeepAspect {
idc = IDC_LASERICON;
colorText[] = {1, 0, 0, 1};
colorBackground[] = {0, 0, 0, 0};
text = "\a3\Ui_F_Curator\Data\CfgCurator\laser_ca.paa";
x = "(6.1) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
y = "0";
w = "(1) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
h = "(1) * ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)";
};
};
};
};
};
};

View File

@ -0,0 +1,5 @@
LOG("prep");
PREP(attackProfile);
PREP(getAttackProfileSettings);
PREP(setupVehicle);
PREP(showHud);

View File

@ -0,0 +1,10 @@
#include "script_component.hpp"
if (!hasInterface) exitWith {};
GVAR(pfID) = -1;
["ace_settingsInitialized", {
["turret", LINKFUNC(showHud), false] call CBA_fnc_addPlayerEventHandler;
["vehicle", LINKFUNC(showHud), true] call CBA_fnc_addPlayerEventHandler; // only one of these needs the retro flag
}] call CBA_fnc_addEventHandler;

View File

@ -0,0 +1,9 @@
#include "script_component.hpp"
ADDON = false;
PREP_RECOMPILE_START;
#include "XEH_PREP.hpp"
PREP_RECOMPILE_END;
ADDON = true;

View File

@ -0,0 +1,3 @@
#include "script_component.hpp"
#include "XEH_PREP.hpp"

View File

@ -0,0 +1,23 @@
#include "script_component.hpp"
class CfgPatches {
class ADDON {
name = COMPONENT_NAME;
units[] = {};
weapons[] = {};
requiredVersion = REQUIRED_VERSION;
requiredAddons[] = {"ace_interaction", "ace_missileguidance"};
author = ECSTRING(common,ACETeam);
authors[] = {"PabstMirror"};
url = ECSTRING(main,URL);
VERSION_CONFIG;
};
};
#include "ACE_GuidanceConfig.hpp"
#include "CfgAmmo.hpp"
#include "CfgEventHandlers.hpp"
#include "CfgMagazines.hpp"
#include "CfgVehicles.hpp"
#include "CfgWeapons.hpp"
#include "RscTitles.hpp"

View File

@ -0,0 +1,85 @@
/*
* Author: PabstMirror
* Hellfire attack profile. Handles all 4 modes LOBL, LOAL-DIR, LOAL-HI, LOAL-LO
*
* Arguments:
* 0: Seeker Target PosASL <ARRAY>
* 1: Guidance Arg Array <ARRAY>
* 2: Attack Profile State <ARRAY>
*
* Return Value:
* Missile Aim PosASL <ARRAY>
*
* Example:
* [[1,2,3], [], []] call ace_hellfire_fnc_attackProfile
*
* Public: No
*/
// #define DEBUG_MODE_FULL
#include "script_component.hpp"
params ["_seekerTargetPos", "_args", "_attackProfileStateParams"];
_args params ["_firedEH", "_launchParams"];
_launchParams params ["","_targetLaunchParams"];
_targetLaunchParams params ["", "", "_launchPos"];
_firedEH params ["","","","","","","_projectile"];
// Get state params:
if (_attackProfileStateParams isEqualTo []) then {
_this call FUNC(getAttackProfileSettings);
};
_attackProfileStateParams params ["_attackStage", "_configLaunchHeightClear"];
private _projectilePos = getPosASL _projectile;
private _distanceFromLaunch2d = _launchPos distance2d _projectilePos;
private _heightAboveLaunch = (_projectilePos select 2) - (_launchPos select 2);
// Add height depending on distance for compensate
private _returnTargetPos = nil;
switch (_attackStage) do {
case STAGE_LAUNCH: { // Gain height quickly to pass terrain mask
_returnTargetPos = _projectilePos getPos [100, getDir _projectile];
_returnTargetPos set [2, (_projectilePos select 2) + 36.4]; // 100 and 36.4 gives a 20 deg angle
if (_heightAboveLaunch > _configLaunchHeightClear) then {
_attackProfileStateParams set [0, STAGE_SEEK_CRUISE];
TRACE_2("New Stage: STAGE_SEEK_CRUISE",_distanceFromLaunch2d,_heightAboveLaunch);
};
};
case STAGE_SEEK_CRUISE: { // Slowly gain altitude while searching for target
// Before 4000 cruise at 5.7 degrees up, then level out
private _cruiseHeight = linearConversion [3000, 5000, _distanceFromLaunch2d, 10, 0, true];
_returnTargetPos = _projectilePos getPos [100, getDir _projectile];
_returnTargetPos set [2, (_projectilePos select 2) + _cruiseHeight];
if (!(_seekerTargetPos isEqualTo [0,0,0])) then {
_attackProfileStateParams set [0, STAGE_ATTACK_CRUISE];
TRACE_1("New Stage: STAGE_ATTACK_CRUISE",_distanceFromLaunch2d);
};
};
case STAGE_ATTACK_CRUISE: {
private _currentHeightOverTarget = (_projectilePos select 2) - (_seekerTargetPos select 2);
private _distanceToTarget2d = _seekerTargetPos distance2d _projectilePos;
private _distToGoRatio = _distanceToTarget2d / (_launchPos distance2d _seekerTargetPos);
// arcing up at 7 degrees to start until 50% left, then smooth curve to a downward attack
private _gainSlope = linearConversion [0.5, 0.1, _distToGoRatio, 7, -7, true];
_returnTargetPos = +_seekerTargetPos;
_returnTargetPos set [2, ((_projectilePos select 2) + (_distanceToTarget2d * sin _gainSlope)) max (_seekerTargetPos select 2)];
if ((_distanceToTarget2d < 500) || {(_currentHeightOverTarget atan2 _distanceToTarget2d) > 15}) then { // Wait until we can come down at a sharp angle
_attackProfileStateParams set [0, STAGE_ATTACK_TERMINAL];
TRACE_2("New Stage: STAGE_ATTACK_TERMINAL",_distanceToTarget2d,_currentHeightOverTarget);
};
};
case STAGE_ATTACK_TERMINAL: {
private _distanceToTarget2d = _seekerTargetPos distance2d _projectilePos;
_returnTargetPos = _seekerTargetPos vectorAdd [0, 0, _distanceToTarget2d * 0.02];
};
};
// TRACE_1("Adjusted target position", _returnTargetPos);
_returnTargetPos;

View File

@ -0,0 +1,46 @@
/*
* Author: PabstMirror
* Gets attack profile parameters for first run of hellfire attack profile function
*
* Arguments:
* 0: Seeker Target PosASL <ARRAY>
* 1: Guidance Arg Array <ARRAY>
* 2: Attack Profile State <ARRAY>
*
* Return Value:
* Nothing
*
* Example:
* [[], [], []] call ace_hellfire_fnc_getAttackProfileSettings;
*
* Public: No
*/
// #define DEBUG_MODE_FULL
#include "script_component.hpp"
params ["_seekerTargetPos", "_args", "_attackProfileStateParams"];
_args params ["_firedEH", "_launchParams"];
_launchParams params ["", "", "", "_attackProfile"];
_firedEH params ["","","","","","","_projectile"];
private _attackConfig = configFile >> QEGVAR(missileguidance,AttackProfiles) >> _attackProfile;
// Launch (clearing terrain mask for LO/HI):
private _configLaunchHeightClear = getNumber (_attackConfig >> QGVAR(launchHeightClear));
// Get starting stage
private _startingStage = if (_configLaunchHeightClear > 0) then {
STAGE_LAUNCH; // LOAL-HI / LO
} else {
if (_seekerTargetPos isEqualTo [0,0,0]) then {
STAGE_SEEK_CRUISE; // LOAL-DIR
} else {
STAGE_ATTACK_CRUISE // LOBL
};
};
// Set data in param array
_attackProfileStateParams set [0, _startingStage];
_attackProfileStateParams set [1, _configLaunchHeightClear];
TRACE_1("new shot settings",_attackProfileStateParams);

View File

@ -0,0 +1,76 @@
/*
* Author: PabstMirror
* Adds interaction menu actions to switch the firemode to a vehicle.
* Also adds a Laser Designator if vehicle is configured for one.
*
* Arguments:
* 0: Vehicle <OBJECT>
* 1: Player's Turret Path <ARRAY>
*
* Return Value:
* Nothing
*
* Example:
* [(vehicle player), [0]] call ace_hellfire_fnc_setupVehicle
*
* Public: No
*/
// #define DEBUG_MODE_FULL
#include "script_component.hpp"
params ["_vehicle", "_turretPath"];
TRACE_2("setupVehicle",_vehicle,_turretPath);
// Add laser if vehicle is configured for one:
if ((getNumber (configFile >> "CfgVehicles" >> (typeOf _vehicle) >> QGVAR(addLaserDesignator))) == 1) then {
[{
params ["_vehicle", "_turretPath"];
TRACE_3("checking for laser",_vehicle,_turretPath,_vehicle turretLocal _turretPath);
if (!alive _vehicle) exitWith {};
if (!(_vehicle turretLocal _turretPath)) then {WARNING("Turret not local");};
private _hasLaser = false;
{
// Most addons just use "Laserdesignator_mounted", but this should cover custom ones
if ((getNumber (configFile >> "CfgWeapons" >> _x >> "Laser")) == 1) exitWith {
_hasLaser = true;
};
} forEach (_vehicle weaponsTurret _turretPath);
if (!_hasLaser) then {
TRACE_1("Adding Laser Designator",typeOf _vehicle);
_vehicle addWeaponTurret ["Laserdesignator_mounted", _turretPath];
_vehicle addMagazineTurret ["Laserbatteries", _turretPath];
};
}, _this, 1] call CBA_fnc_waitAndExecute; // Need to delay slightly for turret to become local (probably only needs a single frame)
};
// Add interaction menu actions:
if (_vehicle getVariable [QGVAR(actionsAdded), false]) exitWith {};
_vehicle setVariable [QGVAR(actionsAdded), true];
private _action = [QUOTE(ADDON), localize LSTRING(hellfireModeAction), "", {}, {true}] call EFUNC(interact_menu,createAction);
private _basePath = [_vehicle, 1, ["ACE_SelfActions"], _action] call EFUNC(interact_menu,addActionToObject);
private _fnc_statement = {
params ["_target", "", "_attackProfile"];
TRACE_2("statement",_target,_attackProfile);
_target setVariable [QEGVAR(missileguidance,attackProfile), _attackProfile];
};
private _fnc_condition = {
params ["_target", "_player", "_attackProfile"];
private _turretPath = if (ACE_player == (driver _target)) then {[-1]} else {ACE_player call CBA_fnc_turretPath};
private _hasWeapon = ({QGVAR(launcher) == _x} count (_target weaponsTurret _turretPath)) > 0;
(_hasWeapon) &&
{(_target getVariable [QEGVAR(missileguidance,attackProfile), "hellfire"]) != _attackProfile};
};
{
private _displayName = getText (configFile >> QEGVAR(missileguidance,AttackProfiles) >> _x >> "name");
private _action = [format [QGVAR(%1),_x], _displayName, "", _fnc_statement, _fnc_condition, {}, _x] call EFUNC(interact_menu,createAction);
[_vehicle, 1, _basePath, _action] call EFUNC(interact_menu,addActionToObject);
} forEach ["hellfire", "hellfire_hi", "hellfire_lo"];
TRACE_2("interactions added",_vehicle,typeOf _vehicle);

View File

@ -0,0 +1,124 @@
/*
* Author: PabstMirror
* Shows the hellfire hud when vehicle is equiped with the weapon.
* Shows laser code, fire mode and seeker status.
*
* Arguments:
* 0: Player <OBJECT>
*
* Return Value:
* Nothing
*
* Example:
* [player] call ace_hellfire_fnc_showHud
*
* Public: No
*/
#include "script_component.hpp"
params ["_player"];
TRACE_1("showHud",_player);
private _enabled = false;
private _vehicle = vehicle _player;
private _turretPath = [-1];
if ((alive _player) && {_player != _vehicle}) then {
if (_player != (driver _vehicle)) then {
_turretPath = _player call CBA_fnc_turretPath
};
{
if ((getNumber (configFile >> "CfgWeapons" >> _x >> QGVAR(enabled))) == 1) then {
TRACE_1("enabled",_x);
_enabled = true;
};
} forEach (_vehicle weaponsTurret _turretPath);
};
if ((!_enabled) && (GVAR(pfID) < 0)) exitWith {TRACE_2("Disabled - No Change",_enabled,GVAR(pfID));};
TRACE_2("Cleaning up old pfeh and display",_enabled,GVAR(pfID));
[GVAR(pfID)] call CBA_fnc_removePerFrameHandler;
if (!isNull (uiNamespace getVariable [QGVAR(display), displayNull])) then {
([QGVAR(modeDisplay)] call BIS_fnc_rscLayer) cutText ["", "PLAIN"];
};
GVAR(pfID) = -1;
if (!_enabled) exitWith {TRACE_2("Disabled - Now Off",_enabled,GVAR(pfID));};
TRACE_2("Enabled - Adding actions and PFEH",_enabled,GVAR(pfID));
[_vehicle, _turretPath] call FUNC(setupVehicle);
private _adjustDown = false; // Flares display will block ours, if present just move ours down a bit
{
if ((getText (configFile >> "CfgWeapons" >> _x >> "simulation")) == "cmlauncher") exitWith {_adjustDown = true};
} forEach (_vehicle weaponsTurret _turretPath);
private _turretConfig = [_vehicle, _turretPath] call CBA_fnc_getTurret;
private _seekerSource = getText (_turretConfig >> "memoryPointGunnerOptics");
TRACE_3("",_adjustDown,_seekerSource,_vehicle selectionPosition _seekerSource);
GVAR(pfID) = [{
params ["_args", "_pfID"];
_args params ["_vehicle", "_turretPath", "_seekerSource", "_adjustDown"];
// Restart display if null (not just at start, this will happen periodicly)
if (isNull (uiNamespace getVariable [QGVAR(display), displayNull])) then {
TRACE_1("creating display",_this);
([QGVAR(modeDisplay)] call BIS_fnc_rscLayer) cutRsc [QGVAR(modeDisplay), "PLAIN", 1, false];
if (_adjustDown) then {
private _ctrl = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl IDC_MODECONTROLGROUP;
private _pos = ctrlPosition _ctrl;
_pos set [1, (_pos select 1) + ((((safezoneW / safezoneH) min 1.2) / 1.2) / 25)];
_ctrl ctrlSetPosition _pos;
_ctrl ctrlCommit 0;
};
};
private _currentWeapon = _vehicle currentWeaponTurret _turretPath;
private _showLockMode = (getNumber (configFile >> "CfgWeapons" >> _currentWeapon >> QGVAR(enabled))) == 1;
private _ctrlGroup = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl 1000;
if (!_showLockMode) exitWith {
_ctrlGroup ctrlShow false;
};
_ctrlGroup ctrlShow true;
private _ctrlText = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl IDC_ATTACKMODE;
private _ctrlCode = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl IDC_LASERCODE;
private _ctrlIcon = (uiNamespace getVariable [QGVAR(display), displayNull]) displayCtrl IDC_LASERICON;
// Do Laser Scan:
private _laserSource = AGLtoASL (_vehicle modelToWorld (_vehicle selectionPosition _seekerSource));
private _laserCode = _vehicle getVariable [QEGVAR(laser,code), ACE_DEFAULT_LASER_CODE];
private _laserResult = [_laserSource, vectorDir _vehicle, 70, 5000, [ACE_DEFAULT_LASER_WAVELENGTH,ACE_DEFAULT_LASER_WAVELENGTH], _laserCode, _vehicle] call EFUNC(laser,seekerFindLaserSpot);
private _foundTargetPos = _laserResult select 0;
private _haveLock = !isNil "_foundTargetPos";
private _modeShort = "ERR";
private _vehicleLockMode = _vehicle getVariable [QEGVAR(missileguidance,attackProfile), ""];
switch (_vehicleLockMode) do { // note: missileguidance is case sensitive
case ("hellfire_hi"): {
_modeShort = getText (configFile >> QEGVAR(missileguidance,AttackProfiles) >> _vehicleLockMode >> "name");
};
case ("hellfire_lo"): {
_modeShort = getText (configFile >> QEGVAR(missileguidance,AttackProfiles) >> _vehicleLockMode >> "name");
};
default {
_vehicleLockMode = "hellfire";
_modeShort = if (_haveLock) then {
getText (configFile >> QEGVAR(missileguidance,AttackProfiles) >> _vehicleLockMode >> "nameLocked");
} else {
getText (configFile >> QEGVAR(missileguidance,AttackProfiles) >> _vehicleLockMode >> "name");
};
};
};
_ctrlIcon ctrlSetTextColor ([[0,0,0,0.25],[1,0,0,0.75]] select _haveLock);
_ctrlText ctrlSetText _modeShort;
_ctrlCode ctrlSetText format ["CODE: %1", _laserCode];
}, 0.1, [_vehicle, _turretPath, _seekerSource, _adjustDown]] call CBA_fnc_addPerFrameHandler;

View File

@ -0,0 +1 @@
#include "\z\ace\addons\hellfire\script_component.hpp"

View File

@ -0,0 +1,27 @@
#define COMPONENT hellfire
#define COMPONENT_BEAUTIFIED Hellfire
#include "\z\ace\addons\main\script_mod.hpp"
// #define DEBUG_MODE_FULL
// #define DISABLE_COMPILE_CACHE
// #define ENABLE_PERFORMANCE_COUNTERS
#ifdef DEBUG_ENABLED_HELLFIRE
#define DEBUG_MODE_FULL
#endif
#ifdef DEBUG_SETTINGS_HELLFIRE
#define DEBUG_SETTINGS DEBUG_SETTINGS_HELLFIRE
#endif
#include "\z\ace\addons\main\script_macros.hpp"
#define STAGE_LAUNCH 1
#define STAGE_SEEK_CRUISE 2
#define STAGE_ATTACK_CRUISE 3
#define STAGE_ATTACK_TERMINAL 4
#define IDC_MODECONTROLGROUP 1000
#define IDC_ATTACKMODE 1001
#define IDC_LASERCODE 1002
#define IDC_LASERICON 1003

View File

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<Project name="ACE">
<Package name="Hellfire">
<Key ID="STR_ACE_Hellfire_Hellfire">
<English>Hellfire</English>
<Spanish>Hellfire</Spanish>
<French>Hellfire</French>
<Polish>Hellfire</Polish>
<German>Hellfire</German>
<Czech>Hellfire</Czech>
<Italian>Hellfire</Italian>
<Portuguese>Hellfire</Portuguese>
<Hungarian>Hellfire</Hungarian>
<Russian>Hellfire</Russian>
<Japanese>Hellfire</Japanese>
</Key>
<Key ID="STR_ACE_Hellfire_hellfireModeAction">
<English>Set Hellfire mode</English>
</Key>
<Key ID="STR_ACE_Hellfire_HF_B_Heli_Attack_01">
<English>RAH-66 Comanche [Hellfire]</English>
<German>RAH-66 Comanche [Hellfire]</German>
<Spanish>RAH-66 Comanche [Hellfire]</Spanish>
<Polish>RAH-66 Comanche [Hellfire]</Polish>
<Czech>RAH-66 Comanche [Hellfire]</Czech>
<French>RAH-66 Commanche [Hellfire]</French>
<Russian>RAH-66 Команч [Hellfire]</Russian>
<Portuguese>RAH-66 Comanche [Hellfire]</Portuguese>
<Hungarian>RAH-66 Comanche [Hellfire]</Hungarian>
<Italian>RAH-66 Comanche [Hellfire]</Italian>
<Japanese>RAH-66 コマンチ [Hellfire]</Japanese>
</Key>
</Package>
</Project>

View File

@ -29,12 +29,13 @@ if (ACE_player call CBA_fnc_canUseWeapon) then {
_currentWeapon = currentWeapon ACE_player; _currentWeapon = currentWeapon ACE_player;
} else { } else {
_currentShooter = vehicle ACE_player; _currentShooter = vehicle ACE_player;
private _turret = [ACE_player] call ace_common_fnc_getTurretIndex; private _turretPath = if (ACE_player == (driver _currentShooter)) then {[-1]} else {ACE_player call CBA_fnc_turretPath};
_currentWeapon = _currentShooter currentWeaponTurret _turret; _currentWeapon = _currentShooter currentWeaponTurret _turretPath;
}; };
TRACE_2("",_currentShooter,_currentWeapon); TRACE_2("",_currentShooter,_currentWeapon);
if ((getNumber (configFile >> "CfgWeapons" >> _currentWeapon >> "laser")) == 0) exitWith {false}; if (((getNumber (configFile >> "CfgWeapons" >> _currentWeapon >> "laser")) == 0) &&
{(getNumber (configFile >> "CfgWeapons" >> _currentWeapon >> QGVAR(canSelect))) == 0}) exitWith {false};
private _oldLaserCode = _currentShooter getVariable [QGVAR(code), ACE_DEFAULT_LASER_CODE]; private _oldLaserCode = _currentShooter getVariable [QGVAR(code), ACE_DEFAULT_LASER_CODE];
private _newLaserCode = _oldLaserCode; private _newLaserCode = _oldLaserCode;
@ -52,7 +53,7 @@ if (((_codeChange < 0) && {_oldLaserCode > ACE_DEFAULT_LASER_CODE}) || {(_codeCh
TRACE_2("",_oldLaserCode,_newLaserCode); TRACE_2("",_oldLaserCode,_newLaserCode);
if (_oldLaserCode != _newLaserCode) then { if (_oldLaserCode != _newLaserCode) then {
_currentShooter setVariable [QGVAR(code), _newLaserCode, false]; _currentShooter setVariable [QGVAR(code), _newLaserCode, true];
}; };
[format ["%1: %2", localize LSTRING(laserCode), _newLaserCode]] call EFUNC(common,displayTextStructured); [format ["%1: %2", localize LSTRING(laserCode), _newLaserCode]] call EFUNC(common,displayTextStructured);

View File

@ -14,7 +14,7 @@ class CfgAmmo {
laserLock = 0; laserLock = 0;
manualControl = 0; manualControl = 0;
maxSpeed = 300; maxSpeed = 300;
EGVAR(rearm,caliber) = 70; EGVAR(rearm,caliber) = 70;
class ADDON { class ADDON {
@ -45,26 +45,6 @@ class CfgAmmo {
}; };
}; };
class ACE_Hellfire_AGM114K: ACE_Hydra70_DAGR {
displayName = CSTRING(Hellfire_AGM114K);
displayNameShort = CSTRING(Hellfire_AGM114K_Short);
description = CSTRING(Hellfire_AGM114K_desc);
descriptionShort = CSTRING(Hellfire_AGM114K_desc);
// @TODO: placeholder model to at least make it look different
model = "\A3\Weapons_F\Ammo\Missile_AT_03_fly_F";
proxyShape = "\A3\Weapons_F\Ammo\Missile_AT_03_F";
hit = 1400;
indirectHit = 71;
indirectHitRange = 4.5;
effectsMissile = "missile2";
//Explicity add guidance config
class ADDON: ADDON {};
};
// Titan // Titan
class M_Titan_AT: MissileBase {}; class M_Titan_AT: MissileBase {};
@ -109,7 +89,7 @@ class CfgAmmo {
seekerMaxRange = 2500; // Range from the missile which the seeker can visually search seekerMaxRange = 2500; // Range from the missile which the seeker can visually search
seekLastTargetPos = 1; // seek last target position [if seeker loses LOS of target, continue to last known pos] seekLastTargetPos = 1; // seek last target position [if seeker loses LOS of target, continue to last known pos]
// Attack profile type selection // Attack profile type selection
defaultAttackProfile = "JAV_TOP"; defaultAttackProfile = "JAV_TOP";
attackProfiles[] = { "JAV_TOP", "JAV_DIR" }; attackProfiles[] = { "JAV_TOP", "JAV_DIR" };

View File

@ -24,30 +24,4 @@ class CfgMagazines {
descriptionShort = "24 Round DAGR"; descriptionShort = "24 Round DAGR";
weight = 72; weight = 72;
}; };
// Hellfires
class 6Rnd_ACE_Hellfire_AGM114K : 12Rnd_PG_missiles {
count = 12;
ammo = "ACE_Hellfire_AGM114K";
displayName = "6Rnd_ACE_Hellfire_AGM114K";
displayNameShort = "6Rnd_ACE_Hellfire_AGM114K";
descriptionShort = "6Rnd_ACE_Hellfire_AGM114K";
weight = 36;
};
class 12Rnd_ACE_Hellfire_AGM114K : 6Rnd_ACE_Hydra70_DAGR {
count = 12;
displayName = "12Rnd_ACE_Hellfire_AGM114K";
displayNameShort = "12Rnd_ACE_Hellfire_AGM114K";
descriptionShort = "12Rnd_ACE_Hellfire_AGM114K";
weight = 72;
};
class 24Rnd_ACE_Hellfire_AGM114K : 6Rnd_ACE_Hydra70_DAGR {
count = 24;
displayName = "24Rnd_ACE_Hellfire_AGM114K";
displayNameShort = "24Rnd_ACE_Hellfire_AGM114K";
descriptionShort = "24Rnd_ACE_Hellfire_AGM114K";
weight = 72;
};
}; };

View File

@ -1,32 +0,0 @@
class CfgVehicles {
class Air;
class Helicopter: Air {
class Turrets;
};
class Helicopter_Base_F: Helicopter {
class Turrets: Turrets {
class MainTurret;
};
};
class Heli_Attack_01_base_F: Helicopter_Base_F {
class Turrets: Turrets {
class MainTurret: MainTurret {};
};
};
class B_Heli_Attack_01_F: Heli_Attack_01_base_F {};
class ACE_Comanche_Test : B_Heli_Attack_01_F { // Comanche testbed (Hidden: Scope=1)
scope = 1;
scopeCurator = 0;
displayName = "ACE_Comanche_Test";
author = "ACE Team";
class Library {
libTextDesc = "ACE_Comanche_Test";
};
class Turrets: Turrets {
class MainTurret: MainTurret {
weapons[] = {"gatling_20mm", "ace_missileguidance_dagr", "Laserdesignator_mounted"};
magazines[] = {"ACE_500Rnd_20mm_shells_Comanche", "6Rnd_ACE_Hydra70_DAGR", "Laserbatteries"};
};
};
};
};

View File

@ -20,4 +20,3 @@ class CfgPatches {
#include "CfgAmmo.hpp" #include "CfgAmmo.hpp"
#include "CfgMagazines.hpp" #include "CfgMagazines.hpp"
#include "CfgWeapons.hpp" #include "CfgWeapons.hpp"
#include "CfgVehicles.hpp"

View File

@ -28,7 +28,8 @@ if (((vehicle ACE_player) == ACE_player) || {ACE_player call CBA_fnc_canUseWeapo
_currentMagazine = currentMagazine ACE_player; _currentMagazine = currentMagazine ACE_player;
} else { } else {
_currentShooter = vehicle ACE_player; _currentShooter = vehicle ACE_player;
_currentMagazine = _currentShooter currentMagazineTurret (ACE_player call CBA_fnc_turretPath); private _turretPath = if (ACE_player == (driver _currentShooter)) then {[-1]} else {ACE_player call CBA_fnc_turretPath};
_currentMagazine = _currentShooter currentMagazineTurret _turretPath;
}; };
if (_currentMagazine == "") exitWith {TRACE_1("no magazine",_currentMagazine)}; if (_currentMagazine == "") exitWith {TRACE_1("no magazine",_currentMagazine)};

View File

@ -0,0 +1,66 @@
---
layout: wiki
title: Hellfire
description: AGM-114K Hellfire Missiles
group: feature
category: equipment
parent: wiki
mod: ace
version:
major: 3
minor: 10
patch: 0
---
## 1. Overview
### 1.1 Guidance
Hellfire missile is a semi-active laser guided weapon.
It requires an observer (either the launch platform or an external source) to provide laser designation.
### 1.2 Attack profiles
Missile does not need line of sight to target when fired and can Lock-On-After-Launch (can also delay lasing target).
This and the attack profile used will effect missile's flight and max altitude.
- LOBL: Lock-On-Before-Launch, standard top attack.
- LOAL-DIR: Missile flies with a low altitude until acquiring a laser.
- LOAL-LOW: Missile immediately gains ~90m altitude.
- LOAL-HI: Missile immediately gains ~300m altitude.
## 2. Usage
- Switching to the hellfire weapon will show additional information about the weapon in weapon status display.
- Shows: lock mode, laser code and a laser receiver indicator. E.G. `LOAL-DIR CODE: 1111`
- Laser receiver indicator turns red when it detects a laser pulse set the the current code.
- Cycle attack profiles with vehicle's ACE3 Interaction Menu or with the missile guidance "Cycle Fire Mode" keybind (default: <kbd>Ctrl</kbd> + <kbd>Tab</kbd>)
## 3 Adding to vehicles
- Easiest way to add is via the 1.70 Pylons system.
- Hellfires can also be added to other vehicles via config or script.
### 3.1 Classnames
- Weapon: `ace_hellfire_launcher`
- Magazines: `6Rnd_ACE_Hellfire_AGM114K`
- Pylon Magazines: `PylonMissile_1Rnd_ACE_Hellfire_AGM114K`, `PylonRack_1Rnd_ACE_Hellfire_AGM114K`, `PylonRack_3Rnd_ACE_Hellfire_AGM114K`, `PylonRack_4Rnd_ACE_Hellfire_AGM114K`
### 3.2 Script Example
- Adding hellfires to the Cessna Civilian Plane:
```
if (local this) then {
this addWeaponTurret ["ace_hellfire_launcher", [-1]];
this addMagazineTurret ["6Rnd_ACE_Hellfire_AGM114K", [-1]];
};
```
## 4 Automaticly adding a laser designator
- Can automaticly add a laser designator if hellfire launcher is present
- Ideal for pylon dynamic loadouts
```cpp
class myChopper: HeliBase {
ace_hellfire_addLaserDesignator = 1;
};
```
## 5. Dependencies
{% include dependencies_list.md component="hellfire" %}