From b3f03d5ffbae7dbc44473ca44973558927ab0a80 Mon Sep 17 00:00:00 2001 From: Jo David Date: Wed, 10 May 2017 18:29:04 +0200 Subject: [PATCH] Zeus Suppression Module (#4977) * add base structure * Add getModuleDestination * Add 2d map support, debug * Cleanup, handle weapon max range * Handle non-local units * Use new showMessage func * Run on groups when placed on leader * Support for Indirect Fire Vehicles * Cleanup * Use doArtilleryFire which was fixed in 1.68 --- addons/zeus/CfgVehicles.hpp | 6 + addons/zeus/XEH_PREP.hpp | 3 + addons/zeus/XEH_postInit.sqf | 1 + addons/zeus/config.cpp | 1 + .../functions/fnc_getModuleDestination.sqf | 79 ++++++++++++ .../functions/fnc_moduleSuppressiveFire.sqf | 117 ++++++++++++++++++ .../fnc_moduleSuppressiveFireLocal.sqf | 39 ++++++ addons/zeus/stringtable.xml | 3 + 8 files changed, 249 insertions(+) create mode 100644 addons/zeus/functions/fnc_getModuleDestination.sqf create mode 100644 addons/zeus/functions/fnc_moduleSuppressiveFire.sqf create mode 100644 addons/zeus/functions/fnc_moduleSuppressiveFireLocal.sqf diff --git a/addons/zeus/CfgVehicles.hpp b/addons/zeus/CfgVehicles.hpp index c7111e42bc..42dc6d4289 100644 --- a/addons/zeus/CfgVehicles.hpp +++ b/addons/zeus/CfgVehicles.hpp @@ -160,6 +160,12 @@ class CfgVehicles { displayName = CSTRING(ModuleSearchNearby_DisplayName); function = QFUNC(moduleSearchNearby); }; + class GVAR(moduleSuppressiveFire): GVAR(moduleBase) { + curatorCanAttach = 1; + category = QGVAR(AI); + displayName = CSTRING(ModuleSuppressiveFire_DisplayName); + function = QFUNC(moduleSuppressiveFire); + }; class GVAR(moduleSetMedic): GVAR(moduleBase) { curatorCanAttach = 1; category = QGVAR(Medical); diff --git a/addons/zeus/XEH_PREP.hpp b/addons/zeus/XEH_PREP.hpp index f52c300355..0090d04015 100644 --- a/addons/zeus/XEH_PREP.hpp +++ b/addons/zeus/XEH_PREP.hpp @@ -4,6 +4,7 @@ PREP(bi_moduleCurator); PREP(bi_moduleMine); PREP(bi_moduleProjectile); PREP(bi_moduleRemoteControl); +PREP(getModuleDestination); PREP(handleZeusUnitAssigned); PREP(moduleAddSpareTrack); PREP(moduleAddSpareWheel); @@ -16,6 +17,8 @@ PREP(moduleSetMedic); PREP(moduleSetMedicalVehicle); PREP(moduleSetMedicalFacility); PREP(moduleSimulation); +PREP(moduleSuppressiveFire); +PREP(moduleSuppressiveFireLocal); PREP(moduleSurrender); PREP(moduleTeleportPlayers); PREP(moduleUnconscious); diff --git a/addons/zeus/XEH_postInit.sqf b/addons/zeus/XEH_postInit.sqf index 44bd406552..0c3ff85cdf 100644 --- a/addons/zeus/XEH_postInit.sqf +++ b/addons/zeus/XEH_postInit.sqf @@ -14,6 +14,7 @@ QGVAR(GlobalSkillAI) addPublicVariableEventHandler FUNC(moduleGlobalSetSkill); [QGVAR(modulePatrolArea), CBA_fnc_taskPatrol] call CBA_fnc_addEventHandler; [QGVAR(moduleSearchNearby), CBA_fnc_searchNearby] call CBA_fnc_addEventHandler; [QGVAR(moduleSearchArea), CBA_fnc_taskSearchArea] call CBA_fnc_addEventHandler; +[QGVAR(suppressiveFire), LINKFUNC(moduleSuppressiveFireLocal)] call CBA_fnc_addEventHandler; // Editable object commands must be ran on server, this events are used in the respective module if (isServer) then { diff --git a/addons/zeus/config.cpp b/addons/zeus/config.cpp index 248d2af71c..5cd88750b9 100644 --- a/addons/zeus/config.cpp +++ b/addons/zeus/config.cpp @@ -12,6 +12,7 @@ class CfgPatches { QGVAR(moduleSearchArea), QGVAR(moduleSearchNearby), QGVAR(moduleSimulation), + QGVAR(moduleSuppressiveFire), QGVAR(moduleTeleportPlayers) }; weapons[] = {}; diff --git a/addons/zeus/functions/fnc_getModuleDestination.sqf b/addons/zeus/functions/fnc_getModuleDestination.sqf new file mode 100644 index 0000000000..d6a19d69b1 --- /dev/null +++ b/addons/zeus/functions/fnc_getModuleDestination.sqf @@ -0,0 +1,79 @@ +/* + * Author: PabstMirror + * Allows zeus to click to indicate a 3d position. + * + * Arguments: + * 0: The souce object + * 1: Code to run when position is ready + * - Code is passed [0: Successful , 1: Object , 2: Position ASL ] + * 2: Text + * 3: Icon image file + * 4: Icon color + * + * Return Value: + * None + * + * Example: + * [player, {systemChat format ["Done %1", _this]}] call ace_zeus_fnc_getModuleDestination + * + * Public: No + */ +#include "script_component.hpp" + +params ["_object", "_code", ["_text", ""], ["_icon", "\a3\ui_f\data\IGUI\Cfg\Cursors\select_target_ca.paa"], ["_color", [1,0,0,1]]]; + +if (missionNamespace getVariable [QGVAR(moduleDestination_running), false]) exitWith { + [false, _object, [0,0,0]] call _code; + ERROR("getModuleDestination already running"); +}; + +GVAR(moduleDestination_running) = true; + +// Add mouse button eh for the zeus display (triggered from 2d or 3d) +GVAR(moduleDestination_displayEH) = [(findDisplay 312), "mouseButtonDown", { + params ["", "_mouseButton"]; + if (_mouseButton != 0) exitWith {}; // Only watch for LMB + _thisArgs params ["_object", "_code"]; + private _mousePosASL = if (ctrlShown ((findDisplay 312) displayCtrl 50)) then { + private _pos2d = (((findDisplay 312) displayCtrl 50) ctrlMapScreenToWorld getMousePosition); + _pos2d set [2, getTerrainHeightASL _pos2d]; + _pos2d + } else { + AGLToASL (screenToWorld getMousePosition); + }; + TRACE_2("placed",_object,_mousePosASL); + [true, _object, _mousePosASL] call _code; + GVAR(moduleDestination_running) = false; +}, [_object, _code]] call CBA_fnc_addBISEventHandler; + +// Add draw eh for the zeus map - draws the 2d icon and line +GVAR(moduleDestination_mapDrawEH) = [((findDisplay 312) displayCtrl 50), "draw", { + params ["_mapCtrl"]; + _thisArgs params ["_object", "_text", "_icon", "_color"]; + + private _pos2d = (((findDisplay 312) displayCtrl 50) ctrlMapScreenToWorld getMousePosition); + _mapCtrl drawIcon [_icon, _color, _pos2d, 24, 24, 45, _text, 1, 0.03, "TahomaB", "right"]; + _mapCtrl drawLine [getPos _object, _pos2d, _color]; +}, [_object, _text, _icon, _color]] call CBA_fnc_addBISEventHandler; + +[{ + (_this select 0) params ["_object", "_code", "_text", "_icon", "_color"]; + if ((isNull _object) || {isNull findDisplay 312} || {!isNull findDisplay 49}) then { + TRACE_3("null-exit",isNull _object,isNull findDisplay 312,isNull findDisplay 49); + GVAR(moduleDestination_running) = false; + [false, _object, [0,0,0]] call _code; + }; + if (GVAR(moduleDestination_running)) then { + // Draw the 3d icon and line + private _mousePosAGL = screenToWorld getMousePosition; + drawIcon3D [_icon, _color, _mousePosAGL, 1.5, 1.5, 45, _text]; + drawLine3D [_mousePosAGL, ASLtoAGL (getPosASL _object), _color];; + } else { + TRACE_3("cleaning up",_this select 1, GVAR(moduleDestination_displayEH), GVAR(moduleDestination_mapDrawEH)); + (_this select 1) call CBA_fnc_removePerFrameHandler; + (findDisplay 312) displayRemoveEventHandler ["mouseButtonDown", GVAR(moduleDestination_displayEH)]; + ((findDisplay 312) displayCtrl 50) ctrlRemoveEventHandler ["draw", GVAR(moduleDestination_mapDrawEH)]; + GVAR(moduleDestination_displayEH) = nil; + GVAR(moduleDestination_mapDrawEH) = nil; + }; +}, 0, [_object, _code, _text, _icon, _color]] call CBA_fnc_addPerFrameHandler; diff --git a/addons/zeus/functions/fnc_moduleSuppressiveFire.sqf b/addons/zeus/functions/fnc_moduleSuppressiveFire.sqf new file mode 100644 index 0000000000..4d18c3f6e4 --- /dev/null +++ b/addons/zeus/functions/fnc_moduleSuppressiveFire.sqf @@ -0,0 +1,117 @@ +/* + * Author: bux, PabstMirror + * Commands the selected unit or group to start suppressive fire on the unit, group or location the module is placed on + * + * Arguments: + * 0: The module logic + * 1: Synchronized units + * 2: Activated + * + * Return Value: + * None + * + * Public: No + */ +// #define DRAW_ZEUS_INFO +#include "script_component.hpp" + +if (canSuspend) exitWith {[FUNC(moduleSuppressiveFire), _this] call CBA_fnc_directCall;}; + +params ["_logic", "_units", "_activated"]; + +if !(_activated && local _logic) exitWith {}; + +// Validate the module target +private _unit = effectiveCommander (attachedTo _logic); +TRACE_3("moduleSuppressiveFire placed",_unit,typeOf _unit,typeOf _logic); + +deleteVehicle _logic; // cleanup logic now, we just needed it to get the attached unit + +if (isNull _unit) exitWith { + [LSTRING(NothingSelected)] call FUNC(showMessage); +}; +if (!alive _unit) exitWith { + [localize LSTRING(OnlyAlive)] call FUNC(showMessage); +}; +if ([_unit] call EFUNC(common,isPlayer)) exitWith { + ["str_a3_cfgvehicles_moduleremotecontrol_f_errorPlayer"] call FUNC(showMessage); +}; + +[_unit, { + params ["_successful", "_unit", "_mousePosASL"]; + TRACE_3("getModuleDestination return",_successful,_unit,_mousePosASL); + if (!_successful) exitWith {}; + if (!alive _unit) exitWith {}; + private _vehicle = vehicle _unit; + + private _targetASL = _mousePosASL vectorAdd [0,0,0.6]; // mouse pos is at ground level zero, raise up a bit; + private _artilleryMag = ""; + + if ((getNumber (configFile >> "CfgVehicles" >> (typeOf _vehicle) >> "artilleryScanner")) == 1) then { + // Artillery - Get mortar ammo type and verify in range + if (isNull gunner _vehicle) exitWith {_targetASL = [];}; + { + private _ammo = getText (configFile >> "CfgMagazines" >> _x >> "ammo"); + private _hit = getNumber (configFile >> "CfgAmmo" >> _ammo >> "hit"); + if (_hit > 20) exitWith {_artilleryMag = _x;}; + } forEach getArtilleryAmmo [_vehicle]; + TRACE_1("getArtilleryAmmo",_artilleryMag); + if (_artilleryMag == "") exitWith {_targetASL = [];}; + private _eta = _vehicle getArtilleryETA [ASLtoAGL _targetASL, _artilleryMag]; + TRACE_1("getArtilleryETA",_eta); + if (_eta < 0) exitWith { + [ELSTRING(Interaction,NotInRange)] call FUNC(showMessage); + _targetASL = []; + }; + ["TOF: %1 sec", _eta toFixed 1] call FUNC(showMessage); + } else { + // Direct fire - Get a target position that will work + private _lis = lineIntersectsSurfaces [eyePos _unit, _targetASL, _unit, _vehicle]; + if ((count _lis) > 0) then { // If point is hidden, unit won't fire, do a ray cast to find where they should shoot at + _targetASL = ((_lis select 0) select 0); + TRACE_1("using ray cast pos",_mousePosASL distance _targetASL); + }; + if (_unit isEqualTo _vehicle) then { // Max range a unit can fire seems to be based on the weapon's config + private _distance = _targetASL vectorDistance eyePos _unit; + private _maxWeaponRange = getNumber (configFile >> "CfgWeapons" >> (currentWeapon _unit) >> "maxRange"); + TRACE_3("",_distance,_maxWeaponRange,currentWeapon _unit); + if (_distance > (_maxWeaponRange - 50)) then { + if (_distance > (2.5 * _maxWeaponRange)) then { + _targetASL = []; + [ELSTRING(Interaction,NotInRange)] call FUNC(showMessage); + } else { + // 1-2.5x the weapon max range, find a virtual point the AI can shoot at (won't have accurate elevation, but it will put rounds downrange) + private _fakeElevation = (_distance / 100000) * (_distance - _maxWeaponRange); + _targetASL = (eyePos _unit) vectorAdd (((eyePos _unit) vectorFromTo _targetASL) vectorMultiply (_maxWeaponRange - 50)) vectorAdd [0,0,_fakeElevation]; + TRACE_2("using virtual halfway point",_mousePosASL distance _targetASL,_fakeElevation); + }; + }; + }; + }; + + if (_targetASL isEqualTo []) exitWith {}; + + private _units = [_unit]; + if (_unit == (leader _unit)) then {_units = units _unit;}; + if (_artilleryMag != "") then {_units = [gunner _vehicle];}; + + { + if (((_unit distance _x) < 30) && {!([_x] call EFUNC(common,isPlayer))} && {[_x] call EFUNC(common,isAwake)}) then { + TRACE_2("sending event",_x,_targetASL); + [QGVAR(suppressiveFire), [_x, _targetASL, _artilleryMag], _x] call CBA_fnc_targetEvent; + }; + } forEach _units; + +#ifdef DRAW_ZEUS_INFO + [eyePos _unit, _mousePosASL, [0,0,1,1]] call EFUNC(common,addLineToDebugDraw); + [eyePos _unit, _targetASL, [1,0,0,1]] call EFUNC(common,addLineToDebugDraw); + if (_unit != _vehicle) then { + [_vehicle] call CBA_fnc_addUnitTrackProjectiles; + } else { + { + [_x] call CBA_fnc_addUnitTrackProjectiles; + } forEach _units; + }; +#endif + +}, (localize LSTRING(ModuleSuppressiveFire_DisplayName))] call FUNC(getModuleDestination); diff --git a/addons/zeus/functions/fnc_moduleSuppressiveFireLocal.sqf b/addons/zeus/functions/fnc_moduleSuppressiveFireLocal.sqf new file mode 100644 index 0000000000..7ab7365c3b --- /dev/null +++ b/addons/zeus/functions/fnc_moduleSuppressiveFireLocal.sqf @@ -0,0 +1,39 @@ +/* + * Author: bux, PabstMirror + * Commands the selected unit or group to start suppressive fire on the unit, group or location the module is placed on + * + * Arguments: + * 0: Unit + * 1: Fire Pos ASL + * 2: Artiller Magazine + * + * Return Value: + * None + * + * Public: No + */ +#include "script_component.hpp" + +params ["_unit", "_targetASL", "_artilleryMag"]; +TRACE_4("moduleSuppressiveFireLocal",_unit,local _unit,_targetASL,_artilleryMag); + +if (_artilleryMag != "") exitWith { + (vehicle _unit) doArtilleryFire [ASLtoAGL _targetASL, _artilleryMag, 4]; + TRACE_3("doArtilleryFire",_unit,_targetASL,_artilleryMag); +}; + +[{ + params ["_unit", "_burstsLeft", "_nextRun", "_targetASL", "_artilleryMag"]; + if (!alive _unit) exitWith {true}; + if (CBA_missionTime >= _nextRun) then { + _burstsLeft = _burstsLeft - 1; + _this set [1, _burstsLeft]; + _this set [2, _nextRun + 4]; + _unit doSuppressiveFire _targetASL; + TRACE_2("doSuppressiveFire",_unit,_targetASL); + }; + (_burstsLeft <= 0) +}, { + TRACE_1("Done",_this); +}, [_unit, 11, CBA_missionTime, _targetASL, _artilleryMag]] call CBA_fnc_waitUntilAndExecute; + diff --git a/addons/zeus/stringtable.xml b/addons/zeus/stringtable.xml index 33276b33af..b9bd67cf3d 100644 --- a/addons/zeus/stringtable.xml +++ b/addons/zeus/stringtable.xml @@ -822,5 +822,8 @@ Ungültiger Radius eingegeben 알 수 없는 반경 입력됨 + + Suppressive Fire +