Jump to content
Sign in to follow this  
tpw

TPWC AI suppression system

Recommended Posts

been play testing, the new version 3 squads against 1 squads where im the leader, played it four times, three times with the script version 2.02 and one time without. Have my combi with zeus (uses the zeus_core_aiskills), asr_ai and ace enabled. The "ai looking in the sky" syndrome, seems to be alot less, than with the 2.00 version. Another thing though is, 20 - 30 minuts in the firefight, the fps drops from 45 - 55 to 14-16. did happen all thre times with the tpw script enabled, but not the fourth time without the script.

Hope this can be for some help, and sorry my bad english.

Robert (sixt) :-)

Edit: It was with the default settings in the userconfig

Edited by Sixt

Share this post


Link to post
Share on other sites

Some Dedicated Server findings:

Changed parameters:

tpwcas_st = 5; (//SHOT THRESHOLD. MORE SHOTS THAN THIS WILL CAUSE UNIT TO DROP/CRAWL)

bdetect_debug_enable = true;

bdetect_debug_levels = [0,2,5,6,7,8,9];

bdetect_bullet_max_distance = 800;

bdetect_bullet_max_lifespan = 1.1;

- Bullet detection framework does work - though in the short test 'bullet discovery' log shows 80 Fired (MG) bullets of which 13 bullets reported 'close to targets'. This number seems a bit low too me, but I may also be a bad shooter. Needs further investigation.

- tpwc_ai_suppress202 part doesn't deem to work as-is.

Debug balls are not shown at al, hence hard to tell what's working.

Some additional logging showed:

1 entry (only) with status tpwcas_supstate=3 => only 1 full suppresion by long MG burst!

5 entries with status tpwcas_supstate=2

7 entries with status tpwcas_supstate=1

This is not to be expected while the enemies I shot at were quite close to each other.

I did see AI reacting to the shooting, but they did not drop/crouch as being seen with SP.

I shall take closer look with additional debugging and some more parameter changes to see what's realy going on and if things can be improved for Dedicated.

Share this post


Link to post
Share on other sites

I replaced the ball objects in the tpwc script and found that the civ count no longer increases. I tried other signs but anything with an EP suffix in the class name seems to behave exactly as do the balls by increasing the civ count. This was not an exhaustive test of such classes, however. What I found were that game objects such as ammo crates etc do not cause the same problem. I now have soldiers running around with pub signs or snowmen on their heads when they're suppressed, but civ count remains stable.I'm sure you can find a more appropriate object or elegant way to resolve this.

Share this post


Link to post
Share on other sites
Bullet detection framework does work - though in the short test 'bullet discovery' log shows 80 Fired (MG) bullets of which 13 bullets reported 'close to targets'. This number seems a bit low too me, but I may also be a bad shooter. Needs further investigation.

Nice. Did you fired some long bursts? If so 13 vs 80 wound be fine, because on long bursts machineguns tend to kick up and any bullets going higher than 6 meters on ground and up are discarded.

Le me know if you discover anything more.

Share this post


Link to post
Share on other sites
- So possibly add an extra suppression affect for nearby casualties.

this is what they do in Red Orchestra and it's quite effective

Share this post


Link to post
Share on other sites

Good morning everyone.

I'm very concerned that the debug balls are causing performance degradation over time. OK so the first thing I did was get up and run a couple of preliminary tests.

I added this debug code into the main loop, which counts the number of debug balls each second

_ballcount =  count nearestObjects [player, ["Sign_sphere25cm_EP1"], 500]; hintsilent format ["%1",_ballcount];

I put 30 units on a map and had them try to plug each other.

1: Does every unit get debug balls? Apparently not. With 30 units the number should be 90, but it's usually 60-80. And it's usually the blufor units who don't get the balls.

2: Do the number of balls increase over time? No. The ball count starts at 60-80, and never goes higher.

3: Are balls removed from dead units? Yes, the ball counter drops throughout the mission.

4: Are units without debug balls still being suppressed? Yes.

So in my experience the debug code might be flawed, but in the opposite direction than has been reported. I don't think it is causing the performance drop over time.

In other news: Thanks Ollem, I fixed that typo which was causing units to not be filtered out of suppression. Still doesn't fix the ball code.

I am serious, if someone knows how to display text onto the screen using worldtoscreen, I want to know about it. I'd far prefer to just have a 0, 1, 2 or 3 hovering over the head of each unit when in debug mode.

On the TPWCAS side of the code, there is nothing "accumulating" on each unit which could be dragging performance down. They get a few variables set, some debug balls, and that's it. Fabrizio_T, could there be anything on the bdetect side which would cause performance loss over time?

Regarding casualty based suppression, it would be reasonably simple to implement, for each unit counting the number of lifestate != "ALIVE" friendlies nearby.

Edited by tpw

Share this post


Link to post
Share on other sites

Good morning ... and good night (it's 00:54 here) ;)

There's nothing in bDetect that should cause degradation as far as i know.

So far i've let it run alone 1 hour without noticing appreciable degradation.

I'll do some more extensive test tomorrow.

About the problem itself, i did put a trigger in a mission as per tadanobu example and i confirm i have seen "CIVILIAN" count gone well over 600 with 96 units on the field.

It kept growing and began having lag when i stopped the mission. I also still saw red balls blinking on corpses.

That was with debug active.

EDIT: tried again and can't reproduce anymore. Weird.

See you tomorrow.

Edited by fabrizio_T

Share this post


Link to post
Share on other sites

I have just ran an 140 unit battle 3 times:first with debug disabled which ran over 1 hour without civ count increasing and no noticeable slow-down of fps and no momentary freezes;second with debug enabled using other objects as markers(snowmen) which I allowed to run for around 20 minutes again with no problems;third time I enabled debug with the original spheres and the civ count increased to 1000+ within 2.5 minutes. By 5 minutes I was experiencing momentary freezes at which time I suspended the game. This using 2.02 and 0.64. I've done this a number of times today with the consistent results. If I have time tomorrow I'll try running game on earlier iterations of the script to see if I can pinpoint where the problem first arises.

Share this post


Link to post
Share on other sites
I have just ran an 140 unit battle 3 times:first with debug disabled which ran over 1 hour without civ count increasing and no noticeable slow-down of fps and no momentary freezes;second with debug enabled using other objects as markers(snowmen) which I allowed to run for around 20 minutes again with no problems;third time I enabled debug with the original spheres and the civ count increased to 1000+ within 2.5 minutes. By 5 minutes I was experiencing momentary freezes at which time I suspended the game. This using 2.02 and 0.64. I've done this a number of times today with the consistent results. If I have time tomorrow I'll try running game on earlier iterations of the script to see if I can pinpoint where the problem first arises.

Thanks mate, I really appreciate you looking into this in such detail. I'm mystified as to what's going on, but will have a good look when I get home from work. At least it's just the debugging that's causing the problem, not underlying issues with the bullet detection and suppression engine. And I will pick a different debug object in the mean time!

Share this post


Link to post
Share on other sites

I believe we could still use the debug balls, but in stead of creating by default, attaching and then hiding/change color we should just create and delete them when applicable.

Yes, there is overhead in creating and deleting, but it's a debug feature anyway..

For debug purposes I've added a simple 'bullet radar' feature to 'tpwcas_fnc_localbullets' which creates dot markers on the map for every detected bullet, and deletes them after 10 seconds.

(NOTE: this only works when 'tpwcas_fnc_localbullets' is spawned in stead of called, while 'sleep' doesn't work in 'call')

I will try to see if same approach could perhaps be used to create colored debug balls in game


//HOOK INTO BDETECT TO ADD TO EACH UNIT'S LOCAL BULLET COUNT AND SUPPRESSION STATUS
tpwcas_fnc_localbullets = 
{
private [ "_bullet", "_time", "_unit", "_distance", "_data", "_shooter","_shooterdistance","_shooterside","_bc","_ebc"];

_unit = _this select 0; //suppressed unit

_distance = _this select 2; //distance of bullet from unit
_data = _this select 3; //
_shooter = _data select 0; //shooter
_shooterdistance = _shooter distance _unit; //distance of shooter from unit
_shooterside = side _shooter; //side of shooter
_bc = _unit getvariable ["tpwcas_bc",0]; //total bullet count
_ebc = _unit getvariable ["tpwcas_ebc",0]; //enemy bullet count

_bullet = _this select 1; // ADDED FOR BULLET RADAR
_time = _data select 2;  // ADDED FOR BULLET RADAR

if (_shooterdistance >= tpwcas_ir) then 
{
_bc = _bc + 1;
_unit setvariable ["tpwcas_skillregain", diag_ticktime + 2 + random 3];       
_unit setvariable ["tpwcas_stanceregain", diag_ticktime + 5 + random 5];  
_unit setvariable ["tpwcas_bc", _bc];
_unit setvariable ["tpwcas_supstate",1];

// Start Bullet radar
if (tpwcas_debug == 1 || !( isnull _bullet )) then     
	{
	_bulletMarker = createMarker [str(_time), _bullet];
		_bulletMarker setMarkerShape "ICON";
		_bulletMarker setMarkerType "dot";
		if !(_shooterside getFriend ( side _unit) < 0.6 ) then 
		    {
		    _bulletMarker setMarkerColor "ColorRed";
		    }
		else
		    {
		    _bulletMarker setMarkerColor "ColorBlue";
		    };
	};
// End Bullet radar

if (_shooterside getFriend ( side _unit) < 0.6 ) then 
	{
	_ebc = _ebc + 1;
	_unit setvariable ["tpwcas_ebc", _ebc];
	_unit reveal [_shooter, tpwcas_reveal];
	};

if (_ebc > 0) then 
	{
	_unit setvariable ["tpwcas_supstate",2];
	};

if (_ebc > tpwcas_st) then 
	{
	_unit setvariable ["tpwcas_supstate",3];
	}; 

_msg = format ["Unit Status: [%1] - %2 - %3", _unit, _unit getvariable ["tpwcas_supstate",-1], _unit getvariable ["tpwcas_bc", -1]];
[ _msg, 9 ] call bdetect_fnc_debug;	

// Start Delete Bullet radar marker
if (tpwcas_debug == 1) then     
	{	
	sleep 10;
	deleteMarkerLocal str(_time);
	};
};
// End Delete Bullet radar marker

.......................

Share this post


Link to post
Share on other sites
I believe we could still use the debug balls, but in stead of creating by default, attaching and then hiding/change color we should just create and delete them when applicable.

Yes, there is overhead in creating and deleting, but it's a debug feature anyway..

For debug purposes I've added a simple 'bullet radar' feature to 'tpwcas_fnc_localbullets' which creates dot markers on the map for every detected bullet, and deletes them after 10 seconds.

(NOTE: this only works when 'tpwcas_fnc_localbullets' is spawned in stead of called, while 'sleep' doesn't work in 'call')

I will try to see if same approach could perhaps be used to create colored debug balls in game


//HOOK INTO BDETECT TO ADD TO EACH UNIT'S LOCAL BULLET COUNT AND SUPPRESSION STATUS
tpwcas_fnc_localbullets = 
{
private [ "_bullet", "_time", "_unit", "_distance", "_data", "_shooter","_shooterdistance","_shooterside","_bc","_ebc"];

_unit = _this select 0; //suppressed unit

_distance = _this select 2; //distance of bullet from unit
_data = _this select 3; //
_shooter = _data select 0; //shooter
_shooterdistance = _shooter distance _unit; //distance of shooter from unit
_shooterside = side _shooter; //side of shooter
_bc = _unit getvariable ["tpwcas_bc",0]; //total bullet count
_ebc = _unit getvariable ["tpwcas_ebc",0]; //enemy bullet count

_bullet = _this select 1; // ADDED FOR BULLET RADAR
_time = _data select 2;  // ADDED FOR BULLET RADAR

if (_shooterdistance >= tpwcas_ir) then 
{
_bc = _bc + 1;
_unit setvariable ["tpwcas_skillregain", diag_ticktime + 2 + random 3];       
_unit setvariable ["tpwcas_stanceregain", diag_ticktime + 5 + random 5];  
_unit setvariable ["tpwcas_bc", _bc];
_unit setvariable ["tpwcas_supstate",1];

// Start Bullet radar
if (tpwcas_debug == 1 || !( isnull _bullet )) then     
	{
	_bulletMarker = createMarker [str(_time), _bullet];
		_bulletMarker setMarkerShape "ICON";
		_bulletMarker setMarkerType "dot";
		if !(_shooterside getFriend ( side _unit) < 0.6 ) then 
		    {
		    _bulletMarker setMarkerColor "ColorRed";
		    }
		else
		    {
		    _bulletMarker setMarkerColor "ColorBlue";
		    };
	};
// End Bullet radar

if (_shooterside getFriend ( side _unit) < 0.6 ) then 
	{
	_ebc = _ebc + 1;
	_unit setvariable ["tpwcas_ebc", _ebc];
	_unit reveal [_shooter, tpwcas_reveal];
	};

if (_ebc > 0) then 
	{
	_unit setvariable ["tpwcas_supstate",2];
	};

if (_ebc > tpwcas_st) then 
	{
	_unit setvariable ["tpwcas_supstate",3];
	}; 

_msg = format ["Unit Status: [%1] - %2 - %3", _unit, _unit getvariable ["tpwcas_supstate",-1], _unit getvariable ["tpwcas_bc", -1]];
[ _msg, 9 ] call bdetect_fnc_debug;	

// Start Delete Bullet radar marker
if (tpwcas_debug == 1) then     
	{	
	sleep 10;
	deleteMarkerLocal str(_time);
	};
};
// End Delete Bullet radar marker

.......................

Nice one. I've been looking at this page http://www.combinedarms.de/wiki/index.php?page=Article&articleID=99&languageID=1 to see if I can strip it down to display on screen text. Not having much luck yet.

A better place for debug ball creation might be in tpwcas_fnc_ballhandler, where it doesn't have to happen in a time critical part. I'll try that out if you try your stuff.

EDIT: So I tried it and creating balls on the fly looks shit. The ball is created but then you can see it whiz up to its position above the unit. Even if it's created, then hidden, then attached to the unit, then unhidden.

I honestly can't see why the approach we were taking wasn't OK, and to be honest it's really starting to give me the shits.

Edited by tpw

Share this post


Link to post
Share on other sites

Ok I've done some quick tests of earlier script versions and what I've noticed is that from the very beginning the balls created a basic civ population every time. Hadn't noticed at that time because I always ran battles with civ pops anyway and there wasn't any extra overhead being generated from them.

Up until 1.04, that civ population reaches a maximum figure then stops. I've also noticed that it falls as casualties are taken.

Unfortunately on version 2, when you start implementing bdetect, the problem kicks in. The civ pop climbs to an initial figure, remains static only until the first casualty is taken and then starts to climb out of control until it reaches thousands. Hope this helps.

Share this post


Link to post
Share on other sites

I wonder if it was due to an earlier bug where I wasn't filtering out dead and injured units properly. I vaguely recall problems in the past with this.

Can you try running TPWCAS 2.03b script which correctly filters out dead/injured units from receiving debug balls:

/*   
TPWC AI SUPPRESSION   

Authors: TPW && -Coulum- && fabrizio_T  
Additional code: Ollem  
Version: 2.03beta 
Last modified: 20120704
Requires: CBA
Disclaimer: Feel free to use and modify this code, on the proviso that you post back changes and improvements so that everyone can benefit from them, and acknowledge the original authors in any derivative works.
*/   

///////////
//VARIABLES
///////////

//DELAY BEFORE SUPPRESSION FUNCTIONS START. ALLOWS TIME FOR OTHER AI MODS TO SET UNIT SKILLS
tpwcas_sleep = 2;

//DEBUGGING. WILL DISPLAY RED BALLS OVER ANY SUPPRESSED UNITS. SET TO 1 FOR DEBUGGING
tpwcas_debug = 1;

//BULLET IGNORE RADIUS (M). BULLETS FROM A SHOOTER CLOSER THAN THIS WILL NOT SUPPRESS. 
tpwcas_ir = 25;

//SHOT THRESHOLD. MORE SHOTS THAN THIS WILL CAUSE UNIT TO DROP/CRAWL
tpwcas_st = 10;

//STARTUP HINT 0 = NO HINT, 1 = HINT
tpwcas_hint = 1;

//PLAYER SUPPRESSION SHAKE. 0 = NO SUPPRESSION, 1 = SUPPRESSION    
tpwcas_playershake = 1;

//PLAYER SUPPRESSION VISUALS. 0 = NO SUPPRESSION, 1 = SUPPRESSION    
tpwcas_playervis = 1;

//PISTOL AND SMG AMMO TO IGNORE. ADD CUSTOM AMMO (EG SUPPRESSED) OR CHANGE TO TASTE.  
tpwcas_mags =  
[  
"30rnd_9x19_MP5",     
"30rnd_9x19_MP5SD",     
"15Rnd_9x19_M9",     
"15Rnd_9x19_M9SD",     
"7Rnd_45ACP_1911",     
"7Rnd_45ACP_1911",    
"8Rnd_9x18_Makarov",    
"8Rnd_9x18_MakarovSD",    
"64Rnd_9x19_Bizon",    
"64Rnd_9x19_SD_Bizon",    
"13Rnd_9mm_SLP",    
"17Rnd_9x19_glock17",    
"6Rnd_45ACP",    
"30Rnd_9x19_UZI",    
"30Rnd_9x19_UZI_SD"  
]; 

//AI SKILL SUPPRESSION. 0 = SKILLS WILL NOT BE CHANGED, ONLY STANCE. 1 = SKILLS AND STANCE CHANGED UNDER SUPPRESSION
tpwcas_skillsup = 0;

//MINIMUM SKILL VALUE, NONE OF A UNIT'S SKILLS WILL DROP BELOW THIS UNDER SUPPRESSION
tpwcas_minskill = 0.05;

//REVEAL VALUE WHEN SUPPRESSED. 0 = SUPPRESSED UNIT KNOWS NOTHING ABOUT SHOOTER. 4 = UNIT KNOWS THE SHOOTER'S SIDE, POSTION, SHOE SIZE ETC
tpwcas_reveal = 2;

//////////  
//SET UP   
//////////  

//DECLARE PRIVATE VARIABLES   
private ["_stanceregain","_skillregain","_unit","_bc","_shots","_originalaccuracy","_originalshake","_originalcourage","_general","_ball","_ball1","_ball2","_ball3","_skillset","_asr","_tint","_blur","_x","_ppmod","_ppmodifier"];

//INITIAL FILTERED UNIT ARRAY = ALL UNITS
tpwcas_filtered_units = allunits;

//WAIT
sleep tpwcas_sleep;   

//START HINT      
if (tpwcas_hint == 1) then {   
0 = [] spawn {  
hintsilent "TPWC AI Suppress 2.02beta Active";   
sleep 3;   
hintsilent ""};   
};


////////////
//FUNCTIONS
////////////

tpwcas_fnc_main_loop =
{  
while {true} do   
   {  
_ballcount =  count nearestObjects [player, ["Sign_sphere25cm_EP1"], 500];
hint format ["%1",_ballcount];
tpwcas_supvisflag = 0;  
       {  
	_unit  = _x; 
	_stanceregain = _unit getvariable ["tpwcas_stanceregain", -1]; 

	//SET INITIAL PARAMETERS FOR EACH UNIT  
	if (_stanceregain == -1) then       
		{   
		_unit setvariable ["asr_ai_sys_aiskill_configured", false];    
		_unit setvariable ["tpwcas_skillset", false];
		_unit setvariable ["tpwcas_originalaccuracy", _unit skill "aimingaccuracy"];     
		_unit setvariable ["tpwcas_originalshake",  _unit skill "aimingshake"];    
		_unit setvariable ["tpwcas_originalcourage", _unit skill "courage"];     
		_unit setvariable ["tpwcas_general", _unit skill "general"];    
		_unit setvariable ["tpwcas_stanceregain", diag_ticktime];     
		_unit setvariable ["tpwcas_skillregain", diag_ticktime];   

		//SET UP COLOURED DEBUGGING BALLS
		if (tpwcas_debug == 1) then     
			{ 
			_ball1 = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;  
			_ball1 setObjectTexture [0,"#(argb,8,8,3)color(0.2,0.9,0.2,0.5,ca)"];  
			_ball1 attachTo [_unit,[0,0,2]];   
			_unit setvariable ["tpwcas_sup1ball",_ball1]; 
			_ball1 hideobject true;  

			_ball2 = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;  
			_ball2 setObjectTexture [0,"#(argb,8,8,3)color(0.6,0.9,0.0,0.7,ca)"]; //yellow  
			_ball2 attachTo [_unit,[0,0,2]];   
			_unit setvariable ["tpwcas_sup2ball",_ball2]; 
			_ball2 hideobject true;  

			_ball3 = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;  
			_ball3 setObjectTexture [0,"#(argb,8,8,3)color(0.8,0.5,0.5,0.5,ca)"]; //red  
			_ball3 attachTo [_unit,[0,0,2]];   
			_unit setvariable ["tpwcas_sup3ball",_ball3]; 
			_ball3 hideobject true;                       
			};  
		};   

	//IF UNIT SKILLS ARE UNSUPPRESSED  
	if (diag_ticktime >= _skillregain && tpwcas_skillsup == 1) then      
		{  
		_originalaccuracy = _unit getvariable "tpwcas_originalaccuracy";        
		_originalshake = _unit getvariable "tpwcas_originalshake";                 
		_originalcourage = _unit getvariable "tpwcas_originalcourage";     
		_general = _unit getvariable "tpwcas_general";           
		//GRADUAL RETURN OF ACCURACY
		if((_unit skill "aimingaccuracy") < _originalaccuracy) then      
			{    
			_unit setskill ["aimingaccuracy",(_unit skill "aimingaccuracy")+((_originalaccuracy-(_unit skill "aimingaccuracy"))*.325)];       
			};    
		//GRADUAL REDUCTION OF SHAKE	
		if((_unit skill "aimingshake") < _originalshake) then      
			{      
			_unit setskill ["aimingshake",(_unit skill "aimingshake")+((_originalshake-(_unit skill "aimingshake"))*.325)];      
			}; 
		//BACK TO NORMAL	
		if 	(((_unit skill "aimingshake") > (_originalshake - 0.05)) &&	((_unit skill "aimingaccuracy") > (_originalaccuracy - 0.05))) then 
			{
			_unit setskill ["aimingshake",_originalshake];
			_unit setskill ["aimingaccuracy",_originalaccuracy];
			_unit setvariable ["tpwcas_skillregain", diag_ticktime + 10];
			};	
		}; 

	//IF UNIT STANCE IS UNSUPPRESSED  
	if ( diag_ticktime >= _stanceregain) then       
		{
		_unit setunitpos "auto";					
		_unit setvariable ["tpwcas_supstate",0]; 
		_unit setvariable ["tpwcas_bc",0];	
		_unit setvariable ["tpwcas_ebc",0];

		//GRADUAL RETURN OF COURAGE
		_originalcourage = _unit getvariable "tpwcas_originalcourage";     
		_general = _unit getvariable "tpwcas_general"; 
		if((_unit skill "courage") < (_originalcourage - 0.05)) then      
			{      
			_unit setskill ["courage",(_unit skill "courage")+(_general)*(0.1)];       
			}     
			else
			{
			//BACK TO NORMAL
			_unit setskill ["courage",_originalcourage];
			_unit setvariable ["tpwcas_stanceregain", diag_ticktime + 10];
			};	
		};  

	//UNIT CHANGES FOR DIFFERENT SUPPRESSION
	switch ( _unit getvariable "tpwcas_supstate" ) do 
		{ 
		case 1: //IF ANY BULLETS NEAR UNIT 
			{ 
			//CROUCH IF NOT PRONE
			if ((_unit call CBA_fnc_getunitanim) select 0 != "prone") then 
				{
				_unit setunitpos "middle";	
				};
			}; 

		case 2: //IF ENEMY BULLETS NEAR UNIT 
			{
			//CROUCH IF NOT PRONE
			if ((_unit call CBA_fnc_getunitanim) select 0 != "prone") then 
				{
				_unit setunitpos "middle";	
				};
			//SKILL MODIFICATION
			if (tpwcas_skillsup == 1) then
				{
				_originalaccuracy = _unit getvariable "tpwcas_originalaccuracy";       
				_originalshake = _unit getvariable "tpwcas_originalshake";     
				_originalcourage = _unit getvariable "tpwcas_originalcourage";      
				_general = _unit getvariable "tpwcas_general";    
				_shots = _unit getvariable "tpwcas_supshots";    
				_newaccuracy = _originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.001);
				_newshake = _originalshake*_originalcourage*_general-(_shots*(1-_general)*.001);
				_newcourage = (_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.02);
				if (_newaccuracy < tpwcas_minskill) then 
					{
					_newaccuracy = tpwcas_minskill;
					};
				if (_newshake < tpwcas_minskill) then 
					{
					_newshake = tpwcas_minskill;
					};
				if (_newcourage < tpwcas_minskill) then 
					{
					_newcourage = tpwcas_minskill;
					};	
				_unit setskill ["aimingaccuracy",_newaccuracy];        
				_unit setskill ["aimingshake",_newshake];       
				_unit setskill ["courage",_newcourage]; 
				};	
			//PLAYER CAMERA SHAKE 
			if ((isplayer unit) && (tpwcas_playershake == 1)) then   
				{   
				addCamShake [1.5 - (skill player),(random 4)-((_unit getvariable "tpwcas_general")+(_unit getvariable "tpwcas_originalcourage")) , 2.5]  
				};	
			}; 

		case 3: //IF UNIT IS SUPPRESSED WITH MORE THAN 5 ENEMY BULLETS  
			{
			//GO PRONE
			_unit setunitpos "down";	
			_unit forcespeed -1; 
			//SKILL MODIFICATION
			if (tpwcas_skillsup == 1) then
				{
				_originalaccuracy = _unit getvariable "tpwcas_originalaccuracy";       
				_originalshake = _unit getvariable "tpwcas_originalshake";     
				_originalcourage = _unit getvariable "tpwcas_originalcourage";      
				_general = _unit getvariable "tpwcas_general";    
				_shots = _unit getvariable "tpwcas_supshots";
				_newaccuracy = _originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.001);
				_newshake = _originalshake*_originalcourage*_general-(_shots*(1-_general)*.001);
				_newcourage = (_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.02);
				if (_newaccuracy < tpwcas_minskill) then 
					{
					_newaccuracy = tpwcas_minskill;
					};
				if (_newshake < tpwcas_minskill) then 
					{
					_newshake = tpwcas_minskill;
					};
				if (_newcourage < tpwcas_minskill) then 
					{
					_newcourage = tpwcas_minskill;
					};	
				_unit setskill ["aimingaccuracy",_newaccuracy];        
				_unit setskill ["aimingshake",_newshake];       
				_unit setskill ["courage",_newcourage];  
				};
			//PLAYER CAMERA SHAKE AND FX
			if (isplayer _unit) then    
				{  
				if (tpwcas_playershake == 1) then 
					{
					addCamShake [2 - (skill _unit),(random 6)-((_unit getvariable "tpwcas_general")+(_unit getvariable "tpwcas_originalcourage")) , 5]  
					};
				if (tpwcas_playervis == 1) then 
					{	
					[] spawn tpwcas_fnc_visuals;  
					};
				};  
			};
		}; 
	} foreach tpwcas_filtered_units;
sleep 1.5; 	
};  
};  

//UNIT FILTER
tpwcas_fnc_filter = 
{
while {true} do
{
tpwcas_filtered_units = [];
	{
	if 
	(
	(vehicle _x == _x) && 
	(lifestate _x == "ALIVE") && 
	(side _x != civilian) && 
	(_x distance player < 1000)
	) then
		{
		tpwcas_filtered_units set [count tpwcas_filtered_units,_x]
		};
	} foreach allunits;
sleep 10;
};
};


//DEBUG BALL HANDLING LOOP  
tpwcas_fnc_ballhandler =
{
while {true} do 
{
	{
	_unit = _x;
	//DISPLAY OR HIDE DEBUG BALLS AS APPROPRIATE	
	_ball1 = _unit getvariable "tpwcas_sup1ball";   
	_ball2 = _unit getvariable "tpwcas_sup2ball";   
	_ball3 = _unit getvariable "tpwcas_sup3ball";  

	switch ( _unit getvariable "tpwcas_supstate" ) do 
		{ 
		case 1:  
			{ 
			tpwcas_ballstatus = [_ball1,false,_ball2,true,_ball3,true]; 
			}; 
		case 2: 
			{ 
			tpwcas_ballstatus = [_ball1,true,_ball2,false,_ball3,true]; 
			}; 
		case 3: 
			{ 
			tpwcas_ballstatus = [_ball1,true,_ball2,true,_ball3,false]; 
			}; 
		default 
			{ 
			tpwcas_ballstatus = [_ball1,true,_ball2,true,_ball3,true]; 
			}; 
		}; 

	if (lifestate _unit != "ALIVE") then 
		{
		tpwcas_ballstatus = [_ball1,true,_ball2,true,_ball3,true]; 
		};	

	tpwcas_ballstatus call 
	{
	(_this select 0) hideobject (_this select 1);
	(_this select 2) hideobject (_this select 3);
	(_this select 4) hideobject (_this select 5);
	};

	if ( isDedicated ) then 
		{ 
		[-1, {(_this select 0) hideobject (_this select 1);(_this select 2) hideobject (_this select 3);(_this select 4) hideobject (_this select 5);}, tpwcas_ballstatus] call CBA_fnc_globalExecute; 
		}; 
	} foreach allunits;

	//REMOVE DEBUG BALLS FROM DEAD AI  
	{  
	_unit = _x;  
	_ball1 = _unit getvariable "tpwcas_sup1ball";  
	if ( !(_ball1 == objNull) ) then   
		{    
		detach _ball1;   
		deleteVehicle _ball1;  
		_unit setvariable ["tpwcas_sup1ball",nil];          
		};  
		_ball2 = _unit getvariable "tpwcas_sup2ball";  
	if ( !(_ball2 == objNull) ) then   
		{    
		detach _ball2;   
		deleteVehicle _ball2;  
		_unit setvariable ["tpwcas_sup2ball",nil];          
		};  
		_ball3 = _unit getvariable "tpwcas_sup3ball";  
	if ( !(_ball3 == objNull) ) then   
		{    
		detach _ball3;   
		deleteVehicle _ball3;  
		_unit setvariable ["tpwcas_sup3ball",nil];          
		};  
	} foreach allDead; 
sleep 2;	
};		
};  


//BLUR PLAYER VISION WHEN SUPPRESSED  
tpwcas_fnc_visuals =   
{ 
if (tpwcas_supvisflag == 0) then   
   { 
tpwcas_supvisflag = 1;  	
_tint = ppEffectCreate ["Colorcorrections", 1552];  
   _tint ppEffectEnable true;  
   _blur = ppEffectCreate ["RadialBlur", 1551];  
   _blur ppEffectEnable true;  
_ppmodifier = 0.001;

	{
	_ppmod = _ppmodifier * _x;
	_blur ppEffectAdjust [_ppmod,_ppmod,0.1,0.1]; 
	_blur ppEffectCommit 0; 
	_tint ppEffectAdjust[1, 1, (-5 * _ppmod), [0,0,0,0], [0,0,0,1], [0,0,0,1]];
	_tint ppEffectCommit 0; 
	sleep 0.1; 	
	} foreach [1,2,3,4,5];

sleep 5;  

	{
	_ppmod = _ppmodifier * _x;
	_blur ppEffectAdjust [_ppmod,_ppmod,0.1,0.1]; 
	_blur ppEffectCommit 0; 
	_tint ppEffectAdjust[1, 1, (-5 * _ppmod), [0,0,0,0], [0,0,0,1], [0,0,0,1]];
	_tint ppEffectCommit 0; 
	sleep 0.1; 	
	} foreach [5,4,3,2,1]; 

ppEffectDestroy _blur;  
   ppEffectDestroy _tint;  
   tpwcas_supvisflag = 0;  
   };  
};  


//IF ASR AI HAS SET UNIT SKILLS, MAKE THESE THE "ORIGINALS" FOR EACH UNIT    
tpwcas_fnc_asrskills =   
{   
sleep 3;   
while {true} do    
   {   
       {   
       _unit = _x;   
       _asr = _unit getVariable "asr_ai_sys_aiskill_configured";   
       _skillset = _unit getVariable "tpwcas_skillset";   
       if ((_asr) && !(_skillset))then    
           {   
           _unit setvariable ["tpwcas_originalaccuracy", _unit skill "aimingaccuracy"];     
           _unit setvariable ["tpwcas_originalshake", _unit skill "aimingshake"];    
           _unit setvariable ["tpwcas_originalcourage", _unit skill "courage"];     
           _unit setvariable ["tpwcas_general",_unit skill "general"];    
           _unit setvariable ["tpwcas_skillset", true];   
           };   
       } foreach allunits;   
   sleep 30;   
   };   
};    


//HOOK INTO BDETECT TO ADD TO EACH UNIT'S LOCAL BULLET COUNT AND SUPPRESSION STATUS
tpwcas_fnc_localbullets = 
{
private [ "_unit", "_distance", "_data", "_shooter","_shooterdistance","_shooterside","_bc","_ebc"];

_unit = _this select 0; //suppressed unit
_distance = _this select 2; //distance of bullet from unit
_data = _this select 3; //
_shooter = _data select 0; //shooter
_shooterdistance = _shooter distance _unit; //distance of shooter from unit
_shooterside = side _shooter; //side of shooter
_bc = _unit getvariable "tpwcas_bc"; //total bullet count
_ebc = _unit getvariable "tpwcas_ebc"; //enemy bullet count

if (_shooterdistance >= tpwcas_ir) then 
{
_bc = _bc + 1;
_unit setvariable ["tpwcas_skillregain", diag_ticktime + 2 + random 3];       
_unit setvariable ["tpwcas_stanceregain", diag_ticktime + 5 + random 5];  
_unit setvariable ["tpwcas_bc", _bc];
_unit setvariable ["tpwcas_supstate",1];

if (_shooterside getFriend ( side _unit) < 0.6 ) then 
	{
	_ebc = _ebc + 1;
	_unit setvariable ["tpwcas_ebc", _ebc];
	_unit reveal [_shooter, tpwcas_reveal];
	};

if (_ebc > 0) then 
	{
	_unit setvariable ["tpwcas_supstate",2];
	};

if (_ebc > tpwcas_st) then 
	{
	_unit setvariable ["tpwcas_supstate",3];
	};        
};
}; 


////////////
//RUN IT ALL
////////////

//CALL BDETECT
call compile preprocessFileLineNumbers "bdetect.sqf";
bdetect_skip_mags = tpwcas_mags;
bdetect_debug_enable = false;
bdetect_callback = "tpwcas_fnc_localbullets";
call bdetect_fnc_init;
waitUntil { bdetect_init_done};


//RUN THE REST
[] spawn tpwcas_fnc_filter;
[] spawn tpwcas_fnc_main_loop;
if (tpwcas_debug == 1) then     
{
[] spawn tpwcas_fnc_ballhandler;
};

with bdetect 0.64

/* 
Basic bullet detection framework 
Version: 0.64 
Date: 03/07/2012 
Author: Fabrizio_T  
Additional code: TPW  
File Name: bdetect.sqf 

CHANGELOG: 

------------- 
Version: 0.64 
------------- 

* Incorporated suggestions from tpw: http://forums.bistudio.com/showthread.php?136304-TPWC-AI-suppression-system&p=2182742&viewfull=1#post2182742 
* Minor optimization / fixes 
* Introduced variable "bdetect_callback_mode": (String, Default "spawn") Allowed values: "call" or "spawn". It controls synchronous vs.asynchronous bdetect_callback() execution. 

------------- 
Version: 0.63 
------------- 

* Temporarily reverted from "diag_tickTime" to "time" for issues to be looked in 

------------- 
Version: 0.62 
------------- 

* Fixed bullet position handling 

------------- 
Version: 0.61 
------------- 

* Fixed problem with units eventually detecting own shots 

------------ 
Version: 0.6 
------------ 

* Incorporated v0.51 changes/fixes by tpw. 
* Function bdetect_fnc_callback() slightly changed. See bottom of this file for example. 
* Function bdetect_fnc_init() waits for 5 seconds to pass by after mission start, before activating the framework. 
* Introduced function bdetect_fnc_eh_fired_add(): suitable to force -immediate- assignment of a fired EH to any unit. Syntax: [unit] call bdetect_fnc_eh_fired_add(); 
* Fixed function bdetect_fnc_benchmark(): internally using code spawning, not to block code execution after function call. 
* Tweaked internals so that "diag_tickTime" is used instead of "time" (Thanks Ollem). 
* Introduced function bdetect_fnc_eh_loop(): iteratively checks ( each "bdetect_eh_assign_cycle_wait" seconds ) for newly spawned units to assign them a 'Fired' event Handler. 
* Renamed "bdetect_bullet_min_distance" to "bdetect_bullet_max_proximity" 
* Introduced variable "bdetect_init_done": (Boolean). It evaulates true when framework is loaded. Usage: "waitUntil { bdetect_init_done};" 
* Introduced variable "bdetect_eh_assign_cycle_wait": (seconds, Default 10). Wait duration foreach cyclic execution of bdetect_fnc_eh_loop() 
* Introduced variable "bdetect_bullet_min_distance": (meters, Default 25). Bullets not having travelled this distance are ignored 
* Introduced variable "bdetect_bullet_initial_min_speed": (meters / second, Default 360). Bullets initially slower than this are ignored (good for subsonic bullets skipping).  
* Introduced variable "bdetect_debug_chat": (Boolean, Default false) Show debug messages also in globalChat. 
* Changed Variable "bdetect_debug_min_level" to "bdetect_debug_levels". (Array). Default [0,1,2,3,4,5,6,7,8,9]. 0-9 are reserved levels. 
* Much more 

------------ 
Version: 0.5 
------------ 

* Framework is named "bdetect" and contained into a single file: bdetect.sqf  
* Renamed variable "bdetect_debug" to "bdetect_debug_enable" 
* Introduced variables "bdetect_name" and "bdetect_version": name and version of the framework; 
* Introduced function bdetect_fnc_init() to handle the framework bootstrap. 
* Introduced variable "bdetect_callback" (string). It defines the name of a custom callback function to be called on a unit when it detects a close bullet (Default: "bdetect_fnc_callback"). 
* Functions and variable names polishing. All vars named bdetect_<varname>. All functions named bdetect_fnc_<funcname>. 
* Added some missing private vars. 

------------ 
Version: 0.4 
------------ 

* Introduced variables "bdetect_enable" (boolean), "bdetect_debug_levels" (Number), "bdetect_skip_mags" (array). 
* Variable "bdetect_enable" act as a toggle to enable / disable framework. 
* Variable "bdetect_debug_levels" allows for selective display / logging of debug messages based on level. 
* Introduced function bdetect_fnc_benchmark() to display performance stats. 
* Added "bdetect_skip_mags", which is a blacklist of bullet types which should not trigger detection 

------------ 
Version: 0.3 
------------ 

* Introduced variables "bdetect_bullet_delay" (seconds) , "bdetect_bullet_max_height" (meters). 
* Variable "bdetect_bullet_delay" (seconds) changes dinamycally between "bdetect_bullet_min_delay" (lower bound) and 1 (upper bound), depending on actual FPS vs "bdetect_fps_min". 
* Variable "bdetect_bullet_max_height" (meters) is the maximum height for bullets, over ground. Bullets higher than this value (Default: 10) and with up vector > 0 are skipped from detection, since they are too high and diverge from ground. 
* Some Code optimization. 

------------ 
Version: 0.2 
------------ 

* Position of bullet as SetPosASL. 
* Introduced variables "bdetect_fps_min" (fps), "bdetect_bullet_max_distance" (meters), "bdetect_bullet_max_lifespan" (seconds). 
* Introduced function bdetect_fnc_diag_min_fps(): in case FPS go under "bdetect_fps_min" value (Default: 25) bdetect_bullet_delay is gradually raised.  
* Function bdetect_fnc_bullet_remove() edited and renamed to bdetect_fnc_bullet_tag_remove(), since actual removal is done elsewhere. 
* Bullets are removed from bdetect_fired_bullets if distance is over "bdetect_bullet_max_distance" meters or lifespan is over "bdetect_bullet_max_lifespan" seconds. 

------------ 
Version: 0.1 
------------ 

* First draft 
*/ 

//BEGINNING OF FRAMEWORK CODE 

// ----------------------------- 
// Constants 
// ----------------------------- 

bdetect_name = "bDetect - Bullet Detection Framework";  
bdetect_short_name = "bDetect";  
bdetect_version = "0.64"; 
bdetect_init_done= false; 

// ----------------------------- 
// Functions 
// ----------------------------- 

bdetect_fnc_init =  
{ 
   private [ "_msg", "_x" ]; 

   sleep 5; 

   // You may override these variables 
   if(isNil "bdetect_enable") then { bdetect_enable = true; }; // (Boolean, Default true) Toggle to Enable / Disable bdetect altogether. 
   if(isNil "bdetect_debug_enable") then { bdetect_debug_enable = false; }; // (Boolean, Default false) Toggle to Enable / Disable debug messages. 
   if(isNil "bdetect_debug_chat") then { bdetect_debug_chat = false; }; // (Boolean, Default false) Show debug messages also in globalChat. 
   if(isNil "bdetect_debug_levels") then { bdetect_debug_levels = [0,1,2,3,4,5,6,7,8,9]; }; // (Array, Default [0,1,2,3,4,5,6,7,8,9]) Filter debug messages by included levels.  
   if(isNil "bdetect_callback") then { bdetect_callback = "bdetect_fnc_callback"; }; // (String, Default "bdetect_fnc_callback") Name for your own callback function 
   if(isNil "bdetect_callback_mode") then { bdetect_callback_mode = "spawn"; }; // (String, Default "spawn") Allowed values: "call" or "spawn" 
   if(isNil "bdetect_fps_min") then { bdetect_fps_min = 25; }; // (Number, Default 25) The minimum FPS you wish to keep 
   if(isNil "bdetect_fps_calc_each_x_frames") then { bdetect_fps_calc_each_x_frames = 16; }; // (Number, Default 16) FPS check is done each "bdetect_fps_min" frames. 1 means each frame. 
   if(isNil "bdetect_eh_assign_cycle_wait") then { bdetect_eh_assign_cycle_wait = 10; }; // (Seconds, Default 10). Wait duration foreach cyclic execution of bdetect_fnc_eh_loop() 
   if(isNil "bdetect_bullet_min_delay") then { bdetect_bullet_min_delay = 0.1; }; // (Seconds, Default 0.1) Minimum time between 2 consecutive shots fired by an unit for the last bullet to be tracked. Very low values may cause lag. 
   if(isNil "bdetect_bullet_max_delay") then { bdetect_bullet_max_delay = 2; }; // (Seconds, Default 2) 
   if(isNil "bdetect_bullet_initial_min_speed") then { bdetect_bullet_initial_min_speed = 360; }; // (Meters/Second, Default 360) Bullets slower than this are ignored. 
   if(isNil "bdetect_bullet_max_proximity") then { bdetect_bullet_max_proximity = 10; }; // (Meters, Default 10) Maximum proximity to unit for triggering detection 
   if(isNil "bdetect_bullet_min_distance") then { bdetect_bullet_min_distance = 25; }; // (Meters, Default 25) Bullets having travelled less than this distance are ignored 
   if(isNil "bdetect_bullet_max_distance") then { bdetect_bullet_max_distance = 400; }; // (Meters, Default 400) Bullets past this distance are ignored 
   if(isNil "bdetect_bullet_max_lifespan") then { bdetect_bullet_max_lifespan = 0.5; }; // (Seconds, Default 0.5) Bullets living more than this are ignored 
   if(isNil "bdetect_bullet_max_height") then { bdetect_bullet_max_height = 6; }; // (Meters, Default 6) Bullets higher than this -and- diverging from ground are ignored 

   if(isNil "bdetect_skip_mags") then { // (Array) Skip these bullet types altogether 
       bdetect_skip_mags =   
       [   
           "30rnd_9x19_MP5",      
           "30rnd_9x19_MP5SD",      
           "15Rnd_9x19_M9",      
           "15Rnd_9x19_M9SD",      
           "7Rnd_45ACP_1911",      
           "7Rnd_45ACP_1911",     
           "8Rnd_9x18_Makarov",     
           "8Rnd_9x18_MakarovSD",     
           "64Rnd_9x19_Bizon",     
           "64Rnd_9x19_SD_Bizon",     
           "13Rnd_9mm_SLP",     
           "17Rnd_9x19_glock17",     
           "6Rnd_45ACP",     
           "30Rnd_9x19_UZI",     
           "30Rnd_9x19_UZI_SD"   
       ];   
   }; 

   // Do not edit the variables below. 
   if(isNil "bdetect_fired_bullets") then { bdetect_fired_bullets = []; }; 
   if(isNil "bdetect_fps") then { bdetect_fps = bdetect_fps_min; }; 
   if(isNil "bdetect_bullet_delay") then { bdetect_bullet_delay = bdetect_bullet_min_delay; }; 
   if(isNil "bdetect_frame_tstamp") then { bdetect_frame_tstamp = 0; }; 
   if(isNil "bdetect_frame_min_duration") then { bdetect_frame_min_duration = (bdetect_bullet_max_proximity * 2 * .66 / 900) max .01; }; 

   // bullet speed converted to kmh 
   bdetect_bullet_initial_min_speed = bdetect_bullet_initial_min_speed * 3.6; 

   // compile callback name into function 
   bdetect_callback_compiled = call compile format["%1", bdetect_callback]; 

   _msg = format["Starting %1 v%2.", bdetect_name, bdetect_version]; 
   [ _msg, 0 ] call bdetect_fnc_debug; 

   bdetect_spawned_loop_handler = [] spawn bdetect_fnc_eh_loop;    

   [bdetect_fnc_detect,0] call cba_fnc_addPerFrameHandler;    

   bdetect_init_done= true; 

   hint format["%1 v%2 started.", bdetect_name, bdetect_version]; 
}; 

// Keep searching units for newly spawned ones and assign fired EH to them 
bdetect_fnc_eh_loop = 
{ 
   private [ "_x", "_msg"]; 

   while { true } do 
   { 
       {  
           [_x] call bdetect_fnc_eh_fired_add; 

       } foreach allUnits; 

       sleep bdetect_eh_assign_cycle_wait; 
   }; 
}; 

// Assign fired EH to a single unit 
bdetect_fnc_eh_fired_add = 
{ 
   private ["_unit", "_msg"]; 

   _unit = _this select 0; 

   if( isNil { _unit getVariable "bdetect_fired_eh" } ) then 
   { 
       _unit setVariable ["bdetect_fired_eh", true];  
       _unit addEventHandler ["Fired", bdetect_fnc_fired]; 

       if (  ( assignedVehicleRole _unit) select 0 == "Turret" && isNil { (vehicle _x) getVariable "bdetect_fired_eh" } ) then    
       {         
           (vehicle _x) setVariable ["bdetect_fired_eh", true];  
           (vehicle _x) addeventhandler ["Fired", bdetect_fnc_fired];     
       };  

       _msg = format["[%1] was assigned 'Fired' EH", _unit]; 
       [ _msg, 3 ] call bdetect_fnc_debug; 
   } 
   else 
   { 
       _msg = format["[%1] already had an assigned 'Fired' EH", _unit]; 
       [ _msg, 3 ] call bdetect_fnc_debug; 
   }; 
}; 

// Fired EH 
bdetect_fnc_fired = 
{ 
    private ["_unit", "_muzzle", "_magazine", "_bullet", "_speed", "_msg", "_time", "_dt"]; 

   if( bdetect_enable ) then 
   { 
       _unit = _this select 0; 
       _muzzle = _this select 2; 
       _magazine = _this select 5; 
       _bullet = _this select 6; 
       _speed = speed _bullet; 
       _time = time; //diag_tickTime 

       _dt = _time - ( _unit getVariable ["bdetect_fired_time", 0] ); 

       if( _dt > bdetect_bullet_delay  
           && !( _magazine in bdetect_skip_mags )  
           && _speed > bdetect_bullet_initial_min_speed  
       ) then 
       { 
           _unit setVariable ["bdetect_fired_time", _time];  

           // Append info to bullets array 
           [ _bullet, _unit, _time ] call bdetect_fnc_bullet_add; 

           _msg = format["[%1] Fired bullet: speed=%2, type=%3, Delay=%4", _unit, _speed, typeOf _bullet, _dt ]; 
           [ _msg, 2 ] call bdetect_fnc_debug; 
       } 
       else 
       { 
           _msg = format["[%1] Skipped bullet: speed=%2, type=%3, Delay=%4 [%5 - %6]", _unit, _speed, typeOf _bullet, _dt,  _time , ( _unit getVariable ["bdetect_fired_time", 0] )]; 
           [ _msg, 2 ] call bdetect_fnc_debug; 
       }; 
   }; 
}; 

// Time-critical detection function, to be executed per-frame 
bdetect_fnc_detect =          
{         
   private ["_n", "_tot", "_bullet", "_data", "_side", "_pos", "_time", "_shooter", "_blacklist", "_update_blacklist", "_dist", "_units", "_x", "_data", "_func", "_t", "_k", "_bpos", "_nul"]; 

   _t = time; //diag_tickTime 

   if( bdetect_enable && (_t - bdetect_frame_tstamp) >= bdetect_frame_min_duration) then 
   { 
       _msg = format["Frame duration=%1, min duration:%2", (_t - bdetect_frame_tstamp), bdetect_frame_min_duration ]; 
       [ _msg, 4 ] call bdetect_fnc_debug; 

       _tot = count bdetect_fired_bullets; 

       bdetect_frame_tstamp = _t; 

       if ( _tot > 0 ) then  
       {  
           if( diag_frameno % bdetect_fps_calc_each_x_frames == 0) then 
           { 
               call bdetect_fnc_diag_min_fps; 
           }; 

           for "_n" from 0 to _tot - 1 step 2 do  
           { 
               _bullet = bdetect_fired_bullets select _n; 
               _data = bdetect_fired_bullets select (_n + 1); 
               _shooter = _data select 0; 
               _pos = _data select 1; 
               _time = _data select 2; 
               _blacklist = _data select 3; 
               _update_blacklist = false; 

               if( !( isnull _bullet ) ) then 
               { 
                   _bpos = getPosATL _bullet; 
                   _dist = _bpos distance _pos;             
                   //_msg = format["Following bullet %1. Time: %2. Distance: %3 Speed: %4. Position: %5", _bullet, _t - _time, _dist, (speed _bullet / 3.6), getPosASL _bullet]; 
                   //[ _msg, 2 ] call bdetect_fnc_debug; 
               }; 

               if( isNull _bullet  
                   || !(alive _bullet)  
                   || _t - _time > bdetect_bullet_max_lifespan  
                   || _dist > bdetect_bullet_max_distance     
                   || speed _bullet < bdetect_bullet_initial_min_speed // funny rebounds handling 
                   || ( ( _bpos select 2) > bdetect_bullet_max_height && ( ( vectordir _bullet ) select 2 ) > 0 )  
                   ) then 
               { 
                   [_bullet] call bdetect_fnc_bullet_tag_remove; 
               } 
               else 
               { 
                   if( _dist > bdetect_bullet_min_distance    ) then 
                   { 
                       _units = _bpos nearEntities [ ["MAN"] , bdetect_bullet_max_proximity]; 

                       { 
                           if( alive _x && !(_x in _blacklist) && _x != _shooter ) then 
                           { 
                               if( vehicle _x == _x && lifestate _x == "ALIVE") then 
                               { 
                                   _blacklist = _blacklist + [_x]; 
                                   _update_blacklist = true; 

                                   if(bdetect_callback_mode == "spawn") then { 
                                       _nul = [_x, _bullet, _x distance _bpos, _data] spawn bdetect_callback_compiled; 
                                   } else { 
                                       [_x, _bullet, _x distance _bpos, _data] call bdetect_callback_compiled; 
                                   }; 

                                   _msg = format["[%1] close to bullet %2 fired by %3, proximity=%4m, data=%5", _x, _bullet, _shooter, _x distance _bpos, _data]; 
                                   [ _msg, 9 ] call bdetect_fnc_debug; 
                               }; 
                           } 
                           else 
                           { 
                               _msg = format["[%1] Blacklisted, bullet %2 ignored", _x, _bullet]; 
                               [ _msg, 5 ] call bdetect_fnc_debug; 
                           }; 

                       } foreach _units; 

                       if(_update_blacklist) then 
                       { 
                           // Update blacklist 
                           bdetect_fired_bullets set[ _n + 1, [_shooter, _pos, _time, _blacklist] ]; 
                       }; 

                       //_msg = format["bdetect_fired_bullets = %1", bdetect_fired_bullets]; 
                       //[ _msg, 2 ] call bdetect_fnc_debug; 
                   }; 
               }; 
           }; 

           // remove dead / expired bullets 
           bdetect_fired_bullets = bdetect_fired_bullets - [-1]; 

           //_msg = format["%1 bullets in array", count ( bdetect_fired_bullets ) / 2]; 
           //[ _msg, 2 ] call bdetect_fnc_debug; 
       }; 
   }; 
}; 

bdetect_fnc_diag_min_fps = 
{ 
   private ["_fps", "_msg"]; 

   _fps = diag_fps; 

   _msg = format["FPS=%1, Min.FPS=%2, Prev. FPS=%3, bdetect_bullet_delay=%4)", _fps, bdetect_fps_min, bdetect_fps, bdetect_bullet_delay ]; 
   [ _msg, 1 ] call bdetect_fnc_debug; 

   if( _fps < bdetect_fps_min * 1.15) then 
   { 
       if( bdetect_bullet_delay + .15 < bdetect_bullet_max_delay && _fps < bdetect_fps_min ) then 
       { 
           bdetect_bullet_delay = bdetect_bullet_delay + .15; 

           _msg = format["FPS down to %1. Augmenting bdetect_bullet_delay to %2", _fps, bdetect_bullet_delay]; 
           [ _msg, 1 ] call bdetect_fnc_debug; 
       }; 
   } 
   else 
   { 
       if( ( bdetect_bullet_delay - .15 ) >= bdetect_bullet_min_delay ) then 
       { 
           bdetect_bullet_delay = bdetect_bullet_delay - .15; 

           _msg = format["FPS up to %1. Reducing bdetect_bullet_delay to %2", _fps, bdetect_bullet_delay]; 
           [ _msg, 1 ] call bdetect_fnc_debug; 
       }; 
   }; 

   bdetect_fps = _fps; 
}; 

// Function to add a bullet to bdetect_fired_bullets  
bdetect_fnc_bullet_add =  
{ 
   private ["_bullet", "_shooter", "_pos", "_time",  "_msg", "_n"]; 

   _bullet = _this select 0; // bullet object 
   _shooter = _this select 1;    // shooter 
   _pos = getPosATL _bullet;    // bullet start position 
   _time = _this select 2;    // bullet shoot time 
   _n = count bdetect_fired_bullets; 

   bdetect_fired_bullets set [ _n,  _bullet  ]; 
   bdetect_fired_bullets set [ _n + 1, [ _shooter, _pos, _time, [] ] ]; 

   _msg = format["bullet %1 added", _bullet, _n / 2]; 
   [ _msg, 2] call bdetect_fnc_debug; 
}; 

// Function to remove a bullet from bdetect_fired_bullets  
bdetect_fnc_bullet_tag_remove =  
{ 
   private ["_bullet", "_n", "_msg" ]; 

   _bullet = _this select 0; 
   _n = bdetect_fired_bullets find _bullet; 

   if( _n != -1 ) then 
   { 
       bdetect_fired_bullets set[ _n, -1 ]; 
       bdetect_fired_bullets set[ _n + 1, -1 ]; 

       _msg = format["null/expired bullet removed"]; 
       [ _msg, 2 ] call bdetect_fnc_debug; 
   }; 
}; 

// Callback function to be executed from within bdetect_fnc_detect 
bdetect_fnc_callback =  
{ 
   private [ "_unit", "_bullet", "_proximity", "_data", "_shooter", "_pos", "_time", "_msg" ]; 

   _unit = _this select 0;        // unit being under fire 
   _bullet = _this select 1;    // bullet object 
   _proximity = _this select 2;    // distance between _bullet and _unit 
   _data = _this select 3;        // Array containing more data 

   _shooter = _data select 0; // shooter 
   _pos = _data select 1;    // starting position of bullet 
   _time = _data select 2; // starting time of bullet 

   _msg = format["[%1] close to bullet %2 fired by %3, proximity=%4m, data=%5", _unit, _bullet, _shooter, _proximity, _data]; 
   [ _msg, 9 ] call bdetect_fnc_debug; 
}; 

// function to display and log stuff (into .rpt file) level zero is intended only for builtin messages 
bdetect_fnc_debug = 
{ 
   private [ "_msg", "_level"]; 

   /* 
   DEBUG LEVELS:  
   From 0-9 are reserved. 

   0 = unclassified messages 
   1 = FPS related messages 
   2 = "bdetect_fired_bullets" related messages 
   3 = EH related messages 
   4 = Frame related messages 
   5 = Unit blacklist messages 
   ... 
   9 = Unit detection related messages 
   */ 

   _level = _this select 1; 

   if( bdetect_debug_enable && _level in bdetect_debug_levels) then 
   { 
       _msg = _this select 0; 

       diag_log format["%1 [%2 v%3] Frame:%4 L%5: %6", time, bdetect_short_name, bdetect_version, diag_frameno, _level, _msg ]; 

       if( bdetect_debug_chat ) then  
       { 
           player globalchat format["%1 - %2", time, _msg ]; 
       }; 
   }; 
}; 

bdetect_fnc_benchmark =  
{ 
   private ["_cnt"]; 

   if(isNil "bdetect_stats_max_bullets") then { bdetect_stats_max_bullets = 0;}; 
   if(isNil "bdetect_stats_min_fps") then { bdetect_stats_min_fps = 999;}; 
   if(isNil "bdetect_fired_bullets") then { bdetect_fired_bullets = [];}; 

   _nul = [] spawn  
   { 
       sleep 5; 

       while { true } do 
       { 
           _cnt = count ( bdetect_fired_bullets ) / 2; 

           if( _cnt > bdetect_stats_max_bullets ) then { bdetect_stats_max_bullets = _cnt; }; 
           if( diag_fps < bdetect_stats_min_fps ) then { bdetect_stats_min_fps = diag_fps }; 
           hintsilent format["TIME: %1\nFPS: %2 (min: %3)\nBULLETS: %4 (max: %5)\nS.DELAY: %6 (Min FPS: %7)", time, diag_fps, bdetect_stats_min_fps, _cnt, bdetect_stats_max_bullets, bdetect_bullet_delay, bdetect_fps_min]; 

           sleep .1; 
       }; 
   }; 
}; 

// END OF FRAMEWORK CODE 

// ----------------------------------------------------------- 
// Example for running the script 
// ----------------------------------------------------------- 
// The following commented code is not part of the framework,  
// just an advice on how to run it from within another file 
// ----------------------------------------------------------- 

/* 
// load framework 
call compile preprocessFileLineNumbers "bdetect.sqf";   

// First declare any optional variables whose value should be other than Default (see the defined variables in bdetect.sqf, function bdetect_fnc_init() 
bdetect_debug_enable = true; 
bdetect_debug_levels = [9]; 
bdetect_debug_chat = true; 

// Then name your own unit callback function (the one that should be triggered when a bullet is close to a unit) 
bdetect_callback = "my_suppression_function"; 

// Define your own callback function, named as above 
my_suppression_function = { 
   private [ "_unit", "_bullet", "_proximity", "_data", "_shooter", "_pos", "_time", "_msg" ]; 

   _unit = _this select 0; 
   _bullet = _this select 1; 
   _proximity = _this select 2; 
   _data = _this select 3; 
   _shooter = _data select 0; // enemy shooter 
   _pos = _data select 1;    // starting position of bullet 
   _time = _data select 2; // starting time of bullet 

   _msg = format["my_suppression_function - [%1] close to bullet %2 fired by %3, proximity=%4m, data=%5", _unit, _bullet, _shooter, _proximity, _data]; 
   [ _msg, 9 ] call bdetect_fnc_debug; 
}; 

// Then initialize framework 
call bdetect_fnc_init; 

// Wait stuff to be loaded 
waitUntil { bdetect_init_done}; 

// finally, activate display of stats if you wish 
call bdetect_fnc_benchmark;  

// All done, now put your other stuff here ... 
*/ 

Share this post


Link to post
Share on other sites

Great mod, but I'm running this as a script version and an error calling for the "TPWC_AI_SUPPRESS_200\bdetect.sqf" when i start a mission. This other sqf was not included in the DLC. Am I missing something?

Share this post


Link to post
Share on other sites

Hi.

After some extensive testing (2 hours mission test) i can confirm that i don't get any sort of degradation with bDetect v0.64 running alone.

Also, other than for 3 minor typos in bdetect_fnc_eh_add() ("_x" instead of "_unit") i didn't found anything seriously broken.

In the meantime i've drafted some features for v0.65:

* enhanced data logging for debug purpose.

* added "Killed" EH to deallocate previously assigned "Fired" EH and variables ( even if dead units don't fire, done just to make things as clean as possible ).

* some more varnames polishing.

* Minor tweaks.

During testing i collected some interesting data, for example for a 10 minutes mission run (48 vs 48) i got in average:

* Maximum number of bullets analyzed per frame: 59-64

* Raw fired bullets: 5473-7518

* Tracked bullets: 5347-7291

* Bullet-close Detections: 12831-14170

* Blacklisted bullet-unit tuples: 3566-5904

It's interesting that Raw fired bullets (bullets collected by EH) is quite close to Tracked bullets. Since framework is not tracking more than 1 bullet for any unit each 0.1 seconds, that means AI units in ArmA2 rarely pull the trigger more than once in 0.1 seconds. This is important since it allows us to somewhat existimate the raw per-frame overhead for a single unit: assuming a frame is in average 0.02 seconds (pretty low end PC), any unit firing almost full-auto would add 0,2 bullets per-frame to be analyzed.

So, if we want to keep per-frame processing on the safe end (under 10 bullets per-frame), we may theorethically scale up to 50 units shooting (almost full-auto) on the same spot in the same second. A fast PC should handle 100. That's for bDetect alone, here i'm assuming the load inducted by the custom callback function is almost negligible.

A second interesting data is the Blacklisted bullet-unit tuple: as you can see it's saving quite some unneeded processing, so benefit is more than cost.

UPDATE:

Did a new test to check the load hypothesis. I've put 384 units on utes airport, within 1000 meters, all clashing in the same point at the same time.

That was the worst scenario i could imagine.

I've run the mission with both Vanilla ArmA2 and ArmA2 + bDetect active.

I had a minimum of 13,0 FPS with vanilla ArmA2 and 11,56 FPS with bDetect active. So at worst the overhead of bDetect was 13%.

It managed to analyze 15.700 bullets, tracking 8.900 of them and getting 35.000 close detections in 10 minutes.

I then retried, same setup, but 542 units on the field.

Minimal FPS were: Vanilla: 6,90 FPS, bDetect active 6,57. Overhead 5%.

Consider that with such low overall FPS the builtin FPS adaptation routine of bDetect almost switches it off, hence the lower difference.

It managed to analyze 19.000 bullets, tracking 7.500 of them getting 15.000 close detections in 10 minutes.

Edited by fabrizio_T

Share this post


Link to post
Share on other sites

ok I ran 2.03 from the script above and its stable.

50 east

51 west including player character

301 civ at start (does not climb after casualties are taken. Actually decreases as they do. 298 after first casualty).

The ball counter reads 210 until first casualty when it then reads 207.

Thought I'd provide figures for you to make out what you will from them. Maybe they'll provide some insight into what's going on.

Edited by tadanobu

Share this post


Link to post
Share on other sites

Just got back from work and retest my performance problem.... It's fixed, magically. The computer probably just needed a bit of rest... (offtopic: those uav/ucav makers that says machine dont need rest/eat/sleep then they havent played ARMA)

Share this post


Link to post
Share on other sites

Releasing bDetect v0.65.

I've create a bDetect thread and inserted download into first post, so people should not scan pages to test latest version and i don't litter here with code.

See here: http://forums.bistudio.com/showthread.php?136770-bDetect-bullet-detection-framework

Changelog:

* Added "killed" event handler

* Renamed function bdetect_fnc_eh_fired_add() to bdetect_fnc_eh_add()

* Renamed variable "bdetect_skip_mags" to "bdetect_bullet_skip_mags"

* Fixed 3 typos ( "_x" instead of "_unit" ) in bdetect_fnc_eh_add()

* Removed initial 5 seconds wait time before framework initialization, since it's easy to code the wait outside of the framework

* Introduced variable "bdetect_fps_adaptation": (Boolean, Default true). Defines whether bDetect should try to keep FPS over "bdetect_fps_min", through degrading quality of detection

* Tweaks to code and example syntax

It may be the last version for a while (unless problems arise), since it's already stable, fully featured and reliable enough in my experience.

If it's proved to be so in the next few days, i'll probably skip to v1.00, which would be the first production version, maybe in a week or so.

Have a nice testing.

tpw: v0.65 is almost backward compatible with all previous 0.6x versions. Even if it's not important anymore, i would only suggest to rename "bdetect_skip_mags" to "bdetect_bullet_skip_magt way s" in your own init.

Also add some sleep if really needed at startup. Tha's all, that way v0.65 would be 100% functional.

Edited by fabrizio_T

Share this post


Link to post
Share on other sites
ok I ran 2.03 from the script above and its stable.

50 east

51 west including player character

301 civ at start (does not climb after casualties are taken. Actually decreases as they do. 298 after first casualty).

The ball counter reads 210 until first casualty when it then reads 207.

Thought I'd provide figures for you to make out what you will from them. Maybe they'll provide some insight into what's going on.

That's pretty much what I see. Every combatant on the map should get assigned 3 different balls (green, yellow, red), which are hidden or shown according to suppression status. Ideally with 101 units on a map there should be 303 balls, but as I mentioned this number always seems to be less for some reason. And since the balls are counted as civilians for some reason, then they should register as 303 civilians.

The main things are:

  • the suppression is working whether the debug balls are correctly shown or not
  • the debug balls (civilians) don't multiply now
  • the script is stable

I'm still looking for a bulletproof debugging display, but in the mean time perhaps we can stick with what we have.

---------- Post added at 23:25 ---------- Previous post was at 23:22 ----------

Releasing bDetect v0.65.

I've create a bDetect thread and inserted download into first post, so people should not scan pages to test latest version and i don't litter here with code.

See here: http://forums.bistudio.com/showthread.php?136770-bDetect-bullet-detection-framework

Changelog:

* Added "killed" event handler

* Renamed function bdetect_fnc_eh_fired_add() to bdetect_fnc_eh_add()

* Renamed variable "bdetect_skip_mags" to "bdetect_bullet_skip_mags"

* Fixed 3 typos ( "_x" instead of "_unit" ) in bdetect_fnc_eh_add()

* Removed initial 5 seconds wait time before framework initialization, since it's easy to code the wait outside of the framework

* Introduced variable "bdetect_fps_adaptation": (Boolean, Default true). Defines whether bDetect should try to keep FPS over "bdetect_fps_min", through degrading quality of detection

* Tweaks to code and example syntax

It may be the last version for a while (unless problems arise), since it's already stable, fully featured and reliable enough in my experience.

If it's proved to be so in the next few days, i'll probably skip to v1.00, which would be the first production version, maybe in a week or so.

Have a nice testing.

tpw: v0.65 is almost backward compatible with all previous 0.6x versions. Even if it's not important anymore, i would only suggest to rename "bdetect_skip_mags" to "bdetect_bullet_skip_magt way s" in your own init.

Also add some sleep if really needed at startup. Tha's all, that way v0.65 would be 100% functional.

Sniff, it'll be sad to not have your code expertise on this thread any more. Thanks Fabrizio_T, your work is absolutely incredible.

Share this post


Link to post
Share on other sites
Sniff, it'll be sad to not have your code expertise on this thread any more. Thanks Fabrizio_T, your work is absolutely incredible.

Don't get me wrong, i've not leaving this thread.

Just i was bored to fill pages with spoilers.

With a seperate bDetect release thread no need for that anymore.

Share this post


Link to post
Share on other sites

Here's 2.03beta.

Changelog:

  • bDetect 0.65, essentially feature complete bullet detection framework
  • Fixed the broken unit filtering. This caused debugging to spawn phantom civilians leading to performance degradation over time

Still to do:

  • Further refine debugging
  • Incorporate friendly casualties into suppression routines
  • Your suggestions...

TPWCAS 2.03beta: http://filesonly.com/download.php?file=77TPWC_AI_SUPPRESS_203.zip

If the negative feedback is not too loud, I'll remove beta status.

Share this post


Link to post
Share on other sites

Thanks for the update, will try it later tonight!

However when using this awesome addition with some other scripts I noticed that it affected the AI's behavior/pathfinding in a very wierd way. I'm not exactly sure whats happening yet but in a script I gave a group a MOVE command to a specific position (while the group was under fire) and with TPWC AI suppression enabled the AI behaved very wierd, kind of running back and forth trying to decide what path to follow (this on a open field on Utes) in a very erratic way.

I will do more testing later to better understand whats exactly going on but it looks like this addon can affect other scripts/addons in a negative way. Maybe "currentCommand" can be used to apply the surpression code only if no other orders are given to the units?

/KC

Share this post


Link to post
Share on other sites
Thanks for the update, will try it later tonight!

However when using this awesome addition with some other scripts I noticed that it affected the AI's behavior/pathfinding in a very wierd way. I'm not exactly sure whats happening yet but in a script I gave a group a MOVE command to a specific position (while the group was under fire) and with TPWC AI suppression enabled the AI behaved very wierd, kind of running back and forth trying to decide what path to follow (this on a open field on Utes) in a very erratic way.

I will do more testing later to better understand whats exactly going on but it looks like this addon can affect other scripts/addons in a negative way. Maybe "currentCommand" can be used to apply the surpression code only if no other orders are given to the units?

/KC

Thanks for the heads up KeyCat. Might be the reveal function, making the units face towards whoever's shooting at them, even when you are trying to move them. Try setting tpwcas_reveal to 0 and see what happens.

I like the currentcommand idea, will look into it further (tomorrow, it's 0021 here, time for bed).

Share this post


Link to post
Share on other sites
Thanks for the heads up KeyCat. Might be the reveal function, making the units face towards whoever's shooting at them, even when you are trying to move them. Try setting tpwcas_reveal to 0 and see what happens.

It's not the reveal function I had that disabled already.

BTW: A quick look at 2.03 Beta I see that "tpwcas_reveal" is not passed to the script in the init.sqf so in this beta it's stuck at default 1.5.

/KC

Edited by KeyCat

Share this post


Link to post
Share on other sites
So I tried it and creating balls on the fly looks shit. The ball is created but then you can see it whiz up to its position above the unit. Even if it's created, then hidden, then attached to the unit, then unhidden.

I honestly can't see why the approach we were taking wasn't OK, and to be honest it's really starting to give me the shits.

I did quick test: no real need to attach them: balls won't move with _unit, but since they are supposed to be suppressed won't hardly move anyway

_unitPos = getPosATL _unit;
_ball = createVehicle ["Sign_sphere25cm_EP1", getPosATL _unit, [], 0, "NONE"];
_ball setPosATL [((_unitPos select 0)), ((_unitPos select 1)), ((_unitPos select 2) + 2)];

e.g. use to make yellow:

	if !(isNull _ball) then { _ball setObjectTexture [0,"#(argb,8,8,3)color(0.6,0.9,0.0,0.7,ca)"]; }; //yellow

and to hide ;)

	deleteVehicle _ball;

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.
Sign in to follow this  

×