Jump to content
Sign in to follow this  
tpw

TPWC AI suppression system

Recommended Posts

Hi all

jiltedjock's post worries me a bit. The whole idea is to try to improve the performance of this system. Sounds to me like we might be getting a dose of Microsoft disease - too many features at the cost of performance. For the sake of realism we are adding more and more and more checks, every one of which takes a little bit of CPU. Might be time for a collective deep breath and just focus on optimisation for a while...

We have a truly enormous amount of if and switch statements. I'm not sure if that might be the core of the problem. I will look into a few things today.

---------- Post added at 09:05 ---------- Previous post was at 08:13 ----------

Here's an optimisation attempt

1 - Removed quite a lot of superfluous if statements

2 - Moved the entire debug stuff out to its own 2 sec loop. Balls may not appear instantaneously over a suppressed unit, but c'est la vie.

3 - Injured and unconscious units cannot be suppressed, nor do they display debug balls (really!)

4 - I've got rid of the combat mode stuff, was still not sure it was working well enough

5 - I slightly simplified Coulum's skill modification for suppression mode 2

6 - I have removed basic and lite mode for the time being, just to simplify the optimising

7 - Added Fabrizio_T's side detection

8 - Stance regain and Skill regain changes are only apllied every 30 seconds if a unit is unsuppressed

I've done a bit of testing and it all definitely works. Whether it works well, I'm not sure. It won't work any worse!

Testing and feedback is very important, thanks!

/*   
TPWC AI SUPPRESSION   

Authors: TPW & -Coulum-   
Additional code: Fabrizio_T, Ollem  
Version: 1.04 
Last modified: 20120630
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.
*/   

if (isServer || isDedicated) then {  

//Log it  
diag_log format ["TPWC AI SUPPRESS STARTED: Server: %1 - Dedicated: %2", isServer, isDedicated];  


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

//Suppression mode. 1 = basic , 2 = light, 3 = full stance / skills modification.  
tpwc_ai_sup_mode = 3;  

//Delay before suppression functions start. 
tpwc_ai_sup_sleep = 5;

//Debugging. Will display coloured balls over any suppressed units. 0 = no debugging, 1 = debugging.
tpwc_ai_sup_debug = 1;

//Bullet detection radius (m). Bullets must pass within this distance of unit to suppress them. If set much below 10m, bullets may not be detected.
tpwc_ai_sup_br = 10;

//Bullet ignore radius (m). Bullets from a shooter closer than this will not suppress. 
tpwc_ai_sup_ir = 25;

//Shot threshold. More shots than this will cause unit to drop/crawl.
tpwc_ai_sup_st = 5;

//Pistol and SMG ammo to ignore. Add custom ammo (eg suppressed) or change to taste.    
tpwc_ai_sup_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"   
]; 

//Startup hint. 0 = no hint, 1 = hint.
tpwc_ai_sup_hint = 1; 

//Player suppression (shake and visuals). 0 = no suppression, 1 = suppression .   
tpwc_ai_sup_playersup = 1;


//////////   
// 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"];   

//WAIT
sleep tpwc_ai_sup_sleep;   

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


//////////////////   
// MAIN FUNCTIONS    
//////////////////    

//BULLET DETECTION LOOP (TIME CRITICAL)  
//Do not touch this please  
tpwc_ai_sup_detect =        
{       
   {      
if !(isnull tpwc_ai_sup_bullet) then 
	{
	_unit = _x;
	if ((vehicle _unit == _unit) && (lifeState _unit == "ALIVE")) then       
		{      
		_bc = count ((getposatl _unit) nearobjects ["bulletbase",tpwc_ai_sup_br]);      
		if (_bc > 0) then       
			{     
			if ((tpwc_ai_sup_fired distance _unit) > tpwc_ai_sup_ir) then       
				{      
				if !(tpwc_ai_sup_mag in tpwc_ai_sup_mags) then       
					{     
					_unit setvariable ["tpwc_skillregain", diag_ticktime + (random 4)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_originalcourage"))];       
					_unit setvariable ["tpwc_stanceregain", diag_ticktime + 5 + random 5];      
					_unit setvariable ["tpwc_supshots",(_unit getvariable "tpwc_supshots") + _bc];      
					_unit setvariable ["tpwc_suppressedstance", 1];  
					if (( side tpwc_ai_sup_fired ) getFriend ( side _unit) < 0.6 ) then 
						{  
						_unit setvariable ["tpwc_suppressedstance", 2];                              
						if (_unit getvariable "tpwc_supshots" > tpwc_ai_sup_st) then      
							{  
							_unit setvariable ["tpwc_suppressedstance", 3];                                  
							};      
						};       
					};      
				};      
			};      
		};
	};			
   } foreach allunits;      
};      

//UNIT STANCE/SKILL MODIFICATION LOOP (NON TIME CRITICAL)  
tpwc_ai_sup_behaviour =   
{  
tpwc_ai_sup_supvisflag = 0;  
while {true} do   
   {  
       {  
       if (alive _x) then   
           {  
           _unit  = _x; 
           _skillregain = _unit getvariable ["tpwc_skillregain", -1];       
           _stanceregain = _unit getvariable ["tpwc_stanceregain", -1]; 

           //SET INITIAL PARAMETERS FOR EACH UNIT  
           if (_stanceregain == -1) then       
               {   
               _unit setvariable ["asr_ai_sys_aiskill_configured", false];    
               _unit setvariable ["tpwc_skillset", false];    
               _unit setvariable ["tpwc_combatmode", "unchanged"];   
               _unit setvariable ["tpwc_originalaccuracy", _unit skill "aimingaccuracy"];     
               _unit setvariable ["tpwc_originalshake",  _unit skill "aimingshake"];    
               _unit setvariable ["tpwc_originalcourage", _unit skill "courage"];     
               _unit setvariable ["tpwc_general", _unit skill "general"];    
               _unit setvariable ["tpwc_stanceregain", diag_ticktime];     
               _unit setvariable ["tpwc_skillregain", diag_ticktime]; 
               _unit addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];      
               if ( ( assignedVehicleRole _unit) select 0 == "Turret"  ) then   
                   {  
                   (vehicle _unit) addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];   
                   };  
               if (tpwc_ai_sup_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 ["tpwc_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 ["tpwc_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 ["tpwc_sup3ball",_ball3]; 
                   _ball3 hideobject true;                       
                   };  
               };   

           //IF UNIT SKILLS ARE UNSUPPRESSED  
           if (diag_ticktime >= _skillregain) then      
               {  
               _originalaccuracy = _unit getvariable "tpwc_originalaccuracy";        
               _originalshake = _unit getvariable "tpwc_originalshake";                 
               _originalcourage = _unit getvariable "tpwc_originalcourage";     
               _general = _unit getvariable "tpwc_general";           
               if((_unit skill "aimingaccuracy") < _originalaccuracy) then      
                   {    
                   _unit setskill ["aimingaccuracy",(_unit skill "aimingaccuracy")+((_originalaccuracy-(_unit skill "aimingaccuracy"))*.325)];       
                   };     
               if((_unit skill "aimingshake") < _originalshake) then      
                   {      
                   _unit setskill ["aimingshake",(_unit skill "aimingshake")+((_originalshake-(_unit skill "aimingshake"))*.325)];      
                   }; 
			if 	(((_unit skill "aimingshake") > (_originalshake - 0.05)) &&	((_unit skill "aimingaccuracy") > (_originalaccuracy - 0.05))) then 
				{
				_unit setvariable ["tpwc_skillregain", diag_ticktime + 30];
				};	
               };  

           //IF UNIT STANCE IS UNSUPPRESSED  
           if ( diag_ticktime >= _stanceregain) then       
               { 
               _unit setvariable ["tpwc_supshots", 0];      
               _unit setunitpos "auto";					
               _originalcourage = _unit getvariable "tpwc_originalcourage";     
               _general = _unit getvariable "tpwc_general"; 
               _unit setvariable ["tpwc_suppressedstance", 0]; 
               if((_unit skill "courage") < (_originalcourage - 0.1)) then      
                   {      
                   _unit setskill ["courage",(_unit skill "courage")+(_general)*(0.1)];       
                   };     
				else
				{
				_unit setvariable ["tpwc_stanceregain", diag_ticktime + 30];
				};	
               };     

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

			case 2: //IF ENEMY BULLETS NEAR UNIT 
				{
				if ((_unit call CBA_fnc_getunitanim) select 0 != "prone") then 
					{
					_unit setunitpos "middle";	
					};
				_originalaccuracy = _unit getvariable "tpwc_originalaccuracy";       
				_originalshake = _unit getvariable "tpwc_originalshake";     
				_originalcourage = _unit getvariable "tpwc_originalcourage";      
				_general = _unit getvariable "tpwc_general";    
				_shots = _unit getvariable "tpwc_supshots";    
				_unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.001)];        
				_unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.001)];       
				_unit setskill ["courage",(_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.03)];   
				 if ((_unit == player) && (tpwc_ai_sup_playersup == 1)) then   
					{   
					addCamShake [1.5 - (skill player),(random 4)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_originalcourage")) , 2.5]  
					};
				}; 

			case 3: //IF UNIT IS SUPPRESSED WITH MORE THAN 5 ENEMY BULLETS  
				{ 
				_unit setunitpos "down";	
				_unit forcespeed -1; 
				_unit setvariable ["tpwc_combatmode", behaviour _unit];  
				_originalaccuracy = _unit getvariable "tpwc_originalaccuracy";       
				_originalshake = _unit getvariable "tpwc_originalshake";     
				_originalcourage = _unit getvariable "tpwc_originalcourage";      
				_general = _unit getvariable "tpwc_general";    
				_shots = _unit getvariable "tpwc_supshots";    
				_unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.003)];        
				_unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.003)];       
				_unit setskill ["courage",(_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.07)];   
				if ((isPlayer _unit) and (tpwc_ai_sup_playersup == 1)) then   
					{  
					[] spawn tpwc_ai_sup_visuals;  
					addCamShake [2 - (skill _unit),(random 6)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_originalcourage")) , 5]  
					};  
				}; 
			}; 
		}    
	} foreach allunits;
sleep 1.5; 		
};  
};  

// DEBUG BALL HANDLING LOOP  
tpwc_ai_sup_ballhandler =
{
while {true} do 
{
	{
	_unit = _x;
	//DISPLAY OR HIDE DEBUG BALLS AS APPROPRIATE	
	_ball1 = _unit getvariable "tpwc_sup1ball";   
	_ball2 = _unit getvariable "tpwc_sup2ball";   
	_ball3 = _unit getvariable "tpwc_sup3ball";  

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

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

	tpwc_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);}, tpwc_ballstatus] call CBA_fnc_globalExecute; 
		}; 
	} foreach allunits;

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

//BLUR PLAYER VISION WHEN SUPPRESSED  
tpwc_ai_sup_visuals =   
{  
if (tpwc_ai_sup_supvisflag == 0) then   
   { 
tpwc_ai_sup_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;  
   tpwc_ai_sup_supvisflag = 0;  
   };  
};  

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


/////////////////  
// RUN IT  
/////////////////  

//FULL MODE	
if (tpwc_ai_sup_mode ==3) then 
{	
//Spawn behaviour loop  
[] spawn tpwc_ai_sup_behaviour;   
sleep 1;  

//Spawn debug stuff
if (tpwc_ai_sup_debug == 1) then     
   {
[] spawn tpwc_ai_sup_ballhandler;
};	

//Spawn ASR_AI skill set loop if running ASR_AI > 1.15.1       
if (isclass (configfile >> "cfgPatches">>"asr_ai_sys_aiskill")) then    
	{   
	_asr_ai_va = getArray (configfile>>"cfgPatches">>"asr_ai_main">>"versionAr");  
	if (_asr_ai_va select 0 >= 1 && _asr_ai_va select 1 >= 15 && _asr_ai_va select 2 >= 1) then   
		{  
		[] spawn tpwc_ai_sup_asrskills;   
		};      
	};   

//Call time critical bullet detection loop  
[tpwc_ai_sup_detect,0] call cba_fnc_addPerFrameHandler;    
};
}; 

Edited by tpw

Share this post


Link to post
Share on other sites

And a bit more fine tuning

This version is working quite well, there are no bad lag issues, the debugging works, and the mysterious "player unable to fire" is gone as far as I can tell.

/*   
TPWC AI SUPPRESSION   

Authors: TPW & -Coulum-   
Additional code: Fabrizio_T, Ollem  
Version: 1.04 
Last modified: 20120630
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.
*/   

if (isServer || isDedicated) then {  

//Log it  
diag_log format ["TPWC AI SUPPRESS STARTED: Server: %1 - Dedicated: %2", isServer, isDedicated];  


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

//Suppression mode. 1 = basic , 2 = light, 3 = full stance / skills modification.  
tpwc_ai_sup_mode = 3;  

//Delay before suppression functions start. 
tpwc_ai_sup_sleep = 5;

//Debugging. Will display coloured balls over any suppressed units. 0 = no debugging, 1 = debugging.
tpwc_ai_sup_debug = 1;

//Bullet detection radius (m). Bullets must pass within this distance of unit to suppress them. If set much below 10m, bullets may not be detected.
tpwc_ai_sup_br = 10;

//Bullet ignore radius (m). Bullets from a shooter closer than this will not suppress. 
tpwc_ai_sup_ir = 25;

//Shot threshold. More shots than this will cause unit to drop/crawl.
tpwc_ai_sup_st = 5;

//Pistol and SMG ammo to ignore. Add custom ammo (eg suppressed) or change to taste.    
tpwc_ai_sup_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"   
]; 

//Startup hint. 0 = no hint, 1 = hint.
tpwc_ai_sup_hint = 1; 

//Player suppression (shake and visuals). 0 = no suppression, 1 = suppression .   
tpwc_ai_sup_playersup = 1;


//////////   
// 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"];   

//WAIT
sleep tpwc_ai_sup_sleep;   

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


//////////////////   
// MAIN FUNCTIONS    
//////////////////    

//BULLET DETECTION LOOP (TIME CRITICAL)  
//Do not touch this please  
tpwc_ai_sup_detect =        
{       
   {      
if !(isnull tpwc_ai_sup_bullet) then 
	{
	_unit = _x;
	if ((vehicle _unit == _unit) && (lifeState _unit == "ALIVE")) then       
		{      
		_bc = count ((getposatl _unit) nearobjects ["bulletbase",tpwc_ai_sup_br]);      
		if (_bc > 0) then       
			{     
			if ((tpwc_ai_sup_fired distance _unit) > tpwc_ai_sup_ir) then       
				{      
				if !(tpwc_ai_sup_mag in tpwc_ai_sup_mags) then       
					{     
					_unit setvariable ["tpwc_skillregain", diag_ticktime + (random 4)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_originalcourage"))];       
					_unit setvariable ["tpwc_stanceregain", diag_ticktime + 5 + random 5];      
					_unit setvariable ["tpwc_supshots",(_unit getvariable "tpwc_supshots") + _bc];      
					_unit setvariable ["tpwc_suppressedstance", 1];  
					if (( side tpwc_ai_sup_fired ) getFriend ( side _unit) < 0.6 ) then 
						{  
						_unit setvariable ["tpwc_suppressedstance", 2];                              
						if (_unit getvariable "tpwc_supshots" > tpwc_ai_sup_st) then      
							{  
							_unit setvariable ["tpwc_suppressedstance", 3];                                  
							};      
						};       
					};      
				};      
			};      
		};
	};			
   } foreach allunits;      
};      

//UNIT STANCE/SKILL MODIFICATION LOOP (NON TIME CRITICAL)  
tpwc_ai_sup_behaviour =   
{  
tpwc_ai_sup_supvisflag = 0;  
while {true} do   
   {  
       {  
       if (alive _x) then   
           {  
           _unit  = _x; 
           _skillregain = _unit getvariable ["tpwc_skillregain", -1];       
           _stanceregain = _unit getvariable ["tpwc_stanceregain", -1]; 

           //SET INITIAL PARAMETERS FOR EACH UNIT  
           if (_stanceregain == -1) then       
               {   
               _unit setvariable ["asr_ai_sys_aiskill_configured", false];    
               _unit setvariable ["tpwc_skillset", false];    
               _unit setvariable ["tpwc_combatmode", "unchanged"];   
               _unit setvariable ["tpwc_originalaccuracy", _unit skill "aimingaccuracy"];     
               _unit setvariable ["tpwc_originalshake",  _unit skill "aimingshake"];    
               _unit setvariable ["tpwc_originalcourage", _unit skill "courage"];     
               _unit setvariable ["tpwc_general", _unit skill "general"];    
               _unit setvariable ["tpwc_stanceregain", diag_ticktime];     
               _unit setvariable ["tpwc_skillregain", diag_ticktime]; 
               _unit addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];      
               if ( ( assignedVehicleRole _unit) select 0 == "Turret"  ) then   
                   {  
                   (vehicle _unit) addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];   
                   };  
               //SET UP COLOURED DEBUGGING BALLS
			if (tpwc_ai_sup_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 ["tpwc_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 ["tpwc_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 ["tpwc_sup3ball",_ball3]; 
                   _ball3 hideobject true;                       
                   };  
               };   

           //IF UNIT SKILLS ARE UNSUPPRESSED  
           if (diag_ticktime >= _skillregain) then      
               {  
               _originalaccuracy = _unit getvariable "tpwc_originalaccuracy";        
               _originalshake = _unit getvariable "tpwc_originalshake";                 
               _originalcourage = _unit getvariable "tpwc_originalcourage";     
               _general = _unit getvariable "tpwc_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 ["tpwc_skillregain", diag_ticktime + 30];
				};	
               };  

           //IF UNIT STANCE IS UNSUPPRESSED  
           if ( diag_ticktime >= _stanceregain) then       
               {
               _unit setvariable ["tpwc_supshots", 0];      
               _unit setunitpos "auto";					
               _originalcourage = _unit getvariable "tpwc_originalcourage";     
               _general = _unit getvariable "tpwc_general"; 
               _unit setvariable ["tpwc_suppressedstance", 0]; 
			//GRADUAL RETURN OF COURAGE
               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 ["tpwc_stanceregain", diag_ticktime + 30];
				};	
               };     

		//UNIT CHANGES FOR DIFFERENT SUPPRESSION
           switch ( _unit getvariable "tpwc_suppressedstance" ) 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
				_originalaccuracy = _unit getvariable "tpwc_originalaccuracy";       
				_originalshake = _unit getvariable "tpwc_originalshake";     
				_originalcourage = _unit getvariable "tpwc_originalcourage";      
				_general = _unit getvariable "tpwc_general";    
				_shots = _unit getvariable "tpwc_supshots";    
				_unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.001)];        
				_unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.001)];       
				_unit setskill ["courage",(_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.02)];   
				//PLAYER CAMERA SHAKE 
				if ((_unit == player) && (tpwc_ai_sup_playersup == 1)) then   
					{   
					addCamShake [1.5 - (skill player),(random 4)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_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
				_originalaccuracy = _unit getvariable "tpwc_originalaccuracy";       
				_originalshake = _unit getvariable "tpwc_originalshake";     
				_originalcourage = _unit getvariable "tpwc_originalcourage";      
				_general = _unit getvariable "tpwc_general";    
				_shots = _unit getvariable "tpwc_supshots";
				_unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.001)];        
				_unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.001)];       
				_unit setskill ["courage",(_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.02)];   
				//PLAYER CAMERA SHAKE AND FX
				if ((isPlayer _unit) and (tpwc_ai_sup_playersup == 1)) then   
					{  
					[] spawn tpwc_ai_sup_visuals;  
					addCamShake [2 - (skill _unit),(random 6)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_originalcourage")) , 5]  
					};  
				}; 
			}; 
		}    
	} foreach allunits;
sleep 1.5; 	
};  
};  

// DEBUG BALL HANDLING LOOP  
tpwc_ai_sup_ballhandler =
{
while {true} do 
{
	{
	_unit = _x;
	//DISPLAY OR HIDE DEBUG BALLS AS APPROPRIATE	
	_ball1 = _unit getvariable "tpwc_sup1ball";   
	_ball2 = _unit getvariable "tpwc_sup2ball";   
	_ball3 = _unit getvariable "tpwc_sup3ball";  

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

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

	tpwc_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);}, tpwc_ballstatus] call CBA_fnc_globalExecute; 
		}; 
	} foreach allunits;

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

//BLUR PLAYER VISION WHEN SUPPRESSED  
tpwc_ai_sup_visuals =   
{  
if (tpwc_ai_sup_supvisflag == 0) then   
   { 
tpwc_ai_sup_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;  
   tpwc_ai_sup_supvisflag = 0;  
   };  
};  

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


/////////////////  
// RUN IT  
/////////////////  

//FULL MODE	
if (tpwc_ai_sup_mode ==3) then 
{	
//Spawn behaviour loop  
[] spawn tpwc_ai_sup_behaviour;   
sleep 1;  

//Spawn debug stuff
if (tpwc_ai_sup_debug == 1) then     
   {
[] spawn tpwc_ai_sup_ballhandler;
};	

//Spawn ASR_AI skill set loop if running ASR_AI > 1.15.1       
if (isclass (configfile >> "cfgPatches">>"asr_ai_sys_aiskill")) then    
	{   
	_asr_ai_va = getArray (configfile>>"cfgPatches">>"asr_ai_main">>"versionAr");  
	if (_asr_ai_va select 0 >= 1 && _asr_ai_va select 1 >= 15 && _asr_ai_va select 2 >= 1) then   
		{  
		[] spawn tpwc_ai_sup_asrskills;   
		};      
	};   

//Call time critical bullet detection loop  
[tpwc_ai_sup_detect,0] call cba_fnc_addPerFrameHandler;    
};
}; 

---------- Post added at 16:12 ---------- Previous post was at 14:37 ----------

And still more...

1 - Lite and full now use the exact same bullet detection loop, same debug loop, but just use different non time critical behaviour loops (no skill modification in lite loop). Mode 2 is pretty much guaranteed to play nice with other AI mods.

2 - Removed all calculation stuff from bullet detection loops. It's just if and setvariable now, for maximum performance.

3 - Other small stuff and cleanups

I will PBO this shortly so it can be more easily tested

/*   
TPWC AI SUPPRESSION   

Authors: TPW & -Coulum-   
Additional code: Fabrizio_T, Ollem  
Version: 1.04 
Last modified: 20120630
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.
*/   

if (isServer || isDedicated) then {  

//Log it  
diag_log format ["TPWC AI SUPPRESS STARTED: Server: %1 - Dedicated: %2", isServer, isDedicated];  


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

//Suppression mode. 1 = basic , 2 = light, 3 = full stance / skills modification.  
tpwc_ai_sup_mode = 3;  

//Delay before suppression functions start. 
tpwc_ai_sup_sleep = 5;

//Debugging. Will display coloured balls over any suppressed units. 0 = no debugging, 1 = debugging.
tpwc_ai_sup_debug = 1;

//Bullet detection radius (m). Bullets must pass within this distance of unit to suppress them. If set much below 10m, bullets may not be detected.
tpwc_ai_sup_br = 10;

//Bullet ignore radius (m). Bullets from a shooter closer than this will not suppress. 
tpwc_ai_sup_ir = 25;

//Shot threshold. More shots than this will cause unit to drop/crawl.
tpwc_ai_sup_st = 5;

//Pistol and SMG ammo to ignore. Add custom ammo (eg suppressed) or change to taste.    
tpwc_ai_sup_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"   
]; 

//Startup hint. 0 = no hint, 1 = hint.
tpwc_ai_sup_hint = 1; 

//Player suppression (shake and visuals). 0 = no suppression, 1 = suppression .   
tpwc_ai_sup_playersup = 1;


//////////   
// 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"];   

//WAIT
sleep tpwc_ai_sup_sleep;   

//START HINT      
if (tpwc_ai_sup_hint == 1) then {   
0 = [] spawn {  
hintsilent format ["TPWC AI Suppress 1.04 Mode %1 Active",tpwc_ai_sup_mode];   
sleep 3;   
hintsilent ""};   
};      


//////////////////   
// MAIN FUNCTIONS    
//////////////////    
//BASIC VERSION
tpwc_ai_sup_basic =     
{   
   {   
   _unit = _x;      
   _stanceregain = _unit getvariable ["tpwc_stanceregain", -1];  

if (_stanceregain == -1) then    
	{      
	_unit setvariable ["tpwc_stanceregain", diag_ticktime];  
	_unit addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_bullet = _this select 6}];   
	if (tpwc_ai_sup_debug == 1) then  
		{
		_ball = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;
		_ball attachTo [_unit,[0,0,2]]; 
		_unit setvariable ["tpwc_supball",_ball];
		};
	};  

if ( diag_ticktime >= _stanceregain) then    
	{
	if (tpwc_ai_sup_debug == 1) then  
		{	
		_ball = _unit getvariable "tpwc_supball"; 
		_ball hideobject true;  
		};	
	_unit setvariable ["tpwc_supshots", 0];   
	_unit setunitpos "auto";
	_unit setvariable ["tpwc_suppressedstance", 0]; 
	_unit setvariable ["tpwc_stanceregain", diag_ticktime + 30];  
	};  

if !(isnull tpwc_ai_sup_bullet) then    
	{   
	_bc = count ((getposatl _unit) nearobjects ["bulletbase",tpwc_ai_sup_br]);   
	if (_bc > 0) then    
		{  
		if ((tpwc_ai_sup_fired distance _unit) > tpwc_ai_sup_ir) then    
			{   
			_unit setvariable ["tpwc_stanceregain", diag_ticktime + 5 + random 5];   
			_shots = _unit getvariable "tpwc_supshots";   
			_unit setvariable ["tpwc_supshots", _shots + _bc];   
			_shots = _unit getvariable "tpwc_supshots";  
			if ((_unit call CBA_fnc_getunitanim) select 0 != "prone") then 
				{	
				_unit setunitpos "middle";
				};
			_unit setvariable ["tpwc_suppressedstance", 1]; 
			if (tpwc_ai_sup_debug == 1) then  
				{ 
				_ball = _unit getvariable "tpwc_supball";
				_ball hideobject false; 
				};             
			if (_shots > tpwc_ai_sup_st) then   
				{  
				_unit setunitpos "down"; 
				};   
			};   
		};   
	};   

   } foreach allunits;   
};   

//LITE/FULL BULLET DETECTION LOOP (TIME CRITICAL)  
//Do not touch this please  
tpwc_ai_sup_detect =        
{       
   {      
if !(isnull tpwc_ai_sup_bullet) then 
	{
	_unit = _x;
	if ((vehicle _unit == _unit) && (lifeState _unit == "ALIVE")) then       
		{      
		_bc = count ((getposatl _unit) nearobjects ["bulletbase",tpwc_ai_sup_br]);      
		if (_bc > 0) then       
			{     
			if ((tpwc_ai_sup_fired distance _unit) > tpwc_ai_sup_ir) then       
				{      
				if !(tpwc_ai_sup_mag in tpwc_ai_sup_mags) then       
					{     
					_unit setvariable ["tpwc_skillregain", diag_ticktime + 2 + random 3];       
					_unit setvariable ["tpwc_stanceregain", diag_ticktime + 5 + random 5];      
					_unit setvariable ["tpwc_supshots",(_unit getvariable "tpwc_supshots") + _bc];      
					_unit setvariable ["tpwc_suppressedstance", 1];  
					if (( side tpwc_ai_sup_fired ) getFriend ( side _unit) < 0.6 ) then 
						{  
						_unit setvariable ["tpwc_suppressedstance", 2];                              
						if (_unit getvariable "tpwc_supshots" > tpwc_ai_sup_st) then      
							{  
							_unit setvariable ["tpwc_suppressedstance", 3];                                  
							};      
						};       
					};      
				};      
			};      
		};
	};			
   } foreach allunits;      
};  

//LITE UNIT STANCE/SKILL MODIFICATION LOOP (NON TIME CRITICAL)
tpwc_ai_sup_behaviour_lite =   
{  
while {true} do   
   {  
       {  
	_unit  = _x; 
	_stanceregain = _unit getvariable ["tpwc_stanceregain", -1]; 

	//SET INITIAL PARAMETERS FOR EACH UNIT  
	if (_stanceregain == -1) then       
		{   
		_unit setvariable ["tpwc_stanceregain", diag_ticktime];     
		_unit addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];      
		if (( assignedVehicleRole _unit) select 0 == "Turret") then   
			{  
			(vehicle _unit) addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];   
			};  
		//SET UP COLOURED DEBUGGING BALLS
		if (tpwc_ai_sup_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 ["tpwc_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 ["tpwc_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 ["tpwc_sup3ball",_ball3]; 
			_ball3 hideobject true;                       
			};  
		};   

	//IF UNIT STANCE IS UNSUPPRESSED  
	if ( diag_ticktime >= _stanceregain) then       
		{
		_unit setvariable ["tpwc_supshots", 0];      
		_unit setunitpos "auto";					
		_unit setvariable ["tpwc_stanceregain", diag_ticktime + 30];
		_unit setvariable ["tpwc_suppressedstance", 0]; 
		};   

	//UNIT CHANGES FOR DIFFERENT SUPPRESSION
	switch ( _unit getvariable "tpwc_suppressedstance" ) 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";	
				};
			}; 

		case 3: //IF UNIT IS SUPPRESSED WITH MORE THAN 5 ENEMY BULLETS  
			{
			//GO PRONE
			_unit setunitpos "down";	
			_unit forcespeed -1; 
			}; 
		}; 
	} foreach allunits;
sleep 1.5; 	
};  
};  


//FULL UNIT STANCE/SKILL MODIFICATION LOOP (NON TIME CRITICAL)  
tpwc_ai_sup_behaviour_full =   
{  
tpwc_ai_sup_supvisflag = 0;  
while {true} do   
   {  
       {  
	_unit  = _x; 
	_skillregain = _unit getvariable ["tpwc_skillregain", -1];       
	_stanceregain = _unit getvariable ["tpwc_stanceregain", -1]; 

	//SET INITIAL PARAMETERS FOR EACH UNIT  
	if (_stanceregain == -1) then       
		{   
		_unit setvariable ["asr_ai_sys_aiskill_configured", false];    
		_unit setvariable ["tpwc_skillset", false];    
		_unit setvariable ["tpwc_combatmode", "unchanged"];   
		_unit setvariable ["tpwc_originalaccuracy", _unit skill "aimingaccuracy"];     
		_unit setvariable ["tpwc_originalshake",  _unit skill "aimingshake"];    
		_unit setvariable ["tpwc_originalcourage", _unit skill "courage"];     
		_unit setvariable ["tpwc_general", _unit skill "general"];    
		_unit setvariable ["tpwc_stanceregain", diag_ticktime];     
		_unit setvariable ["tpwc_skillregain", diag_ticktime]; 
		_unit addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];      
		if ( ( assignedVehicleRole _unit) select 0 == "Turret"  ) then   
			{  
			(vehicle _unit) addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6}];   
			};  
		//SET UP COLOURED DEBUGGING BALLS
		if (tpwc_ai_sup_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 ["tpwc_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 ["tpwc_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 ["tpwc_sup3ball",_ball3]; 
			_ball3 hideobject true;                       
			};  
		};   

	//IF UNIT SKILLS ARE UNSUPPRESSED  
	if (diag_ticktime >= _skillregain) then      
		{  
		_originalaccuracy = _unit getvariable "tpwc_originalaccuracy";        
		_originalshake = _unit getvariable "tpwc_originalshake";                 
		_originalcourage = _unit getvariable "tpwc_originalcourage";     
		_general = _unit getvariable "tpwc_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 ["tpwc_skillregain", diag_ticktime + 30];
			};	
		};  

	//IF UNIT STANCE IS UNSUPPRESSED  
	if ( diag_ticktime >= _stanceregain) then       
		{
		_unit setvariable ["tpwc_supshots", 0];      
		_unit setunitpos "auto";					
		_originalcourage = _unit getvariable "tpwc_originalcourage";     
		_general = _unit getvariable "tpwc_general"; 
		_unit setvariable ["tpwc_suppressedstance", 0]; 
		//GRADUAL RETURN OF COURAGE
		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 ["tpwc_stanceregain", diag_ticktime + 30];
			};	
		};     

	//UNIT CHANGES FOR DIFFERENT SUPPRESSION
	switch ( _unit getvariable "tpwc_suppressedstance" ) 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
			_originalaccuracy = _unit getvariable "tpwc_originalaccuracy";       
			_originalshake = _unit getvariable "tpwc_originalshake";     
			_originalcourage = _unit getvariable "tpwc_originalcourage";      
			_general = _unit getvariable "tpwc_general";    
			_shots = _unit getvariable "tpwc_supshots";    
			_unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.001)];        
			_unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.001)];       
			_unit setskill ["courage",(_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.02)];   
			//PLAYER CAMERA SHAKE 
			if ((_unit == player) && (tpwc_ai_sup_playersup == 1)) then   
				{   
				addCamShake [1.5 - (skill player),(random 4)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_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
			_originalaccuracy = _unit getvariable "tpwc_originalaccuracy";       
			_originalshake = _unit getvariable "tpwc_originalshake";     
			_originalcourage = _unit getvariable "tpwc_originalcourage";      
			_general = _unit getvariable "tpwc_general";    
			_shots = _unit getvariable "tpwc_supshots";
			_unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.001)];        
			_unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.001)];       
			_unit setskill ["courage",(_unit skill "courage")-(_shots*(1-_general)*(1-_originalcourage)*.02)];   
			//PLAYER CAMERA SHAKE AND FX
			if ((isPlayer _unit) and (tpwc_ai_sup_playersup == 1)) then   
				{  
				[] spawn tpwc_ai_sup_visuals;  
				addCamShake [2 - (skill _unit),(random 6)-((_unit getvariable "tpwc_general")+(_unit getvariable "tpwc_originalcourage")) , 5]  
				};  
			}; 
		}; 

	} foreach allunits;
sleep 1.5; 	
};  
};  

// DEBUG BALL HANDLING LOOP  
tpwc_ai_sup_ballhandler =
{
while {true} do 
{
	{
	_unit = _x;
	//DISPLAY OR HIDE DEBUG BALLS AS APPROPRIATE	
	_ball1 = _unit getvariable "tpwc_sup1ball";   
	_ball2 = _unit getvariable "tpwc_sup2ball";   
	_ball3 = _unit getvariable "tpwc_sup3ball";  

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

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

	tpwc_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);}, tpwc_ballstatus] call CBA_fnc_globalExecute; 
		}; 
	} foreach allunits;

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

//BLUR PLAYER VISION WHEN SUPPRESSED  
tpwc_ai_sup_visuals =   
{  
if (tpwc_ai_sup_supvisflag == 0) then   
   { 
tpwc_ai_sup_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;  
   tpwc_ai_sup_supvisflag = 0;  
   };  
};  

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


/////////////////  
// RUN IT  
/////////////////  

//BASIC MODE
if (tpwc_ai_sup_mode ==1) then 
{
[tpwc_ai_sup_basic,0] call cba_fnc_addPerFrameHandler;
};

//LITE/FULL MODE	
if (tpwc_ai_sup_mode > 1) then 
{	
//Spawn appropriate behaviour loop  
if (tpwc_ai_sup_mode ==2 ) then
	{
	//LITE MODE
	[] spawn tpwc_ai_sup_behaviour_lite;   
	}
	else
	{
	//FULL MODE
	[] spawn tpwc_ai_sup_behaviour_full;  
	//Spawn ASR_AI skill set loop if running ASR_AI > 1.15.1       
	if (isclass (configfile >> "cfgPatches">>"asr_ai_sys_aiskill")) then    
		{   
		_asr_ai_va = getArray (configfile>>"cfgPatches">>"asr_ai_main">>"versionAr");  
		if (_asr_ai_va select 0 >= 1 && _asr_ai_va select 1 >= 15 && _asr_ai_va select 2 >= 1) then   
			{  
			[] spawn tpwc_ai_sup_asrskills;   
			};      
		};		
	};
sleep 1;  

//Spawn debug stuff
if (tpwc_ai_sup_debug == 1) then     
	{
	[] spawn tpwc_ai_sup_ballhandler;
	};	

//Call time critical bullet detection loop  
[tpwc_ai_sup_detect,0] call cba_fnc_addPerFrameHandler;    
};
}; 

Edited by tpw

Share this post


Link to post
Share on other sites

Thanks for the hardwork tpw and coulum, this will be a must have mod indeed.

-deleted-

Edited by Muahaha

Share this post


Link to post
Share on other sites
Hi all

jiltedjock's post worries me a bit. The whole idea is to try to improve the performance of this system. Sounds to me like we might be getting a dose of Microsoft disease - too many features at the cost of performance. For the sake of realism we are adding more and more and more checks, every one of which takes a little bit of CPU.

We have a truly enormous amount of if and switch statements. I'm not sure if that might be the core of the problem. I will look into a few things today.

In my opinion IF / SWITCH overhead should be almost negligible here.

What surely matters more is the number of iterations.

Maintaining a per-frame execution what should looked into is the possibility not to execute the loop onto AllUnits.

To me that's the no.1 problem in terms of potential overhead, as load is scaling almost linearly with total number of units on the field, even if they're not all fighting.

Share this post


Link to post
Share on other sites

Thanks for the .PBO.

It seems that the userconfig switch for the init hint isn't working, as I get the hint regardless of whether the value is 1 or 0. Samesame for the AI LOS, by the way. :)

Share this post


Link to post
Share on other sites
Thanks for the .PBO.

It seems that the userconfig switch for the init hint isn't working, as I get the hint regardless of whether the value is 1 or 0. Samesame for the AI LOS, by the way. :)

Aargh that fucking hint. It works perfectly in the script, falls down in the PBO, no matter what bloody addon I make. Leave it with me!

---------- Post added at 19:31 ---------- Previous post was at 19:20 ----------

In my opinion IF / SWITCH overhead should be almost negligible here.

What surely matters more is the number of iterations.

Maintaining a per-frame execution what should looked into is the possibility not to execute the loop onto AllUnits.

To me that's the no.1 problem in terms of potential overhead, as load is scaling almost linearly with total number of units on the field, even if they're not all fighting.

You're right about the ifs FT. But it never hurt anyone to remove superfluous if statements!

Your second point is of course the crux of the problem. Currently I try to keep the overhead down by nested ifs, so even though the PFEH runs for all units, it only goes further if there's a bullet active. But of course excluding units before the PFEH runs would be even better.

Even as I'm typing this I'm thinking about yet another loop which scans allunits every few seconds, and only puts into an array those units which pass certain criteria

1 - unit is alive

2 - unit is uninjured

3 - unit is not driving a vehicle

and how about a few other potential criteria such as

4 - unit is closer than say 1000m to the player (would cause probs in MP though)

5 - unit has line of sight to enemies

6 - unit is not a civilian (although I like to see civs flich when I put a round past them)

7 - etc etc

The PFEH would then only run on the units in the filtered array.

OK, off to try that one out!

Share this post


Link to post
Share on other sites

Speaking about reliability, i bet i've found some weak spots.

is checked a bit the function tpwc_ai_sup_detect, to me looks that tpwc_ai_sup_bullet acts basically as a simple semaphore here.

This value is overridden each time a bullet is fired and lets the suppression loop run per-frame as long as the last fired bullet is "alive".

Is this correct?

* If so, then it's not safe to check side tpwc_ai_sup_fired from within the loop, as we check the side of any nearby unit against the side of the last fired bullet, which is incoherent.

* Another problem is that the lifespan of the last fired bullet may be shorter than the life of any bullets fired just a split seconds before: since suppression detection it tied to the lifespan of the former, we end up potentially ignoring the latters.

The solution may be abandoning tpwc_ai_sup_fired and creating a fired_bullets global array:

* each fired EH should append a bullet to fired_bullets global array;

* per-frame, at every execution of tpwc_ai_sup_detect we should check wheter count of fired_bullets >0;

* if so we should remove any bullets in fired_bullets not alive anymore;

* if still count of fired_bullets > 0 we should iterate on bullets, find close units through nearObjects and finally apply suppression to them as usual;

Basically we would reverse the whole logic: not anymore units -> close bullets, but bullets -> close units.

This solution has advantages and disadvantages:

PRO:

* it's a more robust method in terms of detection;

* it permits not to loop on allUnits per-frame, because we split the detection in multiple tiny suppression "cells" centered around the flying bullets;

CON:

* The max number of fired_bullets potentially alive is hard to existimate. Hundreds of bullets may potentially lag (however we can implement some logics to mitigate this, for example we may cap the max lifespan of a bullet based on the max distance we wish it can travel).

Edited by fabrizio_T

Share this post


Link to post
Share on other sites
Speaking about reliability, i bet i've found some weak spots.

Not hard, I'm sure there are many!

is checked a bit the function tpwc_ai_sup_detect, to me looks that tpwc_ai_sup_bullet acts basically as a simple semaphore here.

This variable is overridden each time a bullet is fired and lets the suppression loop run per-frame as long as the last fired bullet is "alive".

Is this correct?

Yep

* If so, then it's not safe to check side tpwc_ai_sup_fired from within the loop, as we check the side of any nearby bullet against the side of the last fired bullet, which is incoherent.

It works fine if the overall amount of shooting is not too high, since the chance that a bullet in the air around a unit is from tpwc_ai_sup_fired is pretty high. But of course it is not perfect.

* Another problem is that the lifespan of the last fired bullet may be shorter than the life of any bullets fired just a split seconds before: since suppression detection it tied to the lifespan of the former, we end up potentially ignoring the latters.

See above answer!

The solution may be the abandoning tpwc_ai_sup_fired and creating a fired_bullets global array:

* each fired EH should append a bullet to fired_bullets global array;

* pre-frame, at every execution of tpwc_ai_sup_detect we should check wheter count of fired_bullets >0;

* if so we should remove any bullets in fired_bullets not alive anymore;

* if still count of fired_bullets > 0 we should iterate on bullets, find close units through nearObjects and finally apply suppression to them as usual;

This solution has advantages and disadvantages:

PRO:

* it's a more robust method in terms of detection;

* it permits not to loop on allUnits per-frame, because we split the detection in multiple tiny suppression "cells" centered around the flying bullets;

CON:

* The max number of fired_bullets potentially alive is hard to existimate. Hundreds of bullets may potentially lag (however we can implement some logics to mitigate this).

That's a beautiful and elegant solution, making it bullet centered instead of player centered.

I tested out the unit filter idea from the above post and that also works. Perhaps a combination of the two will be beneficial.

EDIT

We'd still need to work out the side of the bullet. Unfortunately you can't setvariable a bullet with the side of the person who fired it. Hence my kludge fix. But I guess you could alway enter [bullet,side of shooter] into fired_bullets []

A bit more thinking has me worried that the overhead of filtering fired_bullets and doing nearobject checks around hundreds of bullets per frame might end up being higher than what we currently have.

Edited by tpw

Share this post


Link to post
Share on other sites

We'd still need to work out the side of the bullet. Unfortunately you can't setvariable a bullet with the side of the person who fired it. Hence my kludge fix.

Not a problem, really.

in my idea we'd save into the global array fired_bullets both the bullet itself and its side.

We have both these info in fired EH and we'd fill the array from there, so no problem.

I tested out the unit filter idea from the above post and that also works.

I'm sure it will work, but filtering allUnits may end up being most costy than benefical

A bit more thinking has me worried that the overhead of filtering fired_bullets and doing nearobject checks around hundreds of bullets per frame might end up being higher than what we currently have.

On a pre-frame basis i doubt we can have hundreds of bullets flying, but it should be checked.

However, even in that case, i see solutions:

* for suppression checking, we may limit the range of bullets to a max distance. Let's say 400-450m. for example: for an assault rifle bullet will fly around 800-900m./s., so we van cap its lifespan to 0.5s. How many bullets we can have flying in 0.5 secs. unless we have hundreds of units fighting in the same place ?

* we can limit the frequency the fired EH causes a new bullet to be appended to the global array fired_bullets. Let's say we don't want track more than a bullet each 0.1 - 0-2 seconds for a single unit ... it means that we lose some bullets in a single burst, but that's not critical and acceptable in my opinion (also almost never AI shoots more than 10 times a second);

* We can check vector dir and height for bullets, so bullets passing high or aimed too high may be skipped.

* we can blacklist units already suppressed to the max level, so they are ignored;

Edited by fabrizio_T

Share this post


Link to post
Share on other sites
in my idea we'd save into the global array fired_bullets both the bullet itself and its side.

We have both these info in fired EH and we'd fill the array from there, so no problem.

Worked that one out for myself eventually!

I'm sure it will work, but filtering allUnits may end up being most costy than benefical

Not at all. It just runs in a 10 second loop

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

Then the PFEH loop just runs on tpwc_ai_sup_filter

EDIT: Just read your edited post. You're a bloody genius, so why the hell aren't you working for BIS?!?!

Edited by tpw

Share this post


Link to post
Share on other sites

We’re trying this today in a small, but full mission, really hope it works out. Looking more and more like a game changing mod. Have not seen any performance issues that would concern, but will see today.

The injured lobbing grenades is a SLX thing, you have to either medic them reasonably quickly (& capture) or shot them again.

They can also play dead on the odd occasion, not sure if that’s gl4 or slx at work, or indeed the mix, makes for it to be interesting,. However when they stop playing dead they stand without a weapon for around 1 or 2 seconds, then the weapon appears for them to fire, would be great if they could fire from a lying position but they seem to have to stand for it to work. Sometimes best just to shot the dead again to make sure for certain, we have lost units with the playing dead getting up thing.. Anyway wondering off.

Back on topic: We want to use this mod in our mix if at all pos, as its showing signs already of being better than the other suppression features in either of the other two we use.

Great work tpwc

Share this post


Link to post
Share on other sites

Wouldn't an initial filter that excluded any unit not in combat mode be simple & hopefully speed things up?

Any unit that is in safe, careless or aware mode would be skipped, & I suppose dead units would be skipped also. Then if you want to exclude units that are injured or driving/flying that could be done afterwards (although I'm not sure what combat mode an injured unit would be in if there was neither enemy nor bullets nearby, it wouldn't matter at all if such units had been excluded in the initial screen).

Danger.fsm causes will kick in if an excluded unit is fired at, etc., mode will be set to combat/RED, & it will be included on the next cycle.

Or am I being too simplistic again?

Edited by Orcinus

Share this post


Link to post
Share on other sites
Worked that one out for myself eventually!

Not at all. It just runs in a 10 second loop

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

Then the PFEH loop just runs on tpwc_ai_sup_filter

EDIT: Just read your edited post. You're a bloody genius, so why the hell aren't you working for BIS?!?!

Sorry, but i still has doubts about this kind of function.

I think that something like

_units = (position _bullet)  nearObjects ["MAN",10]

is going to run pretty fast and already returns only infantrymen not in vehicle.

It will return probably zero or a handful of units. Easy to check for side or wounded state inside a loop.

I'm not sure that suppression effects should be applied only in player proximity, maybe it can be optional?

I'm no way a genius, simply i implemented my own suppression routine some months ago and by trial and error i've found this solution ...

Share this post


Link to post
Share on other sites
Wouldn't an initial filter that excluded any unit not in combat mode be simple & hopefully speed things up?

Any unit that is in safe, careless or aware mode would be skipped, & I suppose dead units would be skipped also. Then if you want to exclude units that are injured or driving/flying that could be done afterwards (although I'm not sure what combat mode an injured unit would be in if there was neither enemy nor bullets nearby, it wouldn't matter at all if such units had been excluded in the initial screen).

Danger.fsm causes will kick in if an excluded unit is fired at, etc., mode will be set to combat/RED, & it will be included on the next cycle.

Or am I being too simplistic again?

I don't think we want to exclude units not in combat mode. The idea is that you can still fire at them and have them suppressed, and go into combat mode if the FSM thinks so.

The little filter snippet I posted already excludes dead/injured units, units driving, non combatants and distant units. That already cuts down the amount of work the PFEH has to do.

I'm busily trying out Fabrizio_T's ideas though....

Share this post


Link to post
Share on other sites
Wouldn't an initial filter that excluded any unit not in combat mode be simpler & hopefully speed things up?

Any unit that is in safe, careless or aware mode would be skipped, & I suppose dead units would be skipped also. Then if you want to exclude units that are injured or driving/flying that could be done afterwards (although I'm not sure what combat mode an injured unit would be in if there was neither enemy nor bullets nearby, it wouldn't matter at all if such units had been excluded in the initial screen).

Danger.fsm causes will kick in if an excluded unit is fired at, etc., mode will be set to combat/RED, & it will be included on the next cycle.

Or am I being too simplistic again?

Skipping units NOT in combat mode would end uo being a bad idea in my opinion. Why?

Let's say you're a sniper and open fire onto a enemy group from 500-600m: with vanilla ARMA2 you normally fire a few shots and the enemy don't move, till you hit someone. Pretty idiotic, isn't it?

Instead a good suppression logic should put those enemies in combat mode as soon as they detect being under fire.

Let me add add i'll give that group also a small knowsabout about me, so they at least try looking into my direction.

Also i suggest not to trust much danger.fsm, as it's not much reliable.

It definetely won't put units in combat mode if fired upon from long range.

EDIT: i saw tpw reply only now ...

Edited by fabrizio_T

Share this post


Link to post
Share on other sites

Ugh, I hadn't realised danger.fsm was so unreliable.

Thanks for the replies, I'll shut up now :)

Share this post


Link to post
Share on other sites

Cutting, pasting & editing code from another project.

Fired EH with capped frequency (min .1 delay between shots).

I edited it a bit, but no way i can check it ingame for syntax now ...

FLEXAI_eh_fired =
{
	private ["_unit", "_muzzle", "_bullet", "_minDelay"];

_unit = _this select 0;
_muzzle = _this select 2;
_bullet = _this select 6;
_minDelay = 0.1;


if( isNil { _unit getVariable "FLEXAI_fire_s_time" } ) then
{
	_unit setVariable ["FLEXAI_fire_s_time", -1]; 
};

if(time - (_unit getVariable "FLEXAI_fire_s_time") > _minDelay ) then
{
	// here you append info to bullets array ...

	_unit setVariable ["FLEXAI_fire_s_time", time]; 
};
};

Feel free to edit / rename / fix / reuse

Share this post


Link to post
Share on other sites
EDIT

We'd still need to work out the side of the bullet. Unfortunately you can't setvariable a bullet with the side of the person who fired it.

It's possible to define additional variable in the Fired EH (which could be used in nested arrays of the bullets).

E.g.:

 _unit addeventhandler ["fired",{tpwc_ai_sup_fired = _this select 0;tpwc_ai_sup_mag = _this select 5; tpwc_ai_sup_bullet = _this select 6;tpwc_bullet_side = side (_this select 0);}]; 

In same way more logic (including if - then ) can be added to the EH

Share this post


Link to post
Share on other sites
Aargh that fucking hint. It works perfectly in the script, falls down in the PBO, no matter what bloody addon I make. Leave it with me!

Note that the switch for your suppression balls is inactive too (i.e. balls all the time). Might be worth having a look at all your userconfig-defined options to see if something needs to be changed. :)

Share this post


Link to post
Share on other sites

Thanks guys

I'm making good progress. For each unit I have got a fired eventhandler adding to a fired_bullets array [shooter, magazine, bullet]. And a PFEH querying the array and detecting units around each active bullet. Any unit then gets the bullet added to their personal bullet array, and the unit itself added to an array of shotat_units. The fired_bullets array is cleared every 0.2 sec so it doesn't get too large.

This is looking very promising, because shotat_units can then be analysed at a more leisurely pace. Each unit's personal bullet array can be queried and bullet type, distance from shooter, and side of shooter determined. This can then be used to determine suppression status for that unit.

Fabrizio_T, I think we are onto something. I'm off to sleep now but will post some code pretty soon.

Share this post


Link to post
Share on other sites

Hi,

i managed to write a small and basic prototype of bullets detection framework, capable to handle the array of bullets i talked about before.

Framework provides revised minimal tpwc_ai_sup_detect function, time-critical and to be executed per-cycle as usual.

It includes needed helper functions, basic fired EH (with configurable shooting frequency cap), debug functions (logging into both .rpt and globalchat ).

All it's in the most basic form, not currently meant for performance and probably including bugs as well.

Stuff may be tuned, moved around and fixed till good robustness is achieved.

Framework does not actually trigger any kind of suppression, it's intended as a testbed to get some reliable underlying backbone for upper-level suppression routines.

It simply takes care of populating, handling and purging an array of "alive" bullets named tpwc_ai_fired_bullets.

based on that it identifies units close to any bullets in the array and triggers a placeholder function that will hopefully contain the real suppression routine.

All is segregated into small functions to achieve better code granularity and greater variables scope control.

Code is straightforward i think.

USAGE FOR BELOW CODE:

---------------------

* Create a new mission in editor

* Add a player

* Add a far enemy

* Name mission as you want and Save it.

* Take the following code and save it as init.sqf into your newly created mission folder.

* Run mission

* Fire some bullets around enemy, see various debug messages showing info.

* try changing player to a machinegunner and shoot a whole magazine, play width the "_minDelay" variable: try setting it to 0.2 or 0.3 and see the outcome.

/*
Basic bullet detection framework
Version: 0.1
Date: 30/06/2012
Author: who cares ...
*/

tpwc_ai_fired_bullets = []; // array containing "alive" bullets and metadata
tpwc_ai_sup_ir = 10;	// minimal distance between unit and bullet to trigger suppression
tpwc_debug = true; // toggle debug

// -----------------------------
// Misc Functions
// -----------------------------

// Function to add a bullet to tpwc_ai_fired_bullets 
tpwc_fnc_bullet_add = 
{
private ["_bullet", "_unit", "_side", "_pos", "_time", "_data", "_msg"];

_bullet = _this select 0;
_unit = _this select 1;
_side = side _unit;
_pos = position _bullet;
_time = time;

_data = [ _side, _pos, _time ]; // pack into _data as much stuff as you want ...

tpwc_ai_fired_bullets = tpwc_ai_fired_bullets + [ _bullet, _data ];

_msg = format["bullet %1 added to tpwc_ai_fired_bullets. %2 items in array.", _bullet, count(tpwc_ai_fired_bullets) / 2];
[ _msg ] call tpwc_fnc_debug;

_msg = format["tpwc_ai_fired_bullets = %1", tpwc_ai_fired_bullets];
[ _msg ] call tpwc_fnc_debug;
};

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

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

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

	tpwc_ai_fired_bullets = tpwc_ai_fired_bullets - [-1];

	_msg = format["1 null bullet removed from tpwc_ai_fired_bullets. %2 items remaining in array.", _bullet, count(tpwc_ai_fired_bullets) / 2];
	[ _msg ] call tpwc_fnc_debug;

};
};

// Callback function to be executed from within tpwc_ai_sup_detect
tpwc_fnc_suppress_unit = 
{
private [ "unit", "_data", "_msg" ];

_unit = _this select 0;
_distance = _this select 1;
_data = _this select 2;

_msg = format["unit %1 is suppressed. Distance %2m. Data: %3", _unit, _distance, _data];
[ _msg ] call tpwc_fnc_debug;

// placeholder. Do here all suppresion stuff ...
};

// function to log stuff into .rpt file
tpwc_fnc_debug =
{
private [ "_msg"];

if( tpwc_debug ) then
{
	_msg = _this select 0;
	diag_log format["%1 - %2", time, _msg ];
	player globalchat format["%1 - %2", time, _msg ];
};
};



// -----------------------------
// EH functions
// -----------------------------

// Fired EH
tpwc_eh_fired =
{
	private ["_unit", "_muzzle", "_bullet", "_minDelay"];

_unit = _this select 0;
_muzzle = _this select 2;
_bullet = _this select 6;
_minDelay = 0.1;

if( isNil { _unit getVariable "tpwc_fired_time" } ) then
{
	_unit setVariable ["tpwc_fired_time", -1]; 
};

if(time - (_unit getVariable "tpwc_fired_time") > _minDelay ) then
{
	// here you append info to bullets array ...

	_unit setVariable ["tpwc_fired_time", time]; 

	[_bullet, _unit] call tpwc_fnc_bullet_add;

	_msg = format["Intercepted bullet fired by %1", _unit];
	[ _msg ] call tpwc_fnc_debug;
}
else
{
	_msg = format["Skipped bullet fired by %1", _unit];
	[ _msg ] call tpwc_fnc_debug;
};
};

// --------------------------------
// Time critical per-frame function
// --------------------------------

tpwc_ai_sup_detect =         
{        
private ["_n", "_tot", "_data", "_side", "_units", "_x"];

// purge tpwc_ai_fired_bullets from "dead" bullets
_tot = count tpwc_ai_fired_bullets;
if ( _tot > 0 ) then 
   { 
	for "_n" from 0 to _tot - 1 step 2 do 
	{
		_bullet = tpwc_ai_fired_bullets select _n;

		if( isNull _bullet || !(alive _bullet) ) then
		{
			[_bullet] call tpwc_fnc_bullet_remove;
		};
	};
};

// find units nearby to each alive bullet
_tot = count tpwc_ai_fired_bullets;
if ( _tot > 0 ) then 
   { 
	for "_n" from 0 to _tot - 1 step 2 do 
	{
		_bullet = tpwc_ai_fired_bullets select _n;
		_data = tpwc_ai_fired_bullets select ( _n + 1);
		_side = _data select 0;
		_units = _bullet nearObjects ["MAN", tpwc_ai_sup_ir];

		{
			if( alive _x && vehicle _x == _x && lifestate _x == "ALIVE") then
			{
				[_x, _x distance _bullet, _data] call tpwc_fnc_suppress_unit;
			};

		} foreach _units;
	};
};
};

// -----------------------------
// Test script
// -----------------------------

_msg = format["Starting logging."];
[ _msg ] call tpwc_fnc_debug;

_eh = player addEventHandler ["Fired", tpwc_eh_fired];

[tpwc_ai_sup_detect,0] call cba_fnc_addPerFrameHandler;     

Edited by fabrizio_T

Share this post


Link to post
Share on other sites
Thanks for the .PBO.

It seems that the userconfig switch for the init hint isn't working, as I get the hint regardless of whether the value is 1 or 0. Samesame for the AI LOS, by the way. :)

It seems the whole userconfig is not processed by that version.

Share this post


Link to post
Share on other sites
It seems the whole userconfig is not processed by that version.

Yeah, I thought as much. Ah well, more for tpw's already full plate! :D

Share this post


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

×