Jump to content
Undeceived

Copy items, weapons + attachments, magazines and content of subcontainers inside a trigger area

Recommended Posts

Hi guys, I would like to "collect" the items, weapons, magazines and backpacks that the player has put on the ground inside a certain trigger and put then into a crate.

Is there a way to detect this stuff and create an array out of it.

 

The relatively new command inAreaArray looked promising at first but unfortunately it doesn't recognize items, weapons and magazines (I couldn't do it at least).

 

Does anyone know more?
 

Thanks a lot!

 

 

 

See the solution here: 

 

Share this post


Link to post
Share on other sites
27 minutes ago, Undeceived said:

The relatively new command inAreaArray looked promising at first but unfortunately it doesn't recognize items, weapons and magazines (I couldn't do it at least).

 

 

That's probably because those items are in virtual containers called "weaponHolders". Look for the  "groundWeaponHolder" or "WeaponHolderSimulated" types of objects, and then check their content.

Warning : it's boring. ^^

 

But I probably wrote something like that for Ravage already - I'll take a look and see if I can find some usefull bits of code.

  • Thanks 1

Share this post


Link to post
Share on other sites

in the meantime you can try this

 

_stuff = [[],[],[],[]]; 

_objs = vehicles;

_objs inAreaArray _trigger apply 
{
	(_stuff select 0) append (weaponCargo _x);
	(_stuff select 1) append (magazineCargo _x);
	(_stuff select 2) append (itemCargo _x);
	(_stuff select 2) append (backpackCargo _x);
};

 

_stuff will contain the output. i used "vehicles". if you are using a trigger that will be activated to get the items you could also use the "thisList" variable inside the onActivation line of the trigger to get every object insdie the trigger area instead of checking all present vehicles in the mission.

 

you could also filter that by class. some examples

 

//to only check certain classes based on parent class do: 
_objs = vehicles select {_x isKindOf "Car"}; 

//or explicit classes: 
_objs = vehicles select {typeOf _x in ["GroundWeaponholder", "GroundWeaponholder_Scripted", "WeaponHolder_Simulated"]};

//or by string search
_objs = vehicles select {toLower (typeOf _x) find "weaponholder" > -1};

 

that's just some quick and dirty examples though. not tested ingame.

Share this post


Link to post
Share on other sites
3 hours ago, bad benson said:

in the meantime you can try this

 


_stuff = [[],[],[],[]]; 

_objs = vehicles;

_objs inAreaArray _trigger apply 
{
	(_stuff select 0) append (weaponCargo _x);
	(_stuff select 1) append (magazineCargo _x);
	(_stuff select 2) append (itemCargo _x);
	(_stuff select 2) append (backpackCargo _x);
};

 

Thanks badbenson, I tried out this code but unfortunately the vehicles thing didn't work, so _stuff didn't have anything. As it seems to me, there are no vehicles in the trigger (I dropped an item, a mag and a rifle).

 

Your explanations after this code were too much for my brain - couldn't understand it - sorry. :icon_biggrin:

 

 

 

 

EDIT:

_objs = vehicles select {_x isKindOf "Car"};

 

worked when I placed a quad. But searching for the weaponholders didn't work.

Share this post


Link to post
Share on other sites

@Undeceived, use this function:

Spoiler

private _allStuff = [
	[], // Weapons
	[], // Magazines
	[], // Items
	[] // Backpacks
];

params [
	["_trigger", objNull, [objNull]]
];

if ((isNull _trigger) or {(typeOf _trigger) != "EmptyDetector"}) exitWith {_allStuff};

private ["_className", "_classNames", "_index", "_quantities", "_quantity", "_stuff", "_stuffOfType", "_weaponHolder", "_weaponHolders"];

_weaponHolders = (nearestObjects [_trigger, ["WeaponHolder", "WeaponHolderSimulated"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger};

{
	_weaponHolder = _x;

	{
		_classNames = _x select 0;
		_quantities = _x select 1;

		_stuffOfType = _allStuff select _forEachIndex;

		{
			_className = _x;
			_quantity = _quantities select _forEachIndex;

			_index = _stuffOfType findIf {(_x select 0) == _className};

			if (_index >= 0) then {
				_stuff = _stuffOfType select _index;

				_stuff set [1, (_stuff select 1) + _quantity];
			} else {
				_stuffOfType pushBack [_className, _quantity];
			};
		} forEach _classNames;
	} forEach [
		getWeaponCargo _weaponHolder,
		getMagazineCargo _weaponHolder,
		getItemCargo _weaponHolder,
		getBackpackCargo _weaponHolder
	];
} forEach _weaponHolders;

_allStuff

 

Usage:

_stuff = [_trigger] call fnc_getStuffInsideTrigger;

Example of result:

[[["hgun_Pistol_heavy_01_F",5]],[["11Rnd_45ACP_Mag",2]],[["H_MilCap_gry",3],["V_BandollierB_blk",1]],[["B_Kitbag_rgr",2]]]
  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

@Undeceived you need to use the right input array. i didn't read the whole function by Schatten but he uses nearestObjects using the trigger properties to get the right input. so it'll probably work better for what you are searching for, if my stuff confuses you.

 

of course you could just modify my version like this (just for completeness' sake)

_objs = (nearestObjects [_trigger, ["All"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger}; //taken from Schatten's code

but his function also gives you an amount for each item while mine just uses doubles (class string for each individual item) which you probably want. so depends on what you need really.

 

just to add something i have noticed debugging, in case someone else finds the useful. these are the classes i found in the config that have the word weaponholder in them. so if you are still not catching any of the ones you want with any of this code you might wanna try these classes for the filter.

 

"GroundWeaponHolder",
"GroundWeaponHolder_Scripted",
"Library_WeaponHolder",
"WeaponHolder",
"WeaponHolder_Single_F",
"WeaponHolder_Single_limited_item_F",
"WeaponHolder_Single_limited_magazine_F",
"WeaponHolder_Single_limited_weapon_F",
"WeaponHolderSimulated",
"WeaponHolderSimulated_Scripted"

didn't have time to find out what those are used for. and several of those are probably parent classes. maybe someone knows more. might be used by Eden when you place a single item maybe.

 

  • Like 1

Share this post


Link to post
Share on other sites

@bad benson

 

Cool, thanks for the addition / change.

 

When using this, the code worked:

 

_trigger = myCollectorTrigger;
_stuff = [[],[],[],[]]; 

_objs = (nearestObjects [_trigger, ["All"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger}; //taken from Schatten's code

_objs inAreaArray _trigger apply 
{
	(_stuff select 0) append (weaponCargo _x);
	(_stuff select 1) append (magazineCargo _x);
	(_stuff select 2) append (itemCargo _x);
	(_stuff select 3) append (backpackCargo _x);
};

 

The only problem left now is that all stuff that the units in the trigger have (and also the stuff that is in vehicles) is also added to the arrays. :icon_biggrin: Is there a way to exclude everything so that only the stuff on the ground is collected?

 

 

 

EDIT:

 

14 hours ago, bad benson said:

but his function also gives you an amount for each item while mine just uses doubles (class string for each individual item) which you probably want. so depends on what you need really.

 

Btw. this is what I need: One individual string for every item. :icon14:

Share this post


Link to post
Share on other sites

@Undeceived yes. that's what the filtering is all about.

 

_objs = (nearestObjects [_trigger, ["All"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger};

 

i thought you wanted everything so i used "All". you should first try using the array that Schatten used in his function. like this

 

_objs = (nearestObjects [_trigger, ["WeaponHolder", "WeaponHolderSimulated"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger};

pretty sure that will give you all fo it. if not try some of the above mentioned classes. i think though that these two are the main parent classes, so it should work.

 

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

bad benson, yeah, that did the trick. Many thanks, man!

 

 

 

 

To summarize the thread for mission designers that might have the same question:

 

With this code you can create an array with items, magazines, weapons and backpacks that are laying on the ground in a trigger area:

//Code by bad benson with elements of Schatten's function:

_trigger = trigger_0; 
_stuff = [[],[],[],[]];  
 
_objs = (nearestObjects [_trigger, ["WeaponHolder", "WeaponHolderSimulated"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger};  
 
_objs inAreaArray _trigger apply  
{ 
 (_stuff select 0) append (weaponCargo _x); 
 (_stuff select 1) append (magazineCargo _x); 
 (_stuff select 2) append (itemCargo _x); 
 (_stuff select 3) append (backpackCargo _x); 
};

 

To show the array:

hint str (_stuff);

 

@bad benson's code creates an array with classname strings for each individual item. E.g.:

 

[["hgun_P07_F"],["30Rnd_65x39_caseless_mag","30Rnd_65x39_caseless_mag","30Rnd_65x39_caseless_mag"],["FirstAidKit"],["B_AssaultPack_rgr_LAT"]]

 

 

 

If you need an array that looks like this:

 

[[["hgun_P07_F",1]],[["30Rnd_65x39_caseless_mag",3]],[["FirstAidKit",1]],[["B_AssaultPack_rgr_LAT",1]]]

 

then you can use @Schatten's function:

SCH_fnc_getStuffInsideTrigger = { 
 private _allStuff = [ 
  [], 
  [], 
  [],  
  []  
 ]; 
 
 params [ 
  ["_trigger", objNull, [objNull]] 
 ]; 
 
 if ((isNull _trigger) or {(typeOf _trigger) != "EmptyDetector"}) exitWith {_allStuff}; 
 
 private ["_className", "_classNames", "_index", "_quantities", "_quantity", "_stuff", "_stuffOfType", "_weaponHolder", "_weaponHolders"]; 
 
 _weaponHolders = (nearestObjects [_trigger, ["WeaponHolder", "WeaponHolderSimulated"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger}; 
 
 { 
  _weaponHolder = _x; 
 
  { 
   _classNames = _x select 0; 
   _quantities = _x select 1; 
 
   _stuffOfType = _allStuff select _forEachIndex; 
 
   { 
    _className = _x; 
    _quantity = _quantities select _forEachIndex; 
 
    _index = _stuffOfType findIf {(_x select 0) == _className}; 
 
    if (_index >= 0) then { 
     _stuff = _stuffOfType select _index; 
 
     _stuff set [1, (_stuff select 1) + _quantity]; 
    } else { 
     _stuffOfType pushBack [_className, _quantity]; 
    }; 
   } forEach _classNames; 
  } forEach [ 
   getWeaponCargo _weaponHolder, 
   getMagazineCargo _weaponHolder, 
   getItemCargo _weaponHolder, 
   getBackpackCargo _weaponHolder 
  ]; 
 } forEach _weaponHolders; 
 
 _allStuff 
}; 

 

Show the result / call the function with:

hint (str ([trigger_0] call SCH_fnc_getStuffInsideTrigger));

 

 

Many thanks to you both, guys.

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Sorry for the double post:

 

bad benson's result array brought up a last question: 

There's a way to remove the brackets so that the array looks like this, isn't it?

 

["hgun_P07_F","30Rnd_65x39_caseless_mag","30Rnd_65x39_caseless_mag","30Rnd_65x39_caseless_mag","FirstAidKit","B_AssaultPack_rgr_LAT"]

 

Nevermind, I messed something up while writing the post above.

 

bad benson's array result looks like this: 

[["hgun_P07_F"],["30Rnd_65x39_caseless_mag","30Rnd_65x39_caseless_mag","30Rnd_65x39_caseless_mag"],["FirstAidKit"],["B_AssaultPack_rgr_LAT"]]
 

 

I checked it and for the achievement of my ultimate goal (fill the stuff into a crate) it's not needed to have all weapons+mags+items+backpacks in one array altogether.

 

This will put the output of bad benson's code into a container:

{crate addWeaponCargoGlobal [_x,1]; } forEach (_stuff select 0);
{crate addMagazineCargoGlobal [_x,1]; } forEach (_stuff select 1);
{crate addItemCargoGlobal [_x,1]; } forEach (_stuff select 2);
{crate addBackpackCargoGlobal [_x,1]; } forEach (_stuff select 3);


 

Share this post


Link to post
Share on other sites

note that, you could've done that with Schatten's script too. this could potentially also be faster. although formating it the way he did takes some resources too, running addXCargoGlobal only once for each type of item is less stuff to do in the second part of it all. i don't want this to get too confusing but for this purpose there is no real reason to have individual strings. maybe i'm missing something.

 

so with his output just do

{_crate addWeaponCargoGlobal _x} forEach (_stuff select 0);
{_crate addMagazineCargoGlobal _x} forEach (_stuff select 1);
{_crate addItemCargoGlobal _x} forEach (_stuff select 2);
{_crate addBackpackCargoGlobal _x} forEach (_stuff select 3);

 

  • Like 2

Share this post


Link to post
Share on other sites

Well, I slightly improved my function:

Spoiler

private _allStuff = [
	[], // Weapons
	[], // Magazines
	[], // Items
	[] // Backpacks
];

params [
	["_area", objNull, [objNull, ""]]
];

if ((_area isEqualType objNull) and {(isNull _area) or {(typeOf _area) != "EmptyDetector"}}) exitWith {_allStuff};
if ((_area isEqualType "") and {(_area == "") or {!(_area in allMapMarkers)} or {!((markerShape _area) in ["ELLIPSE", "RECTANGLE"])}}) exitWith {_allStuff};

private ["_className", "_classNames", "_index", "_predicate", "_quantities", "_quantity", "_stuff", "_stuffOfType", "_stuffType", "_values", "_weaponHolder", "_weaponHolders"];

_values = if (_area isEqualType "") then {
	[getMarkerPos _area, getMarkerSize _area]
} else {
	[ASLToAGL (getPosASL _area), (triggerArea _area) select [0, 2]]
};

_values params ["_areaPosition", "_areaSize"];

_weaponHolders = (nearestObjects [_areaPosition, ["WeaponHolder", "WeaponHolderSimulated"], selectMax _areaSize]) inAreaArray _area;

{
	_weaponHolder = _x;

	{
		_classNames = _x select 0;
		_quantities = _x select 1;

		_stuffType = _forEachIndex;

		_stuffOfType = _allStuff select _stuffType;

		_predicate = if (_stuffType == 1) then {
			{((_x select 0) == _className) and {(_x select 2) == _quantity}}
		} else {
			{(_x select 0) == _className}
		};

		{
			_values = if (_stuffType == 1) then {_x} else {[_x, _quantities select _forEachIndex]};

			_className = _values select 0;
			_quantity = _values select 1;

			_index = _stuffOfType findIf _predicate;

			if (_index >= 0) then {
				if (_stuffType == 1) then {_quantity = 1;};

				_stuff = _stuffOfType select _index;

				_stuff set [1, (_stuff select 1) + _quantity];
			} else {
				_values = if (_stuffType == 1) then {[_className, 1, _quantity]} else {[_className, _quantity]};

				_stuffOfType pushBack _values;
			};
		} forEach _classNames;
	} forEach [
		getWeaponCargo _weaponHolder,
		[magazinesAmmoCargo _weaponHolder, []],
		getItemCargo _weaponHolder,
		getBackpackCargo _weaponHolder
	];
} forEach _weaponHolders;

_allStuff

 

Now it can accept either trigger object or marker name. Also it counts ammo inside magazines.

Example of output:

[
    [["hgun_P07_F", 1], ["arifle_MX_ACO_pointer_F", 1], ["launch_NLAW_F", 1]],
    [["16Rnd_9x21_Mag", 1, 13], ["30Rnd_65x39_caseless_mag", 1, 27], ["Chemlight_green", 1, 1], ["30Rnd_65x39_caseless_mag", 2, 30]],
    [["FirstAidKit", 1], ["NVGoggles", 1], ["V_PlateCarrier2_rgr", 1], ["U_B_CombatUniform_mcam", 1], ["H_HelmetB_sand", 1]],
    [["B_AssaultPack_rgr_LAT", 1]]
]

You can handle this array using this code:

{_crate addWeaponCargoGlobal _x;} forEach (_stuff select 0);
{_crate addMagazineAmmoCargo _x;} forEach (_stuff select 1);
{_crate addItemCargoGlobal _x;} forEach (_stuff select 2);
{_crate addBackpackCargoGlobal _x;} forEach (_stuff select 3);
  • Like 1

Share this post


Link to post
Share on other sites

Hi @Schatten @bad benson (or others who can help), how are you guys doing? After 5 years, I have another question on this topic:

 

Is there a way to include items, magazines and weapons (in weaponsItems format) that are inside a container (e.g. vest or uniform) which is lying in the "collector" trigger area?

 

 

 

EDIT:

 

Brief summary of what I'm looking for:

 

This code creates an array of all items, magazines and weapons that are on the ground in a trigger area (if I'm not mistaken, more precise, they're in a WeaponHolder in that trigger area). Later in the code, I also put all the stuff in a crate.

 

Spoiler

_trigger = triggerName;  
_stuff = [[],[],[],[]];
  
_objs = (nearestObjects [_trigger, ["WeaponHolder", "WeaponHolderSimulated"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger};   
  
_objs inAreaArray _trigger apply   
{  
 (_stuff select 0) append (weaponsItemsCargo _x);  
 (_stuff select 1) append (magazineCargo _x);
 (_stuff select 2) append (itemCargo _x);  
 (_stuff select 3) append (backpackCargo _x); 
};


//Now put stuff in crate:
stuffInTriggerArea = _stuff;

{crate addWeaponWithAttachmentsCargoGlobal [_x,1]; } forEach (stuffInTriggerArea select 0); 
{crate addMagazineCargoGlobal [_x,1]; } forEach (stuffInTriggerArea select 1); 
{crate addItemCargoGlobal [_x,1]; } forEach (stuffInTriggerArea select 2); 
{crate addBackpackCargoGlobal [_x,1]; } forEach (stuffInTriggerArea select 3);

 

 

Now I'd like to expand this code so it also gets items, mags and weapons that are in containers (e.g. vests, uniforms) which are in this trigger area.

 

Any help would be much appreciated, thanks!

Share this post


Link to post
Share on other sites

Hey Undeceived! How's it going?

 

I haven't done any scripting in years, so bear with me... but if my memory doesn't fool me, everyContainer should do the trick.

 

Maybe something like this:

{
	(_stuff select 0) append (weaponsItemsCargo _x);
	(_stuff select 1) append (magazineCargo _x);
	(_stuff select 2) append (itemCargo _x);
} forEach everyContainer (_stuff select 3);

 

Not sure about syntax and stuff, like I said, it's been a long time. But hopefully it can point you in the right direction.

  • Like 1

Share this post


Link to post
Share on other sites

Thanks, @Clayman, and sorry for the late reply, I was busy with other things in the project. Now I got back to this.
I couldn't make it work with your code, but I managed to use Larrow's code here as basis to make it work somehow.

 

 

Collect items, magazines and weapons (in weaponsItems format) from a trigger area, INCLUDING everything that is inside a container (e.g. vest or uniform) and copy everything to a container:

Spoiler

 


_trigger = TRIGGER1; 
_targetContainer = CRATE1;

//1:
//Create array of all WeaponHolders in _trigger area (which means that you can place stuff in different places in the trigger area):

_weaponHoldersInTriggerArea = (nearestObjects [_trigger, ["WeaponHolder", "WeaponHolderSimulated"], selectMax ((triggerArea _trigger) select [0, 2])]) select {_x inArea _trigger};


//2:
//Let the magic begin:
//Larrow's GET and SET code, slightly adapted:

{
	_nul = [_x] call {

		//GET:
		_container = _this;
		
		TAG_fnc_getContents = {
			params[ "_container" ];
			
			private _cargo = [ 
				weaponsItemsCargo _container call BIS_fnc_consolidateArray,
				( itemCargo _container select{ !( toLowerANSI( _x call BIS_fnc_itemType select 1 ) in [ "backpack", "uniform", "vest" ] ) } ) call BIS_fnc_consolidateArray,
				magazinesAmmoCargo _container call BIS_fnc_consolidateArray
			];
			
			private _containerCargo = everyContainer _container;
			{
				_x params[ "_type", "_object" ];

				_containerCargo set[ _forEachIndex, [ _type, _object call TAG_fnc_getContents] ];
			}forEach _containerCargo;
			
			[ _cargo, _containerCargo ]
		};

		_contentWeaponHolder = _container call TAG_fnc_getContents;


		//SET:
		TAG_fnc_setContents = {
			params[ "_container", "_contents" ];
			_contents params[ "_cargo", "_containers" ];
			
			{
				_x params[ "_cont", "_contents" ];
				
				_every = everyContainer _container;
				if ( _cont call BIS_fnc_itemType select 1 == "backpack" ) then {
					_container addBackpackCargoGlobal[ _cont, 1 ];
				}else{
					_container addItemCargoGlobal[ _cont, 1 ];
				};
				_addedContainer = ( everyContainer _container - _every ) select 0 select 1;
				
				clearItemCargoGlobal _addedContainer;
				clearWeaponCargoGlobal _addedContainer;
				clearBackpackCargoGlobal _addedContainer;
				clearMagazineCargoGlobal _addedContainer;
				
				[ _addedContainer, _contents ] call TAG_fnc_setContents;
			}forEach _containers;
			
			_fnc_addContents = {
				params[ "_index", "_details", "_count" ];
				
				switch ( _index ) do {
				
					case ( 0 ) : {
						_container addWeaponWithAttachmentsCargoGlobal[ _details, _count ];
					};
				
					case ( 1 ) : {
						_container addItemCargoGlobal[ _details, _count ];
					};
					
					case ( 2 ) : {
						_details params[ "_mag", "_ammo" ];
						
						_container addMagazineAmmoCargo[ _mag, _count, _ammo ];
					};
				};
			};
			
			{
				private _index = _forEachIndex;
				{
					_x params[ "_item", "_count" ];

					[ _index, _item, _count ] call _fnc_addContents;
				}forEach _x;
			}forEach _cargo;
			
		};

		[ _targetContainer, _contentWeaponHolder ] call TAG_fnc_setContents;

	}

} forEach _weaponHoldersInTriggerArea;

 

 

 

 

Small note: Uniforms, vests and backpacks will stay filled in the container with their content, like they were laying on the ground.

If you want to "empty" the subcontainers in the target container (so that their content is outside of them in the target container), check Larrow's answer here. Super simple.

  • Like 1

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

×