6     protected const int FIRETEAM_MIN_SIZE = 2;
 
    9     protected ref array<ref SCR_AIGroupFireteam> m_aFireteams = {};
 
   10     bool m_bRebalanceFireteams = 
false; 
 
   13     protected ref ScriptInvokerBase<SCR_AIOnFireteamRemoved> Event_OnFireteamRemoved = 
new ScriptInvokerBase<SCR_AIOnFireteamRemoved>();
 
   22     ScriptInvokerBase<SCR_AIOnFireteamRemoved> GetOnFireteamRemoved()
 
   24         return Event_OnFireteamRemoved;
 
   29     void OnAgentAdded(AIAgent agent, SCR_AIInfoComponent infoComp)
 
   35             destFt = CreateFireteam();
 
   36         destFt.AddMember(agent, infoComp);
 
   38         m_bRebalanceFireteams = 
true;
 
   43     void OnAgentRemoved(AIAgent agent)
 
   49             ft.RemoveMember(agent); 
 
   51             int ftSize = ft.GetMemberCount();
 
   56             m_bRebalanceFireteams = 
true;
 
   64         m_aFireteams.Insert(ft);
 
   71         int id = m_aFireteams.Find(ft);
 
   75         if (ft.GetMemberCount() != 0)
 
   77             Print(
"SCR_AIGroupUtilityComponent: removing a non-empty fireteam", LogLevel.ERROR);
 
   81         Event_OnFireteamRemoved.Invoke(ft);
 
   83         m_aFireteams.Remove(
id);
 
   87     protected SCR_AIGroupFireteam FindSmallestFireteam(array<SCR_AIGroupFireteam> fireteamsExclude = 
null)
 
   89         if (m_aFireteams.IsEmpty())
 
   92         int minSize = 
int.MAX;
 
   94         for (
int i = 0; i < m_aFireteams.Count(); i++)
 
   97             int size = ft.GetMemberCount();
 
  100                 if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
 
  112     protected SCR_AIGroupFireteam FindBiggestFireteam(array<SCR_AIGroupFireteam> fireteamsExclude = 
null)
 
  114         if (m_aFireteams.IsEmpty())
 
  117         int maxSize = 
int.MIN;
 
  119         for (
int i = 0; i < m_aFireteams.Count(); i++)
 
  122             int size = ft.GetMemberCount();
 
  125                 if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
 
  138     bool FindFreeFireteams(notnull array<SCR_AIGroupFireteam> outFireteams, 
int count, array<SCR_AIGroupFireteam> fireteamsExclude = 
null)
 
  140         outFireteams.Clear();
 
  141         for (
int i = 0; i < m_aFireteams.Count(); i++)
 
  146                 if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
 
  148                     outFireteams.Insert(ft);
 
  149                     if (outFireteams.Count() == count)
 
  160     void GetFreeFireteams(notnull array<SCR_AIGroupFireteam> outFireteams, array<SCR_AIGroupFireteam> fireteamsExclude = 
null)
 
  162         outFireteams.Clear();
 
  163         for (
int i = 0; i < m_aFireteams.Count(); i++)
 
  168                 if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
 
  170                     outFireteams.Insert(ft);
 
  182             if (ft.HasMember(agent))
 
  189     int GetFireteamCount()
 
  191         return m_aFireteams.Count();
 
  197         foreach (
int id, 
auto _ft : m_aFireteams)
 
  206     protected static int GetMaxFireteamSize(
int groupSize)
 
  210         else if (groupSize > 4)
 
  212         else if (groupSize == 4)
 
  220     void RebalanceFireteams()
 
  222         int groupSize = 
m_Group.GetAgentsCount();
 
  227             m_bRebalanceFireteams = 
false;
 
  231         int maxFtSize = GetMaxFireteamSize(groupSize);
 
  232         float fMaxFtSize = maxFtSize;
 
  233         int agentsCount = 
m_Group.GetAgentsCount();
 
  234         float fAgentsCount = agentsCount;
 
  236         int desiredFtCount = Math.Ceil(fAgentsCount / fMaxFtSize);
 
  238         if (m_aFireteams.Count() < desiredFtCount)
 
  241             int newFtCount = desiredFtCount - m_aFireteams.Count();
 
  242             for (
int i = 0; i < newFtCount; i++)
 
  245         else if (m_aFireteams.Count() > desiredFtCount)
 
  249             int deleteFtCount = m_aFireteams.Count() - desiredFtCount;
 
  250             array<SCR_AIGroupFireteam> fireteamsDelete = {};
 
  253             for (
int i = 0; i < deleteFtCount; i++)
 
  256                 fireteamsDelete.Insert(smallestFt);
 
  263                 destinationFt.MoveMembersFrom(fireteamDelete, fireteamDelete.GetMemberCount());
 
  264                 RemoveFireteam(fireteamDelete);
 
  268         array<SCR_AIGroupFireteam> fireteamsTooBig = {};
 
  269         array<SCR_AIGroupFireteam> fireteamsTooSmall = {};
 
  270         CountUnbalancedFireteams(fireteamsTooBig, fireteamsTooSmall);
 
  275         const int maxIterations = 128;
 
  276         while ((!fireteamsTooBig.IsEmpty() || !fireteamsTooSmall.IsEmpty()) && !failed && nIterations < maxIterations)
 
  278             if (!fireteamsTooBig.IsEmpty())
 
  284                 int nExcessMembers = srcFt.GetMemberCount() - maxFtSize;
 
  285                 for (
int i = 0; i < nExcessMembers; i++)
 
  291                         Print(
string.Format(
"SCR_AIGroupUtilityComponent: failed to reorganize fireteams, all other fireteams are full. %1",
 
  292                             DiagGetFireteamsData()), LogLevel.ERROR);
 
  298                         dstFt.MoveMembersFrom(srcFt, 1);
 
  302             else if (!fireteamsTooSmall.IsEmpty())
 
  307                 int nLackMembers = FIRETEAM_MIN_SIZE - dstFt.GetMemberCount(); 
 
  309                 for (
int i = 0; i < nLackMembers; i++)
 
  318                             Print(
string.Format(
"SCR_AIGroupUtilityComponent: failed to reorganize fireteams, all other fireteams are too small. %1",
 
  319                                 DiagGetFireteamsData()), LogLevel.ERROR);
 
  325                         dstFt.MoveMembersFrom(srcFt, 1);
 
  331             CountUnbalancedFireteams(fireteamsTooBig, fireteamsTooSmall);
 
  335         if (nIterations == maxIterations)
 
  337             Print(
string.Format(
"SCR_AIGroupUtilityComponent: RebalanceFireteams: max amount of iterations has been reached. %1", DiagGetFireteamsData()), LogLevel.ERROR);
 
  340         m_bRebalanceFireteams = 
false;
 
  342     protected void CountUnbalancedFireteams(notnull array<SCR_AIGroupFireteam> fireteamsTooBig, notnull array<SCR_AIGroupFireteam> fireteamsTooSmall)
 
  344         int maxFtSize = GetMaxFireteamSize(
m_Group.GetAgentsCount());
 
  345         fireteamsTooBig.Clear();
 
  346         fireteamsTooSmall.Clear();
 
  349             int size = ft.GetMemberCount();
 
  350             if (size == maxFtSize) 
 
  353             if (size > maxFtSize)
 
  354                 fireteamsTooBig.Insert(ft);
 
  355             else if (size < FIRETEAM_MIN_SIZE)
 
  356                 fireteamsTooSmall.Insert(ft); 
 
  362     string DiagGetFireteamsData()
 
  364         string s = 
string.Format(
"Fireteams: %1: ", m_aFireteams.Count());
 
  367             string strLocked = 
string.Empty;
 
  371             s = s + 
string.Format(
"%1%2, ", ft.GetMemberCount(), strLocked);
 
  377     void DiagDrawFireteams()
 
  379         array<AIAgent> members = {};
 
  382             ft.GetMembers(members);
 
  383             foreach (AIAgent agent : members)
 
  385                 IEntity e = agent.GetControlledEntity();
 
  388                 vector textPos = e.GetOrigin() + Vector (0, 0.5, 0);
 
  389                 string text = 
string.Format(
"FT: %1", fireteamId);
 
  390                 DebugTextWorldSpace.Create(
GetGame().GetWorld(), text, DebugTextFlags.ONCE | DebugTextFlags.CENTER | DebugTextFlags.FACE_CAMERA,
 
  391                     textPos[0], textPos[1], textPos[2], color: Color.GREEN, bgColor: Color.BLACK,