diff --git a/addons/compat_cup_weapons/compat_cup_missileguidance/config.cpp b/addons/compat_cup_weapons/compat_cup_missileguidance/config.cpp index 74506f5563e..c22737ec4b3 100644 --- a/addons/compat_cup_weapons/compat_cup_missileguidance/config.cpp +++ b/addons/compat_cup_weapons/compat_cup_missileguidance/config.cpp @@ -20,3 +20,16 @@ class CfgPatches { #include "\z\ace\addons\missileguidance\script_missileBases.hpp" #include "CfgAmmo.hpp" + +class CfgWeapons { //todo + class Launcher_Base_F; + class CUP_launch_FIM92Stinger_Loaded: Launcher_Base_F { + canLock = 0; + EGVAR(missile_manpad,enabled) = 1; + EGVAR(missile_manpad,lockAngle) = 3; + EGVAR(missile_manpad,lockingTimeMin) = 2; + EGVAR(missile_manpad,lockingTimeMax) = 4; + EGVAR(missile_manpad,lockingSound) = QEGVAR(missile_manpad,stinger_locking); + EGVAR(missile_manpad,lockedSound) = QEGVAR(missile_manpad,stinger_locked); + }; +}; diff --git a/addons/missile_manpad/CfgEventHandlers.hpp b/addons/missile_manpad/CfgEventHandlers.hpp new file mode 100644 index 00000000000..d177322c244 --- /dev/null +++ b/addons/missile_manpad/CfgEventHandlers.hpp @@ -0,0 +1,16 @@ +class Extended_PreStart_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preStart)); + }; +}; + +class Extended_PreInit_EventHandlers { + class ADDON { + init = QUOTE(call COMPILE_SCRIPT(XEH_preInit)); + }; +}; +class Extended_PostInit_EventHandlers { + class ADDON { + clientInit = QUOTE(call COMPILE_SCRIPT(XEH_postInit)); + }; +}; diff --git a/addons/missile_manpad/CfgSounds.hpp b/addons/missile_manpad/CfgSounds.hpp new file mode 100644 index 00000000000..7cb582764f1 --- /dev/null +++ b/addons/missile_manpad/CfgSounds.hpp @@ -0,0 +1,10 @@ +class CfgSounds { + class GVAR(stinger_locked) { + sound[] = {QPATHTOF(sounds\stinger_locked.ogg), 1, 1, 30}; + titles[] = {}; + }; + class GVAR(stinger_locking) { + sound[] = {QPATHTOF(sounds\stinger_locking.ogg), 1, 1, 30}; + titles[] = {}; + }; +}; diff --git a/addons/missile_manpad/CfgWeapons.hpp b/addons/missile_manpad/CfgWeapons.hpp index adba9137d23..f3fbca07531 100644 --- a/addons/missile_manpad/CfgWeapons.hpp +++ b/addons/missile_manpad/CfgWeapons.hpp @@ -19,5 +19,21 @@ class CfgWeapons { class launch_Titan_base: Launcher_Base_F { magazines[] = {QGVAR(stinger_man)}; }; + + #ifdef CREATE_MOCK_PLATFORMS + class launch_RPG7_F; + class GVAR(mockStinger): launch_RPG7_F { + displayName = "Test Stinger"; + canLock = 0; + magazineWell[] = {}; + magazines[] = {QGVAR(stinger_man)}; + GVAR(enabled) = 1; + GVAR(lockAngle) = 3; + GVAR(lockingTimeMin) = 2; + GVAR(lockingTimeMax) = 4; + GVAR(lockingSound) = QGVAR(stinger_locking); + GVAR(lockedSound) = QGVAR(stinger_locked); + }; + #endif }; diff --git a/addons/missile_manpad/XEH_PREP.hpp b/addons/missile_manpad/XEH_PREP.hpp new file mode 100644 index 00000000000..9d580844cd5 --- /dev/null +++ b/addons/missile_manpad/XEH_PREP.hpp @@ -0,0 +1,2 @@ +PREP(eachFrame); +PREP(weaponChanged); diff --git a/addons/missile_manpad/XEH_postInit.sqf b/addons/missile_manpad/XEH_postInit.sqf new file mode 100644 index 00000000000..abfaa931ce8 --- /dev/null +++ b/addons/missile_manpad/XEH_postInit.sqf @@ -0,0 +1,46 @@ +#include "script_component.hpp" + +if (!hasInterface) exitWith {}; + +// Add keybind - todo: move to missile_guidance-common-binds: ["lock", ... }] call EFUNC(missileguidance,keybind_add); +GVAR(isLockKeyDown) = false; +["ACE3 Weapons", QGVAR(trackTarget), "Lock Target (Hold) [stinger]", { + GVAR(isLockKeyDown) = true; +}, { + GVAR(isLockKeyDown) = false; +}, [15, [false, false, false]], false] call CBA_fnc_addKeybind; //Tab Key + + +GVAR(running) = []; +["weapon", LINKFUNC(weaponChanged)] call CBA_fnc_addPlayerEventHandler; +["unit", LINKFUNC(weaponChanged), true] call CBA_fnc_addPlayerEventHandler; + +// todo move to CBA on its next release https://github.com/CBATeam/CBA_A3/pull/1751 +[QGVAR(soundEffect), { + params ["_unit", "_sound"]; + TRACE_2("soundEffect",_unit,_sound); + private _old = _unit getVariable [QGVAR(soundEffect), objNull]; + if (!isNull _old) then { + deleteVehicle _old; + }; + if (_sound != "") then { + private _new = _unit say3D _sound; + _unit setVariable [QGVAR(soundEffect), _new]; + }; +}] call CBA_fnc_addEventHandler; + + +#ifdef ENABLE_QUICK_TESTING +// ace_missileguidance_debug_drawGuidanceInfo = true; +["recompile", "recompile", "recompile", { + private _start = diag_tickTime; + [] call ACE_PREP_RECOMPILE; + [] call ace_common_fnc_dumpPerformanceCounters; + private _end = diag_tickTime; + systemChat format ["recompile took [%1 ms]", (1000 * (_end - _start)) toFixed 1]; + if (productVersion #4 == "Diag") then { + call compile "diag_mergeConfigFile ['P:\z\ace\addons\missile_manpad\config.cpp']"; + }; + false +}, {false}, [59, [false, false, false]], false] call CBA_fnc_addKeybind; // F1 Key +#endif diff --git a/addons/missile_manpad/XEH_preInit.sqf b/addons/missile_manpad/XEH_preInit.sqf new file mode 100644 index 00000000000..b47cf6628db --- /dev/null +++ b/addons/missile_manpad/XEH_preInit.sqf @@ -0,0 +1,9 @@ +#include "script_component.hpp" + +ADDON = false; + +PREP_RECOMPILE_START; +#include "XEH_PREP.hpp" +PREP_RECOMPILE_END; + +ADDON = true; diff --git a/addons/missile_manpad/XEH_preStart.sqf b/addons/missile_manpad/XEH_preStart.sqf new file mode 100644 index 00000000000..022888575ed --- /dev/null +++ b/addons/missile_manpad/XEH_preStart.sqf @@ -0,0 +1,3 @@ +#include "script_component.hpp" + +#include "XEH_PREP.hpp" diff --git a/addons/missile_manpad/config.cpp b/addons/missile_manpad/config.cpp index d285b08cfc8..2f3aa00290a 100644 --- a/addons/missile_manpad/config.cpp +++ b/addons/missile_manpad/config.cpp @@ -6,16 +6,17 @@ class CfgPatches { units[] = {}; weapons[] = {}; requiredVersion = REQUIRED_VERSION; - requiredAddons[] = {"ace_common","ace_missileguidance"}; + requiredAddons[] = {"ace_missileguidance"}; author = ECSTRING(common,ACETeam); - authors[] = {"Dani (TCVM)"}; + authors[] = {"Dani (TCVM)", "PabstMirror"}; url = ECSTRING(main,URL); VERSION_CONFIG; }; }; #include "CfgAmmo.hpp" +#include "CfgEventHandlers.hpp" #include "CfgMagazines.hpp" +#include "CfgSounds.hpp" #include "CfgWeapons.hpp" #include "CfgVehicles.hpp" - diff --git a/addons/missile_manpad/functions/fnc_eachFrame.sqf b/addons/missile_manpad/functions/fnc_eachFrame.sqf new file mode 100644 index 00000000000..ab52146d858 --- /dev/null +++ b/addons/missile_manpad/functions/fnc_eachFrame.sqf @@ -0,0 +1,128 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Each frame. + * + * Arguments: + * 0: Args + * + * Return Value: + * None + * + * Example: + * [[]] call ace_missile_manpad_fnc_eachFrame + * + * Public: No + */ +params ["_args"]; +_args params ["_unit", "_pfid", "_actionId", "_config", "_lockCanidate", "_lockStartTime", "_haveLock", "_lastSound", "_lastSoundTimeout"]; +_config params ["_seekerMaxRange", "_lockAngle", "_uncageAngle", "_lockingTimeMin", "_lockingTimeMax", "_lockingSound", "_lockedSound"]; + +private _fnc_playSound = { + params ["_sound"]; + if ((_lastSound != _sound) || {CBA_missionTime > _lastSoundTimeout}) then { + private _affected = (ASLToAGL eyePos _unit) nearEntities ["CAManBase", 50]; + [QGVAR(soundEffect), [_unit, _sound], _affected] call CBA_fnc_targetEvent; + _args set [7, _sound]; + _args set [8, CBA_missionTime + 10]; // tiny clipping when re-starting track + }; +}; + +private _fnc_searchTarget = { + params ["_target", "_currentAngleMax"]; + + private _aimASL = aimPos _target; + private _targetDiff = _aimASL vectorDiff _seekerASL; + private _angle = acos ((_seekerDir vectorCos _targetDiff) min 1 max -1); // vectorCos will sometimes be 1.0001 :( + private _visibility = [_source, "VIEW", _target] checkVisibility [_seekerASL, _aimASL]; + private _dist = vectorMagnitude _targetDiff; + + if ((_angle > _currentAngleMax) || {_visibility < 0.1} || {_dist > _seekerMaxRange}) exitWith { + if (EGVAR(missileguidance,debug_drawGuidanceInfo)) then { + drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\select_target_ca.paa", [1,0,0,1], + ASLToAGL _aimASL, 1.5, 1.5, 45, format ["a%1 - v%2 - d%3", _angle, _visibility, _dist], 0.5, 0.025, "TahomaB"]; + }; + 0 + }; + + private _ret = _visibility // get a signal strength estimate + * linearConversion [_currentAngleMax/2, _currentAngleMax, _angle, 1, 0.5, true] + * linearConversion [_seekerMaxRange/2, _seekerMaxRange, vectorMagnitude _targetDiff, 1, 0.5, true] + * linearConversion [100, 3000, getMass _target, 0.25, 1, true]; + + if (EGVAR(missileguidance,debug_drawGuidanceInfo)) then { + private _color = [[0,1,0,1], [0,0,1,1]] select isNull (ACE_player getVariable [QEGVAR(missileguidance,target), objNull]); + drawIcon3D ["\a3\ui_f\data\IGUI\Cfg\Cursors\select_target_ca.paa", _color, + ASLToAGL _aimASL, 1.5, 1.5, 45, format ["a%1 - v%2 - d%3 = %4", _angle, _visibility, _dist, _ret], 0.5, 0.025, "TahomaB"]; + }; + _ret +}; + + +if (!GVAR(isLockKeyDown) || {(currentMagazine _unit) == ""}) exitWith { + "" call _fnc_playSound; + _unit setVariable [QEGVAR(missileguidance,target), nil]; + _args set [4, objNull]; // _lockCanidate = objNull +}; + +private _source = _unit; +private _seekerASL = eyePos _source; +private _seekerDir = _source weaponDirection currentWeapon _source; + +private _lockFeedback = 0; +if (isNull _lockCanidate) then { + // find any target within seeker range + private _potentialTargets = _source nearEntities ["Air", _seekerMaxRange]; + private _bestValue = 0; + private _bestTarget = objNull; + { + private _target = _x; + if (_target isKindOf "ParachuteBase") then { continue }; + private _strength = [_x, _lockAngle, _seekerMaxRange] call _fnc_searchTarget; + if (_strength > _bestValue) then { + _bestTarget = _x; + _bestValue = _strength; + }; + } forEach _potentialTargets; + if (!isNull _bestTarget) then { + TRACE_1("new target",_bestTarget); + _args set [4, _bestTarget]; // _lockCanidate = _bestTarget; + _args set [5, CBA_missionTime]; // _lockStartTime = CBA_missionTime; + _args set [6, false]; // _haveLock = false; + }; +} else { + private _angle = [_lockAngle, _uncageAngle] select CBA_events_control; + private _strength = [_lockCanidate, _angle, _seekerMaxRange] call _fnc_searchTarget; + if (_strength == 0) exitWith { + _haveLock = false; + _args set [4, objNull]; // _lockCanidate = objNull; + }; + + if (_haveLock) then { + _lockFeedback = 1; + } else { + private _lockTime = CBA_missionTime - _lockStartTime; + private _lockNeeded = linearConversion [1, 0, _strength, _lockingTimeMin, _lockingTimeMax, true]; + _lockFeedback = linearConversion [0, _lockNeeded, _lockTime, 0, 1, true]; + _haveLock = _lockFeedback >= 1; + if (_haveLock) then { + TRACE_1("new lock",_strength); + _args set [6, true]; + }; + }; + + // hintSilent format ["lf %1\nstr %2", _lockFeedback, _strength]; +}; + +switch (true) do { + case (_lockFeedback == 0): { "" call _fnc_playSound }; + case (_lockFeedback < 1): { _lockingSound call _fnc_playSound }; + case (_lockFeedback >= 1): { _lockedSound call _fnc_playSound }; +}; + + +if ((isNull _lockCanidate) || {!_haveLock}) then { + _unit setVariable [QEGVAR(missileguidance,target), nil]; +} else { + _unit setVariable [QEGVAR(missileguidance,target), _lockCanidate]; +}; diff --git a/addons/missile_manpad/functions/fnc_weaponChanged.sqf b/addons/missile_manpad/functions/fnc_weaponChanged.sqf new file mode 100644 index 00000000000..aff331fc287 --- /dev/null +++ b/addons/missile_manpad/functions/fnc_weaponChanged.sqf @@ -0,0 +1,58 @@ +#include "..\script_component.hpp" +/* + * Author: PabstMirror + * Unit or Weapon changed + * + * Arguments: + * 0: Unit + * + * Return Value: + * None + * + * Example: + * [player] call ace_missile_manpad_fnc_weaponChanged + * + * Public: No + */ +params ["_unit"]; +private _weapon = currentWeapon _unit; +TRACE_2("weaponChanged",_unit,_weapon); + +if (GVAR(running) isNotEqualTo []) then { + TRACE_1("cleanup",GVAR(running)); + GVAR(running) params ["_unit", "_pfid", "_actionId"]; + _pfid call CBA_fnc_removePerFrameHandler; + [_unit, "DefaultAction", _actionId] call EFUNC(common,removeActionEventHandler); + GVAR(running) = []; +}; +if (alive _unit && {_weapon != ""}) then { + private _weapCfg = configFile >> "CfgWeapons" >> _weapon; + if ((getNumber (_weapCfg >> QGVAR(enabled))) != 1) exitWith { TRACE_1("-not enabled",_weapCfg); }; + private _configs = configProperties [_weapCfg, QUOTE(configName _x == QUOTE(QGVAR(enabled))), false]; + if (_configs isEqualTo []) exitWith { TRACE_1("-not enabled explicitly",_weapCfg); }; + + private _mag = (compatibleMagazines _weapon) param [0, ""]; + private _ammo = getText (configFile >> "CfgMagazines" >> _mag >> "ammo"); + private _ammoCfg = configFile >> "CfgAmmo" >> _ammo; + + // _config params ["_seekerMaxRange", "_lockAngle", "_uncageAngle", "_lockingTimeMin", "_lockingTimeMax", "_lockingSound", "_lockedSound"]; + private _config = [ + [_ammoCfg >> "ace_missileguidance" >> "seekerMaxRange", "NUMBER", 9000] call CBA_fnc_getConfigEntry, + [_weapCfg >> QGVAR(lockAngle), "NUMBER", 3] call CBA_fnc_getConfigEntry, + [_ammoCfg >> "ace_missileguidance" >> "seekerAngle", "NUMBER", 3] call CBA_fnc_getConfigEntry, + [_weapCfg >> QGVAR(lockingTimeMin), "NUMBER", 3] call CBA_fnc_getConfigEntry, + [_weapCfg >> QGVAR(lockingTimeMax), "NUMBER", 3] call CBA_fnc_getConfigEntry, + [_weapCfg >> QGVAR(lockingSound), "STRING", ""] call CBA_fnc_getConfigEntry, + [_weapCfg >> QGVAR(lockedSound), "STRING", ""] call CBA_fnc_getConfigEntry + ]; + TRACE_3("-enabled",_mag,_ammo,_config); + + private _actionId = [_unit, "DefaultAction", { + isNull ((_this select 1) getVariable [QEGVAR(missileguidance,target), objNull]) + }, { + TRACE_1("block click",EGVAR(missileguidance,target)); + }] call EFUNC(common,addActionEventHandler); + GVAR(running) = [_unit, -1,_actionId, _config, objNull, -1, false, "", -1]; + private _pfid = [{call FUNC(eachFrame)}, 0, GVAR(running)] call CBA_fnc_addPerFrameHandler; + GVAR(running) set [1, _pfid]; +}; diff --git a/addons/missile_manpad/script_component.hpp b/addons/missile_manpad/script_component.hpp index 7c25a962e15..87db4db7532 100644 --- a/addons/missile_manpad/script_component.hpp +++ b/addons/missile_manpad/script_component.hpp @@ -5,6 +5,8 @@ // #define DEBUG_MODE_FULL // #define DISABLE_COMPILE_CACHE // #define ENABLE_PERFORMANCE_COUNTERS +// #define ENABLE_QUICK_TESTING +// #define CREATE_MOCK_PLATFORMS #ifdef DEBUG_ENABLED_MISSILE_MANPAD #define DEBUG_MODE_FULL diff --git a/addons/missile_manpad/sounds/dev_stinger.dsp b/addons/missile_manpad/sounds/dev_stinger.dsp new file mode 100644 index 00000000000..aa2b2ef1e27 --- /dev/null +++ b/addons/missile_manpad/sounds/dev_stinger.dsp @@ -0,0 +1,17 @@ +import("stdfaust.lib"); + +noiseH = 0.9 + 0.1 * no.noise; +noiseL = 0.99 + 0.01 * no.noise; +chopA = 1 - 0.75 * (os.osc(49) ^ 12); +chopB = 1 - 0.75 * (os.osc(59) ^ 10); +chopC = 1 - 0.1 * (os.osc(.2) ^ 4); +fm1 = (0.7 + 0.3 * os.osc(90)) * (0.9 + 0.1 * os.osc(11)); +fm2 = (0.8 + 0.2 * os.osc(90)) * (0.9 + 0.1 * os.osc(11)); + +harm(f) = 0.5 * os.osc(f) + 0.1 * os.osc(2 * f) + 0.15 * os.osc(3 * f) + 0.1 * os.osc(4 * f) + 0.1 * os.osc(5 * f) + 0.1 * os.osc(6 * f)+ 0.05 * os.osc(8 * f); + +// locking: +//process = noiseL * chopA * chopB * chopC * fm2 * (0.6 * harm(1200) + 0.2 * os.osc(1100) + 0.2 * os.osc(1300)); + +// locked: +process = noiseL * fm2 * (1 * harm(1200)); diff --git a/addons/missile_manpad/sounds/stinger_locked.ogg b/addons/missile_manpad/sounds/stinger_locked.ogg new file mode 100644 index 00000000000..6f90cde260c Binary files /dev/null and b/addons/missile_manpad/sounds/stinger_locked.ogg differ diff --git a/addons/missile_manpad/sounds/stinger_locking.ogg b/addons/missile_manpad/sounds/stinger_locking.ogg new file mode 100644 index 00000000000..c9e992992cf Binary files /dev/null and b/addons/missile_manpad/sounds/stinger_locking.ogg differ diff --git a/addons/missileguidance/dev/mock_ammo.hpp b/addons/missileguidance/dev/mock_ammo.hpp index 1a77535acd2..6077f2a1e15 100644 --- a/addons/missileguidance/dev/mock_ammo.hpp +++ b/addons/missileguidance/dev/mock_ammo.hpp @@ -1,6 +1,6 @@ class M_Titan_AA_static; class GVAR(mock_a_Malyutka): M_Titan_AA_static { - aiAmmoUsageFlags="64 + 128 + 256 + 512"; + aiAmmoUsageFlags = 64 + 128 + 256 + 512; weaponLockSystem = 0; airLock = 0; lockType = 0;