17 const float TARGET_LOST_THRESHOLD_S = 10.0;
21 const float TARGET_FORGET_THRESHOLD_S = 150.0;
32 ref array<ref SCR_AITargetInfo>
m_aTargets =
new array<ref SCR_AITargetInfo>;
73 Event_OnNoEnemy =
new ScriptInvokerBase<SCR_AIGroupPerceptionOnNoEnemy>();
113 outNewTarget =
false;
126 outNewTarget =
false;
127 return oldTargetInfo;
135 newTimestamp = target.GetTimeLastDetected();
137 newTimestamp = target.GetTimeLastSeen();
139 if (oldTargetInfo.m_fTimestamp < newTimestamp)
146 oldTargetInfo.UpdateFromBaseTarget(target);
150 outNewTarget =
false;
151 return oldTargetInfo;
157 if (target.IsDisarmed())
159 outNewTarget =
false;
163 SCR_AITargetInfo targetInfo =
new SCR_AITargetInfo();
164 targetInfo.InitFromBaseTarget(target);
187 (timestamp > oldTargetInfo.m_fTimestamp))
189 oldTargetInfo.UpdateFromGunshot(worldPos, timestamp, endangering);
193 oldTargetInfo.m_bEndangering |= endangering;
202 if (perceivable && perceivable.GetUnitType() ==
EAIUnitType.UnitType_Aircraft)
205 SCR_AITargetInfo targetInfo =
new SCR_AITargetInfo();
206 targetInfo.InitFromGunshot(shooter, perceivable, worldPos, faction, timestamp, endangering);
233 array<AIAgent> agents = {};
236 array<BaseTarget> targets = {};
239 SCR_AITargetInfo targetInfo;
240 bool invokedEvent =
false;
255 if (perceivable.IsInCompartment())
260 if (perceivable.GetUnitType() ==
EAIUnitType.UnitType_Aircraft)
265 if (targetIsNew && !invokedEvent)
269 AIAgent reporter = AIAgent.Cast(infoComp.GetOwner());
286 if (perceivable.GetUnitType() ==
EAIUnitType.UnitType_Aircraft)
290 if (perceivable.IsInCompartment())
295 if (targetIsNew && !invokedEvent)
299 AIAgent reporter = AIAgent.Cast(infoComp.GetOwner());
312 PerceptionManager pm =
GetGame().GetPerceptionManager();
314 timeNow = pm.GetTime();
316 for (
int i =
m_aTargets.Count()-1; i >= 0; i--)
320 if (timeNow - tgtInfo.m_fTimestamp > TARGET_FORGET_THRESHOLD_S)
326 if (tgtInfo.m_DamageManager && tgtInfo.m_DamageManager.IsDestroyed())
328 if (tgtInfo.m_Perceivable && !tgtInfo.m_Perceivable.IsDisarmed())
333 if (tgtInfo.m_DamageManager && tgtInfo.m_DamageManager.IsDestroyed())
335 else if (!tgtInfo.m_Entity)
337 else if (timeNow - tgtInfo.m_fTimestamp > TARGET_LOST_THRESHOLD_S)
339 else if (tgtInfo.m_Perceivable && tgtInfo.m_Perceivable.IsDisarmed())
350 if (centerPos ==
vector.Zero)
353 float minAngularDist =
Math.DEG2RAD * 30.0;
354 array<ref SCR_AIGroupTargetCluster> newClusters = {};
358 array<ref array<int>> association = {};
360 for (
int oldClusterId = 0; oldClusterId <
m_aTargetClusters.Count(); oldClusterId++)
362 array<int> row =
new array<int>();
363 row.Resize(newClusters.Count());
364 association[oldClusterId] = row;
366 for (
int newClusterId = 0; newClusterId < newClusters.Count(); newClusterId++)
369 row[newClusterId] = associationValue;
375 array<int> newClusterStateIds = {};
376 newClusterStateIds.Resize(newClusters.Count());
377 for (
int i = 0; i < newClusters.Count(); i++)
378 newClusterStateIds[i] = -1;
380 for (
int oldClusterId = 0; oldClusterId <
m_aTargetClusters.Count(); oldClusterId++)
383 int maxAssociation = 0;
384 int newClusterIdMaxAssociation = -1;
386 array<int> row = association[oldClusterId];
387 for (
int i = 0; i < row.Count(); i++)
389 if (row[i] > maxAssociation)
391 maxAssociation = row[i];
392 newClusterIdMaxAssociation = i;
396 bool transferClusterState =
false;
398 if (newClusterIdMaxAssociation != -1)
403 int stateTransferedFrom = newClusterStateIds[newClusterIdMaxAssociation];
404 if (stateTransferedFrom != -1)
409 if (association[stateTransferedFrom][newClusterIdMaxAssociation] < association[oldClusterId][newClusterIdMaxAssociation])
412 transferClusterState =
true;
417 transferClusterState =
true;
421 if (transferClusterState)
424 newClusterStateIds[newClusterIdMaxAssociation] = oldClusterId;
440 c.m_State.ProcessTargets();
449 void GenerateClusters(array<ref SCR_AITargetInfo> targets, array<ref SCR_AIGroupTargetCluster> outClusters,
vector centerPos,
float minAngularDist)
454 if (targets.IsEmpty())
457 int nTargets = targets.Count();
461 array<ref SCR_AITargetInfoPolar> targetsPolar = {};
462 targetsPolar.Resize(nTargets);
464 for (
int i = 0; i < nTargets; i++)
466 SCR_AITargetInfo target = targets[i];
467 vector vdir = target.m_vWorldPos - centerPos;
469 float dist =
vector.DistanceXZ(target.m_vWorldPos, centerPos);
471 SCR_AITargetInfoPolar targetPolar =
new SCR_AITargetInfoPolar();
472 targetPolar.m_Target = target;
473 targetPolar.m_fAngle = angle;
474 targetPolar.m_fDistance = dist;
476 targetsPolar[i] = targetPolar;
483 targetsPolar.Sort(
false);
488 currentCluster.m_fAngleMin = targetsPolar[0].m_fAngle;
489 currentCluster.m_fAngleMax = currentCluster.m_fAngleMin;
491 outClusters.Insert(currentCluster);
493 for (
int i = 1; i < nTargets; i++)
495 SCR_AITargetInfoPolar target = targetsPolar[i];
497 float angle = target.m_fAngle;
499 if (angle - currentCluster.m_fAngleMax < minAngularDist)
502 currentCluster.AddTarget(target.m_Target, angle, target.m_fDistance);
503 currentCluster.m_fAngleMax = angle;
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);
518 if (outClusters.Count() > 1)
523 float distLastToFirst =
Math.PI2 - last.m_fAngleMax + first.m_fAngleMin;
524 if (distLastToFirst < minAngularDist)
527 first.AddCluster(last);
528 outClusters.RemoveOrdered(outClusters.Count()-1);
539 int intersection = 0;
540 array<SCR_AITargetInfo> bTargets = b.m_aTargets;
541 foreach (SCR_AITargetInfo aTarget : a.m_aTargets)
544 bTargets.Find(aTarget) != -1)
559 if (score > maxScore)
575 float distClamped =
Math.Min(c.m_State.m_fDistMin, 1000.0);
576 score += 1000.0 - distClamped;
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;
600 newCluster.MoveStateFrom(oldCluster);
617 Print(
"Cluster association:");
618 for (
int oldClusterId = 0; oldClusterId < association.Count(); oldClusterId++)
620 array<int> row = association[oldClusterId];
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());
634 PerceptionManager pm =
GetGame().GetPerceptionManager();
635 float timeNow_s = pm.GetTime();
639 foreach (SCR_AITargetInfo targetInfo : cluster.m_aTargets)
641 vector mrkPos = targetInfo.m_vWorldPos +
Vector(0, 3, 0);
643 string s =
string.Empty;
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));
648 s = s +
string.Format(
"Category: %1, dngr: %2",
typename.EnumToString(
EAITargetInfoCategory, targetInfo.m_eCategory), targetInfo.m_bEndangering);
651 mrkPos[0], mrkPos[1], mrkPos[2],
652 color: 0xFFFFFFFF, bgColor: 0xFF000000,
657 vector clusterCenter = cluster.m_State.GetCenterPosition();
660 string s =
string.Empty;
662 s = s +
string.Format(
"Cluster: %1\n", clusterId);
665 s = s +
string.Format(
"State: %1, Time since Info: %2, Max age: %3",
667 cluster.m_State.GetTimeSinceLastNewInformation().ToString(5,1),
668 cluster.m_State.m_fMaxAge_s.ToString(5, 1));
672 clusterCenter[0], clusterCenter[1] + 10.0, clusterCenter[2],
673 color: 0xFFFFFFFF, bgColor: 0xFF000000,
ArmaReforgerScripted GetGame()
func SCR_AIGroupPerceptionOnEnemyDetectedFiltered
func SCR_AIGroupPerceptionOnNoEnemy
func SCR_AIOnTargetClusterStateDeleted
func SCR_AIGroupPerceptionOnEnemyDetected
class SCR_AIPolar m_Target
void SCR_AITargetClusterState(SCR_AIGroupTargetCluster cluster)
when task is activated by player or by killing enemy
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
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()
void UpdateTargetsFromMembers()
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)
void RemoveTarget(int id)
ref array< IEntity > m_aTargetEntities
void RemoveTarget(IEntity enemy)
ref ScriptInvokerBase< SCR_AIOnTargetClusterStateDeleted > Event_OnTargetClusterStateDeleted
void SelectMostDangerousCluster()
PerceptionComponent m_Perception
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
proto external string ToString()
Plain C++ pointer, no weak pointers, no memory management.
proto native vector Vector(float x, float y, float z)