Jump to content
Sign in to follow this  
dreadedentity

[CODE SNIPPET] The simplest loot system of all time

Recommended Posts

Hello all,

I didn't think I'd be writing another code snippet so soon! I just finished writing an extremely simple loot system for single player so I thought I'd show it off. Below is a video I just recorded, and below that will be the code. I will be privately improving this and re-writing some of the core code to be used in multiplayer missions. Enjoy!

_distanceToSpawn = [_this, 0, 300, [0]] call BIS_fnc_param;
_sleepTimer = [_this, 1, 10, [0]] call BIS_fnc_param;

while {true} do
{
_positionArray = [];
_buildingArray = nearestObjects [player, ["house"], _distanceToSpawn];
{
	_positionArray pushBack ([_x] call BIS_fnc_buildingPositions);
}forEach _buildingArray;

{
	_element = _forEachIndex;
	{
		if (({(typeOf _x) == "Sign_Arrow_Blue_F"} count (nearestObjects[player,["Sign_Arrow_Blue_F"], 1])) == 0) then
		{
			_newMarker = createMarker [format ["%1_%2", _element, _forEachIndex], _x];
			_newMarker setMarkerShape "ICON";
			_newMarker setMarkerType "mil_dot";
			_loot = createVehicle ["Sign_Arrow_Blue_F", _x, [], 0, "CAN_COLLIDE"];
			[_newMarker, _loot, _distanceToSpawn] spawn
			{
				waitUntil {player distance (_this select 1) > (_this select 2)};
				deleteVehicle (_this select 1);
				deleteMarker (_this select 0);
			};
		};
	}forEach _x;
}forEach _positionArray;
sleep _sleepTimer;
};

//Framework by DreadedEntity
//I'll have to ask anybody that uses this code, or any parts of this code, to not delete these comments

And, lastly, as always, I'll always do my best to answer any questions people might have.

Share this post


Link to post
Share on other sites

Looks like it could be a cool system to run :ok:

Share this post


Link to post
Share on other sites

Tried this baby out yesterday and it works like a charm 10/10 would download again.

Share this post


Link to post
Share on other sites

Thanks Dread this is fantastic. I figured a practical example was needed, so I modified your code a bit. I created a weapon objects array from the config and added an optional debug parameter for markers and to diag_log the item + position. Cheers.

Example Mission:

new_logo_shmodel-vfl9sSGTp.png

Notes:

/*

************
** Author **
************	
- Dreadentity (modified by Iceman77)

*****************
** Description **
*****************
- Spawn random weapons inside buildings within a given radius of the player
- Refresh the positions and loot every so often

***********
** Notes **
***********
- Some tinkering may be required to get the weapon objects to sit perfectly flat onto the floor. 
- I used BIS_fnc_selectRandom because it's clean. However there may be better ways to random, for more "even" distribution of the weapon objects.* 
* I say this because back in Arma2 the function was a tad wonky. It may have been optimized by now though.

****************
** Parameters **
****************
_this select 0 - <NUMBER> spawn radius
_this select 1 - <NUMBER> refresh timer
_this select 2 - <BOOL> debug markers + diag_log weapon object and position ( OPTIONAL )

********************
** Usage Examples **
********************
[200, 600, true] spawn DREAD_fnc_lootSpawn;
[100, 300] spawn DREAD_fnc_lootSpawn;

*/

fn_lootSpawn.sqf

private ["_distanceToSpawn","_sleepTimer","_lootArray","_cfgArray","_item","_positionArray","_buildingArray","_element","_newMarker","_loot"];

_distanceToSpawn = [_this, 0, 300, [0]] call BIS_fnc_param;
_sleepTimer = [_this, 1, 10, [0]] call BIS_fnc_param;
_debug = [_this, 2, false, [true]] call BIS_fnc_param;

_lootArray = [];
_cfgArray = "( 
(getNumber (_x >> 'scope') >= 2) && 
{getText (_x >> 'vehicleClass') in ['WeaponsPrimary','weaponsSecondary','weaponsHandGuns'] &&
{_lootArray pushBack ( configName _x );
  true}
}
)" configClasses (configFile >> "CfgVehicles");

while { true } do 
{
   _positionArray = [];
   _buildingArray = nearestObjects [player, ["house"], _distanceToSpawn];
   {
       _positionArray pushBack ([_x] call BIS_fnc_buildingPositions);
   }forEach _buildingArray;

   {
       _element = _forEachIndex;
       {
		_item = _lootArray call BIS_fnc_selectRandom;
           if (({(typeOf _x) in _lootArray} count (nearestObjects[player,[ _item ], 1])) == 0) then 
		{
			if ( _debug ) then 
			{
				_newMarker = createMarker [format ["%1_%2", _element, _forEachIndex], _x];
				_newMarker setMarkerShape "ICON";
				_newMarker setMarkerType "mil_dot";
				diag_log format ["%1 @ %2", _item, _x];
			} else {
				_newMarker = "";
			};

			_loot = createVehicle [_item, _x, [], 0, "CAN_COLLIDE"]; 

               [_newMarker, _loot, _distanceToSpawn] spawn 
			{
                   waitUntil {player distance (_this select 1) > (_this select 2)};
                   deleteVehicle (_this select 1);
                   deleteMarker (_this select 0);
               };
           };
       }forEach _x;
   }forEach _positionArray;
 sleep _sleepTimer;
};

//Framework by DreadedEntity 
//I'll have to ask anybody that uses this code, or any parts of this code, to not delete these comments  

Also, not sure why it's needed to check the nearest objects (1 meter) count. Couldn't it just be:

private ["_distanceToSpawn","_sleepTimer","_lootArray","_cfgArray","_item","_positionArray","_buildingArray","_element","_newMarker","_loot"];

_distanceToSpawn = [_this, 0, 300, [0]] call BIS_fnc_param;
_sleepTimer = [_this, 1, 10, [0]] call BIS_fnc_param;
_debug = [_this, 2, false, [true]] call BIS_fnc_param;

_lootArray = [];
_cfgArray = "( 
(getNumber (_x >> 'scope') >= 2) && 
{getText (_x >> 'vehicleClass') in ['WeaponsPrimary','weaponsSecondary','weaponsHandGuns'] &&
{_lootArray pushBack ( configName _x );
  true}
}
)" configClasses (configFile >> "CfgVehicles");

while { true } do 
{
   _positionArray = [];
   _buildingArray = nearestObjects [player, ["house"], _distanceToSpawn];
   {
       _positionArray pushBack ([_x] call BIS_fnc_buildingPositions);
   }forEach _buildingArray;

   {
       _element = _forEachIndex;
       {
		_item = _lootArray call BIS_fnc_selectRandom;

           if ( _debug ) then 
		{
			_newMarker = createMarker [format ["%1_%2", _element, _forEachIndex], _x];
			_newMarker setMarkerShape "ICON";
			_newMarker setMarkerType "mil_dot";
			diag_log format ["%1 @ %2", _item, _x];
		} else {
			_newMarker = "";
		};

		_loot = createVehicle [_item, _x, [], 0, "CAN_COLLIDE"]; 

		[_newMarker, _loot, _distanceToSpawn] spawn 
		{
			waitUntil {player distance (_this select 1) > (_this select 2)};
			deleteVehicle (_this select 1);
			deleteMarker (_this select 0);
		};  
       }forEach _x;
   }forEach _positionArray;
 sleep _sleepTimer;
};

//Framework by DreadedEntity 
//I'll have to ask anybody that uses this code, or any parts of this code, to not delete these comments  

Edited by Iceman77
Oops, posted wrong edit

Share this post


Link to post
Share on other sites

Yup. Just need an array of objects and you're all set. Fantastic script.

Share this post


Link to post
Share on other sites

Meh! I was excited to see the script and put it to use immediately! Figured I'd post my edit since spawning weapons is a tad more "practical" than spawning the 3d marker objects. No doubt it'd be one of the first questions in the thread "How do I make it spawn actual weapons?". Anyhow, thanks for the pat on the back! :cool:

Edited by Iceman77

Share this post


Link to post
Share on other sites

Thanks for the feedback guys, I'll still be adding multiplayer support for this (script should be run server-side only whenever I release), but this system should be sufficient for single-player missions as is (use iceman's edit for minimal self-tweaking :p )

I wrote this partly because I kept telling everyone I'm working on something, so I probably actually should(!) and because I wanted to see how many spawned threads you could have without impacting performance.

The answer: A LOT (you can set the radius to 1000 and the refresh to ~5 will no obvious impact, despite waitUntil constructs checking their conditions every few frames)

Share this post


Link to post
Share on other sites

hehe. I was actually wondering that myself when I seen your script. That's alot of waitUntil instances (potentially) lol.

Share this post


Link to post
Share on other sites
Also, not sure why it's needed to check the nearest objects (1 meter) count.

I'm glad you asked this Iceman, I added that check to make sure that more loot couldn't be spawned if something was already there (that's why it's only a 1 meter radius check)

Share this post


Link to post
Share on other sites

Hmm. It's iterating through the positions, I don't see how there's even an opportunity for any duplicate spawn positions as no one position will be the same as another. Anyhow it worked fine for me without the check. No double spawning in same positions etc. I'll run some more tests though..

Edited by Iceman77

Share this post


Link to post
Share on other sites

Nice. However, It's so simple that it misses almost anything that a real loot system needs. ;)

I guess it'll be quite useful for anyone who's trying to write his own system and doesn't know where to start.

ps.: I wouldn't run the cleanup loop on every tick, for every object, no matter how small the impact is. Try using at least a 1 second delay.

pps.: How about turning it into a real framework that calls a list of functions that can then be easily rewritten / adjusted based on the current requirements.

Edited by Tajin

Share this post


Link to post
Share on other sites

Yeah it'd be cool to have a loot system that spawned random furniture, tables and the like, and then weapons and stuff ontop. Instead of onto the ground hehe. Idk though, it's just that.. simple. Idk if he wants to reap the fruits of a more "sophisticated" framework. I think for spawning in weapons and such, it's fine and does a good job in any case.

Share this post


Link to post
Share on other sites

Thanks Iceman for pointing me to this thread.

This is quite sophisticated for a beginner like me but I would like to know a little more about it.

Can you tell me how to run debug ?

You don't use an init.sqf as I think you are calling it via the functions.hpp with the postInit=1 ? Correct ?

Share this post


Link to post
Share on other sites

Are you talking about for spawning the vehicles in garages in your other thread? Or are you talking about actually spawning loot (weapons) like the example I've posted above? The two are drastically different... In the example above (for spawning weapons) yes I used postInit = 1 to initialize the script upon mission start. In the LSInit it should be [400, 600, true] spawn DREAD_fnc_lootSpawn; for debugging.

Edited by Iceman77

Share this post


Link to post
Share on other sites

I would suggest to get rid of nearestobjects check. If needed, could be used some array containing eg all already looted houses, to avoid re-looting same house.

But the most I would strongly suggest to get rid of spawning independent thread each loot (especially with per frame waitUntil in each of them...). This makes that system potentially very resource hungry, in extreme cases leaving not much to other scripts, if anything at all. Note, no matter, how many threads you'll spawn, you'll keep same FPS, because sqfs are scheduled and will never get more computational power, than hardcoded limit. Instead, there will be no "room" for any other scripts execution, thus huge exec delays may occur. IMO much better would be to go every cycle through array containing every looted house and check distance from player for each, all in single thread (if you wish to re-loot the house after emptying it, a house should be substracted from that array after emptying). The less parallel threads, the better.

Also, instead of single and same loot class, code could spawn randomly object classes from provided array, so user can set all possible loot objects (eg furniture, but that's much more complex to do reasonable).

Edited by Rydygier

Share this post


Link to post
Share on other sites
Also, instead of single and same loot class, code could spawn randomly object classes from provided array, so user can set all possible loot objects (eg furniture, but that's much more complex to do reasonable).

Pretty much what I'm doing in post #4 with _lootArray

Share this post


Link to post
Share on other sites

@Iceman.

Yes in this example. I figure if I can see how this works the other example will make sense too.

I see this in LSInit. But ... no debug ?

[200, 600, true] spawn DREAD_fnc_lootSpawn;

Share this post


Link to post
Share on other sites
Pretty much what I'm doing in post #4 with _lootArray

Indeed. BTW, nice way to get all weaponry. I have to remember configClasses for future uses. :)

Share this post


Link to post
Share on other sites

_this select 2 aka true. That's enabled debug (for this version... in this thread). But like I said, the two scripts are drastically different. The one I posted in your other thread and the one(s) here. If you're referring to your garageSpawner script, then we'd best carry the conversation over there. That's just how drastically different the code is... given it was initially based off of dreads framework. New parameter indexes etc.

Share this post


Link to post
Share on other sites

Hey guys, just stumbled upon this thread and I really like what I see.

I also started my own loot-system but for multiplayer use and with somekind of persistence but I was very busy the last and couldn't continue with my own script. But maybe I can help you guys with some stuff I already archieved:

First: Persistence would be cool. You check a house, see a magazine you don't need and go on. Later you find a weapon for this mag and want to get it... but it was already despawned. For this you could create a array with all spawned items, or even better: one Array for every house with all of it's (cached) positions and spawned items. If a house is out of range, the list of items gets updated (maybe a player took soemthing). If a house is "activated" the scripts checks the array for objects. If there are no objects yet, objects get spawned by the script above. Of there are objects in the array, the objects are respawned".

To accelerate the checks for the buildings and the corresponding array, I would divide the arrays into larger arrays for every mapgrid. Like somekind of hashing. This can accelerate the looping through all houses in the distance. Also the cleanup could be designed much more ressource friendly :D

Another nice trick: Remove the "while(true)"-loop and but the call in a trigger that activated itself every second. The script is called in non-scheduled environment and is much faster.

If you want I can try to merge your spawning with my persistence system or we can create a repository at github or something for this.

Share this post


Link to post
Share on other sites
Another nice trick: Remove the "while(true)"-loop and but the call in a trigger that activated itself every second. The script is called in non-scheduled environment and is much faster.

This yet again proves that there's always a slight chance to learn something new in these forums, no matter what thread you look at. ;)

Share this post


Link to post
Share on other sites

Shoot. I learn new shit everyday lol. There's just sooo much to learn here ;)

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
Sign in to follow this  

×