diff --git a/AUTHORS.txt b/AUTHORS.txt index 481f604b2..6f97946d9 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -16,6 +16,7 @@ Ampersand 3Mydlo3 654wak654 Alganthe +Ampersand BaerMitUmlaut Beaumont Brett diff --git a/addons/modules/CfgFactionClasses.hpp b/addons/modules/CfgFactionClasses.hpp index 748b38b2c..360b86fc7 100644 --- a/addons/modules/CfgFactionClasses.hpp +++ b/addons/modules/CfgFactionClasses.hpp @@ -42,6 +42,11 @@ class CfgFactionClasses { priority = 2; side = 7; }; + class GVAR(Projectiles) { + displayName = CSTRING(Projectiles); + priority = 2; + side = 7; + }; class GVAR(Spawn) { displayName = CSTRING(Spawn); priority = 2; diff --git a/addons/modules/CfgVehicles.hpp b/addons/modules/CfgVehicles.hpp index fe8d44c7f..b7415d51f 100644 --- a/addons/modules/CfgVehicles.hpp +++ b/addons/modules/CfgVehicles.hpp @@ -333,6 +333,27 @@ class CfgVehicles { displayName = CSTRING(ModulePatrolArea); function = QFUNC(modulePatrolArea); }; + class GVAR(moduleThrowSelect): GVAR(moduleBase) { + curatorCanAttach = 1; + category = QGVAR(Projectiles); + displayName = CSTRING(moduleThrowSelect); + function = QFUNC(moduleThrowSelect); + icon = "\a3\ui_f\data\gui\rsc\rscdisplayarsenal\cargothrow_ca.paa"; + }; + class GVAR(moduleFireGL): GVAR(moduleBase) { + curatorCanAttach = 1; + category = QGVAR(Projectiles); + displayName = CSTRING(moduleFireGL); + function = QFUNC(moduleFireGL); + icon = QPATHTOF(ui\ugl_shell_ca.paa); + }; + class GVAR(moduleFireLauncher): GVAR(moduleBase) { + curatorCanAttach = 1; + category = QGVAR(Projectiles); + displayName = CSTRING(moduleFireLauncher); + function = QFUNC(moduleFireLauncher); + icon = "\a3\ui_f\data\gui\rsc\rscdisplayarsenal\secondaryweapon_ca.paa"; + }; class GVAR(moduleRemoveArsenal): GVAR(moduleBase) { curatorCanAttach = 1; category = GVAR(Inventory); diff --git a/addons/modules/XEH_PREP.hpp b/addons/modules/XEH_PREP.hpp index b79eb2e06..2ced0af57 100644 --- a/addons/modules/XEH_PREP.hpp +++ b/addons/modules/XEH_PREP.hpp @@ -54,6 +54,8 @@ PREP(moduleEffectFire); PREP(moduleEffectFireLocal); PREP(moduleEquipWithECM); PREP(moduleExportMissionSQF); +PREP(moduleFireGL); +PREP(moduleFireLauncher); PREP(moduleFireMission); PREP(moduleFlyHeight); PREP(moduleFunctionsViewer); @@ -81,6 +83,7 @@ PREP(moduleSpawnDestroyer); PREP(moduleSpawnReinforcements); PREP(moduleSuicideBomber); PREP(moduleTeleportPlayers); +PREP(moduleThrowSelect); PREP(moduleToggleFlashlights); PREP(moduleToggleIRLasers); PREP(moduleToggleLamps); @@ -89,3 +92,6 @@ PREP(moduleTurretOptics); PREP(moduleUnGarrison); PREP(moduleVisibility); PREP(moduleWeather); +PREP(projectiles_unit); +PREP(projectiles_zeus); +PREP(projectiles_ballisticVector); diff --git a/addons/modules/config.cpp b/addons/modules/config.cpp index 09afceb4c..3a93ee471 100644 --- a/addons/modules/config.cpp +++ b/addons/modules/config.cpp @@ -36,6 +36,8 @@ class CfgPatches { QGVAR(moduleEquipWithECM), QGVAR(moduleExportMissionSQF), QGVAR(moduleExecuteCode), + QGVAR(moduleFireGL), + QGVAR(moduleFireLauncher), QGVAR(moduleFireMission), QGVAR(moduleFlyHeight), QGVAR(moduleFunctionsViewer), @@ -50,6 +52,8 @@ class CfgPatches { QGVAR(moduleMakeInvincible), QGVAR(moduleNuke), QGVAR(modulePatrolArea), + QGVAR(moduleThrowSelect), + QGVAR(moduleThrowBottle), QGVAR(moduleRemoveArsenal), QGVAR(moduleRotateObject), QGVAR(moduleSearchBuilding), diff --git a/addons/modules/functions/fnc_moduleFireGL.sqf b/addons/modules/functions/fnc_moduleFireGL.sqf new file mode 100644 index 000000000..1088a13df --- /dev/null +++ b/addons/modules/functions/fnc_moduleFireGL.sqf @@ -0,0 +1,77 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Zeus module function to make unit use GL to fire a HE 40mm. + * + * Arguments: + * 0: Logic + * + * Return Value: + * None + * + * Example: + * [LOGIC] call zen_modules_fnc_moduleFireGL + * + * Public: No + */ + +params ["_logic"]; + +private _unit = attachedTo _logic; +deleteVehicle _logic; + +if (isNull _unit) exitWith { + [LSTRING(NoObjectSelected)] call EFUNC(common,showMessage); +}; + +if !(_unit isKindOf "Man") exitWith { + [LSTRING(OnlyInfantry)] call EFUNC(common,showMessage); +}; + +if !(alive _unit) exitWith { + [LSTRING(OnlyAlive)] call EFUNC(common,showMessage); +}; + +// Check if unit has GL +private _weapons = weapons _unit; +private _glMuzzles = []; +{ + private _weapon = _x; + { + private _muzzle = _x; + if (configName inheritsFrom (configFile >> "CfgWeapons" >> _weapon >> _muzzle) isEqualTo "UGL_F") then { + _glMuzzles pushBack [_weapon, _muzzle]; + }; + } forEach getArray(configFile >> "cfgWeapons" >> _weapon >> "muzzles"); +} forEach _weapons; + +if (_glMuzzles isEqualTo []) exitWith { + ["Unit must have GL"] call EFUNC(common,showMessage); +}; + +// Get target position +[_unit, { + params ["_successful", "_unit", "_mousePosASL"]; + if (_successful) then { + private _weapons = weapons _unit; + private _glMuzzles = []; + { + private _weapon = _x; + { + private _muzzle = _x; + if (configName inheritsFrom (configFile >> "CfgWeapons" >> _weapon >> _muzzle) isEqualTo "UGL_F") then { + _glMuzzles pushBack [_weapon, _muzzle]; + }; + } forEach getArray(configFile >> "cfgWeapons" >> _weapon >> "muzzles"); + } forEach _weapons; + + if (_glMuzzles isEqualTo []) exitWith { + [objNull, format ["Unit has no GL: %1", _unit]] call bis_fnc_showCuratorFeedbackMessage; + }; + + private _magazine = "1Rnd_HE_Grenade_shell"; + private _muzzle = _glMuzzles # 0 # 1; + private _firemode = "Single"; + [_unit, _magazine, _muzzle, _firemode, _mousePosASL] call zen_modules_fnc_projectiles_zeus; + }; +}, [], LSTRING(ModuleFireGL)] call EFUNC(common,selectPosition); diff --git a/addons/modules/functions/fnc_moduleFireLauncher.sqf b/addons/modules/functions/fnc_moduleFireLauncher.sqf new file mode 100644 index 000000000..a49ac0830 --- /dev/null +++ b/addons/modules/functions/fnc_moduleFireLauncher.sqf @@ -0,0 +1,53 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Zeus module function to make unit fire unguided rocket launcher. + * + * Arguments: + * 0: Logic + * + * Return Value: + * None + * + * Example: + * [LOGIC] call zen_modules_fnc_moduleFireLauncher + * + * Public: No + */ + +params ["_logic"]; + +private _unit = attachedTo _logic; +deleteVehicle _logic; + +if (isNull _unit) exitWith { + [LSTRING(NoObjectSelected)] call EFUNC(common,showMessage); +}; + +if !(_unit isKindOf "Man") exitWith { + [LSTRING(OnlyInfantry)] call EFUNC(common,showMessage); +}; + +if !(alive _unit) exitWith { + [LSTRING(OnlyAlive)] call EFUNC(common,showMessage); +}; + +// Check if unit has launcher +private _launcher = secondaryWeapon _unit; +if (_launcher isEqualTo "") exitWith { + ["Unit must have launcher"] call EFUNC(common,showMessage); +}; + +// Get target position +[_unit, { + params ["_successful", "_unit", "_mousePosASL"]; + if (_successful) then { + private _weapon = secondaryWeapon _unit; + + private _magazine = getArray (configFile >> "CfgWeapons" >> _weapon >> "Magazines") # 0; + + private _muzzle = _weapon; + private _firemode = "Single"; + [_unit, _magazine, _muzzle, _firemode, _mousePosASL] call zen_modules_fnc_projectiles_zeus; + }; +}, [], LSTRING(ModuleFireLauncher)] call EFUNC(common,selectPosition); diff --git a/addons/modules/functions/fnc_moduleThrowSelect.sqf b/addons/modules/functions/fnc_moduleThrowSelect.sqf new file mode 100644 index 000000000..27bf6c651 --- /dev/null +++ b/addons/modules/functions/fnc_moduleThrowSelect.sqf @@ -0,0 +1,70 @@ +#include "script_component.hpp" +/* + * Author: Ampersand + * Zeus module function to choose a throwable and make unit throw it. + * + * Arguments: + * 0: Logic + * + * Return Value: + * None + * + * Example: + * [LOGIC] call zen_modules_fnc_moduleThrowSelect + * + * Public: No + */ + +params ["_logic"]; + +private _unit = attachedTo _logic; +deleteVehicle _logic; + +if (isNull _unit) exitWith { + [LSTRING(NoObjectSelected)] call EFUNC(common,showMessage); +}; + +if !(_unit isKindOf "Man") exitWith { + [LSTRING(OnlyInfantry)] call EFUNC(common,showMessage); +}; + +if !(alive _unit) exitWith { + [LSTRING(OnlyAlive)] call EFUNC(common,showMessage); +}; + +private _mags = magazines _unit; +private _throwables = (_mags arrayIntersect _mags) select {_x call BIS_fnc_isThrowable}; + +private _action = []; +{ + _action = [ + _x, + getText (configFile >> "CfgMagazines" >> _x >> "displayName"), + getText (configFile >> "CfgMagazines" >> _x >> "picture"), + { + (_this # 6) params ["_unit", "_magazine"]; + // Get target position + [_unit, { + params ["_successful", "_unit", "_mousePosASL", "_arguments"]; + if (_successful) then { + _arguments params ["_magazine"]; + private _muzzle = configName (("_magazine in (getArray (_x >> 'magazines'))" configClasses (configFile >> "CfgWeapons" >> "Throw")) # 0); + private _firemode = _muzzle; + [_unit, _magazine, _muzzle, _firemode, _mousePosASL] call FUNC(projectiles_zeus); + }; + }, [_magazine], LSTRING(ModuleThrowSelect)] call EFUNC(common,selectPosition); + + }, + {true}, + [_unit, _x] + ] call EFUNC(context_menu,createAction); + [_action, [], 0] call EFUNC(context_menu,addAction); +} forEach _throwables; +[] call EFUNC(context_menu,open); + +// remove actions on menu close +[{ + EGVAR(context_menu,contextGroups) isEqualTo [] +},{ + _this apply {[_x] call zen_context_menu_fnc_removeAction}; +}, _throwables, 15, {}] call CBA_fnc_waitUntilAndExecute; diff --git a/addons/modules/functions/fnc_projectiles_ballisticVector.sqf b/addons/modules/functions/fnc_projectiles_ballisticVector.sqf new file mode 100644 index 000000000..2ada16bb8 --- /dev/null +++ b/addons/modules/functions/fnc_projectiles_ballisticVector.sqf @@ -0,0 +1,94 @@ +/* + Author: Ampersand + Find the + + * Arguments: + * 0: Projectile + * 1: Target Pos ASL + * 2: Speed + * 3: useFlatTrajectory + * + * Return Value: + * 0: VectorDirAndUp + * + * Speed <= 0 will boost speed to whatever is necessary to reach target pos. + * + * Example: + * [_projectile, _targetPos, _throwFlatTrajectory] call zen_modules_fnc_projectiles_ballisticVector; + +// Launch Zeus selection at mouse position +private _projectile = (curatorSelected # 0 # 0); +private _targetPos = AGLToASL screenToWorld getMousePosition; +private _vector = [_projectile, _targetPos] execVM "fnc_projectiles_ballisticVector.sqf"; +_projectile setVelocity = _vector; + + */ + +params ["_projectile", "_targetPos", ["_speed", 0, [0]], ["_throwFlatTrajectory", true, [true]]]; + +private _projectilePosASL = getPosASL _projectile; +private _distance = _projectilePosASL distance2D _targetPos; +private _height = _projectilePosASL # 2 - _targetPos # 2; +private _g = 9.8066; +private _angle = ""; + +// physics +if (_speed > 0) then { + _angle = (acos((_g * _distance^2/_speed^2-_height)/(_projectilePosASL distance _targetPos)) + atan (_distance / _height)) / 2; +}; + +// initSpeed too low to reach target +if !(_angle isEqualType 0) then { + + if (_speed > 0) then { + // just go as far as possible + _angle = 45; + } else { + // boost speed + while {_speed < 10000 && {!(_angle isEqualType 0)}} do { + _speed = _speed + 1; + _angle = (acos((_g * _distance^2/_speed^2-_height)/(_projectilePosASL distance _targetPos)) + atan (_distance / _height)) / 2; + }; + _speed = _speed + 2; + _angle = (acos((_g * _distance^2/_speed^2-_height)/(_projectilePosASL distance _targetPos)) + atan (_distance / _height)) / 2; + }; +}; + +if (_angle < 0) then { _angle = _angle + 90; }; + +private _speedY = _speed * sin _angle; +private _speedx = _speed * cos _angle; + +private _vectorLOS = _projectilePosASL vectorFromTo _targetPos; +private _vectorDir = [_projectilePosASL # 0,_projectilePosASL # 1, 0] vectorFromTo [_targetPos # 0, _targetPos # 1, 0]; +private _vectorLaunch = vectorNormalized (_vectorDir vectorAdd [0,0,_speedY/_speedX]); + + +// check if using high angle +if _throwFlatTrajectory then { + private _angleLOS_Vert = acos (_vectorLOS vectorCos [0,0,1]); + private _angleHORZ_LOS = acos (_vectorDir vectorCos _vectorLOS); + private _angleLOS_Launch = acos (_vectorLOS vectorCos _vectorLaunch); + private _angleLaunch_Vert = acos (_vectorLaunch vectorCos [0,0,1]); + //systemChat format ["LV:%1, HL:%2, LA:%3, AV:%4",_angleLOS_Vert, _angleHORZ_LOS, _angleLOS_Launch, _angleLaunch_Vert]; + + if (_angleLOS_Launch > (_angleLOS_Vert / 2)) then { + if (_angleLOS_Vert > 90) then { + _angleHORZ_LOS = -1 * _angleHORZ_LOS; + }; + _angle = _angleLaunch_Vert + _angleHORZ_LOS; + //systemChat format ["FA:%1",_angle]; + + _speedY = _speed * sin _angle; + _speedx = _speed * cos _angle; + _vectorLaunch = vectorNormalized (_vectorDir vectorAdd [0,0,_speedY/_speedX]); + }; +}; + +private _vectorFinal = _vectorLaunch vectorMultiply _speed; + +// projectile orientation +private _vectorSide = _vectorFinal vectorCrossProduct [0,0,-1]; +private _vectorUp = _vectorFinal vectorCrossProduct _vectorSide; + +[_vectorFinal,_vectorUp] diff --git a/addons/modules/functions/fnc_projectiles_unit.sqf b/addons/modules/functions/fnc_projectiles_unit.sqf new file mode 100644 index 000000000..9e65796e9 --- /dev/null +++ b/addons/modules/functions/fnc_projectiles_unit.sqf @@ -0,0 +1,230 @@ +/* + Author: Ampersand + Makes unit throw the specified magazine to the specified location. + + * Arguments: + * 0: Unit + * 1: Magazine + * 2: Muzzle + * 3: Fire Mode + * 4: Target Pos ASL + * 5: Trajectory + * + * Return Value: + * NONE + + * Example: + * [_unit, _magazine, _muzzle, _firemode, _targetPos, _throwFlatTrajectory] call zen_modules_fnc_projectiles_unit; + * [_unit, _magazine, _muzzle, _firemode, _targetPos, _throwFlatTrajectory] remoteExecCall ["zen_modules_fnc_projectiles_unit", _unit]; + +// Zeus selected unit throw a thing at mouse position +private _unit = (curatorSelected # 0 # 0); +(currentThrowable _unit) params ["_magazine", "_muzzle"]; +private _firemode = _muzzle; +private _targetPos = AGLToASL screenToWorld getMousePosition; +[_unit, _magazine, _muzzle, _firemode, _targetPos, true] call zen_modules_fnc_projectiles_unit + +// Zeus selected unit fire launcher at mouse position +private _unit = (curatorSelected # 0 # 0); +private _targetPos = AGLToASL screenToWorld getMousePosition; +private _weapon = secondaryWeapon _unit; +private _magazine = getArray (configFile >> "CfgWeapons" >> _weapon >> "Magazines") # 0; +private _muzzle = _weapon; +private _firemode = "Single"; +[_unit, _magazine, _muzzle, _firemode, _targetPos, true] call zen_modules_fnc_projectiles_unit + + */ + +params ["_unit", "_magazine", "_muzzle", "_firemode", "_targetPos", "_throwFlatTrajectory"]; +if !(local _unit) exitWith {}; + +_unit setVariable ["zen_projectiles_throwParams", _this]; +//_unit setVariable ["zen_projectiles_throwParams", [_targetPos, _throwFlatTrajectory]]; + +_unit disableAI "PATH"; +_unit setBehaviour "COMBAT"; +private _stance = stance _unit; +if (_stance isEqualTo "STAND") then {_unit setUnitPosWeak "UP";}; +if (_stance isEqualTo "CROUCH") then {_unit setUnitPosWeak "Middle";}; +if (_stance isEqualTo "PRONE") then {_unit setUnitPosWeak "DOWN";}; + +_unit doWatch ASLToAGL _targetPos; + +// Launcher needs timely command of forceWeaponFire +_unit addEventHandler ["AnimChanged", { + params ["_unit", "_anim"]; + if !(_unit getVariable ["zen_projectiles_thrown", false]) then { + // haven't fired yet + private _throwParams = _unit getVariable ["zen_projectiles_throwParams", []]; + if (_throwParams isEqualTo []) exitWith {}; + _throwParams params ["_unit", "_magazine", "_muzzle", "_firemode", "_targetPos", "_throwFlatTrajectory"]; + + // fire again + _unit forceWeaponFire [_muzzle, _firemode]; + + } else { + // fired, clean up + _unit removeEventHandler ["AnimChanged", _thisEventHandler]; + }; +}]; + +// set the projectile initial velocity +_unit addEventHandler ["Fired", { + params ["_unit", "_weapon", "_muzzle", "_mode", "_ammo", "_magazine", "_projectile", "_gunner"]; + + // clean up + _unit removeEventHandler ["Fired", _thisEventHandler]; + _unit setVariable ["zen_projectiles_thrown", true]; + _unit enableAI "PATH"; + + private _throwParams = _unit getVariable ["zen_projectiles_throwParams", []]; + if (_throwParams isEqualTo []) exitWith {}; + _throwParams params ["_unit", "_magazine", "_muzzle", "_firemode", "_targetPos", "_throwFlatTrajectory"]; + + // SACLOS + if ( + isNumber (configfile >> "CfgAmmo" >> _ammo >> "manualControl") && + {1 == (getNumber (configfile >> "CfgAmmo" >> _ammo >> "manualControl"))} + ) then { + _projectile setMissileTargetPos (ASLToAGL _targetPos); + }; + + private _projectilePosASL = getPosASL _projectile; + private _distance = _projectilePosASL distance2D _targetPos; + private _height = _projectilePosASL # 2 - _targetPos # 2; + private _g = 9.8066; + + private _speed = getNumber (configFile >> "CfgAmmo" >> _ammo >> "maxSpeed"); + if (_speed == 0) then { + _speed = getNumber (configFile >> "CfgMagazines" >> _magazine >> "initSpeed"); + }; + + _maneuvrability = getNumber (configFile >> "CfgAmmo" >> _ammo >> "maneuvrability"); + private _vectorLaunch = _projectilePosASL vectorFromTo _targetPos; + if (_maneuvrability <= 1) then { + // physics + private _angle = (acos((_g * _distance^2/_speed^2-_height)/(_projectilePosASL distance _targetPos)) + atan (_distance / _height)) / 2; + + // initSpeed too low to reach target + if !(_angle isEqualType 0) then { + /* boost speed + systemChat format ["d:%1, h:%2, s:%3, a:%4",_distance, _height, _speed, _angle]; + while {_speed < 20 && {!(_angle isEqualType 0)}} do { + _speed = _speed + 1; + _angle = (acos((_g * _distance^2/_speed^2-_height)/(_projectilePosASL distance _targetPos)) + atan (_distance / _height)) / 2; + }; + _speed = _speed + 2; + _angle = (acos((_g * _distance^2/_speed^2-_height)/(_projectilePosASL distance _targetPos)) + atan (_distance / _height)) / 2; + */ + // just go as far as possible + _angle = 45; + }; + + if (_angle < 0) then { _angle = _angle + 90; }; + + private _speedY = _speed * sin _angle; + private _speedx = _speed * cos _angle; + + private _vectorLOS = _projectilePosASL vectorFromTo _targetPos; + private _vectorDir = [_projectilePosASL # 0,_projectilePosASL # 1, 0] vectorFromTo [_targetPos # 0, _targetPos # 1, 0]; + _vectorLaunch = vectorNormalized (_vectorDir vectorAdd [0,0,_speedY/_speedX]); + + // check if using high angle + if _throwFlatTrajectory then { + private _angleLOS_Vert = acos (_vectorLOS vectorCos [0,0,1]); + private _angleHORZ_LOS = acos (_vectorDir vectorCos _vectorLOS); + private _angleLOS_Launch = acos (_vectorLOS vectorCos _vectorLaunch); + private _angleLaunch_Vert = acos (_vectorLaunch vectorCos [0,0,1]); + //systemChat format ["LV:%1, HL:%2, LA:%3, AV:%4",_angleLOS_Vert, _angleHORZ_LOS, _angleLOS_Launch, _angleLaunch_Vert]; + + if (_angleLOS_Launch > (_angleLOS_Vert / 2)) then { + if (_angleLOS_Vert > 90) then { + _angleHORZ_LOS = -1 * _angleHORZ_LOS; + }; + _angle = _angleLaunch_Vert + _angleHORZ_LOS; + //systemChat format ["FA:%1",_angle]; + + _speedY = _speed * sin _angle; + _speedx = _speed * cos _angle; + _vectorLaunch = vectorNormalized (_vectorDir vectorAdd [0,0,_speedY/_speedX]); + }; + }; + }; + + private _vectorFinal = _vectorLaunch vectorMultiply _speed; + // projectile orientation + private _vectorSide = _vectorFinal vectorCrossProduct [0,0,-1]; + private _vectorUp = _vectorFinal vectorCrossProduct _vectorSide; + + _projectile setVectorDirAndUp [ + _vectorFinal, + _vectorUp + ]; + + // set velocity + _projectile setVelocity _vectorFinal; + + /* test draw sight and aim lines + amp_projectiles_unit = _unit; + amp_projectiles_projectile = _projectile; + amp_projectiles_projectilePos = getPos _projectile; + amp_projectiles_targetPos = ASLToAGL _targetPos; + amp_projectiles_velocity = _vectorFinal; + onEachFrame { + drawLine3D [amp_projectiles_projectilePos, amp_projectiles_targetPos, [0,1,0,1]]; // sight line + drawLine3D [amp_projectiles_targetPos, amp_projectiles_targetPos vectorAdd [0,0,10], [0,1,0,1]]; // vertical goal post + drawLine3D [amp_projectiles_projectilePos, amp_projectiles_projectilePos vectorAdd amp_projectiles_velocity, [0,0,1,1]]; // aim line, initial angle + }; + */ +}]; + +_unit setVariable ["zen_projectiles_thrown", false]; +_unit setVariable ["zen_projectiles_time", diag_tickTime]; + +// add ammo to unit +private _canAdd = _unit canAdd _magazine; +private _removedItems = []; +if !_canAdd then { + private _backpackContainer = backpackContainer _unit; + if (_backpackContainer isEqualTo objNull) then { + _unit addBackpackGlobal "B_TacticalPack_blk"; + _backpackContainer = backpackContainer _unit; + }; + + while {!(_unit canAddItemToBackpack _magazine)} do { + private _item = (backpackItems _unit) # 0; + _unit removeItemFromBackpack _item; + _removedItems pushBack _item; + }; +}; +_unit addMagazineGlobal _magazine; + +[{ + params ["_unit", "_magazine", "_muzzle", "_firemode", "_targetPos", "_throwFlatTrajectory"]; + // make unit turn towards target + private _dirUnit = getDir _unit; + private _dirTarget = _unit getDir _targetPos; + private _dirDiff = _dirTarget - _dirUnit; + if (abs _dirDiff > 180) then {_dirDiff = abs _dirDiff - 360}; + (abs _dirDiff < 35) +},{ + params ["_unit", "_magazine", "_muzzle", "_firemode", "_targetPos", "_throwFlatTrajectory"]; + // close enough, make unit face target + private _dirUnit = getDir _unit; + private _dirTarget = _unit getDir _targetPos; + private _dirDiff = _dirTarget - _dirUnit; + if (abs _dirDiff > 5) then { _unit setDir _dirTarget;}; + [{ + params ["_unit", "_magazine", "_muzzle", "_firemode", "_targetPos", "_throwFlatTrajectory"]; + _unit forceWeaponFire [_muzzle, _firemode]; + }, _this, 0.1] call CBA_fnc_waitAndExecute; +}, _this, 15, {}] call CBA_fnc_waitUntilAndExecute; + +_unit setVariable ["zen_projectiles_thrown", nil]; +_unit enableAI "PATH"; + +if !_canAdd then { + { + _unit addItemToBackpack _x; + } forEach _removedItems; +}; diff --git a/addons/modules/functions/fnc_projectiles_zeus.sqf b/addons/modules/functions/fnc_projectiles_zeus.sqf new file mode 100644 index 000000000..a3d73370b --- /dev/null +++ b/addons/modules/functions/fnc_projectiles_zeus.sqf @@ -0,0 +1,57 @@ +/* + Author: Ampers + Check if projectile can reach target, then remote executes on the unit. + + * Arguments: + * 0: Unit + * 1: Magazine + * 2: Muzzle + * 3: Fire Mode + * 4: Target Pos ASL + * + * Return Value: + * NONE + + * Example: + * [_unit, _magazine, _muzzle, _firemode, _mousePosASL] call tft_zeus_fnc_zeusProjectile; + */ + +params ["_unit", "_magazine", "_muzzle", "_firemode", "_mousePosASL"]; + +// get location of target in zeus cam view +private _position0 = positionCameraToWorld [0, 0, 0]; +private _intersections = lineIntersectsSurfaces [AGLToASL _position0, _mousePosASL, cameraOn, objNull, true, 1, "GEOM"]; + +private _targetPos = _mousePosASL; +if !(_intersections isEqualTo []) then { + _targetPos = _intersections # 0 # 0; + //systemChat str _targetPos; +}; + +// check if can reach +private _speed = getNumber (configFile >> "CfgAmmo" >> (getText (configFile >> "CfgMagazines" >> _magazine >> "ammo")) >> "maxSpeed"); +if (_speed == 0) then { + _speed = getNumber (configFile >> "CfgMagazines" >> _magazine >> "initSpeed"); +}; + +private _speed = getNumber (configFile >> "CfgMagazines" >> _magazine >> "initSpeed"); +private _eyePos = eyePos _unit; +private _distance = _eyePos distance2D _targetPos; +private _height = _eyePos # 2 - _targetPos # 2; +private _g = 9.8066; +private _angle = (acos((_g * _distance^2/_speed^2-_height)/(_eyePos distance _targetPos)) + atan (_distance / _height)) / 2; + +if !(_angle isEqualType 0) then { + // if can't reach, notify zeus. Will try to throw as far as possible + [objNull, format ["Can't reach target! D:%1 H:%2", _distance, _height]] call bis_fnc_showCuratorFeedbackMessage; +}; + +if (isNil "zen_projectiles_throwFlatTrajectory") then {zen_projectiles_throwFlatTrajectory = true;}; + +// trace bullet +//BIS_tracedShooter = nil; +//[_unit, 1] call BIS_fnc_traceBullets; + +[_unit, _magazine, _muzzle, _firemode, _targetPos, zen_projectiles_throwFlatTrajectory] remoteExecCall ["zen_modules_fnc_projectiles_unit", _unit]; + +true diff --git a/addons/modules/stringtable.xml b/addons/modules/stringtable.xml index 4a2d9af66..143c27c62 100644 --- a/addons/modules/stringtable.xml +++ b/addons/modules/stringtable.xml @@ -1008,6 +1008,18 @@ Zmieni zachowanie, prędkość i formację grupy. グループの移動速度や陣形といった挙動を変更できます。 + + Projectiles + + + Throw Select + + + Fire GL + + + Fire Launcher + Side Relations Отношения сторон diff --git a/addons/modules/ui/ugl_shell_ca.paa b/addons/modules/ui/ugl_shell_ca.paa new file mode 100644 index 000000000..6651ae161 Binary files /dev/null and b/addons/modules/ui/ugl_shell_ca.paa differ diff --git a/docs/user_guide/modules_list.md b/docs/user_guide/modules_list.md index 653fb313f..17bfb66fd 100644 --- a/docs/user_guide/modules_list.md +++ b/docs/user_guide/modules_list.md @@ -147,6 +147,14 @@ When used in JIP mode, the module can be deleted to stop execution on new JIP cl Outputs SQF code that can be executed to restore the current mission. +## Fire GL + +Orders AI with UGL to fire the currently loaded round at the target position. + +## Fire Launcher + +Orders AI with launcher to fire the currently loaded round at the target position. + ## Fly Height Sets the flying altitude of the attached aircraft relative to the ground. @@ -253,6 +261,10 @@ The range of Auto Seek is based on the unit's spot distance skill with a minimum Teleports players from the selected side, group(s), or individual player(s) to the module's position. If the module is placed on a vehicle, players will be teleported into the vehicle. +## Throw Select + +Orders AI to throw the selected throwable from its inventory at the target position. + ## Toggle Flashlights Toggles the flashlights of all AI units of the given side (or group, when placed on a unit) to the selected state.