Jump to content
Sign in to follow this  
zapat

How to check if group is attacked?

Recommended Posts

I have a dynamically created group, that is controlled by an fsm.

What would be the simpliest way of making an "is group attacked?" condition?

My closest bet is the "findNearestEnemy" command: if it returns something, there is an enemy near.

But this won't tell if my group has been attacked already, or just spotted an enemy.

I would need a condition, that returns true, if the group is attacked: that is shots have been fired upon them.

I guess it is not that hard, since the engine knows this, I just can't figure it out how.

  • Like 1

Share this post


Link to post
Share on other sites

Have a look at these two functions I've written for my RUBE library.

That might give you some ideas (or maybe they're already fine for your purposes):

fn_getEnemyContact.sqf:

/*
  Author:
   rübe

  Description:
   returns a list of enemy targets a group has made contact with.

  Parameter(s):
   _this: group (group)

   OR 

   _this select 0: group (group)
   _this select 1: range (scalar)
                   - optional, default = 1000
   _this select 2: cost threshold (scalar)
                      the sum of all targets subjective costs has to
                   reach this value befor any contact gets reported at
                   all. Note that sums might be HUGE numbers (e+006 and bigger)
                   - optional, default = 0

  Returns:
   an empty array for no contacts OR
   [
      0: sum of contact costs
      1: contacts (array of contacts)
         - "contact" is what nearTargets returns, which is an array:
           [
              0: position (percieved/inaccurate!)
              1: type (percieved)
              2: side (percieved)
              3: subjective cost (positive for enemies, the more important/dangerous the higher)
              4: object
              5: position accuracy
           ]
      2: distance from leader to the nearest target (scalar)
      3: highest target(s) knownsAbout about a single unit from the group (scalar)
      4: men:   array of indices for contacts of type "man"
      5: cars:  array of indices for contacts of type "car"
      6: tanks: array of indices for contacts of type "tank"
      7: air:   array of indices for contacts of type "air"
      8: other: array of indices for contacts of type "other"
                (such as motorcycles, ships, static weapons, ...)
   ]

   ^^ the indices of 4-8 are refering to the contacts returned in 1. So you may
   easily check, if some kind of contact has been made (tanks? only men? etc..)
*/

private ["_group", "_range", "_threshold", "_leader", "_units", "_side", "_targets", "_sum", "_beenSpotted", "_ka", "_ntDistance", "_dist", "_contact", "_kindOfMan", "_kindOfCar", "_kindOfTank", "_kindOfAir", "_kindOfOther", "_index"];

_group = grpNull;
_range = 1000;
_threshold = 0;

if ((typeName _this) == "ARRAY") then
{
  _group = _this select 0;
  if ((count _this) > 1) then
  {
     _range = _this select 1;
  };
  if ((count _this) > 2) then
  {
     _threshold = _this select 2;
  };
} else 
{
  _group = _this;
};


// make sure we have a group in case a unit
// got passed...
if ((typeName _group) != "GROUP") then
{
  _group = group _group;
};



_leader = leader _group;
_units = units _group;
_side = side _leader;
_targets = _leader nearTargets _range;
_sum = 0;
_beenSpotted = 0;
_ntDistance = 999999;
_contact = [];

_kindOfMan = [];
_kindOfCar = [];
_kindOfTank = [];
_kindOfAir = [];
_kindOfOther = [];

{
  // different side?
  if ((_x select 2) != _side) then
  {
     // not unknown!
     if (format["%1", (_x select 2)] != "UNKNOWN") then
     {
        // hostile side?
        if ((_side getFriend (_x select 2)) < 0.6) then
        {
           _sum = _sum + (_x select 3);
           _index = count _contact;
           _contact set [_index, _x];

           // distance
           _dist = (_x select 4) distance _leader;
           if (_dist < _ntDistance) then
           {
              _ntDistance = _dist;
           };

           // has anyone been spotted by these guys?
           for "_i" from 0 to ((count _units) - 1) do
           {
              _ka = (_x select 4) knowsAbout (_units select _i);
              if (_ka > _beenSpotted) then
              {
                 _beenSpotted = _ka;
              };
           };

           // register kind of contact
           switch (true) do
           {
              case (((_x select 4) isKindOf "Man")):  { _kindOfMan set [(count _kindOfMan), _index]; };
              case (((_x select 4) isKindOf "Car")):  { _kindOfCar set [(count _kindOfCar), _index]; };
              case (((_x select 4) isKindOf "Tank")): { _kindOfTank set [(count _kindOfTank), _index]; };
              case (((_x select 4) isKindOf "Air")):  { _kindOfAir set [(count _kindOfAir), _index]; };
              default
              {
                 _kindOfOther set [(count _kindOfOther), _index];
              };
           };
        };
     };
  };
} forEach _targets;

// no contacts made
if ((count _contact) == 0) exitWith
{
  []
};

// threshold not exceeded?
if (_sum < _threshold) exitWith
{
  []
};


// return enemy contact
[
  _sum,
  _contact,
  _ntDistance,
  _beenSpotted,
  _kindOfMan,
  _kindOfCar,
  _kindOfTank,
  _kindOfAir,
  _kindOfOther
]

fn_isEngaging.sqf

/*
  Author:
   rübe

  Description:
   returns true if anybody of the given unit(s)/group is engaging

  Parameter(s):
   _this: unit(s)/group (unit, array of units or group)

  Returns:
   boolean
*/

private ["_units", "_engaging"];

_units = [];
_engaging = false;

switch (true) do
{
  case ((typeName _this) == "ARRAY"):
  {
     _units = _this;
  };
  case ((typeName _this) == "GROUP"):
  {
     _units = units _this;
  };
  default
  {
     _units = [_this];
  };
};

{
  if ((currentCommand _x) in ["ATTACK", "ATTACKFIRE", "FIRE"]) exitWith
  {
     _engaging = true;
  };
} forEach _units;


// return status
_engaging

  • Thanks 1

Share this post


Link to post
Share on other sites

Thanks Rübe, I've seen these: I thought there is an easier one though, and the first script doesn't tell if they are attacked or not either.

This would go to an FSM condition, with like 4-5 other conditions, run on around 20ish number of groups all the time. So light-weight is essential.

Although I like the engaged script: if the group is attacked, they fire back, right? So I guess I can get a working method by a simple currentCommand check. A bit of delay (between being attacked and really firing back) is tolerable.

Share this post


Link to post
Share on other sites
This would go to an FSM condition, with like 4-5 other conditions, run on around 20ish number of groups all the time. So light-weight is essential.

Well, then maybe it's time to restructure the flow of your fsm. Do one check after another and break that flow of checks if one of them fires/triggers...

Another strategy is to have an ideling delay-loop (lowest priority on the main-loop) with a delay-condition (time - _t > _delay) as only way back to the main-loop, so all the "heavy" checks are only run all 2 or 3 seconds (or even more and maybe slightly randomised).

Not everything needs to react immediately and often such delayed reactions are absolutely fine. Maybe you need to run two fsm, one without and one with such delays, though there comes an overhead with this, so I'm not sure this would be actually a good idea... haha

But I wouldn't worry too much about performance until you actually see a problem. ;)

  • Like 1

Share this post


Link to post
Share on other sites

Another way is to check combat mode:

AI groups change to beheaviour mode once they are engaged, and revert to original mode once "safe"

If unit is in aware by default, its most likely being engaged or engaging if its in combat mode.

This check will ofc fail if units move around in default combat mode.

if (([url="http://community.bistudio.com/wiki/behaviour"]beheaviour[/url] groupname) == "COMBAT") then {hint "group is in combat"};

Above is sqf and not fsm, but you probably know the equivalent to fsm.

Edit: brainfart resulted in me typing combatmode and linking to that, but i meant ofc beheaviour mode to be combat. above fixed...

Edited by Demonized
edit.
  • Like 1

Share this post


Link to post
Share on other sites
Do one check after another and break that flow of checks if one of them fires/triggers...

This is why I love to ask questions in the forums! This is a cool thing, and it is highly probable that I would have never thought of this simple idea. Time to reFSMize my mission!! :)

Demoinzed: yeah, this could be a good method too, I may combine them... :)

Edited by zapat
  • 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
Sign in to follow this  

×