Jump to content
pierremgi

AI CAN REARM - on crates, corpses, weapons, vehicles, arsenal...

Recommended Posts

Hello all,

Here is a little script enabling AIs for rearming when out of ammo.

If I'm right, the vanilla behavior is:

 - AI leaders sometimes order their AI subordinates for rearming at crates (probably only simple crates/vehicles with accurate magazines). Anyway they don't succeed in that (or barely). What I experienced is AIs running after vehicles, AIs stuck, AIs disobeying... If you have better experience on that, please share.

- as playing leader of AIs, you can order your units to rearm at... known abandoned weapons, crates,...  no matter if you don't know where they are and how far. The list can be looooong (as a day without bread) , and the, totally useless. you can see you AI crossing the map without few chance for actually rearming.

 

Well, I'm trying here another approach, in this way:
- first of all, I limit the script to AIs with played leader. (Just avoiding too many codes on server, but feel free to test what ever units you want);

- only AI units owning a primary weapon are concerned: No player, no tourist;

- When the AI has less than 10 remaining shots in its primary weapon, if magazines or weapons can be picked, within 100 m of the unit (anytime), the AI will move and pick them up.

 

The possibilities and priorities are:

- Any object stated as Arsenal (virtual crates/ vehicles with arsenal code...), is scouted as priority 1. That means the AI will reload at this object, with compatible mags, no matter the content of the arsenal crate/vehicle.

  At this time, and possibly for ever, I don't make any filter about possible limitation on arsenal. That means any enemy (concerned AI) can rearm at any arsenal.

 

- The priority 2 is crates/vehicles fitted with compatible mags. The rearm action on a crate can empty it. This action is the standard behavior of Arma's engine. If you want more, see module! Anyway, the mags can be inside uniforms, vests, backpacks inside the crate or the vehicle. Not so bad.

 

- the priority 3 is friendly fallen corpses... There is neither arsenal, nor crate but some fellows are lying on ground with compatible mags. That's the last chance for AI to keep their actual primary weapon;

 

- the last priority (4) is any weapon found on ground, inside crate or vehicle. This weapon is different from the original. If several choices, the AI will pick one at random... At this time, I don' have filter for all possible cases and skills of AIs... It's just out of my scope. anyway this weapon can be inside a backpack, inside a crate...

I added a mandatory mag for this weapon as I don't want the AIs out of ammo, then picking a weapon, out of ammo then... in a boring cycle. Furthermore, at this time, the abandoned weapon is deleted.

 

Note 1: Vehicles are considered as crates if engine off!
Note 2: You can stop an AI (stop order), so he will not continue his search for mags or weapons (but he will not fight without ammo).

 

Of course, as I'm scripting also for my mod: MGI Advanced Modules, I added a Rearm Module enabling more possibilities:
- You can easily choose different units (to be upgraded perhaps);

- When your AI rearms, the secondary weapon (if any) is refilled in tube (so 1 shot). The AI receives up to 3 first aid kit and hand grenades or smokes. The AI grabs also up to 6 grenades for GL if any.

- the rearm and switchweapon vanilla actions are replaced by animations (same ones) and custom scripts avoiding vanilla inventory (too many mags, no grenades, poor fak)

 

Known limitations:
- the hand guns are not treated at all;

- the GL and launchers are treated in module, but only in case of low primary weapon ammo which stays the triggering factor;

 

Compatible for MP (the treated units are local on each PC/Server), compatible with mods. Depending on mods, you can experience some little difference:

- compatibles mags means... compatible mags and some vanilla mags can be used instead of specific ones, for arsenal. I'm not responsible of these choices.

- on the other hand, there are several "FirstAidKit" along with mods. Some of them keep the class name and modify the aspect, some of them modify the whole stuff (for nuts,imho). Your AI can receive firstAidKit instead of "blabla_holyKitofTheGreatesModEverWritten" . They will not make difference, and you'll see that if you pick items on dead AI fellow.

 

Have fun!

Version: 28 / 07/ 21

 

in init.sqf
 

Spoiler

 


MGI_PRMAGS = compileFinal "
  private _weaponCfg = configFile >> 'CfgWeapons' >> _this;
  private _compatibleMagazines = [];
  private _magazinesList = getarray (_weaponCfg >> 'magazines');
  {
    {
      _magazinesList append (getArray _x);
    } foreach  configproperties [configFile >> 'CfgMagazineWells' >> _x,'isArray _x'];
  } foreach getArray (_weaponCfg >> 'magazineWell');
  {
    private _magazine = _x;
    private _magazineCfg  = configfile >> 'cfgmagazines' >> _magazine;
    if ((getnumber (_magazineCfg >> 'scope') isEqualTo 2)) then {
      _compatibleMagazines pushBackUnique _magazine;
    };
  } foreach _magazinesList;
  _compatibleMagazines
";

MGI_REARMAGS = compileFinal "
  params ['_unit','_crate','_pos',['_arsenalC',FALSE],'_mags','_isCrate'];
  _unit setHitPointDamage ['hitLegs',0];
  _unit setCombatBehaviour 'CARELESS';
  _unit setVariable ['MGI_ONDUTY',TRUE];
  if (_unit distance2D _pos < 120) then {
    _unit doMove _pos;
    _mags = _unit getVariable ['magsInit',[currentMagazine _unit]];
    _isCrate = !(_crate isKindOf 'CAManBase');
    uiSleep 0.5;
    waitUntil {uiSleep 0.5;(unitReady _unit or (!_arsenalC && (_mags arrayIntersect ([magazines _crate,magazineCargo _crate] select _isCrate) isEqualTo [])))};
    if (!_arsenalC && (_mags arrayIntersect ([magazines _crate,magazineCargo _crate] select _isCrate) isEqualTo [])) then {
      _unit doFollow leader _unit;
      _unit setCombatBehaviour 'AWARE';
      _unit setVariable ['MGI_ONDUTY',NIL];
    } else {
      if (_unit distance2D _pos < 5 + ((0 boundingBoxReal _crate)#2 max (0 boundingBoxReal objectParent _crate)#2)) then {
        _unit setdir (_unit getDir _pos);
        _unit action ['rearm', _crate];
        if _arsenalC then {
          private _defaultMag = _mags #0;
          {_unit addMagazine _defaultMag} forEach [1,2,3];
          if (items _unit arrayIntersect ['vn_b_item_firstaidkit','FirstAidKit','vn_o_item_firstaidkit'] isEqualTo []) then {
            _unit addItem 'FirstAidKit';
          };
        };
        if (['ReammoBox'] findIf {_crate isKindOf _x} > -1) then {
          private _otherMags = magazineCargo _crate select {!(_x in _mags)};
          private _allmags = magazineCargo _crate - _otherMags;
          while {_unit canAdd (_allmags #0)} do {
            _addMag = _allmags deleteAt 0;
            _unit addMagazine _addMag;
          };
          if (['vn_b_item_firstaidkit','FirstAidKit','vn_o_item_firstaidkit'] arrayIntersect _otherMags isNotEqualTo []) then {
            _unit addItem ((['vn_b_item_firstaidkit','vn_o_item_firstaidkit','FirstAidKit'] arrayIntersect _otherMags)#0);
          };
          clearMagazineCargoGlobal _crate;
          {_crate addMagazineCargoGlobal [_x,1]} count (_allmags + _otherMags);
        };
        uiSleep 3;
        reload _unit;
        _unit doFollow leader _unit;
        _unit setCombatBehaviour 'AWARE';
        _unit setVariable ['MGI_ONDUTY',NIL];
      };
      _unit selectWeapon primaryWeapon _unit;
    };
  };
";

MGI_TAKEWPN = compileFinal "
  params ['_unit','_crate','_wpn','_pos','_mags','_pos'];
  _unit setHitPointDamage ['hitLegs',0];
  _unit setCombatBehaviour 'CARELESS';
  _unit setVariable ['MGI_ONDUTY',TRUE];
  _unit doMove _pos;
  uiSleep 0.5;
  _mags = _wpn call MGI_PRMAGS;
  waitUntil {uiSleep 0.5; unitReady _unit or isNull _crate or !(_wpn in weaponCargo _crate)};
  private _pWpn = primaryWeapon _unit;
  if (_unit distance2D _pos < 5 +  + ((0 boundingBoxReal _crate)#2 max (0 boundingBoxReal objectParent _crate)#2)) exitWith {
    _unit setdir (_unit getDir _pos);
    private _crateFake = (['WeaponHolder','WeaponHolderSimulated'] findIf {_crate isKindOf _x} > -1);
    if (_mags arrayIntersect magazineCargo _crate isEqualTo []) then {
      _unit addMagazine (_mags #0);
    };
    call {
      if (isNull objectParent _crate && !(_crate isKindOf 'ContainerSupply')) exitWith {
        private _allmags = magazineCargo _crate;
        _unit action ['TakeWeapon',_crate,_wpn];
        private _timer = diag_tickTime;
        waitUntil {_pWpn in weaponCargo _crate or diag_tickTime > _timer + 10};
        if (weaponCargo _crate isEqualTo [_pWpn] && magazineCargo _crate isEqualTo [] && everyContainer _crate isEqualTo [] && _crateFake) then {
          deleteVehicle _crate;
        } else {
          if (_pWpn in weaponCargo _crate) then {
            private _allWpns = + weaponCargo _crate;
            _allWpns deleteAt (_allWpns find _pWpn);
            clearWeaponCargoGlobal _crate;
            clearMagazineCargoGlobal _crate;
            {_crate addWeaponCargoGlobal [_x,1]} count _allWpns;
            {_crate addMagazineCargoGlobal [_x,1]} count _allmags;
          };
        };
        uiSleep 3;
        reload _unit;
      };
      _unit addWeapon _wpn;
      private _allWpns = + weaponCargo _crate;
      _allWpns deleteAt (_allWpns find _wpn);
      clearWeaponCargoGlobal _crate;
      {_crate addWeaponCargoGlobal [_x,1]} count _allWpns;
      uiSleep 3;
      reload _unit;
    };
    _unit setVariable ['magsInit',primaryWeapon _unit call MGI_PRMAGS];
    uiSleep 3;
    _unit doFollow leader _unit;
    _unit setCombatBehaviour 'AWARE';
    _unit setVariable ['MGI_ONDUTY',NIL];
    _unit selectWeapon primaryWeapon _unit;
  };
  if (isNull _crate or !(_wpn in weaponCargo _crate)) then {
    _unit doFollow leader _unit;
    _unit setCombatBehaviour 'AWARE';
    _unit setVariable ['MGI_ONDUTY',NIL];
  };
";

["ReArm","onEachFrame",{
  if (diag_frameNo mod 600 == 0) then {
    {
      private _unit = _x;
      if (isNull objectParent _unit && !isPlayer _unit && simulationEnabled _unit) then {
        if (isNil {_unit getVariable "magsInit"}) then {
          private _compatTypes = primaryWeapon _unit call MGI_PRMAGS;
          _unit setVariable ["magsInit",_compatTypes];
        };
        private _magsPWTypes = ((magazinesAmmoFull _unit) select {(_x#0) in (_unit getVariable "magsInit")});
        private _ammos = 0;
        for "_i" from 0 to count _magsPWTypes -1 do {
          _ammos = _ammos + _magsPWTypes #_i #1
        };
        if (_ammos < 10 && isNil {_unit getVariable "MGI_ONDUTY"}) then {
          private _crate = objNull;
          private _containers = [];
          private _wpnCrates = (nearestObjects [_unit,["ReammoBox_F","LandVehicle","air","WeaponHolderSimulated","WeaponHolder"],100,TRUE]);
          _wpnCrates = _wpnCrates select {private _v = _x; (["LandVehicle","Air"] findIf {_v isKindOf _x} ==-1) or !isEngineOn _x};
          _containers = _wpnCrates apply {[_x,getpos _x]};
          private ["_ev","_ps"];
          {
            _ev = everyContainer _x;
            _ps = getpos _x;
            _containers append (_ev apply {[_x#1,_ps]})
          } count (_wpnCrates select {everyContainer _x isNotEqualTo []});
          private _corpses = allDeadMen select {_x distance2D _unit < 100 && {_x isKindOf "CAManBase"}};
          private _magsInit = _unit getVariable ["magsInit",[]];
          private _wpnConts = _containers apply {_x#0};
          call {
            if (_containers findIf {((_x#0) call BIS_fnc_getVirtualMagazineCargo) isNotEqualTo []} >-1) exitWith {
              _crate = (_containers select {((_x#0) call BIS_fnc_getVirtualMagazineCargo) isNotEqualTo []}) #0#0;
              private _posCrate = (_containers select (_containers findIf {(_x#0) == _crate}))#1;
              if (!stopped _unit) then {[_unit,_crate,_posCrate,TRUE] spawn MGI_REARMAGS};
            };
            if (_containers findif {_magsInit arrayIntersect magazineCargo (_x#0) isNotEqualTo []} >-1) exitWith {
              _crate = (_containers select {_magsInit arrayIntersect magazineCargo (_x#0) isNotEqualTo []}) #0#0;
              private _posCrate = (_containers select (_containers findIf {(_x#0) == _crate}))#1;
              if (!stopped _unit) then {[_unit,_crate,_posCrate,FALSE] spawn MGI_REARMAGS};
            };
            if (_corpses findIf {_magsInit arrayIntersect magazines _x isNotEqualTo []} > -1) exitWith {
              _crate = (_corpses select {_magsInit arrayIntersect magazines _x isNotEqualTo []} select 0);
              if (!stopped _unit) then {[_unit,_crate,getpos _crate,FALSE] spawn MGI_REARMAGS};
            };
            if (_containers findif {weaponCargo (_x#0) select {getnumber (configFile >> "cfgWeapons" >> _x >> "type") isEqualTo 1} isNotEqualTo [] } >-1) exitWith {
              _crate = (_containers select {weaponCargo (_x#0) select {getnumber (configFile >> "cfgWeapons" >> _x >> "type") isEqualTo 1} isNotEqualTo []}) #0#0;
              private _posCrate = (_containers select (_containers findIf {(_x#0) == _crate}))#1;
              private _wpns = weaponCargo _crate select {getnumber (configFile >> "cfgWeapons" >> _x >> "type") isEqualTo 1};
              private _wpn = "";
              private _compatMags = [];
              _wpns = _wpns arrayIntersect _wpns;
              private _mags = (magazineCargo _crate) arrayIntersect (magazineCargo _crate);
              while {_compatMags arrayIntersect _mags isEqualTo [] && _wpns isNotEqualTo []} do {
                _wpn = +_wpns deleteAt (floor random count _wpns);
                _compatMags = _wpn call MGI_PRMAGS;
              };
              if (_wpn == "" or _compatMags arrayIntersect _mags isEqualTo []) then {
                _wpn = _wpns #0;
              };
              if (!stopped _unit) then {[_unit,_crate,_wpn,_posCrate] spawn MGI_TAKEWPN};
            };
          };
        };
      };
    } forEach (flatten (allGroups select {isPlayer leader _x && local leader _x} apply {units _x}));
  }
}] call bis_fnc_addStackedEventHandler;

 

 

 

 

  • Like 4
  • Thanks 6

Share this post


Link to post
Share on other sites

Cool, anything to ease the pain!👍 As far as I experienced throughout Arma series regarding whole AI rearming thing, main issues particularly in A3 seem to be:

  • At some point, AI would really make sure they stock up on grenades even with magazines available. Not sure if this was ever looked at, I don't play much in recent days.
  • Dropped weapon(s) are not necessarily part of its unit container, therefore it is often impossible to open inventory screen/rearm on specific fallen unit because of its weapon(s).
  • As a result, among other reasons, the action list for selecting quickly becomes useless with couple of pages worth of entries and crates are prioritized last. Also, "5, rearm at random ass crate at 10 o'clock and better hope it's the right one"... yeah, not that great. It was fine in 2001 but surely there could be some selectable, object sensitive UI by now. Well, there is that quick command interface or whatever it's called but it has all the wrong actions.
  • The nature of AI navigating/interacting with objects. They have to be about 5 to 10m from certain direction of object in question and then beeline to it. If there is an element preventing that from happening... then, oh well. Object's AI point for interaction in not always obvious or that uniform in 3DEN either. Figuring that out is a matter of trial end error, taking away from actually designing the scenario.

So, that's the little rant on that from long suffering AI babysitter.

  • Like 4

Share this post


Link to post
Share on other sites

This is fantastic and sorely needed. Thanks Pierre!  I know playing Prairie Fire, that my AI team frequently runs out of ammo fighting beaucoup VC. And then its a huge pain in the ass to get them to rearm somewhere nearby .

  • Like 1

Share this post


Link to post
Share on other sites

I just studied your code, and have to say it is beautiful.  If a person takes the time to understand this code they will learn alot  Especially  complex array manipulation. Great job Pierre.  

 

I really need to explore the wiki more, as I learned at least 5 commands that I didn't know existed before (i.e., flatten, diag_frameNo, stopped, allDeadMen, everyContainer, etc).

 

Just to be clear though, I'm not like Dinesh!😂

 

  • Like 1
  • Thanks 1
  • Haha 3

Share this post


Link to post
Share on other sites

Great script have been looking for one like this for my POW mission..

Share this post


Link to post
Share on other sites

MGI ADVANCED MODULES is updated and documentation also!

 

 

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×