Jump to content
Sign in to follow this  
polar bear

New Event Handler: Enemy Detected

Recommended Posts

It would be great if there was an event handler for enemy detected. It would be fired in the exact same situation where an AI calls out "enemy man ahead 200 meters" etc.

It would be useful for a lot of things:

-- For units set up as sentries, you could have them call in a patrol to the area where they spotted an enemy

-- You could take greater control over the action an AI takes upon sighting an enemy by hooking specific commands/behavior on that function

-- A unit trying to evade detection by the enemy could stealthily move away in the opposite direction (e.g., a unit you are meant to be hunting)

-- When detected by the player specifically you could hook mission events off the detection of a specific enemy, or detection of the player by a specific unit

Yes you can do much of this with triggers but triggers only take you so far:

-- Detected by triggers don't fire at the same times

-- It's a pain to make the detected by trigger follow a unit around the map

-- Detected by triggers are less specific and do not allow you to take different action based on WHICH group/unit spotted the enemy

Finally I suspect that in general event handlers are more efficient than triggers, as triggers have to be evaluated repeatedly whereas presumably the event handlers fire on events that were already being handled by the game engine and therefore add only their own execution to the CPU load.

Thoughts?

Edited by Polar Bear

Share this post


Link to post
Share on other sites

Problem scenario:

I create a mission that uses mass reveal as a basis for "information sharing". That mission is played on a server who use an addon that uses this new Detection Event Handler. This is setup to do many things by the addon. But mission is now unplayable because it is fired for so many units at once, bringing the server on its knees.

So you either need a way to prevent scripted information sharing to set off this eventhandler, or a method in the eventhandler to not react to scripted reveal.

Share this post


Link to post
Share on other sites

I agree it should probably only fire for a unit that actually makes visual contact with the enemy as opposed to those who learn about the enemy via the radio. In fact executing a mass reveal is exactly the kind of thing you might want to do inside the event handler. I don't think a reveal causes the AI to announce the location of an enemy unit either so it's already being handled differently by the engine.

More than just performance issues it'd probably also lead to some counter-intuitive stuff if it fired for a unit half way across the map on a 'reveal' and it's hard to think of situations where that capability would be useful.

Edited by Polar Bear

Share this post


Link to post
Share on other sites
Thoughts?

If you really need this right now and here, you can easily "roll your own (pseudo-)event handler" for something like "onContact" with the command nearTargets and some sort of loop.

For example, let's have this:

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
]

Now, assuming we have an fsm running for the group that needs to react on contact, all you need is a loop in there, calling said function in a condition state. If the returned array is not empty, we've made contact:

                                         ___
                        --------------  /   \  < -------
                        |              /delay\          |
                        /              \_____/          |
                  _____________                         |
                 |              |       /\        ____________
        |------> |   main loop  |----> /0 \ ---> | delay loop |
        |        |______________|      \  /      |____________|
        |               |               \/
        |               |
        |               /
        |             ______
        |            /  (1) \
        |           / contact\
        |           \        /
        |            \______/
   ___________          |
  /scriptDone \         |
  \___________/         /
        /        _____________
        |--------| onContact  |
                 ______________

Something like this. In the contact condition state, we call fn_getEnemyContact from above. If it's empty, that group hasn't made any contact yet. Otherwise we progress to onContact, where we spawn our custom onContact script (initially passed to this fsm as parameter).. maybe we set a flag, so that onContact only fires onces, maybe it should fire repeatedly.. anyway we wait until that script is finished (scriptDone) and continue with the fsm, that might do other stuff....

You may also base your decision to fire this "pseudo-event-handler" on other criteria.. only fire it for enemy vehicles, but not for not-that-dangerous soldiers...

Also note that we have a delay loop à la (time - _t) > _delay where delay is somewhere at 4-5 seconds, which is still responsive enough. So that's a really easy check for your cpu. It just takes some more work for you to set it all up...

Sure, real event handlers are really nice and more are always welcome. But at some point it gets really interessting to define your own, higher "events" (as in custom events) and roll such an fsm as depicted.

For example in my infanterySeizePosition group fsm I'm still working on (though it runs well already), I've got such pseudo event-handlers for: onContact, onEngage (the group is on hold fire initially), onCasualties and onDead. All of them running in the same fsm-loop (with the ~4-5s delay loop). I imagine such a solution will be way more easy on your cpu, than having "real" event handlers whose condition is checked every tick.

^^ Just some food for thought. As already stated, "real" event handlers are always nice to have.

Share this post


Link to post
Share on other sites
I imagine such a solution will be way more easy on your cpu, than having "real" event handlers whose condition is checked every tick.

Is that true? Are event handlers checked every tick? I would expect a trigger to be checked very tick, but I would expect an event handler to be something that is unchecked but fires when something happens as a deterministic consequence of something already happening in the game.

Share this post


Link to post
Share on other sites

uhm, yeah :o, you're most likely right. There is probably an event-queue/stack that is check every tick and looks if some event was fired (has been put on the event stack by some entity) or something the like, so it's actually the other way round and naturally real event handlers should be easier on your cpu.

:toilet:

^^ :D

Edited by ruebe

Share this post


Link to post
Share on other sites

This "E.H." do already exist and is called Danger.fsm.

Can be found in "ArmA 2\AddOns\characters\scripts\"

The Danger.fsm will trigger after a specific danger event was happend.

Danger Events:

Enemy Detected

Enemy Near

Dead Body Detected

Hit Detected

All thouse events are covered by the "Danger.fsm" and will be triggered for each unit after a specific danger event was detected by the given unit.

The way of how the "Danger.fsm" can be enhanced is simply unlimited. ;)

Share this post


Link to post
Share on other sites

How do I set different units to have different event handlers using danger.fsm? Can I have it trigger when an enemy is detected by the player?

I think I'd still rather see the event exposed as proper event handler.

Share this post


Link to post
Share on other sites

Nope the "Danger.fsm" can only be used AddOn based becouse it's assigned to each unit in it's config.

So it's not the same like a normal Event Handler which can be added but honestly worth to check it out and give it a try. ;)

You will be suprised what can be made with the different danger events.

Here is something i did with it few weeks back:

qbKxsefL41Y

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  

×