Jump to content
Sign in to follow this  
tpw

TPWC AI suppression system

Recommended Posts

Thanks ChrisB

I'm aware that there have been other AI suppression systems, but had never seen any that reacted to the actual passing bullets.

I've played with SLX (COSLX) for some time and never been able to suppress anyone by firing bullets past their head. Into the ground nearby maybe. Or maybe I'm missing something (possible, since I am a useless old bastard).

Anyway, play with this addon, and use it if you like it. We had fun making it and involving people with it, even if it turns out we reinvented the wheel!

I am the same (useless old bastard), 53 this year playing since ofp and loving every mod made I think, especially ai mods. Play as part of a group, so will spread the word.

I have it and a simple mission (I have for testing) running with it in, so will post with results.

Am looking forward to using it.

Thanks again.

Share this post


Link to post
Share on other sites

Thanks for your work.

At last-i can see we are getting in the right way to have a 'more proper' AI..

I read it works fine with ASR_AI..but i m wondering on something.

ASR_AI 'randomizes' various skill components for each unit.

After your 'suppression effect' -values return to a 'default' number? ..

or to the values given by ASR_AI i.e.?

Edited by GiorgyGR

Share this post


Link to post
Share on other sites
I tried the "B_127x99_Ball_noTracer" ammunition, and the dragunov one, and even after unloading a mag the red balls did not appear. But I'll try a bit more, it may be a question of FPS ?

My point was rather when a AI group is patrolling, and a hidden sniper is shooting down one unit, that every body could be suppressed. I also thought about artillery explosion, and finally about IEDs. That's why i f I knew which part of the code trigger the suppressed mode, I'll could do research on my side, and let you know... For instance, if I remember well Artillery create a crater at the explosion. Perhaps AI could try to detect crater around and become suppressed ?

But this is only ideas, what you did is already wonderful, i cannot stop trying since I ran the script for the first time !

OK, last post before I head off to bed after a long day of Arma nerdism. -Coulum- can hopefully take over while I sleep (good thing about this Australia/Canada collaboration)!

Stupid question GFt: you were more than 25m away when trying to suppress right? After your last post I went back in and did a lot of sniping with decent success. If I sniped out a baddy in a patrol, anyone else within 10m of him was suppressed - but only crouched because I only shot one bullet in 5 seconds. I have a shit computer which routinely drops below 30fps and it still worked.

The code which does the reacting to projectiles is

if !(isnull tpwc_ai_sup_bullet) then   
           {  
           _bc = count ((getposatl _unit) nearobjects ["bulletbase",tpwc_ai_sup_br]);  
           if (_bc > 0) then   ......

Firstly, there has to actually be an active bullet object on the map, and if so, then nearobjects ["bulletbase" counts how many bullet objects are within tpwc_ai_sup_br (10m) of the unit. This means the computationally expensive nearobjects function only runs when it needs to. I guess you could make it try to detect things other than bulletbase, but you might run into the problem that a static thing like a crater will be counted over and over, every frame. Still, it could be finessed.

All food for thought.

Share this post


Link to post
Share on other sites
Currently there is no "suppressed" eventhandler in the game engine, so TPWCAS aims to mimic one by using CBA to set up a per frame eventhandler, which checks whether there is currently an active bullet/shell projectile object, and if so then checks whether each unit has any bullets within a 10m radius. This allows TPWCAS to work for any opfor, blufor, independent or civilian on the map, whether editor-placed or spawned, without a crippling CPU overhead.

tpw you may understand this right, but others probably don't. The PFEH is no silver or magic bullet. It is only a tool to ensure timely execution.

While I haven't checked your code, it sounds like you are doing non trivial checks there.

In other words combining this script/addon with other script heavy missions/scripts/addons, may lead to script lag. Aka delayed script execution.

Not of your code, but of the others.

Therefore it is imperative to keep the checks simple. While for some use cases / scenarios, this approach may be fine, for others it seems way too aggressive.

Again no general criticism, yet only to give everyone a broader perspective.

PS: My judgement be very wrong and I am always open to learn from others. :)

Share this post


Link to post
Share on other sites
I read it works fine with ASR_AI..but i m wondering on something.

ASR_AI 'randomizes' various skill components for each unit.

After your 'suppression effect' -values return to a 'default' number? ..

or to the values given by ASR_AI i.e.?

Yep the suppression addon detects the skill of units after asr has set them all up. It then saves these skills. When suppression occurs ai's skills will then be manipulated, but after suppression ai will always recover to the asr skills that were saved initially.

This is assuming that all units are present at the beginning of the mission. Things do not work out so nicely when units spawn midway through a mission. This is because the suppression addons detects and saves the spawned units skills before asr has time to set them. This is something that I will look into further and hopefully fix soontm

I tried the "B_127x99_Ball_noTracer" ammunition, and the dragunov one, and even after unloading a mag the red balls did not appear. But I'll try a bit more, it may be a question of FPS ?

My point was rather when a AI group is patrolling, and a hidden sniper is shooting down one unit, that every body could be suppressed. I also thought about artillery explosion, and finally about IEDs. That's why i f I knew which part of the code trigger the suppressed mode, I'll could do research on my side, and let you know... For instance, if I remember well Artillery create a crater at the explosion. Perhaps AI could try to detect crater around and become suppressed ?

But this is only ideas, what you did is already wonderful, i cannot stop trying since I ran the script for the first time !

Sniper rifles definitely do cause the suppression effects. The only problem is they don't create as much as in real life, because the suppression mod applies suppression based on the volume of incoming fire rather than the type of incoming fire - and snipers don't put out much "volume".

In vanilla - put a round passed a patrol as a sniper and they don't even notice.

Suppression mod - put a round passed a patrol as a sniper, they go crouched.

Real life - put a round passed a patrolas a sniper and they go prone, seek out cover etc. etc.

One thing that I think might be worth looking into as a possible improvement is simple making a unit that has been suppressed go into danger mode. That way if a sniper takes a shot, not only will units automatically go crouched but they will try to seek out cover and locate the shooter using vanilla ai methods. the only possible problem is units would then always be in danger mode and it would be near impossible to keep them in stealth or aware mode, limiting mission makers abilities.

Another possiblility to improving on this front is to detect what kind of shot is being fired and apply suppression effects accordingly. That way .50 cal > 5.56 in terms of suppression.

Last thought is to make it so if a unit has 0 knows about of the firer and is suppressed, suppresssion will be multiplied. That way snipers will be able to suppress units better.

But after reading PvPscene's post i think we have to be careful about just how much stuff we stuff into this mod. too much and I am sure we will see more lag (script or fps), more bugs, more mod compatability problems and less control for mission makers - all of which we want to avoid. So although we may look into these kind of things it may be best for useability reasons to keep things simple.

tpw you may understand this right, but others probably don't. The PFEH is no silver or magic bullet. It is only a tool to ensure timely execution.

While I haven't checked your code, it sounds like you are doing non trivial checks there.

In other words combining this script/addon with other script heavy missions/scripts/addons, may lead to script lag. Aka delayed script execution.

Not of your code, but of the others.

Therefore it is imperative to keep the checks simple. While for some use cases / scenarios, this approach may be fine, for others it seems way too aggressive.

Thanks PvPscene. We will make sure watch out for this. By "script lag" would that mean scripts are delayed only or could it possibly also mean that scripts are totally cancelled/don't function properly?

Share this post


Link to post
Share on other sites

Having not served I can only presume this from films and books, but in RL would not a squad be unable to tell the difference between a single sniper bullet passing by and a single pot-shot fired from a regular rifle anyway. They would only be aware that a sniper had a bead on them if that one shot was particularly effective. Therefore could one option for your system be that if incoming fire proves immiediately effective and hits someone the whole squad goes into full suppressed mode?

Share this post


Link to post
Share on other sites
Having not served I can only presume this from films and books, but in RL would not a squad be unable to tell the difference between a single sniper bullet passing by and a single pot-shot fired from a regular rifle anyway. They would only be aware that a sniper had a bead on them if that one shot was particularly effective. Therefore could one option for your system be that if incoming fire proves immiediately effective and hits someone the whole squad goes into full suppressed mode?

Yep your right, it is virtually impossible to tell if your are under "real sniper fire", but in those kind of situations you can never be too careful. If a shot rings out and snaps by i don't think the reaction would be "Oh its probably just a stray shot, no worries", because if you wrong, it could very well mean certain death. But I, like you, have not served so I do not know the "proper" procedure. I believe reactions would be more along the lines of "assume and prepare for the worst" though. So if you come under fire you must assume it is a sniper and react accordingly by finding cover and trying to loacate the shooter. Reading some war novels (non fictional) and articles this seems to be the case. There are many accounts where a unit is pinned down for hours by a couple of shots that don't even impact near them let alone hit anybody.

Share this post


Link to post
Share on other sites
This is something that I will look into further and hopefully fix soontm

LOL! - may I 'borrow' this? - I have a feeling I'll be needing it in a few weeks' time :)

But after reading PvPscene's post i think we have to be careful about just how much stuff we stuff into this mod. too much and I am sure we will see more lag (script or fps), more bugs, more mod compatability problems and less control for mission makers - all of which we want to avoid. So although we may look into these kind of things it may be best for useability reasons to keep things simple.

Thanks PvPscene. We will make sure watch out for this. By "script lag" would that mean scripts are delayed only or could it possibly also mean that scripts are totally cancelled/don't function properly?

PVPscene, thanks for the post. which confirmed some suspicions I had. Today I ran a quick test with a HAC + DAC alpha demo (i.e., lots of scripts running especially in the first couple of minutes) where I upped the total unit count from ~300 to ~500, Some of the units though released from DAC didn't get picked up by HAC (especially, all the choppers - which are, in fact, the last-spawned DAC group to be released and transferred). Release worked - DAC group unit count = 0 - but not all units transferred successfully. I'd already seen this in prototype missions where I did not have adequate sleeps & other controls to ensure that all release occurred before transfer, & all transfers were finished before HAC actually initialised.

I'll try to fit some re-runs in over the w/end & use the script instead of the PBO, them I can easily set it up to start a minute or two after the release/adding groups to HAC_included list/HAC initialisation.

Share this post


Link to post
Share on other sites
PVPscene, thanks for the post. which confirmed some suspicions I had. Today I ran a quick test with a HAC + DAC alpha demo (i.e., lots of scripts running especially in the first couple of minutes) where I upped the total unit count from ~300 to ~500, Some of the units though released from DAC didn't get picked up by HAC (especially, all the choppers - which are, in fact, the last-spawned DAC group to be released and transferred). Release worked - DAC group unit count = 0 - but not all units transferred successfully. I'd already seen this in prototype missions where I did not have adequate sleeps & other controls to ensure that all release occurred before transfer, & all transfers were finished before HAC actually initialised.

hmm. That's unfortunate. I am assuming HAC and DAC run fine without our mod, with these number of soldiers right? Do the problems mainly occur during the first couple minutes? Maybe we could take the same approach we did for asr, and simply disable TPWCAS until HAC and DAC have done their business... But I am assuming that both those mods run throughout a mission as well so it would probably cause problems then...

It might be worth it then to at least make a player centric version of our mod optional. Guess we'll see what other people have to say about these situations as well.

Share this post


Link to post
Share on other sites

*

hmm. That's unfortunate. I am assuming HAC and DAC run fine without our mod, with these number of soldiers right? Do th-e problems mainly occur during the first couple minutes? Maybe we could take the same approach we did for asr, and simply disable TPWCAS until HAC and DAC have done their business... But I am assuming that both those mods run throughout a mission as well so it would probably cause problems then...

It might be worth it then to at least make a player centric version of our mod optional. Guess we'll see what other people have to say about these situations as well.

Sorry, I think I was too terse.

Init sequence is roughly (using various custom scripts): initiate various DAC zones & spawn groups into arrays; release them once spawning is complete; add the groups to the HAC_included array; deactivate the zones; initialise HAC; start a few ancillary scripts (Celery's clearance script, for example). This can take around 2 minutes for say 40+ groups. 250-300 units (there are about 90-100 editor-placed units as well)..

Early tests showed that timing of the spawn/release/include scripts was crucial if all groups were to be successfully included in HAC. DAC does still run (as a variant has insurgent & guerrillas generated semi-randomly under DAC control so they are, erm, independent). Other scripts decide if/when a given DAC zone is temporarily re-activated to generate reinforcements, etc. So HAC & DAC run all the time.

I think the problem is that the suppression is kicking in too soon, & screwing up the timing of the other stuff. If I use the script version, I can start it from the end of the init after a sleep when all the spawn/release/transfer scripts & HAC initialisation have finished. It's only a problem because I have a rather unusual set-up in that set of missions. For the PBO version, as long as the user can adjust the start delay via the config.hpp it should not be a problem IMHO.

Later activate zone/release/include into HAC/deactivate zone (e.g., to provide reinforcements) events are less critical - far fewer groups being created & processed.

Share this post


Link to post
Share on other sites
tpw you may understand this right, but others probably don't. The PFEH is no silver or magic bullet. It is only a tool to ensure timely execution.

While I haven't checked your code, it sounds like you are doing non trivial checks there.

In other words combining this script/addon with other script heavy missions/scripts/addons, may lead to script lag. Aka delayed script execution.

Not of your code, but of the others.

Therefore it is imperative to keep the checks simple. While for some use cases / scenarios, this approach may be fine, for others it seems way too aggressive.

Again no general criticism, yet only to give everyone a broader perspective.

PS: My judgement be very wrong and I am always open to learn from others. :)

Thanks very much for the timely comments PVP.

You are absolutely right, calculations are still calculations, no matter how they are scheduled. My blurb is worded in such a way that perhaps gave you the misleading idea that I think CBA PFEH somehow magically lightens the CPU load. What I mean is the script itself is designed to minimise CPU load. Most of the time it just sits there each frame running as a timer for each unit, and checking if anyone has fired a bullet. Only if there is a bullet are the series of nested ifs tested, along with their varying calculations. I make no claims to be au fait with the vagaries of Arma engine task scheduling, and given the scarcity of CBA PFEH documentation, no claims being au fait with CBA PFEH either :)

In fact, as I mentioned early on in the development thread, I know the small amount I know about it by looking at a script written by Falcon_565 to implement my foggy breath parameters http://forums.bistudio.com/showthread.php?113653-Simple-Breath-Fog-Script&p=2094486&viewfull=1#post2094486.

The simple fact is, to get units to respond to really rapid events like a passing bullet, we needed to either: wrap the whole thing in a while loop running every 0.01 sec, which can run unreliably when there is a lot going on; or force things to be checked every frame. Hence CBA PFEH. If you can think of a third way, we would love to hear about it.

I'm well aware of the potential for something like this to cause lag and slowdown, and it's something that we've tried to keep an eye on throughout. I run a very mod heavy Arma (including potentially CPU sensitive ones like JSRS), and in the absence of real quantifiable data I'm surprised and pleased at how little impact TPWCAS actually has in terms of FPS loss or other script lag.

The system is set up to allow people to choose to run 3 levels of suppression, with associated levels of CPU use. There is actually a 4th mode, which is simply to not to use it at all if you think that it is too much of a drain :)

Share this post


Link to post
Share on other sites

Hello,

thank you so much, both of you, for this great and impressiv work.

I gave it a go and recorded a first visual impression on video.

Thanks once again for yours exellent job.

Cheers

McLupo

Share this post


Link to post
Share on other sites

Thanks McL, another quality video! Mind if we use it on the 1st post?

---------- Post added at 09:17 ---------- Previous post was at 09:10 ----------

Yep the suppression addon detects the skill of units after asr has set them all up. It then saves these skills. When suppression occurs ai's skills will then be manipulated, but after suppression ai will always recover to the asr skills that were saved initially.

This is assuming that all units are present at the beginning of the mission. Things do not work out so nicely when units spawn midway through a mission. This is because the suppression addons detects and saves the spawned units skills before asr has time to set them. This is something that I will look into further and hopefully fix soontm

Robalo has just released 1.15.1 in which...

Unit skill assignments moved to the main loop instead of init EHs. Every unit gets

set a "asr_ai_sys_siskill_configured" variable to true after it receives the new skills.

We should be able to get TPWCAS to query this variable and set skills accordingly.

Share this post


Link to post
Share on other sites
Yep the suppression addon detects the skill of units after asr has set them all up. It then saves these skills. When suppression occurs ai's skills will then be manipulated, but after suppression ai will always recover to the asr skills that were saved initially.

This is assuming that all units are present at the beginning of the mission. Things do not work out so nicely when units spawn midway through a mission. This is because the suppression addons detects and saves the spawned units skills before asr has time to set them. This is something that I will look into further and hopefully fix soontm

With ASR_AI 1.15.1 you can now set a variable for the unit like this:

_unit setVariable ["asr_ai_sys_aiskill_configured", false]

This will make ASR_AI reset the skills for that unit.

Share this post


Link to post
Share on other sites
With ASR_AI 1.15.1 you can now set a variable for the unit like this:
_unit setVariable ["asr_ai_sys_aiskill_configured", false]

This will make ASR_AI reset the skills for that unit.

Thanks so much for piping up on this thread Robalo. ASR AI is (IMHO) the gold standard AI mod and we are very keen for ours to play nicely with it. That variable is the key!

Share this post


Link to post
Share on other sites

Hello,

@[GR]Operative:Thx for your nice comment.

@tpw:

Mind if we use it on the 1st post?

Yes of course, you never need ask for permission! It´s a great pleasure when i can support your outstanding work with this simple job. :)

Use the vid as a showcase, if you want.

Tschüss ;)

McLupo

Share this post


Link to post
Share on other sites

ASR AI + suppression system is so good. AIs are so much more humanlike

Share this post


Link to post
Share on other sites
Hello,

thank you so much, both of you, for this great and impressiv work.

I gave it a go and recorded a first visual impression on video.

Thanks for the video Mclupo!

With ASR_AI 1.15.1 you can now set a variable for the unit like this:

Code:

_unit setVariable ["asr_ai_sys_aiskill_configured", false]

This will make ASR_AI reset the skills for that unit.

Thanks Robalo this is great news.

ASR AI + suppression system is so good. AIs are so much more humanlike

Thanks that's good too hear... especially from you.:)

Edited by -Coulum-

Share this post


Link to post
Share on other sites

tpw is there a reason you do not (or did not try to) use the danger FSM?

It may not pick up as many near shots, but it has this element in it.

This would avoid the constant checks by yourself. It might be worth looking into it.

Share this post


Link to post
Share on other sites
tpw is there a reason you do not (or did not try to) use the danger FSM?

It may not pick up as many near shots, but it has this element in it.

This would avoid the constant checks by yourself. It might be worth looking into it.

I know I personally just don't know much about .fsm's or there capabilites. Why would the danger .fsm be better? (just out of curiosity). Does the danger .fsm allow you to track whether a round come near but doesn't impact near a unit? because that is really the big pro of this mod. That ai will now react to rounds that don't impact or kill them. The suppression effects themselves aren't that extraordinary, its the fact that the ai seem more aware of what is and isn't dangerous to them, like bullets snapping by their ears.

Edited by -Coulum-

Share this post


Link to post
Share on other sites

No rest for the wicked...

Here's a development version to test out (script only at this point).

Changes:

  • Mode 3: Unit skill stuff has been moved off to its own loop which runs every 60 seconds, independently of the bullet check loop. Every 60 seconds, _unit setVariable ["asr_ai_sys_aiskill_configured", false]; is run, so that ASR AI 1.15.1 assigns its skills to the unit in the following minute. Any spawned units should gain proper ASR skills after a minute or so. This variable is simply ignored if ASR AI 1.15.1 isn't running.
  • Mode 3: Units go into combat mode when suppressed. At this stage, I can't think of a graceful way for them to come out of combat mode, so I hope they'll do it themselves when appropriate...
  • All modes: MASSIVE per frame calculation reduction, to speed things up. Non suppressed units will now have various variables set every 10 seconds instead of every frame.
  • All modes: Other small optimisations and turd polishing.

The deal is, you guys test it out, and if it passes muster I will PBO it.

Code:

/*
TPWC AI SUPPRESSION

Authors: TPW & -Coulum-
1.00 20120623 - Initial release
1.01 20120624 - Mode 3: Plays better with ASR AI skills, units go into combat mode when suppressed. All modes: massive per frame speed optimisations.   
*/

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

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

//Delay before suppression functions start. Allows time for other AI mods to set unit skills
tpwc_ai_sup_sleep = 5;

//Debugging. Will display red 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;

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

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

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

//Declare private variables
private ["_stanceregain","_skillregain","_unit","_bc","_shots","_originalaccuracy","_originalshake","_originalcourage","_general","_ball"]; 

//Allow time for ASR AI skills to propagate
sleep tpwc_ai_sup_sleep;

//Start hint   
if (tpwc_ai_sup_hint == 1) then {
0 = [] spawn {sleep 3; hintsilent "AI Suppress Active"; sleep 3; hintsilent ""};
};   


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

//Basic version
tpwc_ai_sup_basic =    
{  
   {  
   if (alive _x) then   
       {
       _unit = _x;     
       _stanceregain = _unit getvariable ["stanceregain", -1]; 

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

       if ( diag_ticktime >= _stanceregain) then   
           { 
		_ball = _unit getvariable "supball"; _ball hideobject true; 		
           _unit setvariable ["supshots", 0];  
           _unit setunitpos "auto"; 
		_unit setvariable ["stanceregain", diag_ticktime + 10]; 
           }; 

       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 ["stanceregain", diag_ticktime + 10];  
				_shots = _unit getvariable "supshots";  
				_unit setvariable ["supshots", _shots + _bc];  
				_shots = _unit getvariable "supshots"; 
				_unit setunitpos "middle";
				if (tpwc_ai_sup_debug == 1) then 
					{
					_ball = _unit getvariable "supball";_ball hideobject false;
					};			
				if (_shots > tpwc_ai_sup_st) then  
					{ 
					_unit setunitpos "down"; 
					};  
                   };  
               };  
           };  
       };   
   } foreach allunits;  
};  

//Lite version
tpwc_ai_sup_ai_sup_lite =    
{   
   {  
   if (alive _x) then   
       {
       _unit = _x;     
       _stanceregain = _unit getvariable ["stanceregain", -1]; 

       if (_stanceregain == -1) then   
           {     
           _unit setvariable ["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}];  
           _ball = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;_ball attachTo [_unit,[0,0,2]]; _unit setvariable ["supball",_ball];
		}; 

       if ( diag_ticktime >= _stanceregain) then   
           { 
		_ball = _unit getvariable "supball"; _ball hideobject true; 			
           _unit setvariable ["supshots", 0];  
           _unit setunitpos "auto"; 
		_unit setvariable ["stanceregain", diag_ticktime + 10]; 
           }; 

       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   
                   {  
                   if !(tpwc_ai_sup_mag in tpwc_ai_sup_mags) then   
                       { 
					_unit setvariable ["stanceregain", diag_ticktime + 10];  
                       _shots = _unit getvariable "supshots";  
                       _unit setvariable ["supshots", _shots + _bc];  
                       _unit setunitpos "middle";                                     
                       if ((side tpwc_ai_sup_fired != side _unit) && (vehicle _unit == _unit)) then   
                           {
						_shots = _unit getvariable "supshots"; 
						if (tpwc_ai_sup_debug == 1) then 
							{
							_ball = _unit getvariable "supball";_ball hideobject false;
							};
                           if (_shots > tpwc_ai_sup_st) then  
                               { 
                               _unit setunitpos "down"; 
                               };  
                           };   
                       };  
                   };  
               };  
           };  
       };   
   } foreach allunits;  
};  

//Full version
tpwc_ai_sup_full =    
{   
   {  
if (alive _x) then   
       {
       _unit = _x;     
       _skillregain = _unit getvariable ["skillregain", -1];   
       _stanceregain = _unit getvariable ["stanceregain", -1]; 

       if (_stanceregain == -1) then   
            {
		_unit setvariable ["suppressed", 0];
           _unit setvariable ["stanceregain", diag_ticktime]; 
           _unit setvariable ["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}];  
           _ball = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;_ball attachTo [_unit,[0,0,2]]; _unit setvariable ["supball",_ball];
		}; 

       if ( diag_ticktime >= _stanceregain) then   
           { 
		_ball = _unit getvariable "supball"; _ball hideobject true;		
           _unit setvariable ["supshots", 0];  
           _unit setunitpos "auto"; 
           _originalcourage = _unit getvariable "originalcourage"; 
           _general = _unit getvariable "general";   
           if((_unit skill "courage") < _originalcourage) then  
               {  
               _unit setskill ["courage",(_unit skill "courage")+(_general)*(0.003)];   
               } 
			else
			{
			_unit setvariable ["stanceregain", diag_ticktime + 10]; 	
			};
           }; 

       if (diag_ticktime >= _skillregain) then  
           {
		_unit setvariable ["suppressed", 0];			
           _originalaccuracy = _unit getvariable "originalaccuracy";    
           _originalshake = _unit getvariable "originalshake";             
           _originalcourage = _unit getvariable "originalcourage"; 
           _general = _unit getvariable "general";       
           if((_unit skill "aimingaccuracy") < _originalaccuracy) then  
               {  
               _unit setskill ["aimingaccuracy",(_unit skill "aimingaccuracy")+((_originalaccuracy-(_unit skill "aimingaccuracy"))*.01)];   
               }; 
           if((_unit skill "aimingshake") < _originalshake) then  
               {  
               _unit setskill ["aimingshake",(_unit skill "aimingshake")+((_originalshake-(_unit skill "aimingshake"))*.01)];  
               };
		if (((_unit skill "aimingshake") == _originalshake) && ((_unit skill "aimingaccuracy") == _originalaccuracy)) then 
			{
			_unit setvariable ["skillregain", diag_ticktime + 10];
			}
		}; 

       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   
                   {  
                   if !(tpwc_ai_sup_mag in tpwc_ai_sup_mags) then   
                       { 
					_unit setvariable ["skillregain", diag_ticktime + (random 4)-((_unit getvariable "general")+(_unit getvariable "originalcourage"))];   
                       _unit setvariable ["stanceregain", diag_ticktime + 10];  
                       _shots = _unit getvariable "supshots";  
                       _unit setvariable ["supshots", _shots + _bc];  
                       _unit setunitpos "middle";                                     
                       if ((side tpwc_ai_sup_fired != side _unit) && (vehicle _unit == _unit)) then   
                           {
						_shots = _unit getvariable "supshots"; 
						_originalaccuracy = _unit getvariable "originalaccuracy";   
						_originalshake = _unit getvariable "originalshake"; 
						_originalcourage = _unit getvariable "originalcourage";  
						_general = _unit getvariable "general";
						_unit setvariable ["suppressed", 1];
						_unit setBehaviour "combat";
                           _unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.003)];  
                           _unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.003)]; 
                           _unit setskill ["courage",_originalcourage*_originalcourage*_general-(_shots*(1-_general)*.003)];
						if (tpwc_ai_sup_debug == 1) then 
							{
							_ball = _unit getvariable "supball";_ball hideobject false;
							};						
                           if (_shots > tpwc_ai_sup_st) then  
                               { 
                               _unit setunitpos "down"; 
                               };  
                           };   
                       };  
                   };  
               };  
           };  
       };   
   } foreach allunits;  
};  

//Grab unit skills periodically
tpwc_ai_sup_skillset =
{
while {true} do 
{
	{
	_unit = _x;
	_originalaccuracy = _unit skill "aimingaccuracy"; 
	_unit setvariable ["originalaccuracy", _originalaccuracy];  
	_originalshake = _unit skill "aimingshake";  
	_unit setvariable ["originalshake", _originalshake]; 
	_originalcourage = _unit skill "courage"; 
	_unit setvariable ["originalcourage", _originalcourage];  
	_general = _unit skill "general"; 
	_unit setvariable ["general", _general]; 
	_unit setVariable ["asr_ai_sys_aiskill_configured", false]; //force ASR AI to reset unit skills, if it is running
	} foreach allunits;
sleep 60;
};
};

//CALL APPROPRIATE FUNCTION USING PER FRAME EVENTHANDLER
if (tpwc_ai_sup_mode == 1) then {[tpwc_ai_sup_basic,0] call cba_fnc_addPerFrameHandler};  
if (tpwc_ai_sup_mode == 2) then {[tpwc_ai_sup_lite,0] call cba_fnc_addPerFrameHandler}; 
if (tpwc_ai_sup_mode == 3) then {[] spawn tpwc_ai_sup_skillset;[tpwc_ai_sup_full,0] call cba_fnc_addPerFrameHandler}; 

Share this post


Link to post
Share on other sites

Some nice improvements tpw. Making the "10 second check" to reapply skils and stance is a really great idea and should vastly reduce the possibility of script lag or whatever. But one possible problem is that when ai skills are regenerating they reegenerate slower and slower the closer they get to there original skill. it actually creates an asymptotic kind of behaviour where they will never actually 100% reach their original skill, unless of course arma round up its numbers. I assume it wouldn't though so instead of

if (((_unit skill "aimingshake") == _originalshake) && ((_unit skill "aimingaccuracy") == _originalaccuracy)) then  
               { 
               _unit setvariable ["skillregain", diag_ticktime + 10]; 
               } 

it should be

    if (((_unit skill "aimingshake") > (_originalshake - 0.1)) && ((_unit skill "aimingaccuracy") > (_originalaccuracy - 0.1))) then  
               { 
               _unit setvariable ["skillregain", diag_ticktime + 10]; 

Easy fix that I have posted in full below. Also, concerning behaviour, I think that it would be a real bitch to mission makers. I know I suggested it before, but what if a stray bullet landed near a unit, before fighting even began, and caused the entire group to maker their kilometre walk in combat mode rather than aware. It is possible to detect and reset the ai's behaviour with

_originalbehaviour = behaviour _unit;
...
_unit setbehaviour str(_originalbehaviour)

But what's stumping me is when to detect the behaviour and how to avoid detecting a combat behaviour that was forced by incoming fire...

anyways, I am going to sleep on it. Here's the script with the small little change to the skill check. Great job and keep up the good work!

/* 
TPWC AI SUPPRESSION 

Authors: TPW & -Coulum- 
1.00 20120623 - Initial release 
1.01 20120624 - Mode 3: Plays better with ASR AI skills, units go into combat mode when suppressed. All modes: massive per frame speed optimisations.    
*/ 

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

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

//Delay before suppression functions start. Allows time for other AI mods to set unit skills 
tpwc_ai_sup_sleep = 5; 

//Debugging. Will display red 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; 

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

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

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

//Declare private variables 
private ["_stanceregain","_skillregain","_unit","_bc","_shots","_originalaccuracy","_originalshake","_originalcourage","_general","_ball"];  

//Allow time for ASR AI skills to propagate 
sleep tpwc_ai_sup_sleep; 

//Start hint    
if (tpwc_ai_sup_hint == 1) then { 
0 = [] spawn {sleep 3; hintsilent "AI Suppress Active"; sleep 3; hintsilent ""}; 
};    


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

//Basic version 
tpwc_ai_sup_basic =     
{   
   {   
   if (alive _x) then    
       { 
       _unit = _x;      
       _stanceregain = _unit getvariable ["stanceregain", -1];  

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

       if ( diag_ticktime >= _stanceregain) then    
           {  
           _ball = _unit getvariable "supball"; _ball hideobject true;          
           _unit setvariable ["supshots", 0];   
           _unit setunitpos "auto";  
           _unit setvariable ["stanceregain", diag_ticktime + 10];  
           };  

       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 ["stanceregain", diag_ticktime + 10];   
                   _shots = _unit getvariable "supshots";   
                   _unit setvariable ["supshots", _shots + _bc];   
                   _shots = _unit getvariable "supshots";  
                   _unit setunitpos "middle"; 
                   if (tpwc_ai_sup_debug == 1) then  
                       { 
                       _ball = _unit getvariable "supball";_ball hideobject false; 
                       };             
                   if (_shots > tpwc_ai_sup_st) then   
                       {  
                       _unit setunitpos "down";  
                       };   
                   };   
               };   
           };   
       };    
   } foreach allunits;   
};   

//Lite version 
tpwc_ai_sup_ai_sup_lite =     
{    
   {   
   if (alive _x) then    
       { 
       _unit = _x;      
       _stanceregain = _unit getvariable ["stanceregain", -1];  

       if (_stanceregain == -1) then    
           {      
           _unit setvariable ["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}];   
           _ball = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;_ball attachTo [_unit,[0,0,2]]; _unit setvariable ["supball",_ball]; 
           };  

       if ( diag_ticktime >= _stanceregain) then    
           {  
           _ball = _unit getvariable "supball"; _ball hideobject true;              
           _unit setvariable ["supshots", 0];   
           _unit setunitpos "auto";  
           _unit setvariable ["stanceregain", diag_ticktime + 10];  
           };  

       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    
                   {   
                   if !(tpwc_ai_sup_mag in tpwc_ai_sup_mags) then    
                       {  
                       _unit setvariable ["stanceregain", diag_ticktime + 10];   
                       _shots = _unit getvariable "supshots";   
                       _unit setvariable ["supshots", _shots + _bc];   
                       _unit setunitpos "middle";                                      
                       if ((side tpwc_ai_sup_fired != side _unit) && (vehicle _unit == _unit)) then    
                           { 
                           _shots = _unit getvariable "supshots";  
                           if (tpwc_ai_sup_debug == 1) then  
                               { 
                               _ball = _unit getvariable "supball";_ball hideobject false; 
                               }; 
                           if (_shots > tpwc_ai_sup_st) then   
                               {  
                               _unit setunitpos "down";  
                               };   
                           };    
                       };   
                   };   
               };   
           };   
       };    
   } foreach allunits;   
};   

//Full version 
tpwc_ai_sup_full =     
{    
   {   
   if (alive _x) then    
       { 
       _unit = _x;      
       _skillregain = _unit getvariable ["skillregain", -1];    
       _stanceregain = _unit getvariable ["stanceregain", -1];  

       if (_stanceregain == -1) then    
            { 
           _unit setvariable ["suppressed", 0]; 
           _unit setvariable ["stanceregain", diag_ticktime];  
           _unit setvariable ["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}];   
           _ball = "Sign_sphere25cm_EP1" createvehicle getposatl _unit;_ball attachTo [_unit,[0,0,2]]; _unit setvariable ["supball",_ball]; 
           };  

       if ( diag_ticktime >= _stanceregain) then    
           {  
           _ball = _unit getvariable "supball"; _ball hideobject true;         
           _unit setvariable ["supshots", 0];   
           _unit setunitpos "auto";  
           _originalcourage = _unit getvariable "originalcourage";  
           _general = _unit getvariable "general";	    
           if((_unit skill "courage") < (_originalcourage - 0.1)) then   
               {   
               _unit setskill ["courage",(_unit skill "courage")+(_general)*(0.003)];    
               }  
               else 
               { 
               _unit setBehaviour "aware";
	_unit setvariable ["stanceregain", diag_ticktime + 10];      
               }; 
           };  

       if (diag_ticktime >= _skillregain) then   
           { 
           _unit setvariable ["suppressed", 0];             
           _originalaccuracy = _unit getvariable "originalaccuracy";     
           _originalshake = _unit getvariable "originalshake";              
           _originalcourage = _unit getvariable "originalcourage";  
           _general = _unit getvariable "general";        
           if((_unit skill "aimingaccuracy") < _originalaccuracy) then   
               { 
               _unit setskill ["aimingaccuracy",(_unit skill "aimingaccuracy")+((_originalaccuracy-(_unit skill "aimingaccuracy"))*.01)];    
               };  
           if((_unit skill "aimingshake") < _originalshake) then   
               {   
               _unit setskill ["aimingshake",(_unit skill "aimingshake")+((_originalshake-(_unit skill "aimingshake"))*.01)];   
               }; 
           if (((_unit skill "aimingshake") > (_originalshake - 0.1)) && ((_unit skill "aimingaccuracy") > (_originalaccuracy - 0.1))) then  
               { 
               _unit setvariable ["skillregain", diag_ticktime + 10]; 
               } 
           };  

       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    
                   {   
                   if !(tpwc_ai_sup_mag in tpwc_ai_sup_mags) then    
                       {  
                       _unit setvariable ["skillregain", diag_ticktime + (random 4)-((_unit getvariable "general")+(_unit getvariable "originalcourage"))];    
                       _unit setvariable ["stanceregain", diag_ticktime + 10];   
                       _shots = _unit getvariable "supshots";   
                       _unit setvariable ["supshots", _shots + _bc];   
                       _unit setunitpos "middle";                                      
                       if ((side tpwc_ai_sup_fired != side _unit) && (vehicle _unit == _unit)) then    
                           { 
                           _shots = _unit getvariable "supshots";  
                           _originalaccuracy = _unit getvariable "originalaccuracy";    
                           _originalshake = _unit getvariable "originalshake";  
                           _originalcourage = _unit getvariable "originalcourage";   
                           _general = _unit getvariable "general"; 
                           _unit setvariable ["suppressed", 1]; 
                           _unit setBehaviour "combat"; 
                           _unit setskill ["aimingaccuracy",_originalaccuracy*_originalcourage*_general-(_shots*(1-_general)*.003)];   
                           _unit setskill ["aimingshake",_originalshake*_originalcourage*_general-(_shots*(1-_general)*.003)];  
                           _unit setskill ["courage",_originalcourage*_originalcourage*_general-(_shots*(1-_general)*.003)]; 
                           if (tpwc_ai_sup_debug == 1) then  
                               { 
                               _ball = _unit getvariable "supball";_ball hideobject false; 
                               };                         
                           if (_shots > tpwc_ai_sup_st) then   
                               {  
                               _unit setunitpos "down";  
                               };   
                           };    
                       };   
                   };   
               };   
           };   
       };    
   } foreach allunits;   
};   

//Grab unit skills periodically 
tpwc_ai_sup_skillset = 
{ 
while {true} do  
   { 
       { 
       _unit = _x; 
       _originalaccuracy = _unit skill "aimingaccuracy";  
       _unit setvariable ["originalaccuracy", _originalaccuracy];   
       _originalshake = _unit skill "aimingshake";   
       _unit setvariable ["originalshake", _originalshake];  
       _originalcourage = _unit skill "courage";  
       _unit setvariable ["originalcourage", _originalcourage];   
       _general = _unit skill "general";  
       _unit setvariable ["general", _general];  
       _unit setVariable ["asr_ai_sys_aiskill_configured", false]; //force ASR AI to reset unit skills, if it is running 
       } foreach allunits; 
sleep 60; 
   }; 
}; 

//CALL APPROPRIATE FUNCTION USING PER FRAME EVENTHANDLER 
if (tpwc_ai_sup_mode == 1) then {[tpwc_ai_sup_basic,0] call cba_fnc_addPerFrameHandler};   
if (tpwc_ai_sup_mode == 2) then {[tpwc_ai_sup_lite,0] call cba_fnc_addPerFrameHandler};  
if (tpwc_ai_sup_mode == 3) then {[] spawn tpwc_ai_sup_skillset;[tpwc_ai_sup_full,0] call cba_fnc_addPerFrameHandler};  

--edit--

Oh and one quick question.

How do you make sure that the correct skills are being detected when this loop kicks in every 60 seconds?

tpwc_ai_sup_skillset = 
{ 
while {true} do  
   { 
       { 
       _unit = _x; 
       _originalaccuracy = _unit skill "aimingaccuracy";  
       _unit setvariable ["originalaccuracy", _originalaccuracy];   
       _originalshake = _unit skill "aimingshake";   
       _unit setvariable ["originalshake", _originalshake];  
       _originalcourage = _unit skill "courage";  
       _unit setvariable ["originalcourage", _originalcourage];   
       _general = _unit skill "general";  
       _unit setvariable ["general", _general];  
       _unit setVariable ["asr_ai_sys_aiskill_configured", false]; //force ASR AI to reset unit skills, if it is running 
       } foreach allunits; 
sleep 60; 

For example what happens if this runs during the middle of a firefight when all units skills are low, and not at their true norm?

Edited by -Coulum-

Share this post


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

×