From 2708d9ad5d1ba7c518d9f67243bbcd2a8ee168a4 Mon Sep 17 00:00:00 2001 From: Ken Mikkelsen Date: Tue, 14 Jan 2025 00:19:58 +0100 Subject: [PATCH 1/4] Vehicle brain improvements Added mortars support nearby units with Smoke, Flares and HE Added more chance for vehicles to share information Added Tanks/APC/IFVs will rearrange crew to priorititise guns Added ability for crew to jump out of soft-skinned vehicles and man mounted weapons Added ability for crew of soft-skinned vehicles to jump out to defend themselves Added callouts/vocalization to many vehicle interactions Added ability for Tracked vehicles to rearrange crew (even when fleeing) Improved APC/IFV vehicle dismounts more reliable Improved vehicle rotate on tracked vehicles with 'sendSimpleCommand' Improved hiding ability of fleeing infantry Improved readability of various code Fixed cases where incapacitated gunners still turned turrets Fixed _stupid_ bug where dangerPos was not properly updated in brainVehicle.sqf Fixed bug where banners were often hidden on some vehicles in debugDraw --- addons/danger/functions/fnc_brainVehicle.sqf | 167 +++++++++++++++--- .../functions/UnitAction/fnc_doFleeing.sqf | 92 +++++++--- .../VehicleAction/fnc_doVehicleAssault.sqf | 2 +- .../VehicleAction/fnc_doVehicleJink.sqf | 4 +- .../VehicleAction/fnc_doVehicleRotate.sqf | 57 +++--- .../VehicleAction/fnc_doVehicleSuppress.sqf | 10 +- addons/main/functions/debug/fnc_debugDraw.sqf | 2 +- addons/main/functions/fnc_doCallout.sqf | 5 +- 8 files changed, 265 insertions(+), 74 deletions(-) diff --git a/addons/danger/functions/fnc_brainVehicle.sqf b/addons/danger/functions/fnc_brainVehicle.sqf index 64ca2d83..da3fafba 100644 --- a/addons/danger/functions/fnc_brainVehicle.sqf +++ b/addons/danger/functions/fnc_brainVehicle.sqf @@ -4,7 +4,7 @@ * handles vehicle brain * * Arguments: - * 0: unit doing the avaluation + * 0: unit doing the evaluation * 2: current action queue * * Return Value: @@ -55,7 +55,7 @@ private _attack = _cause in [DANGER_ENEMYDETECTED, DANGER_ENEMYNEAR, DANGER_HIT, // update dangerPos if attacking. Check that the position is not too far above, or below ground. if (_attack) then { - private _dangerPos = _unit getHideFrom _dangerCausedBy; + _dangerPos = _unit getHideFrom _dangerCausedBy; if (_dangerPos isEqualTo [0, 0, 0]) exitWith {_attack = false;}; _dangerPos = ASLToAGL (ATLToASL _dangerPos); if ((_dangerPos select 2) > 6 || {(_dangerPos select 2) < 2}) then {_dangerPos set [2, 1]}; @@ -78,6 +78,63 @@ if (_artillery) exitWith { { _x setSuppression 0.94; // to prevent instant laser aim on exiting vehicle } forEach _vehicleCrew; // There may be more than one unit in vehicle + [_unit, "Combat", "Eject", 125] call EFUNC(main,doCallout); + }; + + // mortars fire rounds + private _mortarTime = _vehicle getVariable [QEGVAR(main,mortarTime), 0]; + if (_attack && {_vehicle isKindOf "StaticMortar"} && {unitReady _vehicle} && {_mortarTime < time}) then { + + // delay + _timeout = _timeout + 2; + _vehicle doWatch _dangerPos; + + // check ammo & range + private _ammo = getArtilleryAmmo [_vehicle]; + private _shell = _ammo param [0, ""]; + if (_shell isEqualTo "") exitWith {}; + private _flareIndex = _ammo findIf {"flare" in (toLower _x)}; + private _smokeIndex = _ammo findIf {"smoke" in (toLower _x)}; + + // check friendlies + private _dangerRound = false; + private _repeatRounds = true; + if ( RND(0.8) || { ([_unit, _dangerPos, 150] call EFUNC(main,findNearbyFriendlies)) isNotEqualTo [] } ) then { + if (_smokeIndex isNotEqualTo -1) then { + _shell = _ammo select _smokeIndex; + _repeatRounds = RND(0.5); + } else { + _dangerRound = true; + }; + }; + + // check night + if ( RND(0.2) && { _unit call EFUNC(main,isNight) } && { _flareIndex isNotEqualTo -1 } ) then { + _dangerPos = _dangerPos getPos [-50 + random 100, (_vehicle getDir _dangerPos) - 45 + random 90]; + _shell = _ammo select _flareIndex; + _dangerRound = false; + _repeatRounds = false; + }; + + // check for issues + if ( _dangerRound || !( _dangerPos inRangeOfArtillery [[_vehicle], _shell] ) ) exitWith {}; + + // execute fire command + _vehicle commandArtilleryFire [_dangerPos getPos [30 + random 80, (_dangerPos getDir _vehicle) - 10 + random 20], _shell, 1 + random 2]; + _vehicle setVariable [QEGVAR(main,mortarTime), time + 24 + random 6]; + _unit setVariable [QEGVAR(main,currentTask), "Mortar Fire", EGVAR(main,debug_functions)]; + if (_repeatRounds) then { + [ + { + params [["_vehicle", objNull], ["_dangerPos", [0, 0, 0]], ["_shell", ""]]; + if (canFire _vehicle && unitReady _vehicle) then { + _vehicle commandArtilleryFire [_dangerPos, _shell, ( 2 + random 1 ) min ((gunner _vehicle) ammo (currentMuzzle (gunner _vehicle)))]; + }; + }, + [_vehicle, _dangerPos, _shell], + 18 + random 6 + ] call CBA_fnc_waitAndExecute; + }; }; [_timeout] + _causeArray }; @@ -97,6 +154,7 @@ if (_vehicle isKindOf "StaticWeapon") exitWith { if ((count (magazines _vehicle)) isEqualTo 0 || {(_unit findNearestEnemy _dangerPos) distance _vehicle < (6 + random 15)}) then { private _vehicleCrew = (crew _vehicle); _vehicleCrew orderGetIn false; + [_unit, "Combat", "Eject"] call EFUNC(main,doCallout); { _x setSuppression 0.94; // to prevent instant laser aim on exiting vehicle } forEach _vehicleCrew; // There may be more than one unit in vehicle @@ -113,7 +171,7 @@ if (_vehicle isKindOf "StaticWeapon") exitWith { }; // update information -if (_cause isEqualTo DANGER_ENEMYNEAR) then {[_unit, _dangerCausedBy] call EFUNC(main,doShareInformation);}; +if (_cause in [DANGER_ENEMYNEAR, DANGER_SCREAM]) then {[_unit, _dangerCausedBy] call EFUNC(main,doShareInformation);}; // select turret ammunition if (_attack && {!EGVAR(main,disableAutonomousMunitionSwitching) && {!(isNull _dangerCausedBy) && { @@ -143,14 +201,6 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { // keep cargo aboard! _vehicle setUnloadInCombat [false, false]; - // vehicle jink - private _oldDamage = _vehicle getVariable [QGVAR(vehicleDamage), 0]; - if (_validTarget && {_distance < (12 + random 15) || {damage _vehicle > _oldDamage}}) exitWith { - _vehicle setVariable [QGVAR(vehicleDamage), damage _vehicle]; - [_unit] call EFUNC(main,doVehicleJink); - [_timeout + _delay] + _causeArray - }; - // foot infantry support ~ unload private _group = group _vehicle; private _cargo = ((fullCrew [_vehicle, "cargo"]) apply {_x select 0}); @@ -179,7 +229,8 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { [ { params [["_cargo", []], ["_side", east], ["_vehicle", objNull]]; - _cargo orderGetIn false; + {_x action ["Eject", _vehicle];} forEach _cargo; + [selectRandom _cargo, "Combat", "Dismount"] call EFUNC(main,doCallout); _cargo allowGetIn false; if (EGVAR(main,debug_functions)) then {["%1 %2 unloading %3 carried troops", _side, getText (configOf _vehicle >> "displayName"), count _cargo] call EFUNC(main,debugLog);}; }, @@ -191,20 +242,40 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { [_timeout + _delay + 1] + _causeArray }; + // move into gunners seat ~ Enemy Detected, commander alive but gunner dead + private _slow = speed _vehicle < 20; + if ( + RND(0.4) + && {_slow} + && {someAmmo _vehicle} + && {_cause isEqualTo DANGER_ENEMYDETECTED} + && {!alive (gunner _vehicle)} + && {(commander _vehicle) call EFUNC(main,isAlive)} + ) exitWith { + (commander _vehicle) assignAsGunner _vehicle; + [_timeout + 3] + _causeArray + }; + + // vehicle jink + private _oldDamage = _vehicle getVariable [QGVAR(vehicleDamage), 0]; + if (_slow && _validTarget && {_distance < (12 + random 15) || {damage _vehicle > _oldDamage}} && {(driver _vehicle) call EFUNC(main,isAlive)}) exitWith { + _vehicle setVariable [QGVAR(vehicleDamage), damage _vehicle]; + [_unit] call EFUNC(main,doVehicleJink); + [_timeout + _delay] + _causeArray + }; + // tank assault - if (_attack && {speed _vehicle < 20}) then { + if (_attack && {_slow} && {(getUnitState _unit) isEqualTo "OK"}) then { // rotate - if ((getUnitState _unit) isEqualTo "OK") then { - [_vehicle, _dangerPos] call EFUNC(main,doVehicleRotate); - }; + private _rotate = [_vehicle, _dangerPos] call EFUNC(main,doVehicleRotate); // assault - if (_distance < 750 && {_dangerCausedBy isKindOf "Man"} && {_cause isEqualTo DANGER_ENEMYDETECTED}) then { + if (!_rotate && {_distance < 750} && {_dangerCausedBy isKindOf "CAManBase"} && {(gunner _vehicle) call EFUNC(main,isAlive)}) then { [ {_this call EFUNC(main,doVehicleAssault)}, [_unit, _dangerPos, _dangerCausedBy], - _delay - 1 + _delay - 1.5 ] call CBA_fnc_waitAndExecute; }; }; @@ -221,14 +292,47 @@ if (_car) exitWith { private _delay = 0; private _slow = speed _vehicle < 30; + // move into gunners seat ~ 30-90 meters and Enemy Detected + if ( + _slow + && {canUnloadInCombat _vehicle} + && {someAmmo _vehicle} + && {_cause isEqualTo DANGER_ENEMYDETECTED} + && {_vehicle distanceSqr _dangerPos < (900 + random 3600)} + && {!alive (gunner _vehicle)} + ) exitWith { + _unit action ["Eject", _vehicle]; + _unit assignAsGunner _vehicle; + [ + { + params ["_unit", "_vehicle"]; + if (_unit call EFUNC(main,isAlive)) then { + [_unit, "Stealth", "Eject"] call EFUNC(main,doCallout); + _unit setDir (_unit getDir _vehicle); + _unit action ["getInGunner", _vehicle]; + }; + }, [_unit, _vehicle], 0.9 + ] call CBA_fnc_waitAndExecute; + [_timeout + 3] + _causeArray + }; + // escape ~ if enemy within 15-50 meters or explosions are nearby! - if (_slow && {(side _dangerCausedBy) isNotEqualTo (side _unit)} && {_cause isEqualTo DANGER_EXPLOSION || {_vehicle distanceSqr _dangerCausedBy < (225 + random 1225)}}) exitWith { + if ( + _slow + && {(side _dangerCausedBy) isNotEqualTo (side _unit)} + && {_cause isEqualTo DANGER_EXPLOSION || {_vehicle distanceSqr _dangerCausedBy < (225 + random 1225)}} + && {(driver _vehicle) call EFUNC(main,isAlive)} + ) exitWith { [_unit] call EFUNC(main,doVehicleJink); [_timeout + 3] + _causeArray }; - // look to danger - if (_attack && {_vehicle knowsAbout _dangerCausedBy > 3}) then {_vehicle doWatch (AGLToASL _dangerPos);}; + // look toward danger + if ( + _attack + && {_vehicle knowsAbout _dangerCausedBy > 3} + && {(gunner _vehicle) call EFUNC(main,isAlive)} + ) then {_vehicle doWatch (AGLToASL _dangerPos);}; // suppression if (_attack && {_slow}) then { @@ -241,6 +345,27 @@ if (_car) exitWith { [_timeout + _delay] + _causeArray }; +// vehicle type ~ Unarmed car +if (_vehicle isKindOf "Car_F" && {!someAmmo _vehicle}) then { + + // speed + private _stopped = speed _vehicle < 2; + + // is static and a driver and enemy near and a threat - enemy within 10-35 meters + if ( + _stopped + && {!isNull (driver _vehicle)} + && {canUnloadInCombat _vehicle} + && {_cause isEqualTo DANGER_ENEMYDETECTED} + && {_vehicle distanceSqr _dangerCausedBy < (100 + random 225)} + ) then { + private _driver = driver _vehicle; + _driver action ["Eject", _vehicle]; + _driver setSuppression 0.94; // to prevent instant laser aim on exiting vehicle + [_driver, "Combat", "Eject"] call EFUNC(main,doCallout); + }; +}; + // Make leadership assessment as infantry if (_unit call FUNC(isLeader)) then { [_unit, _dangerCausedBy] call FUNC(tactics); diff --git a/addons/main/functions/UnitAction/fnc_doFleeing.sqf b/addons/main/functions/UnitAction/fnc_doFleeing.sqf index 2465b9e9..0e85c2fe 100644 --- a/addons/main/functions/UnitAction/fnc_doFleeing.sqf +++ b/addons/main/functions/UnitAction/fnc_doFleeing.sqf @@ -14,7 +14,7 @@ * * Public: No */ -#define SEARCH_FOR_HIDE 6 +#define SEARCH_FOR_HIDE 12 #define SEARCH_FOR_BUILDING 8 params ["_unit"]; @@ -25,7 +25,7 @@ if ( || {!(_unit checkAIFeature "PATH")} || {!(_unit checkAIFeature "MOVE")} || {GVAR(disableAIFleeing)} - || {currentCommand _unit in ["GET IN", "ACTION", "REARM", "HEAL"]} + || {(currentCommand _unit) in ["GET IN", "ACTION", "REARM", "HEAL"]} ) exitWith {false}; // check for vehicle @@ -38,30 +38,70 @@ _unit setVariable [QGVAR(currentTarget), objNull, GVAR(debug_functions)]; // eventhandler [QGVAR(OnFleeing), [_unit, group _unit]] call FUNC(eventCallback); -// Abandon vehicles in need! -private _vehicle = vehicle _unit; -if ( - RND(0.5) - && {!_onFoot} - && {morale _unit < 0} - && {canUnloadInCombat _vehicle || {_vehicle isKindOf "StaticWeapon"}} - && {(speed _vehicle) < 3} - && {isTouchingGround _vehicle} -) exitWith { - [_unit] orderGetIn false; - _unit setSuppression 1; // prevents instant laser aim - nkenny + +// Vehicle sub-actions +if (!_onFoot) exitWith { + + // get vehicle + private _vehicle = vehicle _unit; + private _changeSeats = (speed _vehicle) < 3 && { isTouchingGround _vehicle }; + + // move into gunners seat ~ Enemy Detected, commander alive but gunner dead + private _candidate = call { + if ((commander _vehicle) call EFUNC(main,isAlive)) exitWith {commander _vehicle}; + if ((driver _vehicle) call EFUNC(main,isAlive)) exitWith {driver _vehicle}; + objNull + }; + + if ( + _changeSeats + && {!isNull _candidate} + && {someAmmo _vehicle} + && {!((gunner _vehicle) call EFUNC(main,isAlive))} + ) exitWith { + if (_vehicle isKindOf "Tank") then { + _candidate assignAsGunner _vehicle; + } else { + _candidate action ["Eject", _vehicle]; + _candidate assignAsGunner _vehicle; + [ + { + params ["_unit", "_vehicle"]; + if (_unit call EFUNC(main,isAlive)) then { + _unit setDir (_unit getDir _vehicle); + _unit action ["getInGunner", _vehicle]; + }; + }, [_candidate, _vehicle], 0.8 + ] call CBA_fnc_waitAndExecute; + }; + false + }; + + // Abandon vehicles in need! + private _abandonChance = ( (1 - damage _vehicle) + (_unit skillFinal "courage") ) * 0.5; + if (!canMove _vehicle || {fuel _vehicle < 0.1} || {_vehicle isKindOf "StaticWeapon"}) then { _abandonChance = _abandonChance * 0.25 }; + if (someAmmo _vehicle) then { _abandonChance = _abandonChance * 1.3 }; + if ( + RND(_abandonChance) + && {canUnloadInCombat _vehicle || (damage _vehicle) > 0.9} + && {_changeSeats} + ) exitWith { + if (_abandonChance < 0.5) then {_unit leaveVehicle _vehicle;}; + [_unit] orderGetIn false; + _unit setSuppression 1; // prevents instant laser aim - nkenny + false + }; + + // exit false }; -// no further action in vehicle -if (!_onFoot) exitWith {false}; // enemy private _enemy = _unit findNearestEnemy _unit; private _distance2D = _unit distance2D _enemy; // get destination -private _pos = (expectedDestination _unit) select 0; private _eyePos = eyePos _unit; private _suppression = getSuppression _unit; @@ -94,11 +134,18 @@ if (_onFootAndSeen) then { _unit forceSpeed -1; _unit setUnitPos (["MIDDLE", "DOWN"] select (_suppression > 0 || {_cover isNotEqualTo []})); // test nkenny + // update cover + _cover = _cover apply {_x getPos [1.5, _enemy getDir _x]}; + // find buildings to hide - private _buildings = [_unit, SEARCH_FOR_BUILDING, true, true] call FUNC(findBuildings); - _buildings append (_cover apply {getPos _x}); - if ((_buildings isNotEqualTo []) && {_distance2D > 5}) then { - _unit doMove selectRandom _buildings; + if (_distance2D > 30) then { + private _buildings = [_unit, SEARCH_FOR_BUILDING, true, true] call FUNC(findBuildings); + _cover append _buildings; + }; + + // execute move + if (_cover isNotEqualTo [] && {_distance2D > 5}) then { + _unit doMove selectRandom _cover; }; } else { @@ -107,6 +154,7 @@ if (_onFootAndSeen) then { // reset _unit setUnitPos "AUTO"; + _unit setUnitPosWeak "MIDDLE"; }; // debug @@ -115,7 +163,7 @@ if (GVAR(debug_functions)) then { "%1 Fleeing! %2 (%3m %4%5%6)", side _unit, name _unit, - [format ["Enemy @ %1", round _distance2D], format ["Destination @ %1", round (_unit distance2D _pos)]] select (isNull _enemy), + [format ["Enemy @ %1", round _distance2D], format ["Destination @ %1", round (_unit distance2D ((expectedDestination _unit) select 0))]] select (isNull _enemy), ["", "- suppressed "] select (_suppression > 0), ["", "- inside "] select (lineIntersects [_eyePos, _eyePos vectorAdd [0, 0, 10], _unit]), ["", "- spotted "] select (([objNull, "VIEW", objNull] checkVisibility [_eyePos, eyePos _enemy]) > 0.01) diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf index 46c20f25..9db4a3f5 100644 --- a/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf +++ b/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf @@ -66,7 +66,7 @@ _unit setVariable [QEGVAR(main,currentTarget), _target, EGVAR(main,debug_functio _unit setVariable [QEGVAR(main,currentTask), "Vehicle Assault", EGVAR(main,debug_functions)]; // minor jink if no suppression possible -if (!_suppression) then {[_unit, 25] call FUNC(doVehicleJink)}; +if (!_suppression) exitWith {[_unit, 35] call FUNC(doVehicleJink)}; // cannon direction ~ threshold 30 degrees private _fnc_turretDir = { diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleJink.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleJink.sqf index b47840b2..316e0189 100644 --- a/addons/main/functions/VehicleAction/fnc_doVehicleJink.sqf +++ b/addons/main/functions/VehicleAction/fnc_doVehicleJink.sqf @@ -23,8 +23,8 @@ private _vehicle = vehicle _unit; // cannot move or moving if ( !canMove _vehicle - || {currentCommand _vehicle isEqualTo "MOVE" - || currentCommand _vehicle isEqualTo "ATTACK"} + || { (fuel _vehicle) < 0.1 } + || { (currentCommand _vehicle) in ["MOVE", "ATTACK"] } ) exitWith { getPosASL _unit }; diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf index 82dad283..f611c870 100644 --- a/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf +++ b/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf @@ -10,7 +10,7 @@ * Arguments: * 0: Vehicle rotating * 1: Direction which to turn towards - * 2: Acceptible threshold in degrees + * 2: Acceptable threshold in degrees * * Return Value: * success @@ -26,42 +26,52 @@ if (_target isEqualTo []) then { _target = _unit getHideFrom (_unit findNearestEnemy _unit); }; if (_target isEqualTo [0, 0, 0] || {_unit distanceSqr _target < 2}) exitWith {false}; +_target = _target call CBA_fnc_getPos; // cannot move or moving -if (!canMove _unit || {currentCommand _unit isEqualTo "MOVE"}) exitWith {false}; +private _vehicle = vehicle _unit; +if (!canMove _vehicle || {(currentCommand _vehicle) isEqualTo "MOVE"} || {!alive (driver _vehicle)}) exitWith {false}; -// CQB tweak -- target within 75m - look instead -if (_unit distanceSqr _target < 5625) exitWith { - (vehicle _unit) doWatch (ATLToASL _target); +// CQB tweak -- target within 35m - look instead +if (_unit distanceSqr _target < 1225) exitWith { + _vehicle doWatch (ATLToASL _target); false }; _unit setVariable [QGVAR(currentTarget), _target, GVAR(debug_functions)]; _unit setVariable [QGVAR(currentTask), "Vehicle Rotate", GVAR(debug_functions)]; -// within acceptble limits +// within acceptable limits if (_unit getRelDir _target < _threshold || {_unit getRelDir _target > (360-_threshold)}) exitWith { false }; -// settings -private _pos = []; -private _min = 20; // Minimum range +// move +_unit setFormDir (_unit getDir _target); +if (_vehicle isKindOf "Tank") then { + + // turn vehicle + _vehicle sendSimpleCommand (["LEFT", "RIGHT"] select (_unit getRelDir _target < 180)); + +} else { -for "_i" from 0 to 5 do { - _pos = (_unit getPos [_min, _unit getDir _target]) findEmptyPosition [0, 2.2, typeOf _unit]; + // settings + private _pos = []; + private _min = 20; // Minimum range - // water or exit - if !(_pos isEqualTo [] || {surfaceIsWater _pos}) exitWith {}; + for "_i" from 0 to 5 do { + _pos = (_unit getPos [_min, _unit getDir _target]) findEmptyPosition [0, 2.2, typeOf _unit]; - // update - _min = _min + 15; + // water or exit + if !(_pos isEqualTo [] || {surfaceIsWater _pos}) exitWith {}; + + // update + _min = _min + 15; + }; + if (_pos isEqualTo []) then {_pos = _unit modelToWorldVisual [0, -100, 0]}; + _unit doMove _pos; }; -if (_pos isEqualTo []) then {_pos = _unit modelToWorldVisual [0, -100, 0]}; -// move -_unit doMove _pos; -_unit setFormDir (_unit getDir _pos); // waitUntil [ @@ -74,12 +84,17 @@ _unit setFormDir (_unit getDir _pos); if (canMove _unit && {(crew _unit) isNotEqualTo []}) then { // refresh ready - (effectiveCommander _unit) doMove (getPosASL _unit); + (vehicle _unit) sendSimpleCommand "STOPTURNING"; + (effectiveCommander _unit) doMove (_unit getPos [10, _unit getDir _target]); // refresh formation (group _unit) setFormDir (_unit getDir _target); }; - }, [_unit, _target, _threshold], (4 + random 6) + }, [_unit, _target, _threshold * 2], 4 + random 3, + { + params ["_unit"]; + (vehicle _unit) sendSimpleCommand "STOPTURNING"; + } ] call CBA_fnc_waitUntilAndExecute; // end diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf index c08f4977..7fbc616e 100644 --- a/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf +++ b/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf @@ -18,13 +18,13 @@ params ["_unit", ["_pos", [0, 0, 0]]]; private _vehicle = vehicle _unit; -_pos = _pos call CBA_fnc_getPos; +private _pos = _pos call CBA_fnc_getPos; // exit if vehicle is moving too fast or target is too high -if (speed _vehicle > 30 || {(_pos select 2) > 100}) exitWith {false}; +if (speed _vehicle > 30 || {(_pos select 2) > 100} || {!((gunner _vehicle) call FUNC(isAlive))}) exitWith {false}; // pos -private _eyePos = eyePos _unit; +private _eyePos = eyePos _vehicle; _pos = (AGLToASL _pos) vectorAdd [0.5 - random 1, 0.5 - random 1, 0.3 + random 1.3]; // target is close or terrain occludes target @@ -34,7 +34,7 @@ if ( ) exitWith {false}; // artillery (no tactical options) -if (_vehicle getVariable [QGVAR(isArtillery), getNumber (configOf (vehicle _unit) >> "artilleryScanner") > 0]) exitWith { +if (_vehicle getVariable [QGVAR(isArtillery), getNumber (configOf _vehicle >> "artilleryScanner") > 0]) exitWith { _vehicle setVariable [QGVAR(isArtillery), true]; false }; @@ -44,7 +44,7 @@ _unit setVariable [QGVAR(currentTarget), _pos, GVAR(debug_functions)]; _unit setVariable [QGVAR(currentTask), "Vehicle Suppress", GVAR(debug_functions)]; // trace -private _vis = lineIntersectsSurfaces [_eyePos, _pos, _unit, vehicle _unit, true, 1, "GEOM", "VIEW"]; +private _vis = lineIntersectsSurfaces [_eyePos, _pos, _unit, _vehicle, true, 1, "GEOM", "VIEW"]; if (_vis isNotEqualTo []) then {_pos = (_vis select 0) select 0;}; // recheck diff --git a/addons/main/functions/debug/fnc_debugDraw.sqf b/addons/main/functions/debug/fnc_debugDraw.sqf index 57524309..068eb08a 100644 --- a/addons/main/functions/debug/fnc_debugDraw.sqf +++ b/addons/main/functions/debug/fnc_debugDraw.sqf @@ -127,7 +127,7 @@ private _posCam = positionCameraToWorld [0, 0, 0]; private _renderPos = getPosATLVisual _unit; private _isLeader = _unit isEqualTo (leader _unit); private _sideColor = [side (group _unit), false] call BIS_fnc_sideColor; - if ((_posCam distance _renderPos) <= _viewDistance && {((units _unit - [_unit]) findIf {_x distanceSqr _unit < 1}) isEqualTo -1}) then { + if ((_posCam distance _renderPos) <= _viewDistance) then { if (!GVAR(debug_drawAllUnitsInVehicles) && {_unit isNotEqualTo (effectiveCommander (vehicle _unit))}) exitWith {}; private _textData = [""]; diff --git a/addons/main/functions/fnc_doCallout.sqf b/addons/main/functions/fnc_doCallout.sqf index 0a8379fe..d640cbfb 100644 --- a/addons/main/functions/fnc_doCallout.sqf +++ b/addons/main/functions/fnc_doCallout.sqf @@ -59,6 +59,9 @@ switch (toLowerANSI(_callout)) do { case ("flank"): { _callout = selectRandom ["OnYourFeet", "Advance", "FlankLeft", "FlankRight"]; }; + case ("eject"): { + _callout = selectRandom ["Eject", "EndangeredE"]; + }; }; private _cacheName = format ["%1_%2_%3_%4", QGVAR(callouts), _speaker, _behavior, _callout]; @@ -79,7 +82,7 @@ if (isNil "_cachedSounds") then { _cachedSounds set [_forEachIndex, objNull]; continue; }; - + private _hasFileEnding = _sound regexMatch ".+?\.(?:ogg|wss|wav|mp3)$/io"; if (_sound select [0, 1] != "\") then { From ad1b634f0fbc9c8dd831ebea9cad58c1bfea6e10 Mon Sep 17 00:00:00 2001 From: nk3nny Date: Wed, 15 Jan 2025 18:26:28 +0100 Subject: [PATCH 2/4] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Performance improvements by @rautemiekka Co-authored-by: Jouni Järvinen --- addons/danger/functions/fnc_brainVehicle.sqf | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/danger/functions/fnc_brainVehicle.sqf b/addons/danger/functions/fnc_brainVehicle.sqf index da3fafba..e7a9ad45 100644 --- a/addons/danger/functions/fnc_brainVehicle.sqf +++ b/addons/danger/functions/fnc_brainVehicle.sqf @@ -93,18 +93,18 @@ if (_artillery) exitWith { private _ammo = getArtilleryAmmo [_vehicle]; private _shell = _ammo param [0, ""]; if (_shell isEqualTo "") exitWith {}; - private _flareIndex = _ammo findIf {"flare" in (toLower _x)}; - private _smokeIndex = _ammo findIf {"smoke" in (toLower _x)}; + private _flareIndex = _ammo findIf {"flare" in (toLowerANSI _x)}; + private _smokeIndex = _ammo findIf {"smoke" in (toLowerANSI _x)}; // check friendlies private _dangerRound = false; private _repeatRounds = true; if ( RND(0.8) || { ([_unit, _dangerPos, 150] call EFUNC(main,findNearbyFriendlies)) isNotEqualTo [] } ) then { - if (_smokeIndex isNotEqualTo -1) then { + if (_smokeIndex isEqualTo -1) then { + _dangerRound = true; + } else { _shell = _ammo select _smokeIndex; _repeatRounds = RND(0.5); - } else { - _dangerRound = true; }; }; @@ -246,7 +246,7 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { private _slow = speed _vehicle < 20; if ( RND(0.4) - && {_slow} + && _slow && {someAmmo _vehicle} && {_cause isEqualTo DANGER_ENEMYDETECTED} && {!alive (gunner _vehicle)} @@ -265,7 +265,7 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { }; // tank assault - if (_attack && {_slow} && {(getUnitState _unit) isEqualTo "OK"}) then { + if (_attack && _slow && {(getUnitState _unit) isEqualTo "OK"}) then { // rotate private _rotate = [_vehicle, _dangerPos] call EFUNC(main,doVehicleRotate); From 07d2569c644390668e9339132edceb6dbf208565 Mon Sep 17 00:00:00 2001 From: Ken Mikkelsen Date: Wed, 15 Jan 2025 18:31:38 +0100 Subject: [PATCH 3/4] Responding to review Fixed missing lazy eval Removed unnecessary 'private' tag. --- addons/danger/functions/fnc_brainVehicle.sqf | 4 ++-- addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/danger/functions/fnc_brainVehicle.sqf b/addons/danger/functions/fnc_brainVehicle.sqf index e7a9ad45..fbd43c37 100644 --- a/addons/danger/functions/fnc_brainVehicle.sqf +++ b/addons/danger/functions/fnc_brainVehicle.sqf @@ -117,7 +117,7 @@ if (_artillery) exitWith { }; // check for issues - if ( _dangerRound || !( _dangerPos inRangeOfArtillery [[_vehicle], _shell] ) ) exitWith {}; + if ( _dangerRound || { !(_dangerPos inRangeOfArtillery [[_vehicle], _shell]) } ) exitWith {}; // execute fire command _vehicle commandArtilleryFire [_dangerPos getPos [30 + random 80, (_dangerPos getDir _vehicle) - 10 + random 20], _shell, 1 + random 2]; @@ -127,7 +127,7 @@ if (_artillery) exitWith { [ { params [["_vehicle", objNull], ["_dangerPos", [0, 0, 0]], ["_shell", ""]]; - if (canFire _vehicle && unitReady _vehicle) then { + if ( canFire _vehicle && { unitReady _vehicle } ) then { _vehicle commandArtilleryFire [_dangerPos, _shell, ( 2 + random 1 ) min ((gunner _vehicle) ammo (currentMuzzle (gunner _vehicle)))]; }; }, diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf index 7fbc616e..d5463297 100644 --- a/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf +++ b/addons/main/functions/VehicleAction/fnc_doVehicleSuppress.sqf @@ -18,7 +18,7 @@ params ["_unit", ["_pos", [0, 0, 0]]]; private _vehicle = vehicle _unit; -private _pos = _pos call CBA_fnc_getPos; +_pos = _pos call CBA_fnc_getPos; // exit if vehicle is moving too fast or target is too high if (speed _vehicle > 30 || {(_pos select 2) > 100} || {!((gunner _vehicle) call FUNC(isAlive))}) exitWith {false}; From a3894d36e3c739523b0c47ec9749a281472f96de Mon Sep 17 00:00:00 2001 From: Ken Mikkelsen Date: Sun, 23 Feb 2025 19:41:04 +0100 Subject: [PATCH 4/4] Added vehicle assault pattern Added Vehicle Assault pattern. Allows armored vehicles to hunt down other vehicles or certain infantry Fixed vehicle in "ATTACK" stance getting stuck on targets Fixed mortar attacking air targets Fixed vehicles never calling tactics state (especially if group leader and vehicle commander were not identical) Fixed vehicles getting "stuck" in combat dismounting Fixed cases where doVehicleRotate would not correctly trigger Improved balance of mortar rate of fire Improved many cases where debug information were not assigned to vehicle commander --- addons/danger/functions/fnc_brainForced.sqf | 5 - addons/danger/functions/fnc_brainVehicle.sqf | 56 +++++++---- addons/main/XEH_PREP.hpp | 1 + .../VehicleAction/fnc_doVehicleAssault.sqf | 4 +- .../fnc_doVehicleAssaultMove.sqf | 97 +++++++++++++++++++ .../VehicleAction/fnc_doVehicleRotate.sqf | 54 ++++++----- 6 files changed, 166 insertions(+), 51 deletions(-) create mode 100644 addons/main/functions/VehicleAction/fnc_doVehicleAssaultMove.sqf diff --git a/addons/danger/functions/fnc_brainForced.sqf b/addons/danger/functions/fnc_brainForced.sqf index 8ac20a44..eabe9cea 100644 --- a/addons/danger/functions/fnc_brainForced.sqf +++ b/addons/danger/functions/fnc_brainForced.sqf @@ -39,11 +39,6 @@ if (fleeing _unit) exitWith { _timeout }; -// units in vehicles -if (!isNull objectParent _unit) exitWith { - _timeout + 1.5 -}; - // attack speed and stance if ((currentCommand _unit) isEqualTo "ATTACK") then { private _attackTarget = getAttackTarget _unit; diff --git a/addons/danger/functions/fnc_brainVehicle.sqf b/addons/danger/functions/fnc_brainVehicle.sqf index fbd43c37..04a0f8ac 100644 --- a/addons/danger/functions/fnc_brainVehicle.sqf +++ b/addons/danger/functions/fnc_brainVehicle.sqf @@ -51,7 +51,7 @@ _causeArray params ["_cause", "_dangerPos", "", "_dangerCausedBy"]; // "_dangerU _unit setVariable [QEGVAR(main,FSMDangerCauseData), _causeArray, EGVAR(main,debug_functions)]; // is it an attack? -private _attack = _cause in [DANGER_ENEMYDETECTED, DANGER_ENEMYNEAR, DANGER_HIT, DANGER_CANFIRE, DANGER_BULLETCLOSE] && {(side _dangerCausedBy) isNotEqualTo (side _unit)} && {!isNull _dangerCausedBy}; +private _attack = _cause in [DANGER_ENEMYDETECTED, DANGER_ENEMYNEAR, DANGER_HIT, DANGER_CANFIRE, DANGER_BULLETCLOSE] && {(side _dangerCausedBy) isNotEqualTo (side _unit)} && {!isNull _dangerCausedBy} && {(behaviour _unit) isEqualTo "COMBAT"}; // update dangerPos if attacking. Check that the position is not too far above, or below ground. if (_attack) then { @@ -83,7 +83,7 @@ if (_artillery) exitWith { // mortars fire rounds private _mortarTime = _vehicle getVariable [QEGVAR(main,mortarTime), 0]; - if (_attack && {_vehicle isKindOf "StaticMortar"} && {unitReady _vehicle} && {_mortarTime < time}) then { + if (_attack && {_vehicle isKindOf "StaticMortar"} && {unitReady _vehicle} && {_mortarTime < time} && {isTouchingGround _dangerCausedBy}) then { // delay _timeout = _timeout + 2; @@ -121,7 +121,7 @@ if (_artillery) exitWith { // execute fire command _vehicle commandArtilleryFire [_dangerPos getPos [30 + random 80, (_dangerPos getDir _vehicle) - 10 + random 20], _shell, 1 + random 2]; - _vehicle setVariable [QEGVAR(main,mortarTime), time + 24 + random 6]; + _vehicle setVariable [QEGVAR(main,mortarTime), time + 24 + random 66]; _unit setVariable [QEGVAR(main,currentTask), "Mortar Fire", EGVAR(main,debug_functions)]; if (_repeatRounds) then { [ @@ -152,7 +152,7 @@ if (_vehicle isKindOf "StaticWeapon") exitWith { // get out if enemy near OR out of ammunition if ((count (magazines _vehicle)) isEqualTo 0 || {(_unit findNearestEnemy _dangerPos) distance _vehicle < (6 + random 15)}) then { - private _vehicleCrew = (crew _vehicle); + private _vehicleCrew = crew _vehicle; _vehicleCrew orderGetIn false; [_unit, "Combat", "Eject"] call EFUNC(main,doCallout); { @@ -170,6 +170,12 @@ if (_vehicle isKindOf "StaticWeapon") exitWith { [_timeout + random 4] + _causeArray }; +// Make leadership assessment as infantry +private _leader = leader _unit; +if (((vehicle _leader) isEqualTo _vehicle) && {_leader call FUNC(isLeader)}) then { + [_leader, _dangerCausedBy] call FUNC(tactics); +}; + // update information if (_cause in [DANGER_ENEMYNEAR, DANGER_SCREAM]) then {[_unit, _dangerCausedBy] call EFUNC(main,doShareInformation);}; @@ -223,9 +229,9 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { // define enemy direction _group setFormDir (_vehicle getDir _dangerCausedBy); - _cargo doMove _dangerPos; // delayed unload + _unit setVariable [QEGVAR(main,currentTask), "Dismounting troops", EGVAR(main,debug_functions)]; [ { params [["_cargo", []], ["_side", east], ["_vehicle", objNull]]; @@ -233,6 +239,7 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { [selectRandom _cargo, "Combat", "Dismount"] call EFUNC(main,doCallout); _cargo allowGetIn false; if (EGVAR(main,debug_functions)) then {["%1 %2 unloading %3 carried troops", _side, getText (configOf _vehicle >> "displayName"), count _cargo] call EFUNC(main,debugLog);}; + _vehicle doMove (getPosASL _vehicle); }, [_cargo, side _group, _vehicle], 0.1 @@ -265,18 +272,32 @@ if (_armored && {!isNull _dangerCausedBy}) exitWith { }; // tank assault - if (_attack && _slow && {(getUnitState _unit) isEqualTo "OK"}) then { + if (_attack && _slow && {(getUnitState _unit) in ["OK", "DELAY"]}) then { // rotate - private _rotate = [_vehicle, _dangerPos] call EFUNC(main,doVehicleRotate); - - // assault - if (!_rotate && {_distance < 750} && {_dangerCausedBy isKindOf "CAManBase"} && {(gunner _vehicle) call EFUNC(main,isAlive)}) then { - [ - {_this call EFUNC(main,doVehicleAssault)}, - [_unit, _dangerPos, _dangerCausedBy], - _delay - 1.5 - ] call CBA_fnc_waitAndExecute; + private _rotate = [_unit, _dangerPos] call EFUNC(main,doVehicleRotate); + + // assault + vehicle assault + if (!_rotate && {_distance < 750} && {(gunner _vehicle) call EFUNC(main,isAlive)}) then { + + // infantry + if ( _dangerCausedBy isKindOf "CAManBase" && { !( terrainIntersectASL [ eyePos _vehicle, eyePos _dangerCausedBy ] ) } ) exitWith { + [ + {_this call EFUNC(main,doVehicleAssault)}, + [_unit, _dangerPos, _dangerCausedBy], + _delay - 1.5 + ] call CBA_fnc_waitAndExecute; + }; + + // everything else -- assault! + if ( + isTouchingGround _dangerCausedBy + && { unitReady _vehicle } + && { (driver _vehicle) call EFUNC(main,isAlive) } + && { [_vehicle, "VIEW", vehicle _dangerCausedBy] checkVisibility [eyePos _vehicle, eyePos _dangerCausedBy] < 0.5 } + ) then { + [_unit, _dangerPos, _dangerCausedBy, _distance] call EFUNC(main,doVehicleAssaultMove); + }; }; }; @@ -366,10 +387,5 @@ if (_vehicle isKindOf "Car_F" && {!someAmmo _vehicle}) then { }; }; -// Make leadership assessment as infantry -if (_unit call FUNC(isLeader)) then { - [_unit, _dangerCausedBy] call FUNC(tactics); -}; - // end [_timeout] + _causeArray diff --git a/addons/main/XEH_PREP.hpp b/addons/main/XEH_PREP.hpp index 74a4b984..8acf2397 100644 --- a/addons/main/XEH_PREP.hpp +++ b/addons/main/XEH_PREP.hpp @@ -55,6 +55,7 @@ SUBPREP(UnitAction,doSuppress); SUBPREP(UnitAction,doUGL); SUBPREP(VehicleAction,doVehicleAssault); +SUBPREP(VehicleAction,doVehicleAssaultMove); SUBPREP(VehicleAction,doVehicleJink); SUBPREP(VehicleAction,doVehicleRotate); SUBPREP(VehicleAction,doVehicleSuppress); diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf index 9db4a3f5..0ecc329d 100644 --- a/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf +++ b/addons/main/functions/VehicleAction/fnc_doVehicleAssault.sqf @@ -62,8 +62,8 @@ _vehicle doWatch (AGLToASL _pos); private _suppression = [_unit, _pos] call FUNC(doVehicleSuppress); // set task -_unit setVariable [QEGVAR(main,currentTarget), _target, EGVAR(main,debug_functions)]; -_unit setVariable [QEGVAR(main,currentTask), "Vehicle Assault", EGVAR(main,debug_functions)]; +_unit setVariable [QGVAR(currentTarget), _target, GVAR(debug_functions)]; +_unit setVariable [QGVAR(currentTask), "Vehicle Assault", GVAR(debug_functions)]; // minor jink if no suppression possible if (!_suppression) exitWith {[_unit, 35] call FUNC(doVehicleJink)}; diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleAssaultMove.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleAssaultMove.sqf new file mode 100644 index 00000000..5249fb01 --- /dev/null +++ b/addons/main/functions/VehicleAction/fnc_doVehicleAssaultMove.sqf @@ -0,0 +1,97 @@ +#include "script_component.hpp" +/* + * Author: nkenny + * Vehicle moves aggressively to superior position + * + * Arguments: + * 0: _unit moving + * 1: dangerous position + * 2: dangerous object + * 3: distance to position and object + * + * Return Value: + * bool + * + * Example: + * [bob, getPos angryJoe, angryJoe] call lambs_main_fnc_doVehicleAssaultMove; + * + * Public: No +*/ +params ["_unit", "_pos", ["_target", objNull], ["_distance", -1]]; + +// settings +private _vehicle = vehicle _unit; + +// distance to position +if (_distance < 0) then {_distance = _vehicle distance _pos}; +if (isNull _target) then {_target = _vehicle;}; + +// cannot move or moving or enemy too close or too far away +if ( + !canMove _vehicle + || { (fuel _vehicle) < 0.1 } + || { (currentCommand _vehicle) in ["MOVE", "ATTACK"] } + || {_distance < (precision _vehicle)} + || {_distance > 200} + ) exitWith { + _vehicle doMove (getPosASL _vehicle); + false +}; + +private _destination = call { + + // 25 meters ahead + private _typeOf = typeOf _vehicle; + private _distance = _vehicle distance _pos; + private _movePos = _vehicle getPos [50 min _distance, _vehicle getDir _pos]; + _movePos = _movePos findEmptyPosition [0, 15, _typeOf]; + if (_movePos isNotEqualTo [] && {[vehicle _target, "VIEW", objNull] checkVisibility [(AGLToASL _movePos) vectorAdd [0, 0, 3], AGLToASL _pos] > 0}) exitWith { + _movePos + }; + + // random 200 + road adjustment + _movePos = (_vehicle getPos [200 min _distance, (_vehicle getDir _pos) - 45 + random 90]) findEmptyPosition [10, 30, _typeOf]; + if (_movePos isNotEqualTo [] && {[vehicle _target, "VIEW", objNull] checkVisibility [(AGLToASL _movePos) vectorAdd [0, 0, 3], AGLToASL _pos] > 0}) exitWith { + + // road adjust + private _roads = _movePos nearRoads 20; + if (_roads isNotEqualTo []) then {_movePos = (ASLToAGL (getPosASL (selectRandom _roads)));}; + + // return + _movePos + }; + + // On top of + _movePos = _pos findEmptyPosition [5, 35, _typeOf]; + if (_movePos isNotEqualTo []) exitWith { + _movePos + }; + + // none + [] +}; + +// check it! +if (_destination isEqualTo []) exitWith { + false +}; + +// set task +_unit setVariable [QGVAR(currentTarget), _destination, GVAR(debug_functions)]; +_unit setVariable [QGVAR(currentTask), "Vehicle Assault Move", GVAR(debug_functions)]; + +// execute +_vehicle doMove _destination; + +// debug +if (GVAR(debug_functions)) then { + [ + "%1 assault move (%2 moves %3m | visiblity %4)", + side _unit, getText (configOf _vehicle >> "displayName"), + round (_unit distance _destination), + [vehicle _target, "VIEW", objNull] checkVisibility [(AGLToASL _destination) vectorAdd [0, 0, 5], AGLToASL _pos] + ] call FUNC(debugLog); +}; + +// exit +true diff --git a/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf b/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf index f611c870..58d30416 100644 --- a/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf +++ b/addons/main/functions/VehicleAction/fnc_doVehicleRotate.sqf @@ -30,7 +30,14 @@ _target = _target call CBA_fnc_getPos; // cannot move or moving private _vehicle = vehicle _unit; -if (!canMove _vehicle || {(currentCommand _vehicle) isEqualTo "MOVE"} || {!alive (driver _vehicle)}) exitWith {false}; +if ( + !canMove _vehicle + || {(currentCommand _vehicle) isEqualTo "MOVE"} + || {!((driver _vehicle) call FUNC(isAlive))} + || {((vehicleMoveInfo _vehicle) select 1) in ["LEFT", "RIGHT"]} +) exitWith { + false +}; // CQB tweak -- target within 35m - look instead if (_unit distanceSqr _target < 1225) exitWith { @@ -38,14 +45,14 @@ if (_unit distanceSqr _target < 1225) exitWith { false }; -_unit setVariable [QGVAR(currentTarget), _target, GVAR(debug_functions)]; -_unit setVariable [QGVAR(currentTask), "Vehicle Rotate", GVAR(debug_functions)]; - // within acceptable limits -if (_unit getRelDir _target < _threshold || {_unit getRelDir _target > (360-_threshold)}) exitWith { +if (_vehicle getRelDir _target < _threshold || {_vehicle getRelDir _target > (360-_threshold)}) exitWith { false }; +_unit setVariable [QGVAR(currentTarget), _target, GVAR(debug_functions)]; +_unit setVariable [QGVAR(currentTask), "Vehicle Rotate", GVAR(debug_functions)]; + // move _unit setFormDir (_unit getDir _target); if (_vehicle isKindOf "Tank") then { @@ -60,7 +67,7 @@ if (_vehicle isKindOf "Tank") then { private _min = 20; // Minimum range for "_i" from 0 to 5 do { - _pos = (_unit getPos [_min, _unit getDir _target]) findEmptyPosition [0, 2.2, typeOf _unit]; + _pos = (_vehicle getPos [_min, _vehicle getDir _target]) findEmptyPosition [0, precision _vehicle, typeOf _vehicle]; // water or exit if !(_pos isEqualTo [] || {surfaceIsWater _pos}) exitWith {}; @@ -68,32 +75,31 @@ if (_vehicle isKindOf "Tank") then { // update _min = _min + 15; }; - if (_pos isEqualTo []) then {_pos = _unit modelToWorldVisual [0, -100, 0]}; - _unit doMove _pos; + if (_pos isEqualTo []) then {_pos = _vehicle modelToWorldVisual [0, -100, 0]}; + _vehicle doMove _pos; }; // waitUntil [ { - params ["_unit", "_target", "_threshold"]; - ((_unit getRelDir _target) < _threshold || {(_unit getRelDir _target) > (360 - _threshold)}) + params ["_vehicle", "_target", "_threshold"]; + ((_vehicle getRelDir _target) < _threshold || {(_vehicle getRelDir _target) > (360 - _threshold)}) }, { - params ["_unit", "_target"]; - // check vehicle - if (canMove _unit && {(crew _unit) isNotEqualTo []}) then { - - // refresh ready - (vehicle _unit) sendSimpleCommand "STOPTURNING"; - (effectiveCommander _unit) doMove (_unit getPos [10, _unit getDir _target]); - - // refresh formation - (group _unit) setFormDir (_unit getDir _target); - }; - }, [_unit, _target, _threshold * 2], 4 + random 3, + params ["_vehicle", "_target"]; + // refresh ready + _vehicle sendSimpleCommand "STOPTURNING"; + _vehicle sendSimpleCommand "STOP"; + _vehicle doMove (_vehicle getPos [precision _vehicle, _vehicle getDir _target]); + + // refresh formation + (group _vehicle) setFormDir (_vehicle getDir _target); + }, [_vehicle, _target, _threshold * 3], 4 + random 3, { - params ["_unit"]; - (vehicle _unit) sendSimpleCommand "STOPTURNING"; + params ["_vehicle", "_target"]; + _vehicle doWatch (ATLToASL _target); + _vehicle sendSimpleCommand "STOPTURNING"; + _vehicle doMove (getPosASL _vehicle); } ] call CBA_fnc_waitUntilAndExecute;