Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AIGroupPerception.c
Go to the documentation of this file.
3
6
7void SCR_AIGroupPerceptionOnEnemyDetected(SCR_AIGroup group, SCR_AITargetInfo target);
9
10void SCR_AIGroupPerceptionOnEnemyDetectedFiltered(SCR_AIGroup group, SCR_AITargetInfo target, AIAgent reporter);
12
13class SCR_AIGroupPerception : Managed
14{
15 // Target is considered lost if noone has seen it for this time.
16 // This value should be consistent with actual duration of AI combat behavior (see TARGET_MAX_LAST_SEEN)
17 const float TARGET_LOST_THRESHOLD_S = 10.0;
18
19 // Time till target is totally forgotten and is removed from memory.
20 // This doesn't need to be much longer than MAX_CLUSTER_AGE_S
21 const float TARGET_FORGET_THRESHOLD_S = 150.0;
22
25
26 protected ref ScriptInvokerBase<SCR_AIOnTargetClusterStateDeleted> Event_OnTargetClusterStateDeleted = new ScriptInvokerBase<SCR_AIOnTargetClusterStateDeleted>();
27 protected ref ScriptInvokerBase<SCR_AIGroupPerceptionOnEnemyDetected> Event_OnEnemyDetected;
28 protected ref ScriptInvokerBase<SCR_AIGroupPerceptionOnEnemyDetectedFiltered> Event_OnEnemyDetectedFiltered;
29 protected ref ScriptInvokerBase<SCR_AIGroupPerceptionOnNoEnemy> Event_OnNoEnemy;
30
31 ref array<IEntity> m_aTargetEntities = new array<IEntity>; // Read only! Don't dare to modify it!
32 ref array<ref SCR_AITargetInfo> m_aTargets = new array<ref SCR_AITargetInfo>; // Read only!
33 ref array<ref SCR_AIGroupTargetCluster> m_aTargetClusters = {}; // Read only!
35
36 //---------------------------------------------------------------------------------------------------
38 {
39 m_Group = group;
40 m_Utility = utility;
41 }
42
43 //---------------------------------------------------------------------------------------------------
44 ScriptInvokerBase<SCR_AIOnTargetClusterStateDeleted> GetOnTargetClusterDeleted()
45 {
47 }
48
49 //---------------------------------------------------------------------------------------------------
50 ScriptInvokerBase<SCR_AIGroupPerceptionOnEnemyDetected> GetOnEnemyDetected()
51 {
53 Event_OnEnemyDetected = new ScriptInvokerBase<SCR_AIGroupPerceptionOnEnemyDetected>();
54
56 }
57
58 //---------------------------------------------------------------------------------------------------
61 ScriptInvokerBase<SCR_AIGroupPerceptionOnEnemyDetectedFiltered> GetOnEnemyDetectedFiltered()
62 {
64 Event_OnEnemyDetectedFiltered = new ScriptInvokerBase<SCR_AIGroupPerceptionOnEnemyDetectedFiltered>();
65
67 }
68
69 //---------------------------------------------------------------------------------------------------
70 ScriptInvokerBase<SCR_AIGroupPerceptionOnNoEnemy> GetOnNoEnemy()
71 {
72 if (!Event_OnNoEnemy)
73 Event_OnNoEnemy = new ScriptInvokerBase<SCR_AIGroupPerceptionOnNoEnemy>();
74
75 return Event_OnNoEnemy;
76 }
77
78 //---------------------------------------------------------------------------------------------------
79 protected void RemoveTarget(IEntity enemy)
80 {
81 if (!enemy)
82 return;
83
84 int index = m_aTargetEntities.Find(enemy);
85
86 if (index > -1)
87 {
89 m_aTargets.Remove(index);
90 if (m_aTargetEntities.IsEmpty() && Event_OnNoEnemy)
92 }
93 }
94
95 //---------------------------------------------------------------------------------------------------
96 protected void RemoveTarget(int id)
97 {
98 m_aTargetEntities.Remove(id);
99 m_aTargets.Remove(id);
100
101 if (m_aTargetEntities.IsEmpty() && Event_OnNoEnemy)
102 Event_OnNoEnemy.Invoke(m_Group);
103 }
104
105 //---------------------------------------------------------------------------------------------------
106 // Adds or updates target from BaseTarget
107 SCR_AITargetInfo AddOrUpdateTarget(notnull BaseTarget target, out bool outNewTarget)
108 {
109 IEntity enemy = target.GetTargetEntity();
110
111 if (!enemy)
112 {
113 outNewTarget = false;
114 return null;
115 }
116
117 int id = m_aTargetEntities.Find(enemy);
118 if (id > -1)
119 {
120 SCR_AITargetInfo oldTargetInfo = m_aTargets[id];
121 EAITargetInfoCategory oldCategory = m_aTargets[id].m_eCategory;
122
123 // Ignore if destroyed or disarmed
124 if (oldCategory == EAITargetInfoCategory.DESTROYED || oldCategory == EAITargetInfoCategory.DISARMED)
125 {
126 outNewTarget = false;
127 return oldTargetInfo;
128 }
129
130 // This target was already found
131 // Which newTimestamp to use? Depends on target category
132 float newTimestamp;
133 ETargetCategory targetCategory = target.GetTargetCategory();
134 if (targetCategory == ETargetCategory.DETECTED)
135 newTimestamp = target.GetTimeLastDetected();
136 else
137 newTimestamp = target.GetTimeLastSeen();
138
139 if (oldTargetInfo.m_fTimestamp < newTimestamp)
140 {
141 // New information is newer
142 // Is new data more relevant?
143 if ((targetCategory == ETargetCategory.ENEMY) || // If enemy, always update
144 ((targetCategory == ETargetCategory.DETECTED) && (oldCategory != EAITargetInfoCategory.IDENTIFIED)) )
145 {
146 oldTargetInfo.UpdateFromBaseTarget(target);
147 }
148 }
149
150 outNewTarget = false;
151 return oldTargetInfo;
152 }
153
154 // new enemy found
155
156 // Ignore if disarmed
157 if (target.IsDisarmed())
158 {
159 outNewTarget = false;
160 return null;
161 }
162
163 SCR_AITargetInfo targetInfo = new SCR_AITargetInfo();
164 targetInfo.InitFromBaseTarget(target);
165
166 m_aTargetEntities.Insert(enemy);
167 m_aTargets.Insert(targetInfo);
168
170 {
171 Event_OnEnemyDetected.Invoke(m_Group, targetInfo);
172 }
173
174 outNewTarget = true;
175 return targetInfo;
176 }
177
178 //---------------------------------------------------------------------------------------------------
179 void AddOrUpdateGunshot(notnull IEntity shooter, vector worldPos, Faction faction, float timestamp, bool endangering)
180 {
181 int id = m_aTargetEntities.Find(shooter);
182 if (id > -1)
183 {
184 // Update data
185 SCR_AITargetInfo oldTargetInfo = m_aTargets[id];
186 if ((oldTargetInfo.m_eCategory != EAITargetInfoCategory.IDENTIFIED) && // Update only if it wasn't seen yet
187 (timestamp > oldTargetInfo.m_fTimestamp)) // Update only if new data is newer
188 {
189 oldTargetInfo.UpdateFromGunshot(worldPos, timestamp, endangering);
190 }
191
192 // Update endangering flag
193 oldTargetInfo.m_bEndangering |= endangering;
194 }
195 else
196 {
197 // Create new data
198 PerceivableComponent perceivable = PerceivableComponent.Cast(shooter.FindComponent(PerceivableComponent));
199
200 // Ignore aircrafts. Fix to prevent suppression of aircrafts,
201 // but also there is no reason to track aircrafts in group perception.
202 if (perceivable && perceivable.GetUnitType() == EAIUnitType.UnitType_Aircraft)
203 return;
204
205 SCR_AITargetInfo targetInfo = new SCR_AITargetInfo();
206 targetInfo.InitFromGunshot(shooter, perceivable, worldPos, faction, timestamp, endangering);
207
208 m_aTargets.Insert(targetInfo);
209 m_aTargetEntities.Insert(shooter);
210 }
211 }
212
213 //---------------------------------------------------------------------------------------------------
214 void Update()
215 {
216 UpdateTargetsFromMembers(); // These calls ...
217 MaintainTargets(); // ... must be ... | <- this one removed targets
218 ClusterTargets(); // ... in this order. | thus clustering must happen after it
219
221 }
222
223 //---------------------------------------------------------------------------------------------------
225 {
226 IEntity leaderEntity = m_Group.GetLeaderEntity();
227
228 // Makes no sense without a leader
229 if (!leaderEntity)
230 return;
231
232 // Update all targets from our group members
233 array<AIAgent> agents = {};
234 m_Group.GetAgents(agents);
235
236 array<BaseTarget> targets = {};
237
238 bool targetIsNew;
239 SCR_AITargetInfo targetInfo;
240 bool invokedEvent = false;
241
242 foreach (SCR_AIInfoComponent infoComp : m_Utility.m_aInfoComponents)
243 {
244 PerceptionComponent perception = infoComp.m_Perception;
245
246 targets.Clear();
247 perception.GetTargetsList(targets, ETargetCategory.DETECTED);
248 foreach (BaseTarget baseTarget : targets)
249 {
250 PerceivableComponent perceivable = baseTarget.GetPerceivableComponent();
251 if (!perceivable)
252 continue;
253
254 // Ignore if target is in vehicle, from group perspective we don't care about vehicle occupants
255 if (perceivable.IsInCompartment())
256 continue;
257
258 // Ignore aircrafts. Fix to prevent suppression of aircrafts,
259 // but also there is no reason to track aircrafts in group perception.
260 if (perceivable.GetUnitType() == EAIUnitType.UnitType_Aircraft)
261 continue;
262
263 targetInfo = AddOrUpdateTarget(baseTarget, targetIsNew);
264
265 if (targetIsNew && !invokedEvent)
266 {
268 {
269 AIAgent reporter = AIAgent.Cast(infoComp.GetOwner());
270 Event_OnEnemyDetectedFiltered.Invoke(m_Group, targetInfo, reporter);
271 }
272 invokedEvent = true;
273 }
274 }
275
276 targets.Clear();
277 perception.GetTargetsList(targets, ETargetCategory.ENEMY);
278 foreach (BaseTarget baseTarget : targets)
279 {
280 PerceivableComponent perceivable = baseTarget.GetPerceivableComponent();
281 if (!perceivable)
282 continue;
283
284 // Ignore aircrafts. Fix to prevent suppression of aircrafts,
285 // but also there is no reason to track aircrafts in group perception.
286 if (perceivable.GetUnitType() == EAIUnitType.UnitType_Aircraft)
287 continue;
288
289 // Ignore if target is in vehicle, from group perspective we don't care about vehicle occupants
290 if (perceivable.IsInCompartment())
291 continue;
292
293 targetInfo = AddOrUpdateTarget(baseTarget, targetIsNew);
294
295 if (targetIsNew && !invokedEvent)
296 {
298 {
299 AIAgent reporter = AIAgent.Cast(infoComp.GetOwner());
300 Event_OnEnemyDetectedFiltered.Invoke(m_Group, targetInfo, reporter);
301 }
302 invokedEvent = true;
303 }
304 }
305 }
306 }
307
308 //---------------------------------------------------------------------------
309 protected void MaintainTargets()
310 {
311 float timeNow;
312 PerceptionManager pm = GetGame().GetPerceptionManager();
313 if (pm)
314 timeNow = pm.GetTime();
315
316 for (int i = m_aTargets.Count()-1; i >= 0; i--)
317 {
318 SCR_AITargetInfo tgtInfo = m_aTargets[i];
319 EAITargetInfoCategory category = tgtInfo.m_eCategory;
320 if (timeNow - tgtInfo.m_fTimestamp > TARGET_FORGET_THRESHOLD_S) // Time to forget?
321 {
322 RemoveTarget(i);
323 }
324 else if (category == EAITargetInfoCategory.DISARMED) // Disarmed?
325 {
326 if (tgtInfo.m_DamageManager && tgtInfo.m_DamageManager.IsDestroyed()) // Destroyed?
327 tgtInfo.m_eCategory = EAITargetInfoCategory.DESTROYED;
328 if (tgtInfo.m_Perceivable && !tgtInfo.m_Perceivable.IsDisarmed()) // Not disarmed any more?
329 tgtInfo.m_eCategory = EAITargetInfoCategory.DETECTED; // Back to detected
330 }
331 else if (category != EAITargetInfoCategory.DESTROYED) // Not destroyed?
332 {
333 if (tgtInfo.m_DamageManager && tgtInfo.m_DamageManager.IsDestroyed()) // Destroyed?
334 tgtInfo.m_eCategory = EAITargetInfoCategory.DESTROYED;
335 else if (!tgtInfo.m_Entity) // Deleted? Treat as destroyed.
336 tgtInfo.m_eCategory = EAITargetInfoCategory.DESTROYED;
337 else if (timeNow - tgtInfo.m_fTimestamp > TARGET_LOST_THRESHOLD_S) // Lost?
338 tgtInfo.m_eCategory = EAITargetInfoCategory.LOST;
339 else if (tgtInfo.m_Perceivable && tgtInfo.m_Perceivable.IsDisarmed()) // Disarmed?
340 tgtInfo.m_eCategory = EAITargetInfoCategory.DISARMED;
341 }
342 }
343 }
344
345 //---------------------------------------------------------------------------
346 protected void ClusterTargets()
347 {
348 vector centerPos = m_Group.GetCenterOfMass();
349
350 if (centerPos == vector.Zero) // It's zero when group is empty
351 return;
352
353 float minAngularDist = Math.DEG2RAD * 30.0;
354 array<ref SCR_AIGroupTargetCluster> newClusters = {};
355 GenerateClusters(m_aTargets, newClusters, centerPos, minAngularDist);
356
357 // Calculate association of old clusters to new clusters
358 array<ref array<int>> association = {};
359 association.Resize(m_aTargetClusters.Count());
360 for (int oldClusterId = 0; oldClusterId < m_aTargetClusters.Count(); oldClusterId++)
361 {
362 array<int> row = new array<int>();
363 row.Resize(newClusters.Count());
364 association[oldClusterId] = row;
365
366 for (int newClusterId = 0; newClusterId < newClusters.Count(); newClusterId++)
367 {
368 int associationValue = GetClusterAssociation(m_aTargetClusters[oldClusterId], newClusters[newClusterId]);
369 row[newClusterId] = associationValue;
370 }
371 }
372
373 // Transfer previous orders from old clusters to new clusters
374
375 array<int> newClusterStateIds = {}; // Index is ID of new cluster, value is ID of old cluster
376 newClusterStateIds.Resize(newClusters.Count()); // From which we are transfering state
377 for (int i = 0; i < newClusters.Count(); i++)
378 newClusterStateIds[i] = -1;
379
380 for (int oldClusterId = 0; oldClusterId < m_aTargetClusters.Count(); oldClusterId++)
381 {
382 // Find the new cluster with which this one associates most
383 int maxAssociation = 0;
384 int newClusterIdMaxAssociation = -1;
385
386 array<int> row = association[oldClusterId];
387 for (int i = 0; i < row.Count(); i++)
388 {
389 if (row[i] > maxAssociation)
390 {
391 maxAssociation = row[i];
392 newClusterIdMaxAssociation = i;
393 }
394 }
395
396 bool transferClusterState = false;
397
398 if (newClusterIdMaxAssociation != -1)
399 {
400 // Old cluster associates with some new cluster
401
402
403 int stateTransferedFrom = newClusterStateIds[newClusterIdMaxAssociation];
404 if (stateTransferedFrom != -1)
405 {
406 // There is a conflict, we transfered state to this new cluster already
407 // Decide which association value is greater
408
409 if (association[stateTransferedFrom][newClusterIdMaxAssociation] < association[oldClusterId][newClusterIdMaxAssociation])
410 {
411 DeleteClusterState(newClusters[newClusterIdMaxAssociation]);
412 transferClusterState = true;
413 }
414 }
415 else
416 {
417 transferClusterState = true;
418 }
419 }
420
421 if (transferClusterState)
422 {
423 TransferClusterState(m_aTargetClusters[oldClusterId], newClusters[newClusterIdMaxAssociation]);
424 newClusterStateIds[newClusterIdMaxAssociation] = oldClusterId;
425 }
426 else
427 {
428 // Can't transfer state from old cluster to any new cluster, delete it
430 }
431 }
432
433 // Create state for all new clusters which did not inherit state
434 // Process targets in all new clusters
435 foreach (SCR_AIGroupTargetCluster c : newClusters)
436 {
437 if (!c.m_State)
438 c.m_State = new SCR_AITargetClusterState(c);
439
440 c.m_State.ProcessTargets();
441 }
442
443 m_aTargetClusters = newClusters;
444
445 //DiagPrintClusterAssociation(association);
446 }
447
448 //---------------------------------------------------------------------------
449 void GenerateClusters(array<ref SCR_AITargetInfo> targets, array<ref SCR_AIGroupTargetCluster> outClusters, vector centerPos, float minAngularDist)
450 {
451 outClusters.Clear();
452
453 // Bail if no targets are provided
454 if (targets.IsEmpty())
455 return;
456
457 int nTargets = targets.Count();
458
459 //-------------------------------------------
460 // Convert all positions to polar coordinates
461 array<ref SCR_AITargetInfoPolar> targetsPolar = {};
462 targetsPolar.Resize(nTargets);
463
464 for (int i = 0; i < nTargets; i++)
465 {
466 SCR_AITargetInfo target = targets[i];
467 vector vdir = target.m_vWorldPos - centerPos; // Not normalized!
468 float angle = SCR_AIPolar.DirToAngle(vdir);
469 float dist = vector.DistanceXZ(target.m_vWorldPos, centerPos);
470
471 SCR_AITargetInfoPolar targetPolar = new SCR_AITargetInfoPolar();
472 targetPolar.m_Target = target;
473 targetPolar.m_fAngle = angle;
474 targetPolar.m_fDistance = dist;
475
476 targetsPolar[i] = targetPolar;
477 }
478
479 //-------------------------------------------
480 // Iterate points and make clusters
481
482 // First sort by angle
483 targetsPolar.Sort(false);
484
485 // Iterate targets sorted by angle
487 currentCluster.AddTarget(targetsPolar[0].m_Target, targetsPolar[0].m_fAngle, targetsPolar[0].m_fDistance);
488 currentCluster.m_fAngleMin = targetsPolar[0].m_fAngle;
489 currentCluster.m_fAngleMax = currentCluster.m_fAngleMin;
490
491 outClusters.Insert(currentCluster);
492
493 for (int i = 1; i < nTargets; i++)
494 {
495 SCR_AITargetInfoPolar target = targetsPolar[i];
496
497 float angle = target.m_fAngle;
498
499 if (angle - currentCluster.m_fAngleMax < minAngularDist)
500 {
501 // Add the target
502 currentCluster.AddTarget(target.m_Target, angle, target.m_fDistance);
503 currentCluster.m_fAngleMax = angle; // Increase max angle
504 }
505 else
506 {
507 // Otherwise make a new cluster
508 currentCluster = new SCR_AIGroupTargetCluster();
509 currentCluster.AddTarget(target.m_Target, target.m_fAngle, target.m_fDistance);
510 currentCluster.m_fAngleMin = target.m_fAngle;
511 currentCluster.m_fAngleMax = currentCluster.m_fAngleMin;
512 outClusters.Insert(currentCluster);
513 }
514 }
515
516 // Merge last and first cluster if they are too close
517
518 if (outClusters.Count() > 1)
519 {
520 SCR_AIGroupTargetCluster first = outClusters[0];
521 SCR_AIGroupTargetCluster last = outClusters[outClusters.Count()-1];
522
523 float distLastToFirst = Math.PI2 - last.m_fAngleMax + first.m_fAngleMin;
524 if (distLastToFirst < minAngularDist)
525 {
526 // Merge last cluster into first one and remove it
527 first.AddCluster(last);
528 outClusters.RemoveOrdered(outClusters.Count()-1);
529 }
530 }
531
532 // We are done!
533 }
534
535 //---------------------------------------------------------------------------
536 // Returns integer, how cluster 'a' associates with cluster 'b'
538 {
539 int intersection = 0;
540 array<SCR_AITargetInfo> bTargets = b.m_aTargets;
541 foreach (SCR_AITargetInfo aTarget : a.m_aTargets)
542 {
543 if (aTarget && // Don't associate null-targets
544 bTargets.Find(aTarget) != -1)
545 intersection++;
546 }
547 return intersection;
548 }
549
550 //---------------------------------------------------------------------------
552 {
553 float maxScore = 0;
554 SCR_AIGroupTargetCluster maxScoreCluster;
555
557 {
558 float score = CalculateClusterDangerScore(c);
559 if (score > maxScore)
560 {
561 maxScore = score;
562 maxScoreCluster = c;
563 }
564 }
565
566 m_MostDangerousCluster = maxScoreCluster;
567 }
568
569 //---------------------------------------------------------------------------
571 {
572 float score = 0;
573
574 // distance
575 float distClamped = Math.Min(c.m_State.m_fDistMin, 1000.0);
576 score += 1000.0 - distClamped;
577
578 // If it's lost, it matters less, it's about to be forgotten
579 if (c.m_State.m_eState != EAITargetClusterState.LOST)
580 score += 600.0;
581
582 // Identified or endangering targets
583 score += 200.0 * c.m_State.m_iCountEndangering;
584 score += 50.0 * c.m_State.m_iCountDetected;
585 score += 100.0 * c.m_State.m_iCountIdentified;
586
587 return score;
588 }
589
590 //---------------------------------------------------------------------------
592 {
593 Event_OnTargetClusterStateDeleted.Invoke(c.m_State);
594 c.m_State = null;
595 }
596
597 //---------------------------------------------------------------------------
599 {
600 newCluster.MoveStateFrom(oldCluster);
601 }
602
603 //---------------------------------------------------------------------------
605 {
607 {
608 if (c.m_State == s)
609 return i;
610 }
611 return -1;
612 }
613
614 //---------------------------------------------------------------------------
615 void DiagPrintClusterAssociation(array<ref array<int>> association)
616 {
617 Print("Cluster association:");
618 for (int oldClusterId = 0; oldClusterId < association.Count(); oldClusterId++)
619 {
620 array<int> row = association[oldClusterId];
621
622 string s = string.Format("%1: ", oldClusterId);
623 for (int newClusterId = 0; newClusterId < row.Count(); newClusterId++)
624 s = s + string.Format("%1\t", row[newClusterId].ToString());
625
626 Print(s);
627 }
628 }
629
630
631 //---------------------------------------------------------------------------
633 {
634 PerceptionManager pm = GetGame().GetPerceptionManager();
635 float timeNow_s = pm.GetTime();
636
637 foreach (int clusterId, SCR_AIGroupTargetCluster cluster : m_aTargetClusters)
638 {
639 foreach (SCR_AITargetInfo targetInfo : cluster.m_aTargets)
640 {
641 vector mrkPos = targetInfo.m_vWorldPos + Vector(0, 3, 0);
642
643 string s = string.Empty;
644
645 float timeSinceTimestamp_s = timeNow_s - targetInfo.m_fTimestamp;
646 s = s + string.Format("C: %1, time-timestamp: %2\n", clusterId.ToString(), timeSinceTimestamp_s.ToString(5, 1));
647
648 s = s + string.Format("Category: %1, dngr: %2", typename.EnumToString(EAITargetInfoCategory, targetInfo.m_eCategory), targetInfo.m_bEndangering);
649
650 DebugTextWorldSpace.Create(GetGame().GetWorld(), s, DebugTextFlags.ONCE | DebugTextFlags.CENTER | DebugTextFlags.FACE_CAMERA,
651 mrkPos[0], mrkPos[1], mrkPos[2],
652 color: 0xFFFFFFFF, bgColor: 0xFF000000,
653 size: 10.0);
654 //float size = 20.0, int , int , int priority = 1000);
655 }
656
657 vector clusterCenter = cluster.m_State.GetCenterPosition();
658
659 // Mark for cluster
660 string s = string.Empty;
661
662 s = s + string.Format("Cluster: %1\n", clusterId);
663 if (cluster.m_State)
664 {
665 s = s + string.Format("State: %1, Time since Info: %2, Max age: %3",
666 typename.EnumToString(EAITargetClusterState, cluster.m_State.m_eState),
667 cluster.m_State.GetTimeSinceLastNewInformation().ToString(5,1),
668 cluster.m_State.m_fMaxAge_s.ToString(5, 1));
669 }
670
671 DebugTextWorldSpace.Create(GetGame().GetWorld(), s, DebugTextFlags.ONCE | DebugTextFlags.CENTER | DebugTextFlags.FACE_CAMERA,
672 clusterCenter[0], clusterCenter[1] + 10.0, clusterCenter[2],
673 color: 0xFFFFFFFF, bgColor: 0xFF000000,
674 size: 12.0);
675 }
676 }
677}
AddonBuildInfoTool id
ArmaReforgerScripted GetGame()
Definition game.c:1398
int size
func SCR_AIGroupPerceptionOnEnemyDetectedFiltered
func SCR_AIGroupPerceptionOnNoEnemy
func SCR_AIOnTargetClusterStateDeleted
func SCR_AIGroupPerceptionOnEnemyDetected
float m_fDistance
float m_fAngle
class SCR_AIPolar m_Target
void SCR_AITargetClusterState(SCR_AIGroupTargetCluster cluster)
EAITargetInfoCategory
when task is activated by player or by killing enemy
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Definition Math.c:13
ref ScriptInvokerBase< SCR_AIGroupPerceptionOnEnemyDetectedFiltered > Event_OnEnemyDetectedFiltered
int GetClusterAssociation(SCR_AIGroupTargetCluster a, SCR_AIGroupTargetCluster b)
SCR_AIGroupUtilityComponent m_Utility
ref ScriptInvokerBase< SCR_AIGroupPerceptionOnEnemyDetected > Event_OnEnemyDetected
void DeleteClusterState(SCR_AIGroupTargetCluster c)
ScriptInvokerBase< SCR_AIGroupPerceptionOnNoEnemy > GetOnNoEnemy()
ref array< ref SCR_AITargetInfo > m_aTargets
ref ScriptInvokerBase< SCR_AIGroupPerceptionOnNoEnemy > Event_OnNoEnemy
void AddOrUpdateGunshot(notnull IEntity shooter, vector worldPos, Faction faction, float timestamp, bool endangering)
void GenerateClusters(array< ref SCR_AITargetInfo > targets, array< ref SCR_AIGroupTargetCluster > outClusters, vector centerPos, float minAngularDist)
SCR_AIGroupTargetCluster m_MostDangerousCluster
void DiagPrintClusterAssociation(array< ref array< int > > association)
void SCR_AIGroupPerception(SCR_AIGroupUtilityComponent utility, SCR_AIGroup group)
ScriptInvokerBase< SCR_AIGroupPerceptionOnEnemyDetected > GetOnEnemyDetected()
float CalculateClusterDangerScore(SCR_AIGroupTargetCluster c)
void TransferClusterState(SCR_AIGroupTargetCluster oldCluster, SCR_AIGroupTargetCluster newCluster)
ScriptInvokerBase< SCR_AIGroupPerceptionOnEnemyDetectedFiltered > GetOnEnemyDetectedFiltered()
int GetTargetClusterStateId(SCR_AITargetClusterState s)
ref array< ref SCR_AIGroupTargetCluster > m_aTargetClusters
ScriptInvokerBase< SCR_AIOnTargetClusterStateDeleted > GetOnTargetClusterDeleted()
SCR_AITargetInfo AddOrUpdateTarget(notnull BaseTarget target, out bool outNewTarget)
ref array< IEntity > m_aTargetEntities
void RemoveTarget(IEntity enemy)
ref ScriptInvokerBase< SCR_AIOnTargetClusterStateDeleted > Event_OnTargetClusterStateDeleted
PerceptionComponent m_Perception
EAIUnitType
Definition EAIUnitType.c:13
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
DebugTextFlags
ETargetCategory
proto external string ToString()
Plain C++ pointer, no weak pointers, no memory management.
proto native vector Vector(float x, float y, float z)