Jump to content
Sign in to follow this  
crashdome

FSM File

Recommended Posts

Great work Whisper. Hope we can figure this out between the few of us even before BIS releases more info. wink_o.gif

Would be great to get a jump start!

Share this post


Link to post
Share on other sites

Hello, It's all SoldierIsNotHistory's work, not mine at all wink_o.gif I just posted his results on Wiki.

Sorry for confusion. He did all, I did nothing smile_o.gif

Share this post


Link to post
Share on other sites

I've been playing around with FSM's today. Here are a few findings. Note, this applies to 'normal' fsms contained in fsm files rather than the strange ones in the config files.

All the XML gubbins is irrelevant to the actual workings of the machine. As Crashdome suggested, it appears to only be there for the editing tools.

Format is standard sqf except that fields that point to code appear to want to take quoted strings which are then preprocessed to turn them into code. I tried using the bracketed code syntax but it caused a crash. I suspect that anyone who really wants to play around with FSMs will end up doing what I'm planning to do and write a preprocessor to 'stringify' normal code.

You can call out to external functions from an fsm file though.

Almost any syntax error in an FSM will cause ArmA to CTD with an error message describing the line in the fsm that caused the problem.

The 'name' field in states is unused and can be set to anything although you do need one.

The 'finalStates' variable contains a list of terminal states which are to be considered 'final' Ie the state-machine will terminate when it reaches these states.

A state can be terminal if its 'Links' class is empty even if the state is not listed in the 'finalStates' array. (The class must exist though.)

The init code for terminal states is executed before leaving the state-machine.

Private variables have scope across all states in the machine. Scope is limited to the current context though so you can run the same machine on different target units and they will each have their own copy of the variables. (I suspect that fsms are executed very similarly to exec'd scripts so the same rules would be expected to apply.)

FSM's do not 'nest'. Ie when an fsm terminates, a unit that was originally told to hold formation will not necessarily revert to its previous behaviour.

doFSM is invoked with 'unit doFSM["file.fsm",pos,target]. I couldn't get the _this variable to work inside my fsm but a bit of guesswork led me to the discovery that the following variables are available:-

_units is an array containing 'unit'

_destination corresponds to the pos argument

_target corresponds to the target argument

FSM's override each other. Starting a new fsm effectively terminates any existing FSM's.

Some commands such as 'doMove' appear to invoke mini fsms of their own and therefore effectively terminate the calling fsm. (Speculate this may be true of all the 'do' commands.)

Despite the previous comments, there does seem to be some interaction between fsms run using doFSM and those defined in the unit config which I don't yet fully understand. I set up an fsm to cause civilians to run around constantly. Killing a few causes the usual behaviour to kick in of them standing rooted to the spot. This appears to be due to the Drop_down__stop state in formationCDanger.fsm. This fsm is set by the fsmDanger variable for the Civilian class. Despite this, the fsm I had set up continued to run through states and produce debug output.

I'm currently having mixed feelings about the fsm files. So far they don't seem any more powerful than simply hand-coding behaviour in a script and they add the pain of an unfriendly format. I'm guessing that some aspects of AI behaviour (eg behaviour state) are part of the engine and may not be changeable at all. To get any really fundamental change in behaviour almost certainly requires messing around with config files. I'll try playing around with these later but first I need to learn about addons ... banghead.gif Anyone have a link to a good tutorial ?

Share this post


Link to post
Share on other sites

Thank you for the confirmation. This is all certainly what I had expected, but not having the full game means unable to test reliably. I did not know the parameter reserved vars, so thank you for that.

I also did not know config FSMs effectively run tandem with the doFSM. This means rethinking a few things. It also means good addons will need a proper coder to be of high quality and flexible enough to allow script modifications. On the other hand, it makes it 10x easier to make a behavior unique to an addon/addon pack.

I appreciate your help.

Quote[/b] ]So far they don't seem any more powerful than simply hand-coding behaviour in a script and they add the pain of an unfriendly format.  I'm guessing that some aspects of AI behaviour (eg behaviour state) are part of the engine and may not be changeable at all. To get any really fundamental change in behaviour almost certainly requires messing around with config files.

Exactly. However, the power is in that they are not scripts... which is good because scripts do not override the doMove/doThis/doThat and other behavior. Trying to get a heli to land properly in OFP is nonsense. Theoretically with an FSM it should be easier and more distributable. Easier, simply because you know it will override any movement commands issued by the AI (pilot/leader/etc..) unless the commands come from the config. In this case, it might be wise to publish a list of config characteristics we can expect to never override.

The FSM format may appear unfriendly, but I find it much more friendly than coding everything into an SQF. (Maybe because I code for a living... I don't know). Not much different than coding a dialog. I wish it had all been XML.. it would be much smaller and more third-party friendly than this C-style nonsense they seem to want everything in. Coding a parsing engine for this format they put out is not fun and simply work intensive (for my FSM program above).

Share this post


Link to post
Share on other sites
Quote[/b] ]The FSM format may appear unfriendly, but I find it much more friendly than coding everything into an SQF. (Maybe because I code for a living... I don't know).

I also code for a living (or at least I used to, having grown increasingly pointy-haired over the last few years wink_o.gif ) What I meant by the format being unfriendly was that the annoying requirement for quoted code strings (as a single line - grr) rather than bracketed code-blocks means that hand-writing fsm files is a pain - most people are going to want to use a preprocessor or generator tool such as the one you're working on.

Quote[/b] ]config FSMs effectively run tandem with the doFSM

I'm still trying to work out the interaction - it may be that behaviour jumps back to the config fsm only on certain transitions within the unit (eg 'safe' to 'combat' behaviour).

It's also still unclear how much behaviour is encapsulated by the engine. For example, is the transition to combat mode controlled within the engine and triggered by nearby firing or is there a setBehaviour in a config fsm somewhere ? If the former, is the 'combat' state simply a label or does it control some low-level activity in the engine ?

Anyway - lots of interesting discoveries still to be made I think. smile_o.gif

Quote[/b] ]However, the power is in that they are not scripts... which is good because scripts do not override the doMove/doThis/doThat and other behavior.

My point was just that 'doFSM' at the mission level seems to offer no extra capability than a 'hand-rolled' state machine written in a standard sqf file. At the config level there might be more control but that's only because you're at that level and replacing rather than overlaying the default behaviour. The fact that it's a .fsm rather than .sqf istate-machine s irrelevant. So there's a disadvantage in that you have to be an addon maker to produce 'interesting' behaviour from a unit. The counter-advantage is that you get to inherit default behaviour from a class and can temporarily overlay it with doFSM.

Share this post


Link to post
Share on other sites
Quote[/b] ]It's also still unclear how much behaviour is encapsulated by the engine

I've now got a simple fsm working from the config file. It just makes the civilian follow the player. Interestingly these new civilians still go prone when there is nearby fire despite the fact I have done nothing to control this in the fsm. Since I've inherited from the Cvilian class I suppose it's still possible there may be behaviour in CAManBase that controls this but I haven't been able to find it yet. More to follow....

*Edit* Using 'setUnitPos "UP"' in the initialisation state fixes this. Default behaviour must be 'Auto' indicating that this is controlled by the engine.

*Edit again*

Another reserved variable gleaned from the civilian formationCDanger fsm file..

_queue appears to be an array set by the engine to record events. Each event is a triple of [type, position, time]. Type is one of:-

0 DCEnemyDetected

1 DCFire

2 DCHit

3 DCEnemyNear

4 DCExplosion

5 DCDeadBodyGroup

6 DCDeadBody

7 DCScream

Share this post


Link to post
Share on other sites

Are there any downloadable examples of this floating around yet?

Just wondering.

marcus

Share this post


Link to post
Share on other sites

The main problem I see is that the doFSM and commandFSM commands are pretty much useless.

First because those don't seem to be able to override the config fsms and second because every additional use of them causes the previous run fsm terminate abruptly, which can leave the unit in all sort of undefined states that you can't control.

While I greatly appreciate the control we've got over the AI with the new AI commands and the fsm config hooks, I think it will hinder mission makers even more to use addons in their missions just because of the reasons stated above.

While it's nice that you can include enhanced behaviors with your unit addons, most time anything special in behavior is highly mission specific. Yet there's no way to effectively override config fsms as a mission designer. So the more addons make use of those nice ways to enhance the AI, the less likely they can be used in missions that need some kind of specific AI scripting.

Right now even the standard units give mission makers lots of headaches because of their fsms.

The only way out of this dilemma is that addon makers should include hooks and ways into the config fsms to make it possible for the mission designer to override the behavior of the config fsm. But getting this into some kind of standard, looks highly improbable to me.

Are there any downloadable examples of this floating around yet?

The ones included in the characters.pbo should provide all you need

Share this post


Link to post
Share on other sites

Hmmm ...

Has anybody a clue about what "_partArray [x,y]" and "_source setParticleParams _partArray" are designed to do ?

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">

_partArray set [6, _up];" \n " _partArray set [12, _black];" \n " _partArray set [18, _vehicle];" \n " _source setParticleParams _partArray;" \n "" \n "

Share this post


Link to post
Share on other sites

Not quite sure of the scope of your question

http://community.bistudio.com/wiki/setParticleParams describes the command. In the context of the file you grabbed this from I believe it's intended to give a visual 'smoke trail' of the units movements and intentions. What those exact parameters do is beyond my knowledge.

Share this post


Link to post
Share on other sites

Thanks.

formation.fsm in a pseudo-intellegible format (the best i can make, at the moment wink_o.gif):

<table border="0" align="center" width="95%" cellpadding="0" cellspacing="0"><tr><td>Code Sample </td></tr><tr><td id="CODE">class FSM

{

 fsmName = "Formation";

 class States

 {

   

   class Init

   {

     name = "Init";

     init =private ["_destination", "_timeNow", "_vehicle", "_isMan", "_commander", "_simulation", "_type"];

     |  _vehicle = vehicle _this;

     |  

     |  _isMan = true;

     |  if (_vehicle != _this) then

     |  {

     |   _isMan = false;

     |  };

     |  

     |  _commander = effectiveCommander _vehicle;

     |  

     |  comment "Returns the current task of the closest formation member.";

     |  private ["_funcNeighbourTask"];

     |  _funcNeighbourTask =  

     |  {

     |   private ["_neighbour", "_minDist", "_formMembers"];

     |   _formMembers = formationMembers _commander;

     |  

     |   for "_i" from 0 to ((count _formMembers) - 1) do

     |   {

     |   private ["_compareTo"];

     |   _compareTo = _formMembers select _i;

     |   if ((_compareTo != _commander) && (_compareTo != player)) then

     |   {

     |   private ["_dist"];

     |   _dist = _compareTo distance _commander;

     |  

     |   if (isNil "_minDist") then

     |   {

     |   _neighbour = _compareTo;

     |   _minDist = _dist;

     |   }

     |   else

     |   {

     |   if (_dist < _minDist) then

     |   {

     |   _neighbour = _compareTo;

     |   _minDist = _dist;

     |   };

     |   };

     |   };

     |   };

     |  

     |   formationTask _neighbour

     |  };

     |  

     |  comment "Returns the amount of currently covering formation members.";

     |  private ["_funcCoveringMembers"];

     |  _funcCoveringMembers =

     |  {

     |   private ["_coveringMembers", "_formMembers"];

     |   _coveringMembers = 0;

     |   _formMembers = formationMembers _commander;

     |  

     |   for "_i" from 0 to ((count _formMembers) - 1) do

     |   {

     |   if ((formationTask (_formMembers select _i)) == "COVER") then

     |   {

     |   _coveringMembers = _coveringMembers + 1;

     |   };

     |   };

     |  

     |   _coveringMembers

     |  };

     |  

     |  private ["_coverInterval", "_timeLastCover"];

     |  _coverInterval = 10 + (random 15);

     |  _timeLastCover = time;

     |  

     |  private ["_useCover"];

     |  _useCover = random 1;

     |  

     |  

     |  comment "On several places there is debug output, based on the value of this variable.";

     |  private ["_debug"];

     |  _debug = false;

     |  

     |  private ["_source", "_source2", "_white", "_red", "_blue", "_black", "_yellow", "_green", "_partArray", "_up", "_neutral"];

     |  if (_debug) then

     |  {

     |   _source = "#particlesource" createVehicleLocal position player;

     |   _source setDropInterval 0.1;

     |  

     |   _source2 = "#particlesource" createVehicleLocal position player;

     |   _source2 setDropInterval 0.1;

     |  

     |   _white = [[1, 1, 1, 1], [1, 1, 1, 0]];

     |   _red = [[1, 0, 0, 1], [1, 0, 0, 0]];

     |   _blue = [[0, 0, 1, 1], [0, 0, 1, 0]];

     |   _yellow = [[1, 1, 0, 1], [1, 1, 0, 0]];

     |   _green = [[0, 1, 0, 1], [0, 1, 0, 0]];

     |   _black = [[0, 0, 0, 1], [0, 0, 0, 0]];

     |  

     |   _up = [0, 0, 2];

     |   _neutral = [0, 0, 0];

     |  

     |   _partArray = ["\ca\data\cl_basic", "", "Billboard", 1, 30, [0, 0, 0], _neutral, 0, 1.275, 1, 0, [0.3], _white, [0], 0, 0, "", "", _vehicle];

     |  };;

     class Links

     {

       

       class Always

       {

         priority = 0.000000;

         to="Start";

         condition=true;

         action="";

       };

       

     };

   };

   

   

   class Combat

   {

     name = "Combat";

     init =_vehicle = vehicle _this;

     |  _commander = effectiveCommander _vehicle;;

     class Links

     {

       

       class Member

       {

         priority = 1.000000;

         to="Member";

         condition=!(isFormationLeader _commander);

         action="";

       };

       

       

       class Leader

       {

         priority = 0.000000;

         to="Leader";

         condition=true;

         action="";

       };

       

     };

   };

   

   

   class Return_toformation";

     init =_destination = expectedDestination _this;

     |  _this setDestination _destination;

     |  ;

     class Links

     {

       

       class Always

       {

         priority = 0.000000;

         to="Start";

         condition=true;

         action="";

       };

       

     };

   };

   

   

   class Member

   {

     name = "Member";

     init =_vehicle = vehicle _this;

     |  

     |  private ["_limit", "_limitCover"];

     |  _limit = precision _vehicle;

     |  _limitCover = _limit * 10;

     |  if (_limitCover > 50) then

     |  {

     |   _limitCover = 50;

     |  };

     |  

     |  private ["_position", "_formPosition"];

     |  _position = position _vehicle;

     |  _formPosition = formationPosition _vehicle;

     |  

     |  _commander = effectiveCommander _vehicle;

     |  

     |  if !(isHideBehindScripted _commander) then

     |  {

     |   _commander hideBehindScripted true;

     |   _commander setHideBehind [objNull, []];

     |  };

     |  

     |  if (reloadEnabled _commander) then

     |  {

     |   _commander enableReload false;

     |  };

     |  

     |  private ["_reloadNeeded"];

     |  _reloadNeeded = needReload _vehicle;

     |  

     |  private ["_enemy", "_hideFrom", "_objectWhereHide", "_random"];

     |  _enemy = _this findNearestEnemy (position _this);

     |  _random = random 1;;

     class Links

     {

       

       class Reload

       {

         priority = 3.000000;

         to="Check_forcover";

         condition=!(isNull _enemy)

         |  &&

         |  ((_this distance _enemy) <= 200)

         |  &&

         |  (_useCover >= 0.35);

         action="";

       };

       

       

       class Too_far

       {

         priority = 1.000000;

         to="Return_toAre_covered

       {

         priority = 1.000000;

         to="Clean_up";

         condition=_commander = effectiveCommander (vehicle _this);

         |  

         |  _formPosition = formationPosition _vehicle;

         |  

         |  private ["_neighbourTask"];

         |  _neighbourTask = call _funcNeighbourTask;

         |  

         |  !(isFormationLeader _commander)

         |  &&

         |  (

         |   (

         |   ((_formPosition distance (position _vehicle)) > _limitCover)

         |   &&

         |   (

         |   ((time - _timeStart) >= _timeMax)

         |   ||

         |   (

         |   ((time - _timeStart) >= _timeMin)  

         |   &&

         |   (

         |   (_neighbourTask == "COVER")

         |   ||

         |   (count (formationMembers _this) == 2)

         |   )

         |   )

         |   )

         |   )

         |   ||

         |   ((behaviour _this) != "COMBAT")

         |   ||

         |   ((vehicle _this) != _vehicle)

         |  );

         action="";

       };

       

       

       class Random_delay

       {

         priority = 0.000000;

         to="Next_target";

         condition=(time - _timeNow) >= _delay;

         action="";

       };

       

     };

   };

   

   

   class Start

   {

     name = "Start";

     init =_vehicle = vehicle _this;

     |  

     |  _type = typeOf _vehicle;

     |  _simulation = getText (configFile >> "CfgVehicles" >> _type >> "simulation");;

     class Links

     {

       

       class Air_vehicle

       {

         priority = 2.000000;

         to="No_combat";

         condition=_simulation in ["airplane", "helicopter"];

         action="";

       };

       

       

       class Combat

       {

         priority = 1.000000;

         to="Combat";

         condition=(behaviour _this) == "Combat";

         action="";

       };

       

       

       class No_combat

       {

         priority = 0.000000;

         to="No_combat";

         condition=true;

         action="";

       };

       

     };

   };

   

   

   class Provide_cover

   {

     name = "Provide_cover";

     init =_vehicle = vehicle _this;

     |  _isMan = true;

     |  if (_vehicle != _this) then

     |  {

     |   _isMan = false;

     |  };

     |  _commander = effectiveCommander _vehicle;

     |  

     |  _this forceSpeed 0;

     |  if !(isFormationLeader _commander) then

     |  {

     |   _this setDestination [position _this, "DoNotPlan", true];

     |  };

     |  

     |  private ["_timeMin", "_timeStart", "_timeMax"];

     |  _timeMin = 3 + (random 5);

     |  _timeMax = _timeMin + (5 + (random 10));

     |  _timeNow = time;

     |  _timeStart = time;

     |  

     |  private ["_height", "_dist", "_xSpacing", "_ySpacing", "_unitPos"];

     |  

     |  if (_isMan) then

     |  {

     |   _dist = 15;

     |  

     |   _unitPos = unitPos _this;

     |  

     |   if (_unitPos != "DOWN") then

     |   {

     |   if ((random 1) > 0.5) then

     |   {

     |   _unitPos = "MIDDLE";

     |   }

     |   else

     |   {

     |   _unitPos = "DOWN";

     |   };

     |   };

     |  

     |   if (_unitPos == "MIDDLE") then

     |   {

     |   _height = 1.5;

     |   _xSpacing = 6;

     |   _ySpacing = 1.5;

     |   }

     |   else

     |   {

     |   _height = 0.5;

     |   _xSpacing = 4;

     |   _ySpacing = 0.5;

     |   };

     |  

     |   _this setUnitPos _unitPos;

     |  }

     |  else

     |  {

     |   _dist = 30;

     |   _height = 2;

     |   _xSpacing = 10;

     |   _ySpacing = 2;

     |  };

     |  

     |  _useCover = random 1;

     |  

     |  private ["_pos", "_posX", "_posY", "_dir"];

     |  _pos = position _vehicle;

     |  _posX = _pos select 0;

     |  _posY = _pos select 1;

     |  _pos = [_posX, _posY, _height];

     |  _dir = formationDirection _vehicle;

     |  

     |  private ["_posCX", "_posCY"];

     |  _posCX = _posX + ((sin _dir) * _dist);

     |  _posCY = _posY + ((cos _dir) * _dist);

     |  

     |  private ["_posLX", "_posLY", "_posRX", "_posRY"];

     |  _posLX = _posCX + ((sin (_dir - 90)) * _xSpacing);

     |  _posLY = _posCY + ((cos (_dir - 90)) * _xSpacing);

     |  _posRX = _posCX + ((sin (_dir + 90)) * _xSpacing);

     |  _posRY = _posCY + ((cos (_dir + 90)) * _xSpacing);

     |  

     |  private ["_targetC", "_targetTL", "_targetTR", "_targetBL", "_targetBR"];

     |  _targetC = [_posCX, _posCY, _height];

     |  _targetTL = [_posLX, _posLY, _height + _ySpacing];

     |  _targetTR = [_posRX, _posRY, _height + _ySpacing];

     |  _targetBL = [_posLX, _posLY, _height - _ySpacing];

     |  _targetBR = [_posRX, _posRY, _height - _ySpacing];

     |  

     |  private ["_target"];

     |  _target = "FireSectorTarget" createVehicle _targetC;

     |  _target setPos _targetC;

     |  

     |  private ["_gunner", "_glanceAtSet"];

     |  _glanceAtSet = false;

     |  

     |  if (_isMan) then

     |  {

     |   if (isNull (assignedTarget _this)) then

     |   {

     |   _this doTarget _target;

     |   _this glanceAt _target;

     |   _glanceAtSet = true;

     |   };

     |  }

     |  else

     |  {

     |   _gunner = (gunner _vehicle);

     |   if (isNull _gunner) then

     |   {

     |   _gunner = _vehicle;

     |   };

     |   _gunner doWatch _target;

     |  };

     |  

     |  private ["_targets"];

     |  _targets = [_targetC, _targetTL, _targetTR, _targetBL, _targetBR];

     |  

     |  private ["_delay"];

     |  _delay = (1 + (random 4));

     |  

     |  _commander setFormationTask "COVER";

     |  

     |  _timeLastCover = time;

     |  

     |  

     |  

     |  private ["_lifeTime"];

     |  if (_debug) then

     |  {

     |   _lifeTime = _timeMax;

     |   drop ["\ca\data\cl_basic", "", "Billboard", 1, _lifeTime, _pos, [0, 0, 0], 0, 1.275, 1, 0, [0.1], [[0, 0, 1, 1]], [0], 0, 0, "", "", ""];

     |   drop ["\ca\data\cl_basic", "", "Billboard", 1, _lifeTime, _targetC, [0, 0, 0], 0, 1.275, 1, 0, [0.1], [[1, 0, 0, 1]], [0], 0, 0, "", "", ""];

     |   drop ["\ca\data\cl_basic", "", "Billboard", 1, _lifeTime, _targetTL, [0, 0, 0], 0, 1.275, 1, 0, [0.1], [[0, 1, 0, 1]], [0], 0, 0, "", "", ""];

     |   drop ["\ca\data\cl_basic", "", "Billboard", 1, _lifeTime, _targetTR, [0, 0, 0], 0, 1.275, 1, 0, [0.1], [[0, 1, 0, 1]], [0], 0, 0, "", "", ""];

     |   drop ["\ca\data\cl_basic", "", "Billboard", 1, _lifeTime, _targetBL, [0, 0, 0], 0, 1.275, 1, 0, [0.1], [[0, 1, 0, 1]], [0], 0, 0, "", "", ""];

     |   drop ["\ca\data\cl_basic", "", "Billboard", 1, _lifeTime, _targetBR, [0, 0, 0], 0, 1.275, 1, 0, [0.1], [[0, 1, 0, 1]], [0], 0, 0, "", "", ""];

     |  

     |   _partArray set [6, _up];

     |   _partArray set [12, _blue];

     |   _source setParticleParams _partArray;

     |  

     |   _partArray set [6, _neutral];

     |   drop _partArray;

     |  };;

     class Links

     {

       

       class Deadlock

       {

         priority = 2.000000;

         to="Clean_up";

         condition=_commander = effectiveCommander (vehicle _this);

         |  

         |  private ["_oldTarget"];

         |  

         |  if (_glanceAtSet && ((assignedTarget _this) != _target)) then

         |  {

         |   _oldTarget = assignedTarget _this;

         |   _this doWatch objNull;

         |   _this doTarget _oldTarget;

         |  

         |   _glanceAtSet = false;  

         |  };

         |  

         |  (isFormationLeader _commander)

         |  &&

         |  (

         |   (

         |   (

         |   ((time - _timeStart) >= _timeMin)

         |   &&

         |   ((call _funcCoveringMembers) >= (floor ((count (formationMembers _this)) / 2)))

         |   )

         |   ||

         |   ((time - _timeStart) >= _timeMax)

         |   )

         |   ||

         |   ((behaviour _this) != "COMBAT")

         |   ||

         |   ((vehicle _this) != _vehicle)

         |  );

         action="";

       };

       

       

       class Too_farcover

       {

         priority = 1.000000;

         to="Provide_cover";

         condition=private ["_coveringMembers", "_formMembers"];

         |  _coveringMembers = call _funcCoveringMembers;

         |  _formMembers = formationMembers _this;

         |  

         |  if (_currentCommand != "MOVE") then

         |  {

         |   false

         |  }

         |  else

         |  {

         |   if

         |   (

         |   (

         |   ((count _formMembers) > 2)

         |   &&

         |   (_coveringMembers < (floor ((count _formMembers) / 2)))

         |   )

         |   ||

         |   (

         |   ((count _formMembers) == 2)

         |   &&

         |   (_coveringMembers == 0)

         |   )

         |   )

         |   then {true} else

         |   {

         |   if

         |   (

         |   ((count _formMembers) == 1)

         |   &&

         |   ((time - _timeLastCover) >= _coverInterval)

         |   &&

         |   _isMan

         |   )

         |   then

         |   {

         |   private ["_return"];

         |   _return = (_useCover >= 0.5);

         |   _timeLastCover = time;

         |   _useCover = random 1;

         |              _return

         |   };

         |   };

         |  };

         action="";

       };

       

       

       class Enough_cover

       {

         priority = 0.000000;

         to="Return_toformation";

         condition=true;

         action="";

       };

       

     };

   };

   

   

   class Go_to_coverto_reload";

     init =_vehicle = vehicle _this;

     |  _commander = effectiveCommander _vehicle;

     |  

     |  _commander setHideBehind [_objectWhereHide, _hideFrom];

     |  

     |  

     |  

     |  if (_debug) then

     |  {

     |   _partArray set [6, _neutral];

     |   _partArray set [12, _yellow];

     |   _source2 setParticleParams _partArray;

     |  

     |   _source2 setDropInterval 0.1;

     |  };;

     class Links

     {

       

       class Became_leader

       {

         priority = 1.000000;

         to="Reload";

         condition=isFormationLeader _commander;

         action="";

       };

       

       

       class Cover_reached

       {

         priority = 0.000000;

         to="Reload";

         condition=_vehicle = vehicle _this;

         |  _commander = effectiveCommander _vehicle;

         |  

         |  isHidden _commander;

         action=if (_debug) then

         |  {

         |   _source2 setDropInterval 0;

         |  };;

       };

       

     };

   };

   

   

   class Drop_to_ground_Reload

   {

     name = "Drop_to_ground_Reload";

     init =_this setUnitPos "DOWN";;

     class Links

     {

       

       class Always

       {

         priority = 0.000000;

         to="Reload";

         condition=true;

         action="";

       };

       

     };

   };

   

   

   class Check_forreload

   {

     name = "Check_forreload";

     init =_hideFrom = _this getHideFrom _enemy;

     |  _objectWhereHide = _this findCover [(position _this), _hideFrom, 20];;

     class Links

     {

       

       class Cover

       {

         priority = 1.000000;

         to="Go_to_coverenemy

   {

     name = "Check_forcover_tocover

   {

     name = "Check_for__cover";

     init =_hideFrom = _this getHideFrom _enemy;

     |  _objectWhereHide = _this findCover [(position _this), _hideFrom, 20];

     |  

     |  

     |  

     |  if (_debug) then

     |  {

     |   _partArray set [6, _up];

     |   _partArray set [12, _red];

     |   _partArray set [18, _vehicle];

     |   _source setParticleParams _partArray;

     |  

     |   _partArray set [6, _neutral];

     |   drop _partArray;

     |  };;

     class Links

     {

       

       class Cover

       {

         priority = 1.000000;

         to="Go_to_cover";

         condition=!(isNull _objectWhereHide);

         action="";

       };

       

       

       class Always

       {

         priority = 0.000000;

         to="Provide_cover";

         condition=true;

         action="";

       };

       

     };

   };

   

 };

 initState="Init";

 finalStates[] =

 {

 };

};

Share this post


Link to post
Share on other sites
The main problem I see is that the doFSM and commandFSM commands are pretty much useless.

First because those don't seem to be able to override the config fsms and second because every additional use of them causes the previous run fsm terminate abruptly, which can leave the unit in all sort of undefined states that you can't control.

While I greatly appreciate the control we've got over the AI with the new AI commands and the fsm config hooks, I think it will hinder mission makers even more to use addons in their missions just because of the reasons stated above.

While it's nice that you can include enhanced behaviors with your unit addons, most time anything special in behavior is highly mission specific. Yet there's no way to effectively override config fsms as a mission designer. So the more addons make use of those nice ways to enhance the AI, the less likely they can be used in missions that need some kind of specific AI scripting.

Right now even the standard units give mission makers lots of headaches because of their fsms.

The only way out of this dilemma is that addon makers should include hooks and ways into the config fsms to make it possible for the mission designer to override the behavior of the config fsm. But getting this into some kind of standard, looks highly improbable to me.

Are there any downloadable examples of this floating around yet?

The ones included in the characters.pbo should provide all you need

Sometimes I wonder where you base your opinions from Rom, wink_o.gif

Why do mission makers need to have hooks and such to FSMs built within add-ons? I see this as the addon producers problem.. they need to make sure it works right. Alternatively, you could produce addons without FSMs.

I think FSMs will be an addition to this engine not unlike functions were to OFP. People will shun them and call them worthless but in all honesty they are powerful methods to increase performance and reliability for coding. The reason we see FSMs act they way sbsmac describes is that is how they are supposed to work. Jumping from FSM to FSM is exactly how the AI currently work in ArmA and so far that has proven to be effective. In fact, I have thoughts running through my mind right now on how they can be used in a very elaborate manner to create an entirely new game-play style.

Anyhow, I completely disagree that it will hinder mission-makers unless the addon is some fancy-pants addon that someone makes to show off rather than to work properly. Will it further this type of behavior? probably, but the cream always rises to the top with addons in this community - the other 80% is all trial and error.

Further, I think the ability to not over-ride the config FSM is the fact that if you called an FSM, you really don't want to over-ride all behavior.... for example the prone issue with the civilians is just default "fear" behavior. You need that seperated unless you code for it in evey singel FSM!! The real problem with that might simply be that we cannot control the level of fear individually without affecting other things (i.e. skill value). This is where I agree with you Romolus, I think we need better access to 'adjusting' default FSM behavior.... but I think BIS has really taken that a step further from OFP with all the other new commands (and that knowledgebase command which isn't implemented yet? will that also help?)

Anyways, I look at it as glass half-full wink_o.gif

Share this post


Link to post
Share on other sites
Sometimes I wonder where you base your opinions from Rom, wink_o.gif

Same as everyone else I guess smile_o.gif

In this case by experimenting with FSMs and the new AI scripting commands and having a bit experience in addon and mission making.

Why do mission makers need to have hooks and such to FSMs built within add-ons? I see this as the addon producers problem.. they need to make sure it works right. Alternatively, you could produce addons without FSMs.

Addons without FSMs don't really work since then the AI isn't doing anything. Now you could completely script that behavior, but that's a far worse workaround than placing scripting hooks into FSMs.

The reason why I believe that mission makers need hooks into the FSMs is that it's almost impossible to create a FSM that models the complete human behavior. That would be quite a bit of revolution in the field of AI.

So you have to leave some bits out and those will be exactly the bits that a mission maker might want an AI unit to do in his mission. Now he's got all those nice new AI scripting commands in his arsenal compared to OFP, so he could actually get done what he wants in ArmA. If it just wouldn't be for the config called FSMs that use the same scripting commands and constantly override what he's trying to do with his scripts. You can already see this in the many threads about setUnitPos or doMove commands not working reliable anymore.

He also can't use the doFSM or commandFSM commands with a self written FSM for his purposes since those seem to not override the config FSMs either. So the only way would be to open up the addon and adjusting the config FSM to do what he wants. Which leads to even more mission specific addons that you need to download to play a mission. We all know how well that helps to play missions.

My experience from creating OFP addons tells me that there's no way to create a completely self-satisfying addon. For OFP there were addons with tons of scripts but there were always people who wanted to do even more things with those addons. Now with the FSMs those "features" aren't even optional since they're running by default through the config property.

I just believe that addons should be for missions and not the other way round. Missions is what people play in the end so the one who's creating the mission should be in control what the addons do that he's using.

If you give him an option to control some kind of "fear level", he still can't use the scripting commands that are available. So why only give him only limited possibilities of what he can do when he's the one who's actually trying to get a mission done with an addon?

The most simple case of such a hook would be just a switch that halts the FSM of a specific unit. The mission maker could then just flip that switch for the unit and run his own script without having to fight a constant battle with the FSM overriding his commands.

I think FSMs will be an addition to this engine not unlike functions were to OFP. People will shun them and call them worthless but in all honesty they are powerful methods to increase performance and reliability for coding

I would agree with that if the doFSM and commandFSM commands would behave a bit different.

The way it seems to be implemented right now is that you run one FSM via the doFSM and it just quits in the instant that you try to run no matter at what state or line it currently is. This means that if a single script you're using is calling doFSM you can't use another doFSM call because you never know when your FSM will be terminated and in what state. What would be perfect if FSMs could run in parallel, so that you can have a FSM for that and another FSM for something else that is unrelated to the first FSM. But it just doesn't work that way right now.

Right now using doFSM or commandFSM calls in scripts is nothing more than playing Russian roulette.

Those are two separate issues:

The first one is that you can't adjust config FSMs without placing scripting hooks into them or editing the addon and that they override the regular scripting commands that a mission maker usually has at his disposal.

The second one is that calling FSMs from scripts via doFSM and commandFSM is unreliable and limited because successive calls instantly terminate each other and can't run in parallel.

I don't think that I'm really pessimistic about FSMs. Just pointing out the current problems for further discussion smile_o.gif

Share this post


Link to post
Share on other sites
I would agree with that if the doFSM and commandFSM commands would behave a bit different.

The way it seems to be implemented right now is that you run one FSM via the doFSM and it just quits in the instant that you try to run no matter at what state or line it currently is. This means that if a single script you're using is calling doFSM you can't use another doFSM call because you never know when your FSM will be terminated and in what state. What would be perfect if FSMs could run in parallel, so that you can have a FSM for that and another FSM for something else that is unrelated to the first FSM. But it just doesn't work that way right now.

Right now using doFSM or commandFSM calls in scripts is nothing more than playing Russian roulette.

To be honest, I think parallel run FSMs would contridict the idea behind a state machine. You would also have conflicts no different than the ones with the config class. Which is why every time I think about it, I wonder if we could have more documentation from BIS. (I know they have something. They wouldn't be advertising it on VBS2 site if they didn't have something to give to their VBS2 customers) Personally, I am getting a little tired of their downplay to us hobbyists. I feel like we are hungry dogs being fed scraps of information when they feel like giving it to us. Ok, a little OT, but my point is that I often wonder if it's possible to perform better structuring of the FSMs than we think. Perhaps maybe even we haven't but scratched the surface of the possibilities. Negatives aside, you do think they are useful? or atleast potentially useful even without BIS intervention to change behavior?

Quote[/b] ]Those are two separate issues:

The first one is that you can't adjust config FSMs without placing scripting hooks into them or editing the addon and that they override the regular scripting commands that a mission maker usually has at his disposal.

The second one is that calling FSMs from scripts via doFSM and commandFSM is unreliable and limited because successive calls instantly terminate each other and can't run in parallel.

1) Should you really be able to? A well done addon need not any scripting hooks IMO. In fact, I am tending to think FSMs are more for the addon creators than mission makers.

2) Again, I think this is actually an intended behavior rather than a neglect on BI's part. We cannot confuse the change of states between changes of behavior. An FSM designed to have civies follow the player is fine, but when the bullets start flying do you really want the civs to just continue to blindly follow the player? IMO that is less realistic and would require more coding to check state conditions between FSMs than just switching between FSMs. (i.e. your FSM would consistantly need to check if another FSM should have control of things like targetting, movement, etc..)

Share this post


Link to post
Share on other sites

@CrashDome:

You're tip-toeing around the issues instead of addressing them in your comments smile_o.gif

A well done addon need not any scripting hooks IMO. In fact, I am tending to think FSMs are more for the addon creators than mission makers.

So how would such a "well done addon" look like in your opinion?

FSMs are there to contain AI behavior (at least for the formation and danger parameters in the unit config). So what you're saying is that AI scripting only belongs into addons and those need to be done in a way that a mission maker doesn't need to have any influence on it.

So how would you create a FSM that contains all the human behavior that you would expect from any unit in a mission?

How do you make sure that you didn't forget anything so that a mission maker would have no need to write his own AI script?

How big would it be?

Also how many different implementations of AI behavior scripts are there for OFP? Which one would you choose as an addon maker to include as FSM in your addons?

What if another addon maker choses a different implementation? What do you do to make sure units from both addons can even work together if they're using a different AI behavior implementation?

And what do you think a mission maker would say to all this?

What I'm saying is that all those issues (if not addressed) will lead to addons being even less compatible to each other than they already were with OFP. That for mission makers it will be even harder to use addons in their missions, especially when they can't even influence the AI behavior defined in the addons.

The only thing I can see that you're suggesting to solve those issues is to create one single monolithic FSM that covers all the AI behavior that is ever needed and to use it in every addon that will ever be made.

Don't you think that there would already be much better AI around in games than the basic stuff we see right now if you would be able to cram all the complex AI behavior into a single FSM that is needed to fare excellent in every situation of every ArmA mission?

FSMs aren't exactly new in the field of AI programming and many games use FSMs, so why don't we already have that perfect AI unit FSM that you're describing (or not really describing, but rather hinting at)?

Again, I think this is actually an intended behavior rather than a neglect on BI's part. We cannot confuse the change of states between changes of behavior

And again I think you're not addressing the problems that I mentioned smile_o.gif

It doesn't really matter how it's intended to work, the question is what problems result from exactly that?

I'll repeat what I think the problems are:

- The use of the DoFSM and commandFSM is limited because they can't be used to override the config FSMs while at the same time the config FSMS can (and most likely will) interfere with what a mission maker wants to do with the AI. Even creating the ultimate FSM mentioned above doesn't make this point invalid.

- The use of doFSM and commandFSM is unreliable because the calls are not cued and instead any successive calls to doFSM or commandFSM abruptly end the currently running FSM no matter what state it is right now. How do you ensure that any FSM you're writing doesn't have any negative effects when it's abruptly ended at any state? At best this will limit the things you can do with your FSMs.

If your current running FSM for example uses the disableAI command, then does something and then uses enableAI and you want to call another FSM via doFSM or commandFSM then you either have to check whether the AI is still disabled (since you don't know at all at where exactly your previous FSM will be stopped) or check whether the previous FSM is still running. So no matter what, if you want to use successive doFSM or commandFSM calls then you either have to check tons of things or you're headed for trouble.

I wonder if we could have more documentation from BIS

What kind of things would you like to have documented from BIS?

Right now we already know that there are basically two kinds of FSMs:

Config-defined ones that access hardcoded functions to get done what they need. The hardcoded functions that are used in the current FSMs are rather obvious and I don't think there are much more hardcoded functions that those. Why should BIS include tons of undocumented hardcoded functions when they could just provide them as a scripting command? (Which they did for quite some AI-related commands.)

The other kind of FSMs are scripting based FSMs. Those can be called via the doFSM and commandFSM command or attached to two parameters in the config. With those FSMs you seem to be able to use all the scripting commands that there are for regular scripting.

The FSMs themself aren't really that interesting at all. Everything you can do with an FSM (a scripted one) you can also do with a script. The only difference might be in performance (can easily be tested) and what input parameters you have access to in a scripted FSM (either called through the config or through the two scripting commands).

Those input parameters are and whether there are other config parameters than just formation and danger where a scripted FSM can be attached to, are pretty much the only things that I'd like to get some info on.

I doubt there'll be an easy way to create compiled FSMs. Nor do I think that BIS would be willing to provide the tools for that if it would be possible.

What makes FSMs a nice thing (if you have a graphical editor), is that it's a more graphical way to code AI scripts and it takes care of some of the control structures that you would need. But that's about it then, I think.

Share this post


Link to post
Share on other sites
Quote[/b] ]@CrashDome:

You're tip-toeing around the issues instead of addressing them in your comments

Actually, I am weighing all the arguments and trying to see if anything new comes to light before stating anything solidly. There's a difference wink_o.gif

Look, I am not saying there are not any problems as we see it now. However, I am wondering if they are really problems or if FSMs are just too young in our minds yet. Obviously BIS would have had intentions when they released the commands. What I want documented are what those plans are and what we are missing. Until about a month or so ago, no one even knew what they did or what they even looked like. I want to think this through before I go screaming around saying we need it *fixed*.

Seccondly, I am not nor ever would advocate a large single FSM. I have no idea where you got that from.

Third, my thoughts are that addon packs such as WGL, BAS, FDF, etc.. etc.. all will contain FSMs at some point. It's just a given that since they are there, they will be used. How they will be used is what I am trying to figure out. The mission maker might complain about being unable to override FSMs in addons, but before I join in on complaining I wonder if it would be a valid complaint. Hell, we have no idea what is going to happen yet and really neither do you. In the grand scheme of things I also tend  to imagine ArmA FSMs catering more towards groups of modmakers rather than the individual addon makers we had so many of in OFP. We had a problem in OFP that scripting and addon making were sort of seperate... they could be combined and often were, but they were by nature two different beings. What I am starting to see in ArmA is the drive for collaboration between addon makers, mission makers, and scriptors. In that term, we cannot assume to make addons without FSMs simply because there might be that lone mission maker who wants to override something. The fact that addons were often incompatible in OFP doesn't mean we should somehow strive to bring everyone on the same level. That will also impede creativity. IMO we should strive to bring groups of like-minded people together to create addon packs that support their own unique (but group) desires.

Quote[/b] ]The FSMs themself aren't really that interesting at all. Everything you can do with an FSM (a scripted one) you can also do with a script. The only difference might be in performance (can easily be tested) and what input parameters you have access to in a scripted FSM (either called through the config or through the two scripting commands).

I still say I do not agree with this idea that scripting is no different. Yes, you can script a state machine, but you cannot override standard movement commands. One BIG problem I had in OFP was the desire to make civs walk around a city individually, but keep them grouped together (for limitation reasons). The problem was after a doMove command, the leader would call everyone back together. I would have to loop "doMove" so many times to keep everyone seperate it would bring any PC to a grinding halt. This FSM idea theoretically makes that problem go away.

If you even dare say that this idea won't work because of config FSMs I swear I will shoot a kitten in your name... wink_o.gif I realize a config FSM might still bring about that problem, but my point was to prove how a single BIG problem cannot be solved via script but CAN be solved via these FSMs.

Share this post


Link to post
Share on other sites
I still say I do not agree with this idea that scripting is no different. Yes, you can script a state machine, but you cannot override standard movement commands.

A FSM doesn't do that either, that's my point. What does make it override the standard movement is the formation and danger hook in the config. Whether you create a FSM that implements that standard movement or if you just create a FSM with only one state that calls a script, it doesn't matter in what you can do. The only reason you need a FSM in the first place is because BIS has a graphical editor that makes it easier than just writing some scripts. So the engine requires a FSM to be called from those hooks. But the part that does all the work in a (scripted) FSM is plain old scripting. The part that the FSM does is just switching states and running the conditions (which are also plain scripting commands). So the part that the actual FSM does is nothing else than loops and conditions in a script.

So instead of wondering about FSMs I rather think about what kind of things can be done with the new AI scripting commands, because those are what does all the work smile_o.gif

Also the problems I mentioned don't have much to do with FSMs either. BIS could just have taken plain scripts instead of FSMs so that a script would be called by the config hooks and the doFSM and commandFSM command would be obsolete.

So there's not that much about FSMs to be thought through. The main thing that gave us more control over the AI are the scripting commands like setDestination, setHideBehind, setFormationTask and the like. Those are the commands used in the formation.fsm and those are the ones that are directly connected to the AI. Not the FSMs, those are just a mere shell for the commands.

Actually what you're describing about addons, addon makers and mission makers is exactly what I meant in my previous posts.

Of course everyone will be using those new AI commands, because you can do so much more with the AI now. But the way it's currently implemented leads to problems with incompatibility between addons and severe limitations for mission makers - if nothing is done about it.

I'm not screaming that it's wrong anf it needs to be fixed, I'm just saying that something needs to be done about it. Hence my suggestion about the scripting hooks for FSMs.

In the past, there were many approaches to get some kind of common standards into OFP editing and not one if them really took off because of various reasons. So I don't really believe that we'll get some kind of standard for the AI scripting either because people are just the way they are. Also a common standard for addon FSMs don't really solve the problems for mission makers.

In the past, usually the people who created the addons didn't do much of the mission work. The exceptions were more or less all complete conversions where it was also difficult to use them in combination with other addons.

So the bulk of mission makers will experience severe limitations in what they can do with addons if addon maker start using the new AI commands in their config attached FSMs.

That's why I think having some scripting hooks that can enable/disable the AI FSMs temporally could be a rather simple solution for all this. Just like the switch included in the most addon scripts where the mission maker can disable things he doesn't want to have in his mission. Since that was more or less common practice, I think somehting similar could work for the AI FSMs as well.

Addon makers could create their AI FSMs that do what they think their addons should be doing, and mission makers can disable them to get the things done that they want or even create scripts to make addons with incompatible AI FSMs work together again.

An even better solution in my opinion would be to only create a stub-FSM that calls a script stored in a global variable.

That way a mission maker can put his own script into that var and it gets called by every unit. The script can then do what the mission maker wants or just call the original script that was intended to be run with the unit by the addon maker.

If the addon maker creates the AI behavior as a FSM attached to the config via the formation or danger hook, then this FSM can't be called in a script via doFSM or commandFSM because the input parameters that the FSM gets form the config hooks are different than what the doFSM or commandFSM commands deliver to the FSM they run. Nor can it be overridden if it doesn't do what the mission requires.

But maybe someone else has an even better solution, so that's why I posted those things in the first place.

I'm sorry for assuming that you suggest a single big FSM instead of scripting hooks, but since you didn't agree that we need some ways to interfere with FSMs, such a huge all-including FSM seemed the only possible solution to counter the problems I mentioned (beside getting all addon makers and mission makers agree to common standards which I don't believe in being possible).

Share this post


Link to post
Share on other sites

Wow a lot to read through here, I hope once the results of everyone’s experiments are finalised, a summary can be posted?

From what I've seen of the FSM files included with Arma, it's not supposed to be an all singing, all dancing solution to AI behaviour. It seems like it's only used to control basic actions i.e getting a Dragonfly to randomly fly around a map, or AI units to adhere to the squads formation. Perhaps we should not expect much more from it, than that?

Quote[/b] ]we need some ways to interfere with FSMs

Out of curiosity, will disableAI stop any config based FSM's from running?

Share this post


Link to post
Share on other sites

Is there a way to completely disable the FSMs for certain units?

I have a problem with domoving zombies around the map, that don't need to look for cover etc.

After a while they just stop moving and it looks like they

are stuck in an infinite FSM loop. They keep switching their formation task and their destination modes to and fro.

Maybe using too many

domoves after each other exceeded their "memory" ?

That's why i insert a dostop every 8-10 seconds, but still

they won't move after some time has passed.

Anyone has an idea how to possibly fix that?

Share this post


Link to post
Share on other sites

Try using "moveTo" instead there is a known problem with "doMove".

EDIT: "moveTo" is not "implemented yet" according to the wiki, but it is working for me in my very limited testing today... You can also try "setDestination".

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  

×