zapat 56 Posted August 16, 2011 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. 1 Share this post Link to post Share on other sites
rübe 127 Posted August 16, 2011 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 1 Share this post Link to post Share on other sites
zapat 56 Posted August 16, 2011 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
rübe 127 Posted August 16, 2011 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. ;) 1 Share this post Link to post Share on other sites
demonized 20 Posted August 16, 2011 (edited) 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 August 16, 2011 by Demonized edit. 1 Share this post Link to post Share on other sites
zapat 56 Posted August 16, 2011 (edited) 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 August 16, 2011 by zapat 1 Share this post Link to post Share on other sites