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
This commit is contained in:
Jo David 2017-05-10 18:29:04 +02:00 committed by PabstMirror
parent 08217f460e
commit b3f03d5ffb
8 changed files with 249 additions and 0 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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 {

View File

@ -12,6 +12,7 @@ class CfgPatches {
QGVAR(moduleSearchArea),
QGVAR(moduleSearchNearby),
QGVAR(moduleSimulation),
QGVAR(moduleSuppressiveFire),
QGVAR(moduleTeleportPlayers)
};
weapons[] = {};

View File

@ -0,0 +1,79 @@
/*
* Author: PabstMirror
* Allows zeus to click to indicate a 3d position.
*
* Arguments:
* 0: The souce object <OBJECT>
* 1: Code to run when position is ready <CODE>
* - Code is passed [0: Successful <BOOL>, 1: Object <OBJECT>, 2: Position ASL <ARRAY>]
* 2: Text <STRING><OPTIONAL>
* 3: Icon image file <STRING><OPTIONAL>
* 4: Icon color <ARRAY><OPTIONAL>
*
* 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;

View File

@ -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 <OBJECT>
* 1: Synchronized units <ARRAY>
* 2: Activated <BOOL>
*
* 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);

View File

@ -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 <OBJECT>
* 1: Fire Pos ASL <ARRAY>
* 2: Artiller Magazine <STRING>
*
* 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;

View File

@ -822,5 +822,8 @@
<German>Ungültiger Radius eingegeben</German>
<Korean>알 수 없는 반경 입력됨</Korean>
</Key>
<Key ID="STR_ACE_Zeus_ModuleSuppressiveFire_DisplayName">
<English>Suppressive Fire</English>
</Key>
</Package>
</Project>