Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_AIGroupFireteamManager.c
Go to the documentation of this file.
3 
5 {
6  protected const int FIRETEAM_MIN_SIZE = 2;
7 
8  protected SCR_AIGroup m_Group;
9  protected ref array<ref SCR_AIGroupFireteam> m_aFireteams = {};
10  bool m_bRebalanceFireteams = false; // True when fireteams become unbalanced
11 
12  // Fireteam events
13  protected ref ScriptInvokerBase<SCR_AIOnFireteamRemoved> Event_OnFireteamRemoved = new ScriptInvokerBase<SCR_AIOnFireteamRemoved>();
14 
15  //---------------------------------------------------------------------------------------------------
17  {
18  m_Group = group;
19  }
20 
21  //---------------------------------------------------------------------------------------------------
22  ScriptInvokerBase<SCR_AIOnFireteamRemoved> GetOnFireteamRemoved()
23  {
24  return Event_OnFireteamRemoved;
25  }
26 
27  //---------------------------------------------------------------------------------------------------
29  void OnAgentAdded(AIAgent agent, SCR_AIInfoComponent infoComp)
30  {
31  // Add to fireteam
32  // We find smallest fireteam and rebalance them later
33  SCR_AIGroupFireteam destFt = FindSmallestFireteam();
34  if (!destFt)
35  destFt = CreateFireteam();
36  destFt.AddMember(agent, infoComp);
37 
38  m_bRebalanceFireteams = true;
39  }
40 
41  //---------------------------------------------------------------------------------------------------
43  void OnAgentRemoved(AIAgent agent)
44  {
45  // Remove from fireteam
46  SCR_AIGroupFireteam ft = FindFireteam(agent);
47  if (ft)
48  {
49  ft.RemoveMember(agent); // bye
50 
51  int ftSize = ft.GetMemberCount();
52  if (ftSize == 0)
53  RemoveFireteam(ft);
54 
55  // Rebalance fireteams later
56  m_bRebalanceFireteams = true;
57  }
58  }
59 
60  //---------------------------------------------------------------------------------------------------
61  protected SCR_AIGroupFireteam CreateFireteam()
62  {
64  m_aFireteams.Insert(ft);
65  return ft;
66  }
67 
68  //---------------------------------------------------------------------------------------------------
69  protected void RemoveFireteam(SCR_AIGroupFireteam ft)
70  {
71  int id = m_aFireteams.Find(ft);
72  if (id == -1)
73  return;
74 
75  if (ft.GetMemberCount() != 0)
76  {
77  Print("SCR_AIGroupUtilityComponent: removing a non-empty fireteam", LogLevel.ERROR);
78  return;
79  }
80 
81  Event_OnFireteamRemoved.Invoke(ft);
82 
83  m_aFireteams.Remove(id);
84  }
85 
86  //---------------------------------------------------------------------------------------------------
87  protected SCR_AIGroupFireteam FindSmallestFireteam(array<SCR_AIGroupFireteam> fireteamsExclude = null)
88  {
89  if (m_aFireteams.IsEmpty())
90  return null;
91 
92  int minSize = int.MAX;
93  SCR_AIGroupFireteam outFt;
94  for (int i = 0; i < m_aFireteams.Count(); i++)
95  {
96  SCR_AIGroupFireteam ft = m_aFireteams[i];
97  int size = ft.GetMemberCount();
98  if (size < minSize)
99  {
100  if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
101  {
102  minSize = size;
103  outFt = ft;
104  }
105  }
106  }
107 
108  return outFt;
109  }
110 
111  //---------------------------------------------------------------------------------------------------
112  protected SCR_AIGroupFireteam FindBiggestFireteam(array<SCR_AIGroupFireteam> fireteamsExclude = null)
113  {
114  if (m_aFireteams.IsEmpty())
115  return null;
116 
117  int maxSize = int.MIN;
118  SCR_AIGroupFireteam outFt;
119  for (int i = 0; i < m_aFireteams.Count(); i++)
120  {
121  SCR_AIGroupFireteam ft = m_aFireteams[i];
122  int size = ft.GetMemberCount();
123  if (size > maxSize)
124  {
125  if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
126  {
127  maxSize = size;
128  outFt = ft;
129  }
130  }
131  }
132 
133  return outFt;
134  }
135 
136  //---------------------------------------------------------------------------------------------------
137  // Tries to find at least 'count' free fireteams. Returns true if it was able to achieve this amount of fireteams.
138  bool FindFreeFireteams(notnull array<SCR_AIGroupFireteam> outFireteams, int count, array<SCR_AIGroupFireteam> fireteamsExclude = null)
139  {
140  outFireteams.Clear();
141  for (int i = 0; i < m_aFireteams.Count(); i++)
142  {
143  SCR_AIGroupFireteam ft = m_aFireteams[i];
144  if (!ft.IsLocked())
145  {
146  if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
147  {
148  outFireteams.Insert(ft);
149  if (outFireteams.Count() == count)
150  return true;
151  }
152  }
153  }
154 
155  return false;
156  }
157 
158  //---------------------------------------------------------------------------------------------------
159  // Returns all free fireteams
160  void GetFreeFireteams(notnull array<SCR_AIGroupFireteam> outFireteams, array<SCR_AIGroupFireteam> fireteamsExclude = null)
161  {
162  outFireteams.Clear();
163  for (int i = 0; i < m_aFireteams.Count(); i++)
164  {
165  SCR_AIGroupFireteam ft = m_aFireteams[i];
166  if (!ft.IsLocked())
167  {
168  if (!fireteamsExclude || (fireteamsExclude && fireteamsExclude.Find(ft) == -1))
169  {
170  outFireteams.Insert(ft);
171  }
172  }
173  }
174  }
175 
176  //---------------------------------------------------------------------------------------------------
178  SCR_AIGroupFireteam FindFireteam(AIAgent agent)
179  {
180  foreach (SCR_AIGroupFireteam ft : m_aFireteams)
181  {
182  if (ft.HasMember(agent))
183  return ft;
184  }
185  return null;
186  }
187 
188  //---------------------------------------------------------------------------------------------------
189  int GetFireteamCount()
190  {
191  return m_aFireteams.Count();
192  }
193 
194  //---------------------------------------------------------------------------------------------------
195  int GetFireteamId(notnull SCR_AIGroupFireteam ft)
196  {
197  foreach (int id, auto _ft : m_aFireteams)
198  {
199  if (_ft == ft)
200  return id;
201  }
202  return -1;
203  }
204 
205  //---------------------------------------------------------------------------------------------------
206  protected static int GetMaxFireteamSize(int groupSize)
207  {
208  if (groupSize >= 12)
209  return 4;
210  else if (groupSize > 4)
211  return 3;
212  else if (groupSize == 4)
213  return 2; // When exactly 4 members, we want two fireteams of 2 members
214  else
215  return groupSize; // When below 4 members, one fireteam or 1-2-3
216  }
217 
218 
219  //---------------------------------------------------------------------------------------------------
220  void RebalanceFireteams()
221  {
222  int groupSize = m_Group.GetAgentsCount();
223 
224  // Bail if group size is 0
225  if (groupSize == 0)
226  {
227  m_bRebalanceFireteams = false;
228  return;
229  }
230 
231  int maxFtSize = GetMaxFireteamSize(groupSize);
232  float fMaxFtSize = maxFtSize;
233  int agentsCount = m_Group.GetAgentsCount();
234  float fAgentsCount = agentsCount;
235 
236  int desiredFtCount = Math.Ceil(fAgentsCount / fMaxFtSize);
237 
238  if (m_aFireteams.Count() < desiredFtCount)
239  {
240  // Create new fireteams
241  int newFtCount = desiredFtCount - m_aFireteams.Count();
242  for (int i = 0; i < newFtCount; i++)
243  CreateFireteam();
244  }
245  else if (m_aFireteams.Count() > desiredFtCount)
246  {
247  // Delete fireteams ...
248 
249  int deleteFtCount = m_aFireteams.Count() - desiredFtCount;
250  array<SCR_AIGroupFireteam> fireteamsDelete = {};
251 
252  // Find smallelst fireteams for deletion
253  for (int i = 0; i < deleteFtCount; i++)
254  {
255  SCR_AIGroupFireteam smallestFt = FindSmallestFireteam(fireteamsDelete);
256  fireteamsDelete.Insert(smallestFt);
257  }
258 
259  // Move members from those selected fireteams, and delete them
260  foreach (SCR_AIGroupFireteam fireteamDelete : fireteamsDelete)
261  {
262  SCR_AIGroupFireteam destinationFt = FindSmallestFireteam(fireteamsDelete);
263  destinationFt.MoveMembersFrom(fireteamDelete, fireteamDelete.GetMemberCount());
264  RemoveFireteam(fireteamDelete);
265  }
266  }
267 
268  array<SCR_AIGroupFireteam> fireteamsTooBig = {};
269  array<SCR_AIGroupFireteam> fireteamsTooSmall = {};
270  CountUnbalancedFireteams(fireteamsTooBig, fireteamsTooSmall);
271 
272  //array<SCR_AIGroupFireteam> fireteamsExclude = {};
273  bool failed = false;
274  int nIterations = 0;
275  const int maxIterations = 128;
276  while ((!fireteamsTooBig.IsEmpty() || !fireteamsTooSmall.IsEmpty()) && !failed && nIterations < maxIterations)
277  {
278  if (!fireteamsTooBig.IsEmpty())
279  {
280  // First split big fireteams
281  SCR_AIGroupFireteam srcFt = fireteamsTooBig[0];
282 
283  // Take one big fireteam and move some members to smaller fireteams
284  int nExcessMembers = srcFt.GetMemberCount() - maxFtSize;
285  for (int i = 0; i < nExcessMembers; i++)
286  {
287  SCR_AIGroupFireteam dstFt = FindSmallestFireteam(fireteamsTooBig);
288  if (!dstFt)
289  {
290  // It shouldn't be possible
291  Print(string.Format("SCR_AIGroupUtilityComponent: failed to reorganize fireteams, all other fireteams are full. %1",
292  DiagGetFireteamsData()), LogLevel.ERROR);
293  failed = true;
294  break;
295  }
296  else
297  {
298  dstFt.MoveMembersFrom(srcFt, 1);
299  }
300  }
301  }
302  else if (!fireteamsTooSmall.IsEmpty())
303  {
304  // Second fill up the smallest fireteams
305  SCR_AIGroupFireteam dstFt = fireteamsTooSmall[0];
306 
307  int nLackMembers = FIRETEAM_MIN_SIZE - dstFt.GetMemberCount(); // How many more members we need
308 
309  for (int i = 0; i < nLackMembers; i++)
310  {
311  SCR_AIGroupFireteam srcFt = FindBiggestFireteam(fireteamsTooSmall);
312 
313  if (!srcFt)
314  {
315  // It shouldn't be possible in general case
316  // It could only happen when group size is very small
317  //if (agentsCount > maxFtSize)
318  Print(string.Format("SCR_AIGroupUtilityComponent: failed to reorganize fireteams, all other fireteams are too small. %1",
319  DiagGetFireteamsData()), LogLevel.ERROR);
320  failed = true; // For very small group size (1) it might be impossible to make 'balanced' fireteams at all, due to lack of group members
321  break;
322  }
323  else
324  {
325  dstFt.MoveMembersFrom(srcFt, 1);
326  }
327  }
328 
329  }
330 
331  CountUnbalancedFireteams(fireteamsTooBig, fireteamsTooSmall);
332  nIterations++;
333  }
334 
335  if (nIterations == maxIterations)
336  {
337  Print(string.Format("SCR_AIGroupUtilityComponent: RebalanceFireteams: max amount of iterations has been reached. %1", DiagGetFireteamsData()), LogLevel.ERROR);
338  }
339 
340  m_bRebalanceFireteams = false;
341  }
342  protected void CountUnbalancedFireteams(notnull array<SCR_AIGroupFireteam> fireteamsTooBig, notnull array<SCR_AIGroupFireteam> fireteamsTooSmall)
343  {
344  int maxFtSize = GetMaxFireteamSize(m_Group.GetAgentsCount());
345  fireteamsTooBig.Clear();
346  fireteamsTooSmall.Clear();
347  foreach (SCR_AIGroupFireteam ft : m_aFireteams)
348  {
349  int size = ft.GetMemberCount();
350  if (size == maxFtSize) // Perfect
351  continue;
352 
353  if (size > maxFtSize)
354  fireteamsTooBig.Insert(ft);
355  else if (size < FIRETEAM_MIN_SIZE)
356  fireteamsTooSmall.Insert(ft); // Includes empty or with one member
357  }
358  }
359 
360  //---------------------------------------------------------------------------------------------------
362  string DiagGetFireteamsData()
363  {
364  string s = string.Format("Fireteams: %1: ", m_aFireteams.Count());
365  foreach (SCR_AIGroupFireteam ft : m_aFireteams)
366  {
367  string strLocked = string.Empty;
368  if (ft.IsLocked())
369  strLocked = "L";
370 
371  s = s + string.Format("%1%2, ", ft.GetMemberCount(), strLocked);
372  }
373  return s;
374  }
375 
376  //---------------------------------------------------------------------------------------------------
377  void DiagDrawFireteams()
378  {
379  array<AIAgent> members = {};
380  foreach (int fireteamId, SCR_AIGroupFireteam ft : m_aFireteams)
381  {
382  ft.GetMembers(members);
383  foreach (AIAgent agent : members)
384  {
385  IEntity e = agent.GetControlledEntity();
386  if (!e)
387  continue;
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,
392  size: 13.0);
393  }
394  }
395  }
396 }
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
m_Group
protected SCR_AIGroup m_Group
Definition: SCR_CallsignGroupComponent.c:10
func
func
Definition: SCR_AIThreatSystem.c:5
SCR_AIGroupFireteamManager
Definition: SCR_AIGroupFireteamManager.c:4
SCR_AIGroupFireteam
Definition: SCR_AIGroupFireteam.c:1
SCR_AIGroup
Definition: SCR_AIGroup.c:68
SCR_AIOnFireteamRemoved
func SCR_AIOnFireteamRemoved
Definition: SCR_AIGroupFireteamManager.c:2