tpw 2315 Posted June 29, 2012 (edited) 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 June 29, 2012 by tpw Share this post Link to post Share on other sites
tpw 2315 Posted June 30, 2012 (edited) 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 June 30, 2012 by tpw Share this post Link to post Share on other sites
Muahaha 10 Posted June 30, 2012 (edited) Thanks for the hardwork tpw and coulum, this will be a must have mod indeed. -deleted- Edited June 30, 2012 by Muahaha Share this post Link to post Share on other sites
CameronMcDonald 146 Posted June 30, 2012 PBO it up and I'm down for a test this evening. :) Thanks again for the effort. Share this post Link to post Share on other sites
tpw 2315 Posted June 30, 2012 1.04 addon and script version: http://filesonly.com/download.php?file=552TPWC_AI_SUPPRESS104.zip Good luck! Share this post Link to post Share on other sites
fabrizio_t 58 Posted June 30, 2012 Hi alljiltedjock'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
CameronMcDonald 146 Posted June 30, 2012 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
tpw 2315 Posted June 30, 2012 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
fabrizio_t 58 Posted June 30, 2012 (edited) 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 June 30, 2012 by fabrizio_T Share this post Link to post Share on other sites
tpw 2315 Posted June 30, 2012 (edited) 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 June 30, 2012 by tpw Share this post Link to post Share on other sites
fabrizio_t 58 Posted June 30, 2012 (edited) 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 June 30, 2012 by fabrizio_T Share this post Link to post Share on other sites
tpw 2315 Posted June 30, 2012 (edited) 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 June 30, 2012 by tpw Share this post Link to post Share on other sites
chrisb 196 Posted June 30, 2012 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
orcinus 121 Posted June 30, 2012 (edited) 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 June 30, 2012 by Orcinus Share this post Link to post Share on other sites
fabrizio_t 58 Posted June 30, 2012 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
tpw 2315 Posted June 30, 2012 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
fabrizio_t 58 Posted June 30, 2012 (edited) 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 June 30, 2012 by fabrizio_T Share this post Link to post Share on other sites
orcinus 121 Posted June 30, 2012 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
fabrizio_t 58 Posted June 30, 2012 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
ollem 4 Posted June 30, 2012 EDITWe'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
CameronMcDonald 146 Posted June 30, 2012 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
tpw 2315 Posted June 30, 2012 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
fabrizio_t 58 Posted June 30, 2012 (edited) 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 June 30, 2012 by fabrizio_T Share this post Link to post Share on other sites
oldy41 61 Posted June 30, 2012 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
CameronMcDonald 146 Posted June 30, 2012 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