Jump to content

Recommended Posts

I made a script that choreographs an execution sequence (yeah its pretty grim). For it to work there is constant meddling in AI behaviour settings. The problem is that some enemy might suddenly appear and try to stop it, so AI must react accordingly and return to its normal settings if that happens. To do this I put a check for enemies before each block in the execution sequence. It works but it seems an overcomplicated way of achieving this. I read about exitwith {} and breakout but they seem to be of use inside control structures or loops and not for interrupting a single sequence. To stop a script from continue and do something else instead is a pretty useful thing even in other situations so I was wondering: anybody have a solution for this?

 

//execution script
//place a group of soldiers in front of a group of prisioners. name each group "executioners" and "prisioners" and launch this script; other soldiers can also be around and they won't engage the prisioners.

//systemchat "start";
private _exec = units executioners;
private _pris = units prisioners;
private _officer = leader executioners;
private _all = [];
private _squad = _exec - [_officer];

//put all friendly units to executioners side in a radius of 300 in an array
{
	if (((_x distance (getpos _officer)) < 300) && (side _x == side executioners)) then {
		_all pushback _x;
	};
} forEach allunits;

//make all of them pacifists
{
	_x setbehaviour "careless";
	_x setcombatMode "blue";
} forEach _all;

//take note of each units voice to give them back later, also officers weapons
private _voicesquad = [];
private _voicepris = [];
{_voicesquad pushback (speaker _x)} forEach _squad;
{_voicepris pushback (speaker _x)} forEach _pris;
private _officervoice = speaker _officer;
private _weapon = primaryweapon (_squad select 0);
private _mags = (magazines (_squad select 0)) select 0;

//executioners and prisioners stand up, shut up and stay put
{
	_x disableAI "path";
	_x setunitPos "UP";
	_x setSpeaker "NoVoice"
} forEach (_exec + _pris);

//remove stuff from prisioners
{
	private _unit = _x;
	_unit setcaptive true;
	removeallweapons _unit;
	removeAllAssignedItems _unit;
	{_unit removeMagazine _x} forEach magazines _unit;
} forEach _pris;

//create directions for facing
private _dir = (_officer getdir (_exec select 1)) - 90;
private _front = _officer getpos [300,_dir];
private _right = _officer getpos [300,_dir + 90];
private _back = _officer getpos [300,_dir + 180];
private _left = _officer getpos [300,_dir - 90];
private _pos = getpos _officer;
removeallweapons _officer;
removeAllAssignedItems _officer;

//make groups face each other
{_x doWatch _front} forEach _squad;
{_x doWatch _back} forEach _pris;

//STOP SCRIPT CONDITION---------------------------------------------
//if an enemy comes the execution sequence should interrupt and the menace dealt with.
private _execution = true;
_reset = {
	{
		_x setbehaviour "safe";
		_x enableAI "path";
		_x enableAI "target";
		_x setcombatmode "YELLOW";
		_x forceWalk false;
	} forEach _all;
	{
		_x setSpeaker (_voicesquad select _forEachIndex);
	} forEach _squad;
	{
		_x setSpeaker (_voicepris select _forEachIndex);
		_x setcaptive false;
	} forEach _pris;
	_officer setSpeaker _officervoice;
	_officer addmagazines [_mags,4];
	_officer addweapon _weapon;
	_execution = false;
};

//the above reset function will be called if an enemy (with the exeception of the prisioner group) closer than 400 is detected. the enemy detection function will be called before every step in the execution sequence
private _enemypresent = false;
_checkenemies = {
	private _enemy = [];
	{
		if (((_x distance _pos) < 400) && (side _x != side executioners)) then {
			_enemy pushback _x;
		};
	} forEach allunits;
	_enemy = _enemy - _pris;
	{
		if ((side executioners) knowsAbout _x > 1.5) then {_enemypresent = true};
	} forEach _enemy;
	if _enemypresent then {call _reset};
};

sleep 5;
//systemchat "aim";
//AIM-------------------------------------------------
call _checkenemies;
if _execution then {
	executioners setBehaviour "aware";
	{_x setCaptive false} forEach _pris;
	{_x doTarget (leader prisioners)} forEach _squad;
	_officer doWatch _right;
	sleep 5;
};

//systemchat "fire";
//FIRE-------------------------------------------------

_officer setspeaker _officervoice;
executioners setcombatmode "red";

//wait until all prisioners are dead
waituntil  {{alive _x} count _pris == 0};

//systemchat "lower weapons";
//LOWERWEAPONS----------------------------------------
call _checkenemies;
if _execution then {
	{
		_x disableAI 'TARGET';
	} forEach _squad;
	executioners setbehaviour "careless";
	_officer doWatch _right;
	_officer setspeaker "noVoice";
	sleep 5;
};
//systemchat "turn";
//TURN------------------------------------------------
call _checkenemies;
if _execution then {
		{_x dowatch _back} forEach _squad;
		sleep 5;
};

//systemchat "move";
//MOVE-------------------------------------------------
call _checkenemies;
if _execution then {
	{
		_x enableAI "path";
		_x enableAI 'TARGET';
		_x forceWalk true;
		_x doMove _back;
	} forEach _squad;
	sleep 10;
};

//AT EASE-----------------------------------------------
call _checkenemies;
if _execution then {
	{_x doWatch _right} forEach _exec;
	_officer enableAI "path";
	_squad doFollow _officer;
	//return weapon to officer
	_officer addmagazines [_mags,4];
	_officer addweapon _weapon;
	_officer setspeaker _officervoice;
	sleep 2;
};

//GARRISON-------------------------------------------------
call _checkenemies;
if _execution then {
	{_x setbehaviour "safe"} forEach _all;
	private _defendpos = getpos (nearestbuilding _pos);
	if (_defendpos isEqualTo [0,0,0]) then {_defendpos = _pos};
	[executioners,_defendpos,100,3,0,0.5] call CBA_fnc_taskDefend;
	sleep 2;
};
//RETURN TO NORMAL-----------------------------------------
call _reset;

 

  • Like 1

Share this post


Link to post
Share on other sites

Yes I saw that some people say it's not reliable for exiting scripts but even assuming it works still I would have to put before each block something like:

 

 if  _enemypresent then exitwith {_reset};

 

Is there a way to make it like a loop running parallel to the main script that checks continuously if _enemypresent is true and then make the main body to exit?

Share this post


Link to post
Share on other sites

i mean if you want it to be a loop, you could store all the code sequences in an array and then loop through them via foreach or count and inside that you would only need to put one single exitwith check...i think. so if you don't want to copy paste it between every sequence, just create the needed structure to avoid that more generally speaking.

 

_execution = 
[
  {
      params ["_units", "_args"];

      ///actual stuff
      systemchat "sequence 1";
  },
  {
      params ["_units", "_args"];

      ///actual stuff
      systemchat "sequence 2";
  },
  {
      params ["_units", "_args"];

      ///actual stuff
      systemchat "sequence 3";
  }
];

{
	if _enemyclose exitwith {systemchat "execution aborted"};
	[_units, [_arg1, _arg2]] call _x
} foreach _execution;

this is semi pseudo code. not tested. but to illustrate what i meant.

  • Like 2

Share this post


Link to post
Share on other sites

Create a trigger (enemy faction present & detected by friendly faction), add in the on-activation of the trigger the following code:

_nil = [] call resetFunction;
terminate _scripthandle;

 

You'll have to make the reset function global to be able to do this.

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites

They are good ideas for the next time I need this. Thanks a lot!

It also hit me based on stanhope's suggestion, that if I don't want to use a trigger I could start another script that would be running parallel in loop, checking the condition and terminate the main script via scripthandle. 

Share this post


Link to post
Share on other sites

oh yea i didn't notice you used sleep that extensively. terminate makes a lot more sense in that case.

Share this post


Link to post
Share on other sites

You can also assign a global handle to it if needed outside.

someHandle = ...
termiante someHandle;

Also keep in mind:

Note: The given script will not terminate immediately upon terminate command execution, it will do so the next time the script is processed by the scheduler.

 

  • Like 1

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×