diff --git a/addons/danger/functions/fnc_tacticsAssess.sqf b/addons/danger/functions/fnc_tacticsAssess.sqf index 1116fb620..8171a529a 100644 --- a/addons/danger/functions/fnc_tacticsAssess.sqf +++ b/addons/danger/functions/fnc_tacticsAssess.sqf @@ -250,7 +250,7 @@ _plan = selectRandom _plan; switch (_plan) do { case TACTICS_FLANK: { // flank - [{_this call FUNC(tacticsFlank)}, [_group, _pos, _units], 22 + random 8] call CBA_fnc_waitAndExecute; + [{_this call FUNC(tacticsFlank)}, [_group, _pos], 22 + random 8] call CBA_fnc_waitAndExecute; }; case TACTICS_GARRISON: { // garrison ~ nb units not carried here - nkenny @@ -262,11 +262,11 @@ switch (_plan) do { }; case TACTICS_SUPPRESS: { // suppress - [{_this call FUNC(tacticsSuppress)}, [_group, _pos, _units], 4 + random 4] call CBA_fnc_waitAndExecute; + [{_this call FUNC(tacticsSuppress)}, [_group, _pos], 2 + random 2] call CBA_fnc_waitAndExecute; }; case TACTICS_ATTACK: { // group attacks as one - [{_this call FUNC(tacticsAttack)}, [_group, _pos, _units], 1 + random 1] call CBA_fnc_waitAndExecute; + [{_this call FUNC(tacticsAttack)}, [_group, _pos], 1 + random 1] call CBA_fnc_waitAndExecute; }; default { // hide from armor diff --git a/addons/danger/functions/fnc_tacticsFlank.sqf b/addons/danger/functions/fnc_tacticsFlank.sqf index 1df129b2e..046066cfd 100644 --- a/addons/danger/functions/fnc_tacticsFlank.sqf +++ b/addons/danger/functions/fnc_tacticsFlank.sqf @@ -7,8 +7,7 @@ * 0: group executing tactics or group leader * 1: group target or position * 2: units in group, default all - * 3: how many assault cycles - * 4: default overwatch destination + * 3: default overwatch destination * 4: delay until unit is ready again * * Return Value: @@ -19,7 +18,7 @@ * * Public: No */ -params ["_group", "_target", ["_units", []], ["_cycle", 5], ["_overwatch", []], ["_delay", 120]]; +params ["_group", "_target", ["_units", []], ["_overwatch", []], ["_delay", 120]]; // group is missing if (isNull _group) exitWith {false}; @@ -42,37 +41,47 @@ if (_unit distance2D _target < GVAR(cqbRange)) exitWith { }; // reset tactics +_group setVariable [QGVAR(isExecutingTactic), true]; [ { - params [["_group", grpNull], ["_speedMode", "NORMAL"], ["_formation", "WEDGE"]]; + params [["_group", grpNull], ["_delay", 0]]; + time > _delay || {isNull _group} || { !(_group getVariable [QGVAR(isExecutingTactic), false]) } + }, + { + params [["_group", grpNull], "", ["_speedMode", "NORMAL"], ["_formation", "WEDGE"]]; if (!isNull _group) then { _group setVariable [QGVAR(isExecutingTactic), nil]; _group setVariable [QEGVAR(main,currentTactic), nil]; _group setSpeedMode _speedMode; _group setFormation _formation; { + _x setVariable [QEGVAR(main,currentTask), nil, EGVAR(main,debug_functions)]; _x setVariable [QGVAR(forceMove), nil]; _x setUnitPos "AUTO"; [_x] allowGetIn true; + _x doFollow (leader _x); } forEach (units _group); }; }, - [_group, speedMode _unit, formation _unit], - _delay -] call CBA_fnc_waitAndExecute; + [_group, time + _delay, speedMode _unit, formation _unit] +] call CBA_fnc_waitUntilAndExecute; // find units if (_units isEqualTo []) then { _units = _unit call EFUNC(main,findReadyUnits); }; + +// add loaded units +_units append ((units _unit) select {((assignedVehicleRole _x) select 0) isEqualTo "cargo"}); if (_units isEqualTo []) exitWith {false}; // find vehicles private _vehicles = [_unit] call EFUNC(main,findReadyVehicles); // sort building locations -private _pos = [_target, 12, true, false] call EFUNC(main,findBuildings); -_pos pushBack _target; +private _posList = [_target, 12, true, false] call EFUNC(main,findBuildings); +_posList append ((nearestTerrainObjects [ _target, ["HIDE", "TREE", "BUSH", "SMALL TREE"], 8, false, true]) apply { (getPosATL _x) vectorAdd [0, 0, random 2] }); +_posList pushBack _target; // find overwatch position if (_overwatch isEqualTo []) then { @@ -94,7 +103,7 @@ _group setVariable [QEGVAR(main,currentTactic), "Flanking", EGVAR(main,debug_fun // gesture [_unit, ["gestureGo"]] call EFUNC(main,doGesture); -[_units select (count _units - 1), "gestureGoB"] call EFUNC(main,doGesture); +[_units select -1, "gestureGoB"] call EFUNC(main,doGesture); // leader callout [_unit, "combat", "flank", 125] call EFUNC(main,doCallout); @@ -103,7 +112,7 @@ _group setVariable [QEGVAR(main,currentTactic), "Flanking", EGVAR(main,debug_fun _unit setSpeedMode "FULL"; // prevent units from being mounted! -((units _unit) select {((assignedVehicleRole _x) select 0) isEqualTo "cargo"}) allowGetIn false; +_units allowGetIn false; // ready group _group setFormDir (_unit getDir _target); @@ -117,11 +126,11 @@ _group setFormation "FILE"; if (!GVAR(disableAutonomousSmokeGrenades)) then {[_unit, _overwatch] call EFUNC(main,doSmoke);}; // function -[{_this call EFUNC(main,doGroupFlank)}, [_cycle, _units, _vehicles, _pos, _overwatch], 2 + random 8] call CBA_fnc_waitAndExecute; +[{_this call EFUNC(main,doGroupFlank)}, [_group, _units, _vehicles, _posList, _overwatch], 2 + random 8] call CBA_fnc_waitAndExecute; // debug if (EGVAR(main,debug_functions)) then { - ["%1 TACTICS FLANK (%2 with %3 units and %6 vehicles @ %4m with %5 positions)", side _unit, name _unit, count _units, round (_unit distance2D _overwatch), count _pos, count _vehicles] call EFUNC(main,debugLog); + ["%1 TACTICS FLANK (%2 with %3 units and %6 vehicles @ %4m with %5 positions)", side _unit, name _unit, count _units, round (_unit distance2D _overwatch), count _posList, count _vehicles] call EFUNC(main,debugLog); private _m = [_unit, "tactics flank", _unit call EFUNC(main,debugMarkerColor), "hd_arrow"] call EFUNC(main,dotMarker); private _mt = [_overwatch, "", _unit call EFUNC(main,debugMarkerColor), "hd_objective"] call EFUNC(main,dotMarker); {_x setMarkerSizeLocal [0.6, 0.6];} forEach [_m, _mt]; diff --git a/addons/danger/functions/fnc_tacticsSuppress.sqf b/addons/danger/functions/fnc_tacticsSuppress.sqf index 85837b43c..ad53d9016 100644 --- a/addons/danger/functions/fnc_tacticsSuppress.sqf +++ b/addons/danger/functions/fnc_tacticsSuppress.sqf @@ -17,7 +17,7 @@ * * Public: No */ -params ["_group", "_target", ["_units", []], ["_delay", 17]]; +params ["_group", "_target", ["_units", []], ["_delay", 45]]; // group is missing if (isNull _group) exitWith {false}; @@ -38,22 +38,28 @@ if !([_unit, (ATLToASL _target) vectorAdd [0, 0, 5]] call EFUNC(main,shouldSuppr [_group, _target] call FUNC(tacticsFlank); }; -// sort cycles -private _cycle = selectRandom [2, 3, 3, 4]; - // reset tactics +_group setVariable [QGVAR(isExecutingTactic), true]; [ { - params ["_group", "_enableAttack"]; + params [["_group", grpNull], ["_delay", 0]]; + time > _delay || {isNull _group} || { !(_group getVariable [QGVAR(isExecutingTactic), false]) } + }, + { + params ["_group", "", ["_enableAttack", false], ["_formation", "WEDGE"]]; if (!isNull _group) then { _group setVariable [QGVAR(isExecutingTactic), nil]; - _group enableAttack _enableAttack; _group setVariable [QEGVAR(main,currentTactic), nil]; + _group enableAttack _enableAttack; + _group setFormation _formation; + { + _x setVariable [QEGVAR(main,currentTask), nil, EGVAR(main,debug_functions)]; + _x doFollow (leader _x); + } forEach (units _group); }; }, - [group _unit, attackEnabled _unit], - _delay * _cycle -] call CBA_fnc_waitAndExecute; + [group _unit, time + _delay, attackEnabled _unit, formation _group] +] call CBA_fnc_waitUntilAndExecute; // alive unit if !(_unit call EFUNC(main,isAlive)) exitWith {false}; @@ -64,13 +70,13 @@ if (_units isEqualTo []) then { }; if (_units isEqualTo []) exitWith {false}; -// find vehicles -private _vehicles = [_unit] call EFUNC(main,findReadyVehicles); +// find vehicles with weapons +private _vehicles = ([_unit] call EFUNC(main,findReadyVehicles)) select {someAmmo _x}; // sort building locations -private _pos = [_target, 20, true, false] call EFUNC(main,findBuildings); -_pos append ((nearestTerrainObjects [ _target, ["HIDE", "TREE", "BUSH", "SMALL TREE"], 8, false, true]) apply { (getPosATL _x) vectorAdd [0, 0, random 2] }); -_pos pushBack _target; +private _postList = [_target, 20, true, false] call EFUNC(main,findBuildings); +_postList append ((nearestTerrainObjects [ _target, ["HIDE", "TREE", "BUSH", "SMALL TREE"], 8, false, true]) apply { (getPosATL _x) vectorAdd [0, 0, random 2] }); +_postList pushBack _target; // set tasks _unit setVariable [QEGVAR(main,currentTarget), _target, EGVAR(main,debug_functions)]; @@ -79,6 +85,7 @@ _unit setVariable [QEGVAR(main,currentTask), "Leader Suppress", EGVAR(main,debug // set group task _group = group _unit; _group enableAttack false; +_group setFormation "LINE"; _group setVariable [QEGVAR(main,currentTactic), "Suppressing", EGVAR(main,debug_functions)]; // gesture @@ -91,11 +98,11 @@ _group setVariable [QEGVAR(main,currentTactic), "Suppressing", EGVAR(main,debug_ _group setFormDir (_unit getDir _target); // execute recursive cycle -[_cycle, _units, _vehicles, _pos] call EFUNC(main,doGroupSuppress); + [{_this call EFUNC(main,doGroupSuppress)}, [_group, _units, _vehicles, _postList], 1 + random 1] call CBA_fnc_waitAndExecute; // debug if (EGVAR(main,debug_functions)) then { - ["%1 TACTICS SUPPRESS (%2 with %3 units and %6 vehicles @ %4m with %5 positions for %7 cycles)", side _unit, name _unit, count _units, round (_unit distance2D _target), count _pos, count _vehicles, _cycle] call EFUNC(main,debugLog); + ["%1 TACTICS SUPPRESS (%2 with %3 units and %6 vehicles @ %4m with %5 positions)", side _unit, name _unit, count _units, round (_unit distance2D _target), count _postList, count _vehicles] call EFUNC(main,debugLog); private _m = [_unit, "tactics suppress", _unit call EFUNC(main,debugMarkerColor), "hd_arrow"] call EFUNC(main,dotMarker); private _mt = [_target, "", _unit call EFUNC(main,debugMarkerColor), "hd_destroy"] call EFUNC(main,dotMarker); {_x setMarkerSizeLocal [0.6, 0.6];} forEach [_m, _mt]; diff --git a/addons/main/XEH_PREP.hpp b/addons/main/XEH_PREP.hpp index 74a4b9844..dcc930a4b 100644 --- a/addons/main/XEH_PREP.hpp +++ b/addons/main/XEH_PREP.hpp @@ -8,6 +8,7 @@ PREP(doShareInformation); PREP(getLauncherUnits); PREP(getShareInformationParams); PREP(shouldSuppressPosition); +PREP(checkVisibilityList); PREP(eventCallback); PREP(findBuildings); diff --git a/addons/main/functions/GroupAction/fnc_doGroupFlank.sqf b/addons/main/functions/GroupAction/fnc_doGroupFlank.sqf index 12accab11..179062f3c 100644 --- a/addons/main/functions/GroupAction/fnc_doGroupFlank.sqf +++ b/addons/main/functions/GroupAction/fnc_doGroupFlank.sqf @@ -4,7 +4,7 @@ * Actualises flanking cycle * * Arguments: - * 0: cycles + * 0: group conducting the flanking * 1: units list * 2: list of group vehicles * 3: list of building/enemy positions @@ -18,54 +18,96 @@ * * Public: No */ -params ["_cycle", "_units", "_vehicles", "_pos", "_overwatch", ["_teamAlpha", 0]]; +params [["_group", grpNull], ["_units", []], ["_vehicles", []], ["_posList", []], ["_overwatch", [0, 0, 0]], ["_teamAlpha", 0]]; + +// exit! +if !(_group getVariable [QEGVAR(danger,isExecutingTactic), false]) exitWith {false}; // update -_units = _units select { _x call FUNC(isAlive) && { !isPlayer _x } && {_x distance2D _overwatch > 5}}; +_units = _units select { !( _x getVariable [QEGVAR(danger,disableAI), false] ) && { _x call FUNC(isAlive) } && { !isPlayer _x } }; _vehicles = _vehicles select { canFire _x }; +// group has reached destination +private _leader = leader _group; +if ( _leader distance2D _overwatch < 4 ) exitWith { + _group setVariable [QEGVAR(danger,isExecutingTactic), false]; + _group setVariable [QGVAR(groupMemory), _posList, false]; +}; + +// leader has no friendlies within 45 meters +private _leaderAlone = ( ( _units - crew _leader) findIf { _x distanceSqr _leader < 2025 } ) isEqualTo -1; + { + private _unit = _x; private _suppressed = (getSuppression _x) > 0.5; - _x setUnitPos (["MIDDLE", "DOWN"] select _suppressed); + _unit setUnitPos (["MIDDLE", "DOWN"] select (_suppressed || {_unit isEqualTo _leader && _leaderAlone})); + _unit setVariable [QEGVAR(danger,forceMove), !_suppressed]; // move - _x doMove (_overwatch vectorAdd [-2 + random 4, -2 + random 4, 0]); - _x setDestination [_overwatch, "LEADER PLANNED", true]; - _x setVariable [QEGVAR(danger,forceMove), !_suppressed]; + _unit doMove _overwatch; + _unit setDestination [_overwatch, "LEADER PLANNED", false]; + _unit setVariable [QGVAR(currentTask), "Group Flank", GVAR(debug_functions)]; // suppress - private _posASL = AGLToASL (selectRandom _pos); + private _posASL = AGLToASL (selectRandom _posList); + private _eyePos = eyePos _unit; + _posASL = _eyePos vectorAdd ((_posASL vectorDiff _eyePos) vectorMultiply 0.6); + if ( (_forEachIndex % 2) isEqualTo _teamAlpha - && {!(terrainIntersectASL [eyePos _x, _posASL vectorAdd [0, 0, 3]])} + && {!(_leaderAlone && {isNull (objectParent (effectiveCommander _leader))})} + && {(currentCommand _unit) isNotEqualTo "Suppress"} + && {_unit isNotEqualTo (leader _unit)} + && {[_unit, "VIEW", objNull] checkVisibility [_eyePos, _posASL] isEqualTo 1} ) then { - [{_this call FUNC(doSuppress)}, [_x, _posASL vectorAdd [0, 0, random 1], true], 1 + random 3] call CBA_fnc_waitAndExecute; + + // shoot + [{_this call FUNC(doSuppress)}, [_unit, _posASL vectorAdd [0, 0, random 1], false], random 2] call CBA_fnc_waitAndExecute; + }; + } forEach _units; // reset alpha status _teamAlpha = parseNumber (_teamAlpha isEqualTo 0); // vehicles -if ((_cycle % 2) isEqualTo 0) then { - { - private _posAGL = selectRandom _pos; - _x doWatch _posAGL; - [_x, _posAGL] call FUNC(doVehicleSuppress); - } forEach _vehicles; -} else { - // check for roads - private _roads = _overwatch nearRoads 50; - if (_roads isNotEqualTo []) exitWith {_vehicles doMove (ASLToAGL (getPosASL (selectRandom _roads)));}; - _vehicles doMove _overwatch; -}; +_vehicles doWatch (selectRandom _posList); +[_posList, true] call CBA_fnc_shuffle; +{ + + // loaded vehicles move quickly + if (count (crew _x) > 3 || {_leaderAlone} || { _x isNotEqualTo _leader && { _leader distance2D _overwatch < 35 } } ) exitWith {_vehicles doMove _overwatch;}; + + // sort out vehicles + private _index = [_x, _posList] call FUNC(checkVisibilityList); + + if ( + _index isEqualTo -1 + ) then { + + // move up behind leader + private _leaderPos = _leader getPos [35 min (_leader distance2D _x), _overwatch getDir _leader]; + if ((vehicle _leader) isEqualTo _x) then {_leaderPos = _x getPos [35, _x getDir _overwatch]}; + + // check for roads + private _roads = _leaderPos nearRoads 50; + if (_roads isNotEqualTo []) exitWith {_x doMove (ASLToAGL (getPosASL (selectRandom _roads)));}; + _x doMove _leaderPos; + + } else { + + // do suppressive fire + [_x, _posList select _index] call FUNC(doVehicleSuppress); + }; +} forEach _vehicles; // recursive cyclic -if !(_cycle <= 1 || {_units isEqualTo []}) then { +if (_units isNotEqualTo [] && { _group getVariable [QEGVAR(danger,isExecutingTactic), false] }) then { [ {_this call FUNC(doGroupFlank)}, - [_cycle - 1, _units, _vehicles, _pos, _overwatch, _teamAlpha], - 10 + random 8 + [_group, _units, _vehicles, _posList, _overwatch, _teamAlpha], + 11 + random 8 ] call CBA_fnc_waitAndExecute; }; diff --git a/addons/main/functions/GroupAction/fnc_doGroupSuppress.sqf b/addons/main/functions/GroupAction/fnc_doGroupSuppress.sqf index 173759b6a..cbfbbe72d 100644 --- a/addons/main/functions/GroupAction/fnc_doGroupSuppress.sqf +++ b/addons/main/functions/GroupAction/fnc_doGroupSuppress.sqf @@ -4,7 +4,7 @@ * Actualisation of Suppression cycle * * Arguments: - * 0: cycles + * 0: group conducting the suppression * 1: units list * 2: list of group vehicles * 3: list of building/enemy positions @@ -17,58 +17,80 @@ * * Public: No */ -params ["_cycle", "_units", "_vehicles", "_pos"]; +params [["_group", grpNull], ["_units", []], ["_vehicles", []], ["_posList", []]]; + +// exit! +if !(_group getVariable [QEGVAR(danger,isExecutingTactic), false]) exitWith {false}; // update -_units = _units select {_x call FUNC(isAlive) && { !isPlayer _x }}; +_units = _units select { !( _x getVariable [QEGVAR(danger,disableAI), false] ) && { _x call FUNC(isAlive) } && { !isPlayer _x } }; _vehicles = _vehicles select { canFire _x }; +// get leader +private _leader = leader _group; + // infantry +[_posList, true] call CBA_fnc_shuffle; { - // ready - private _posAGL = selectRandom _pos; - _posAGL = _posAGL vectorAdd [0, 0, random 1]; - - // suppressive fire - _x forceSpeed 1; - _x setUnitPosWeak "MIDDLE"; - private _suppress = [_x, AGLToASL _posAGL] call FUNC(doSuppress); - _x setVariable [QGVAR(currentTask), "Group Suppress", GVAR(debug_functions)]; - - // no LOS - if !(_suppress || {(currentCommand _x isEqualTo "Suppress")}) then { + // find target + private _index = [_x, _posList] call FUNC(checkVisibilityList); + + // execute suppression + if ( + _index isNotEqualTo -1 + ) then { + + // suppressive fire + _x forceSpeed 1; + _x setUnitPosWeak "MIDDLE"; + [_x, AGLToASL ((_posList select _index) vectorAdd [0, 0, random 1])] call FUNC(doSuppress); + _x setVariable [QGVAR(currentTask), "Group Suppress", GVAR(debug_functions)]; + + } else { + // move forward _x forceSpeed 3; - _x doMove (_x getPos [20, _x getDir _posAGL]); + _x doMove (_x getPos [20, _x getDir (_posList select -1)]); _x setVariable [QGVAR(currentTask), "Group Suppress (Move)", GVAR(debug_functions)]; - }; - // follow-up fire - [ - { - params ["_unit", "_posASL"]; - if (_unit call FUNC(isAlive) && {(currentCommand _unit isNotEqualTo "Suppress")}) then { - [_unit, _posASL vectorAdd [2 - random 4, 2 - random 4, 0.8], true] call EFUNC(main,doSuppress); - }; - }, - [_x, AGLToASL _posAGL], - 5 - ] call CBA_fnc_waitAndExecute; -} forEach _units; + }; +} forEach (_units select {(currentCommand _x) isNotEqualTo "Suppress"}); // vehicles { - private _posAGL = selectRandom _pos; - _x doWatch _posAGL; - [_x, _posAGL] call FUNC(doVehicleSuppress); -} forEach _vehicles; + + // find target + private _index = [_x, _posList] call FUNC(checkVisibilityList); + + // execute suppression + if ( + _index isNotEqualTo -1 + ) then { + + // vehicle suppress + [_x, (_posList select _index) vectorAdd [0, 0, random 1]] call FUNC(doVehicleSuppress); + + } else { + + // move up behind leader + _x doWatch (_posList select _index); + private _leaderPos = _leader getPos [35 min (_x distance2D _leader), (_posList select _index) getDir _leader]; + + // check for roads + private _roads = _leaderPos nearRoads 50; + if (_roads isNotEqualTo []) exitWith {_x doMove (ASLToAGL (getPosASL (selectRandom _roads)));}; + _x doMove _leaderPos; + + }; + +} forEach (_vehicles select {(currentCommand _x) isNotEqualTo "Suppress"}); // recursive cyclic -if !(_cycle <= 1 || {_units isEqualTo []}) then { +if (_units isNotEqualTo [] && { _group getVariable [QEGVAR(danger,isExecutingTactic), false] }) then { [ {_this call FUNC(doGroupSuppress)}, - [_cycle - 1, _units, _vehicles, _pos], - 16 + random 2 + [_group, _units, _vehicles, _posList], + 6 + random 2 ] call CBA_fnc_waitAndExecute; }; diff --git a/addons/main/functions/fnc_checkVisibilityList.sqf b/addons/main/functions/fnc_checkVisibilityList.sqf new file mode 100644 index 000000000..80824d22a --- /dev/null +++ b/addons/main/functions/fnc_checkVisibilityList.sqf @@ -0,0 +1,36 @@ +#include "script_component.hpp" +/* + * Author: nkenny + * Find a location a unit can see from a list of position AGL + * + * Arguments: + * 0: Unit doing looking + * 1: Array of possible locations in AGL + * 2: Max arrays , default 4 + * + * Return Value: + * index + * + * Example: + * [bob, []] call lambs_main_fnc_checkVisibilityList; + * + * Public: Yes +*/ + +params [["_unit", objNull], ["_posList", []], ["_max", 4]]; +private _vehicle = vehicle _unit; +private _eyePos = eyePos _vehicle; +private _checkList = +_posList; + +if ((count _checkList) > _max) then {_checkList resize _max}; + +private _return = _checkList findIf { + + // get variables + private _posASL = AGLToASL _x; + _posASL = _eyePos vectorAdd ((_posASL vectorDiff _eyePos) vectorMultiply 0.6); + + // check visibility + [_vehicle, "VIEW", objNull] checkVisibility [_eyePos, _posASL] isEqualTo 1 +}; +_return