diff --git a/addons/hellfire/$PBOPREFIX$ b/addons/hellfire/$PBOPREFIX$ new file mode 100644 index 0000000000..5bd6c77aff --- /dev/null +++ b/addons/hellfire/$PBOPREFIX$ @@ -0,0 +1 @@ +z\ace\addons\hellfire diff --git a/addons/hellfire/ACE_GuidanceConfig.hpp b/addons/hellfire/ACE_GuidanceConfig.hpp new file mode 100644 index 0000000000..1c5d4705f3 --- /dev/null +++ b/addons/hellfire/ACE_GuidanceConfig.hpp @@ -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 + }; +}; diff --git a/addons/hellfire/CfgAmmo.hpp b/addons/hellfire/CfgAmmo.hpp new file mode 100644 index 0000000000..b5df3994f2 --- /dev/null +++ b/addons/hellfire/CfgAmmo.hpp @@ -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"}; + }; + }; +}; diff --git a/addons/hellfire/CfgEventHandlers.hpp b/addons/hellfire/CfgEventHandlers.hpp new file mode 100644 index 0000000000..77ea0239bd --- /dev/null +++ b/addons/hellfire/CfgEventHandlers.hpp @@ -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)); + }; +}; diff --git a/addons/hellfire/CfgMagazines.hpp b/addons/hellfire/CfgMagazines.hpp new file mode 100644 index 0000000000..6f636c2c87 --- /dev/null +++ b/addons/hellfire/CfgMagazines.hpp @@ -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}; + }; +}; diff --git a/addons/hellfire/CfgVehicles.hpp b/addons/hellfire/CfgVehicles.hpp new file mode 100644 index 0000000000..89dbf131d7 --- /dev/null +++ b/addons/hellfire/CfgVehicles.hpp @@ -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; + }; +}; diff --git a/addons/hellfire/CfgWeapons.hpp b/addons/hellfire/CfgWeapons.hpp new file mode 100644 index 0000000000..1b9a76352e --- /dev/null +++ b/addons/hellfire/CfgWeapons.hpp @@ -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}; + }; +}; diff --git a/addons/hellfire/README.md b/addons/hellfire/README.md new file mode 100644 index 0000000000..bb9eef711e --- /dev/null +++ b/addons/hellfire/README.md @@ -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) diff --git a/addons/hellfire/RscTitles.hpp b/addons/hellfire/RscTitles.hpp new file mode 100644 index 0000000000..2bbedae1b6 --- /dev/null +++ b/addons/hellfire/RscTitles.hpp @@ -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)"; + }; + }; + }; + + }; + }; +}; diff --git a/addons/hellfire/XEH_PREP.hpp b/addons/hellfire/XEH_PREP.hpp new file mode 100644 index 0000000000..8b07d00b2c --- /dev/null +++ b/addons/hellfire/XEH_PREP.hpp @@ -0,0 +1,5 @@ +LOG("prep"); +PREP(attackProfile); +PREP(getAttackProfileSettings); +PREP(setupVehicle); +PREP(showHud); diff --git a/addons/hellfire/XEH_postInit.sqf b/addons/hellfire/XEH_postInit.sqf new file mode 100644 index 0000000000..34fc5b92ba --- /dev/null +++ b/addons/hellfire/XEH_postInit.sqf @@ -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; diff --git a/addons/hellfire/XEH_preInit.sqf b/addons/hellfire/XEH_preInit.sqf new file mode 100644 index 0000000000..3464490b65 --- /dev/null +++ b/addons/hellfire/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/hellfire/XEH_preStart.sqf b/addons/hellfire/XEH_preStart.sqf new file mode 100644 index 0000000000..022888575e --- /dev/null +++ b/addons/hellfire/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/hellfire/config.cpp b/addons/hellfire/config.cpp new file mode 100644 index 0000000000..14c00904f1 --- /dev/null +++ b/addons/hellfire/config.cpp @@ -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" diff --git a/addons/hellfire/functions/fnc_attackProfile.sqf b/addons/hellfire/functions/fnc_attackProfile.sqf new file mode 100644 index 0000000000..84f1c39f0a --- /dev/null +++ b/addons/hellfire/functions/fnc_attackProfile.sqf @@ -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 + * 1: Guidance Arg Array + * 2: Attack Profile State + * + * Return Value: + * Missile Aim PosASL + * + * 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; diff --git a/addons/hellfire/functions/fnc_getAttackProfileSettings.sqf b/addons/hellfire/functions/fnc_getAttackProfileSettings.sqf new file mode 100644 index 0000000000..a4854faa55 --- /dev/null +++ b/addons/hellfire/functions/fnc_getAttackProfileSettings.sqf @@ -0,0 +1,46 @@ +/* + * Author: PabstMirror + * Gets attack profile parameters for first run of hellfire attack profile function + * + * Arguments: + * 0: Seeker Target PosASL + * 1: Guidance Arg Array + * 2: Attack Profile State + * + * 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); diff --git a/addons/hellfire/functions/fnc_setupVehicle.sqf b/addons/hellfire/functions/fnc_setupVehicle.sqf new file mode 100644 index 0000000000..bec56d52a7 --- /dev/null +++ b/addons/hellfire/functions/fnc_setupVehicle.sqf @@ -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 + * 1: Player's Turret Path + * + * 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); diff --git a/addons/hellfire/functions/fnc_showHud.sqf b/addons/hellfire/functions/fnc_showHud.sqf new file mode 100644 index 0000000000..c79780c3fe --- /dev/null +++ b/addons/hellfire/functions/fnc_showHud.sqf @@ -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 + * + * 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; diff --git a/addons/hellfire/functions/script_component.hpp b/addons/hellfire/functions/script_component.hpp new file mode 100644 index 0000000000..899f29a758 --- /dev/null +++ b/addons/hellfire/functions/script_component.hpp @@ -0,0 +1 @@ +#include "\z\ace\addons\hellfire\script_component.hpp" diff --git a/addons/hellfire/script_component.hpp b/addons/hellfire/script_component.hpp new file mode 100644 index 0000000000..2e8cf04eff --- /dev/null +++ b/addons/hellfire/script_component.hpp @@ -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 diff --git a/addons/hellfire/stringtable.xml b/addons/hellfire/stringtable.xml new file mode 100644 index 0000000000..ee8c91a071 --- /dev/null +++ b/addons/hellfire/stringtable.xml @@ -0,0 +1,34 @@ + + + + + Hellfire + Hellfire + Hellfire + Hellfire + Hellfire + Hellfire + Hellfire + Hellfire + Hellfire + Hellfire + Hellfire + + + Set Hellfire mode + + + RAH-66 Comanche [Hellfire] + RAH-66 Comanche [Hellfire] + RAH-66 Comanche [Hellfire] + RAH-66 Comanche [Hellfire] + RAH-66 Comanche [Hellfire] + RAH-66 Commanche [Hellfire] + RAH-66 Команч [Hellfire] + RAH-66 Comanche [Hellfire] + RAH-66 Comanche [Hellfire] + RAH-66 Comanche [Hellfire] + RAH-66 コマンチ [Hellfire] + + + diff --git a/addons/laser/functions/fnc_keyLaserCodeChange.sqf b/addons/laser/functions/fnc_keyLaserCodeChange.sqf index f4f485bcd0..d43dc52fe5 100644 --- a/addons/laser/functions/fnc_keyLaserCodeChange.sqf +++ b/addons/laser/functions/fnc_keyLaserCodeChange.sqf @@ -29,12 +29,13 @@ if (ACE_player call CBA_fnc_canUseWeapon) then { _currentWeapon = currentWeapon ACE_player; } else { _currentShooter = vehicle ACE_player; - private _turret = [ACE_player] call ace_common_fnc_getTurretIndex; - _currentWeapon = _currentShooter currentWeaponTurret _turret; + private _turretPath = if (ACE_player == (driver _currentShooter)) then {[-1]} else {ACE_player call CBA_fnc_turretPath}; + _currentWeapon = _currentShooter currentWeaponTurret _turretPath; }; 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 _newLaserCode = _oldLaserCode; @@ -52,7 +53,7 @@ if (((_codeChange < 0) && {_oldLaserCode > ACE_DEFAULT_LASER_CODE}) || {(_codeCh TRACE_2("",_oldLaserCode,_newLaserCode); 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); diff --git a/addons/missileguidance/CfgAmmo.hpp b/addons/missileguidance/CfgAmmo.hpp index 49fa40331e..7c2fb90d22 100644 --- a/addons/missileguidance/CfgAmmo.hpp +++ b/addons/missileguidance/CfgAmmo.hpp @@ -14,7 +14,7 @@ class CfgAmmo { laserLock = 0; manualControl = 0; maxSpeed = 300; - + EGVAR(rearm,caliber) = 70; 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 class M_Titan_AT: MissileBase {}; @@ -109,7 +89,7 @@ class CfgAmmo { 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] - + // Attack profile type selection defaultAttackProfile = "JAV_TOP"; attackProfiles[] = { "JAV_TOP", "JAV_DIR" }; diff --git a/addons/missileguidance/CfgMagazines.hpp b/addons/missileguidance/CfgMagazines.hpp index e4a84862ae..7cc33b17a3 100644 --- a/addons/missileguidance/CfgMagazines.hpp +++ b/addons/missileguidance/CfgMagazines.hpp @@ -24,30 +24,4 @@ class CfgMagazines { descriptionShort = "24 Round DAGR"; 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; - }; - }; diff --git a/addons/missileguidance/CfgVehicles.hpp b/addons/missileguidance/CfgVehicles.hpp deleted file mode 100644 index 6aae81b316..0000000000 --- a/addons/missileguidance/CfgVehicles.hpp +++ /dev/null @@ -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"}; - }; - }; - }; -}; diff --git a/addons/missileguidance/config.cpp b/addons/missileguidance/config.cpp index c97a542a7f..ce75e277b4 100644 --- a/addons/missileguidance/config.cpp +++ b/addons/missileguidance/config.cpp @@ -20,4 +20,3 @@ class CfgPatches { #include "CfgAmmo.hpp" #include "CfgMagazines.hpp" #include "CfgWeapons.hpp" -#include "CfgVehicles.hpp" diff --git a/addons/missileguidance/functions/fnc_cycleAttackProfileKeyDown.sqf b/addons/missileguidance/functions/fnc_cycleAttackProfileKeyDown.sqf index 05db3748da..dcc9d7c607 100644 --- a/addons/missileguidance/functions/fnc_cycleAttackProfileKeyDown.sqf +++ b/addons/missileguidance/functions/fnc_cycleAttackProfileKeyDown.sqf @@ -28,7 +28,8 @@ if (((vehicle ACE_player) == ACE_player) || {ACE_player call CBA_fnc_canUseWeapo _currentMagazine = currentMagazine ACE_player; } else { _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)}; diff --git a/docs/wiki/feature/hellfire.md b/docs/wiki/feature/hellfire.md new file mode 100644 index 0000000000..0f8376db20 --- /dev/null +++ b/docs/wiki/feature/hellfire.md @@ -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: Ctrl + Tab) + +## 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" %}