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,