Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AIGroupUtilityComponent.c
Go to the documentation of this file.
1void ScriptInvoker_GroupMoveFailed_Callback(int moveResult, IEntity vehicleUsed, bool isWaypointRelated, vector moveLocation);
3typedef ScriptInvokerBase<ScriptInvoker_GroupMoveFailed_Callback> ScriptInvoker_GroupMoveFailed;
4
11
12[ComponentEditorProps(category: "GameScripted/AI", description: "Component for utility AI system for groups")]
13class SCR_AIGroupUtilityComponentClass : SCR_AIBaseUtilityComponentClass
16
17class SCR_AIGroupUtilityComponent : SCR_AIBaseUtilityComponent
18{
19 static const float AUTONOMOUS_DISTANCE_MAX_DEFAULT = 9999;
20
21 SCR_AIGroup m_Owner;
22 SCR_AIConfigComponent m_ConfigComponent;
23 SCR_AIGroupInfoComponent m_GroupInfo;
24 SCR_AIGroupMovementComponent m_GroupMovementComponent;
25 SCR_MailboxComponent m_Mailbox;
26 SCR_AIGroupSettingsComponent m_SettingsComponent;
27 ref array<SCR_AIInfoComponent> m_aInfoComponents = {};
28
29 ref ScriptInvoker_GroupMoveFailed m_OnMoveFailed;
30 ref ScriptInvokerBase<SCR_AIOnAgentLifeStateChanged> m_OnAgentLifeStateChanged = new ScriptInvokerBase<SCR_AIOnAgentLifeStateChanged>();
31
32 // Settings for cluster suppression
33 const float SUPPRESS_MAX_CLUSTER_INFO_AGE_S = 90; // For how long since last info we'll be suppressing cluster
34 const float SUPPRESS_OLD_CLUSTER_INFO_AGE_S = 15; // Time since last cluster info that we'll consider as old (starts scaling of fire rate to save ammo)
35 const float SUPPRESS_MAX_DESTROYED_CLUSTER_INFO_AGE_S = 10; // How long to suppress a target cluster which has only destroyed targets
36 const float SUPPRESS_MIN_DIST_TO_CLUSTER_M = 30; // What is the minimal distance of units to suppression bbox to stop firing
37 const float SUPPRESS_MAX_DIST_TO_CLUSTER_M = 800; // Max distance ...
38
39 protected float m_fLastUpdateTime = -1.0;
41
42 // See SetMaxAutonomousDistance()
43 protected float m_fMaxAutonomousDistance = AUTONOMOUS_DISTANCE_MAX_DEFAULT;
44
45 // Update interval of group perception and target clusters and their processing
46 protected const float PERCEPTION_UPDATE_TIMER_MS = 2000.0;
47
48 protected bool m_bNewGroupMemberAdded;
49 protected ref SCR_AIActionBase m_CurrentActivity;
50
51 // Waypoint state
53
54 // Group perception and clusters
56
58
59 // Fireteams
61
62 // Vehicles
64
65 // Used by SCR_AIGetMemberByGoal nodes
67
68 // Current group's threat measure
69 protected float m_fThreatMeasure;
70
71 // Current group's fire rate (external)
72 protected float m_fFireRateCoef = 1;
73
74 // Currently suppressed cluster state
76
77 // Combat modes
78 protected EAIGroupCombatMode m_eCombatModeExternal = EAIGroupCombatMode.FIRE_AT_WILL; // Combat mode dictated by external settings (scenario, commanding, game master)
79 protected EAIGroupCombatMode m_eCombatModeActual = EAIGroupCombatMode.FIRE_AT_WILL; // Actual combat mode at given moment, if automatic combat mode is used
80
81 //------------------------------------------------------------------------------------------------
82 protected void UpdateThreatMeasure()
83 {
84 // Exit if no agents
85 float count = m_aInfoComponents.Count();
86 if (count == 0)
87 return;
88
89 float threat = 0;
90 foreach (SCR_AIInfoComponent infoComp : m_aInfoComponents)
91 threat += infoComp.GetThreatSystem().GetThreatMeasure();
92
93 threat /= count;
94 m_fThreatMeasure = threat;
95 }
96
97 //------------------------------------------------------------------------------------------------
98 // Sets group's fire rate coef
99 void SetFireRateCoef(float coef = 1, bool overridePersistent = false)
100 {
101 m_fFireRateCoef = coef;
102
103 foreach (SCR_AIInfoComponent infoComp : m_aInfoComponents)
104 {
105 SCR_AICombatComponent comp = infoComp.GetCombatComponent();
106 if (comp)
107 comp.SetGroupFireRateCoef(coef, overridePersistent);
108 }
109 }
110
111 //------------------------------------------------------------------------------------------------
112 // Get group's current fire rate coef
114 {
115 return m_fFireRateCoef;
116 }
117
118 //------------------------------------------------------------------------------------------------
119 // Get group's current threat measure
121 {
122 return m_fThreatMeasure;
123 }
124
125 //------------------------------------------------------------------------------------------------
128 {
129 m_fMaxAutonomousDistance = Math.Max(0, dist);
130 }
131
132 //------------------------------------------------------------------------------------------------
134 {
136 }
137
138 //------------------------------------------------------------------------------------------------
141 {
142 m_eCombatModeExternal = combatMode;
143 }
144
145 //------------------------------------------------------------------------------------------------
151
152 //------------------------------------------------------------------------------------------------
158
159 //------------------------------------------------------------------------------------------------
163 SCR_AIActionBase EvaluateActivity(out bool restartActivity)
164 {
165 SCR_AIActionBase activity;
166 restartActivity = false;
167
168 if (!m_ConfigComponent)
169 return null;
170
171 float currentTime = GetGame().GetWorld().GetWorldTime();
172 float deltaTime_ms = 0;
173 if (m_fLastUpdateTime != -1.0)
174 deltaTime_ms = currentTime - m_fLastUpdateTime;
175
176 #ifdef AI_DEBUG
177 AddDebugMessage("EvaluateActivity START");
178 if (m_bEvaluationBreakpoint)
179 {
180 Print("EvaluateActivity breakpoint triggered");
181 debug;
182 m_bEvaluationBreakpoint = false;
183 }
184 #endif
185
186 // Read messages
187 AIMessage msgBase = m_Mailbox.ReadMessage(true);
188 if (msgBase)
189 {
190 SCR_AIMessageGoal msgGoal = SCR_AIMessageGoal.Cast(msgBase);
191 if (msgGoal)
192 {
193 // Process goal message
194 #ifdef AI_DEBUG
195 AddDebugMessage(string.Format("PerformGoalReaction: %1, from BT: %2", msgGoal, msgGoal.m_sSentFromBt));
196 #endif
197 m_ConfigComponent.PerformGoalReaction(this, msgGoal);
198 }
199 else
200 {
201 SCR_AIMessageInfo msgInfo = SCR_AIMessageInfo.Cast(msgBase);
202 if (msgInfo)
203 {
204 // Process info message
205
206 bool overrideReaction = CallActionsOnMessage(msgInfo);
207
208 if (!overrideReaction)
209 {
210 #ifdef AI_DEBUG
211 AddDebugMessage(string.Format("PerformInfoReaction: %1, from BT: %2", msgInfo, msgInfo.m_sSentFromBt));
212 #endif
213
214 m_ConfigComponent.PerformInfoReaction(this, msgInfo);
215 }
216 #ifdef AI_DEBUG
217 else
218 {
219 #ifdef AI_DEBUG
220 AddDebugMessage(string.Format("InfoMessage consumed by action: %1, from BT: %2", msgInfo, msgInfo.m_sSentFromBt));
221 #endif
222 }
223 #endif
224 }
225 }
226 }
227
229
230 activity = SCR_AIActionBase.Cast(EvaluateActions());
231 #ifdef AI_DEBUG
233 DebugLogActionsPriority();
234 #endif
235
236 if (activity && (!m_CurrentActivity || (m_CurrentActivity != activity && m_CurrentActivity.IsActionInterruptable())))
237 restartActivity = true;
238 else if (m_bNewGroupMemberAdded && activity)
239 restartActivity = true;
240
241 if (restartActivity)
242 {
243 SetCurrentAction(activity);
244 UpdateGroupControlMode(activity);
245 m_CurrentActivity = activity;
246
247#ifdef WORKBENCH
248 if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_PRINT_ACTIVITY))
249 PrintFormat("Agent %1 activity %2",m_Owner,m_CurrentActivity.GetActionDebugInfo());
250#endif
251 }
252
253 #ifdef AI_DEBUG
254 AddDebugMessage("EvaluateActivity END\n");
255 #endif
256
257 // Update group perception, update fire teams
258 bool isMilitary = IsMilitary();
259 bool isSlave = m_Owner.IsSlave();
260 if (isMilitary)
261 {
262 if (!isSlave && m_FireteamMgr.m_bRebalanceFireteams)
263 {
264 if (CanRebalanceFireteams()) // In some cases we can't rebalance fireteams yet
265 {
266 m_FireteamMgr.RebalanceAllFireteams();
267 }
268 }
269
270 // Perception and clusters
271 m_fPerceptionUpdateTimer_ms += deltaTime_ms;
273 {
274 m_Perception.Update();
275
276 if (!isSlave)
277 {
281
282 if (!m_Perception.m_aTargetClusters.IsEmpty())
284 }
285
287 }
288 }
289
290 // Combat modes
291 if (isMilitary)
293
294 m_fLastUpdateTime = currentTime;
295 m_bNewGroupMemberAdded = false; // resetting reaction on group member added
296
297 return m_CurrentActivity;
298 }
299
300 //------------------------------------------------------------------------------------------------
305 {
306 if (m_eCombatModeExternal != EAIGroupCombatMode.RETURN_FIRE)
307 {
308 // Simple case - actual combat mode is same as external
310 }
311 else
312 {
313 // Automatic - actual combat mode depends if we're under attack or not
314 foreach (SCR_AIGroupTargetCluster c : m_Perception.m_aTargetClusters)
315 {
316 if (c.m_State && c.m_State.m_iCountEndangering != 0 && c.m_State.m_iCountAlive != 0)
317 {
318 // Any target cluster is endangering us
320 return;
321 }
322 }
323
324 // Nothing is endangering us
326 }
327 }
328
329 //------------------------------------------------------------------------------------------------
331 {
332 if (!SCR_AIWorldHandling.IsLowLightEnvironment() || !m_GroupInfo || !m_GroupInfo.IsIllumFlareAllowed())
333 return;
334
336 return;
337
338 vector targetPosition = m_CurrentSuppressClusterState.GetCenterPosition();
339
341 if (illumFeature.Execute(this, targetPosition, null))
342 m_GroupInfo.OnIllumFlareUsed();
343 }
344
345 //------------------------------------------------------------------------------------------------
350
351 //------------------------------------------------------------------------------------------------
353 {
354 // Check if any friends are inside bbox
355 foreach (SCR_AIInfoComponent infoComp : m_aInfoComponents)
356 {
357 AIAgent agent = AIAgent.Cast(infoComp.GetOwner());
358 if (!agent)
359 continue;
360
361 IEntity character = agent.GetControlledEntity();
362 if (!character)
363 continue;
364
365 vector unitPos = character.GetOrigin();
366
367 if (Math.IsInRange(unitPos[0], bbMin[0], bbMax[0]) && Math.IsInRange(unitPos[1], bbMin[1], bbMax[1]) && Math.IsInRange(unitPos[2], bbMin[2], bbMax[2]))
368 return true;
369 }
370
371 return false;
372 }
373
374 //------------------------------------------------------------------------------------------------
376 {
377 // Disregard if combat mode doesn't allow it
379 return false;
380
381 // Ignore clusters without endangering & identified targets or members too close
382 if (clusterState.m_iCountIdentified == 0 && clusterState.m_iCountEndangering == 0)
383 return false;
384
385 // Ignore clusters too close or too far
386 if (clusterState.m_fDistMin < SUPPRESS_MIN_DIST_TO_CLUSTER_M || clusterState.m_fDistMin > SUPPRESS_MAX_DIST_TO_CLUSTER_M)
387 return false;
388
389 // Max suppression time
390 float maxSuppressionTime = SUPPRESS_MAX_CLUSTER_INFO_AGE_S;
391 if (clusterState.m_iCountAlive == 0)
392 maxSuppressionTime = SUPPRESS_MAX_DESTROYED_CLUSTER_INFO_AGE_S;
393
394 // Ignore too old clusters
395 if (clusterState.GetTimeSinceLastNewInformation() > maxSuppressionTime)
396 return false;
397
398 vector bbMin = clusterState.m_vBBMin;
399 vector bbMax = clusterState.m_vBBMax;
400
401 // Scale BBox
402 SCR_AISuppressionObjectVolumeBox.ScaleTargetBBox(bbMin, bbMax, clusterState.m_fDistMin, clusterState.m_iCountIdentified > 0);
403
404 // Ignore cluster if some group members are inside it
405 if (AnyMembersInsideBBox(bbMin, bbMax))
406 return false;
407
408 return true;
409 }
410
411 //------------------------------------------------------------------------------------------------
412 protected void UpdateSuppressCluster()
413 {
414 SCR_AIGroupTargetCluster cluster = m_Perception.m_MostDangerousCluster;
416
417 if (cluster)
418 {
419 state = cluster.m_State;
420
421 if (!ShouldSuppressCluster(state))
422 state = null;
423 }
424
426 {
428
429 // Alert agents about change in cluster
430 foreach (SCR_AIInfoComponent infoComp : m_aInfoComponents)
431 {
432 SCR_AICombatComponent comp = infoComp.GetCombatComponent();
433 if (comp)
434 comp.SetGroupSuppressClusterState(state);
435 }
436 }
437 }
438
439 //------------------------------------------------------------------------------------------------
442 protected void OnAgentAdded(AIAgent agent)
443 {
444 // Add to array of AIInfo
445 SCR_ChimeraAIAgent chimeraAgent = SCR_ChimeraAIAgent.Cast(agent);
446 if (!chimeraAgent)
447 return;
448
449 SCR_AIInfoComponent info = chimeraAgent.m_InfoComponent;
450
451 if (!info)
452 return;
453
454 m_aInfoComponents.Insert(info);
455
456 m_FireteamMgr.OnAgentAdded(agent);
457
462
463 if (info.HasUnitState(EUnitState.IN_VEHICLE))
464 {
465 OnJoinGroupFromVehicle(agent, info.HasUnitState(EUnitState.PILOT));
466 }
467 }
468
469 //------------------------------------------------------------------------------------------------
472 protected void OnAgentRemoved(SCR_AIGroup group, AIAgent agent)
473 {
474 SCR_AIUtilityComponent utility = SCR_AIUtilityComponent.Cast(agent.FindComponent(SCR_AIUtilityComponent));
475 if(!utility)
476 return Debug.Error("Null AI utility");
477
478 if(agent)
479 utility.CancelAllGroupActivityBehaviors(this);
480
481 // Remove from array of AIInfo
482 for (int i = m_aInfoComponents.Count() - 1; i >= 0; i--)
483 {
484 if (!m_aInfoComponents[i])
485 {
486 Debug.Error("Null AI info occured"); // investigate when this happens!
487 m_aInfoComponents.RemoveOrdered(i);
488 }
489 else if (m_aInfoComponents[i].IsOwnerAgent(agent))
490 {
491 // Unsubscribe from compartment event
492 SCR_AIInfoComponent infoComp = m_aInfoComponents[i];
495
496 m_aInfoComponents.RemoveOrdered(i);
497 break;
498 }
499 }
500
501 m_FireteamMgr.OnAgentRemoved(agent);
502 m_VehicleMgr.OnAgentRemoved(agent, group);
503 }
504
505 //------------------------------------------------------------------------------------------------
507 protected void OnWaypointCompleted(AIWaypoint waypoint)
508 {
509 if (m_WaypointState && waypoint && m_WaypointState.GetWaypoint() == waypoint)
510 {
511 m_WaypointState.OnDeselected();
512 m_WaypointState = null;
513 }
514 }
515
516 //------------------------------------------------------------------------------------------------
519 protected void OnWaypointToRemove(AIWaypoint waypoint, bool isCurrentWaypoint)
520 {
521 // Remove old wp state, if it existed
522 if (isCurrentWaypoint)
523 {
524 if (waypoint && m_WaypointState && m_WaypointState.GetWaypoint() == waypoint)
525 m_WaypointState.OnDeselected();
526
527 m_WaypointState = null;
528 }
529 }
530
531 //------------------------------------------------------------------------------------------------
532 protected void OnAgentCompartmentEntered(AIAgent agent, IEntity targetEntity, BaseCompartmentManagerComponent manager, int mgrID, int slotID, bool move)
533 {
534 if (!agent || !targetEntity)
535 return;
536 // ignoring attachments such as turrets, cargo slots, etc.
537 SCR_AIVehicleUsageComponent vehicleUsageComp = SCR_AIVehicleUsageComponent.FindOnNearestParent(targetEntity, targetEntity);
538 if (!vehicleUsageComp)
539 {
541 return;
542 }
543
544 if (!vehicleUsageComp.IsVehicleTypeValid())
545 {
547 return;
548 }
549
550 AddUsableVehicle(vehicleUsageComp);
551
552 // Not from our group? Should not happen.
553 if (agent.GetParentGroup() != m_Owner)
554 return;
555
556 BaseCompartmentSlot compSlot = manager.FindCompartment(slotID, mgrID);
557
558 if (m_GroupMovementComponent && compSlot)
559 {
560 bool isDriving = compSlot.GetType() == ECompartmentType.PILOT;
561 SCR_AIGroupVehicle groupVehicle = m_VehicleMgr.FindVehicle(vehicleUsageComp);
562 int vehicleId = groupVehicle.GetSubgroupHandleId();
563 if (vehicleId > -1) // agent entered vehicle with subgroup
564 JoinSubgroup(agent, vehicleId, isDriving);
565 else // agent entered vehicle, that does not have subgroup, yet
566 CreateNewSubgroup(agent, isDriving, groupVehicle);
567 }
568
569 m_FireteamMgr.OnAgentAssignedToVehicle(agent, vehicleUsageComp, compSlot.GetType());
570 }
571
572 //------------------------------------------------------------------------------------------------
573 protected void JoinSubgroup(AIAgent agent, int handleId, bool isSubgroupLeader)
574 {
575
576 int oldHandleId = m_GroupMovementComponent.GetAgentMoveHandlerId(agent);
577 if (isSubgroupLeader)
578 m_GroupMovementComponent.SetMoveHandlerLeader(agent, oldHandleId, handleId);
579 else
580 m_GroupMovementComponent.MoveAgentToHandler(agent, oldHandleId, handleId);
581 }
582
583 //------------------------------------------------------------------------------------------------
584 protected bool OnJoinGroupFromVehicle(AIAgent agent, bool isDriving)
585 {
586 ChimeraCharacter charact = ChimeraCharacter.Cast(agent.GetControlledEntity());
587 if (!charact)
588 return false;
589 CompartmentAccessComponent comp = charact.GetCompartmentAccessComponent();
590 if (!comp)
591 return false;
592 IEntity vehicleEntity = comp.GetVehicleIn(charact);
593 if (!vehicleEntity)
594 return false;
595 SCR_AIVehicleUsageComponent vehicleUsageComp = SCR_AIVehicleUsageComponent.FindOnNearestParent(vehicleEntity, vehicleEntity);
596 if (!vehicleUsageComp)
597 return false;
598
599 BaseCompartmentSlot compartment = comp.GetCompartment();
600 if (!compartment)
601 return false;
602
603 OnAgentCompartmentEntered(agent, vehicleEntity, compartment.GetManager(), compartment.GetCompartmentMgrID(), compartment.GetCompartmentSlotID(), false);
604 return true;
605 }
606
607 //------------------------------------------------------------------------------------------------
608 protected int CreateNewSubgroup(AIAgent agent, bool isSubgroupLeader, SCR_AIGroupVehicle groupVehicle)
609 {
610 int oldHandleId = m_GroupMovementComponent.GetAgentMoveHandlerId(agent);
611 AIFormationDefinition formationDef = m_GroupMovementComponent.GetFormationDefinition(oldHandleId);
612 if (!formationDef)
613 return -1;
614 string formationType = formationDef.GetName();
615 int newHandleId = m_GroupMovementComponent.CreateGroupMoveHandler(formationType);
616 if (isSubgroupLeader)
617 m_GroupMovementComponent.SetMoveHandlerLeader(agent, oldHandleId, newHandleId);
618 else
619 m_GroupMovementComponent.MoveAgentToHandler(agent, oldHandleId, newHandleId);
620 groupVehicle.SetSubgroupHandleId(newHandleId);
621 return newHandleId;
622 }
623
624 //------------------------------------------------------------------------------------------------
625 protected void LeaveSubgroup(AIAgent agent, SCR_AIGroupVehicle groupVehicle)
626 {
627 int oldHandleId = m_GroupMovementComponent.GetAgentMoveHandlerId(agent);
628 m_GroupMovementComponent.MoveAgentToHandler(agent, oldHandleId, AIGroupMovementComponent.DEFAULT_HANDLER_ID);
629 bool isHandleEmpty = m_GroupMovementComponent.GetMoveHandlerAgentCount(oldHandleId) == 0;
630 if (isHandleEmpty)
631 {
632 m_GroupMovementComponent.RemoveGroupMoveHandler(oldHandleId);
633 if (groupVehicle)
634 groupVehicle.SetSubgroupHandleId();
635 }
636 }
637
638 //------------------------------------------------------------------------------------------------
639 protected void OnAgentCompartmentLeft(AIAgent agent, IEntity targetEntity, BaseCompartmentManagerComponent manager, int mgrID, int slotID, bool move)
640 {
641 if (!agent || !targetEntity)
642 return;
643
644 // ignoring attachments such as turrets, cargo slots, etc.
645 SCR_AIVehicleUsageComponent vehicleUsageComp = SCR_AIVehicleUsageComponent.FindOnNearestParent(targetEntity, targetEntity);
646
647 // Not from our group? Should not happen.
648 if (agent.GetParentGroup() != m_Owner)
649 return;
650
651 SCR_AIGroupVehicle groupVehicle = m_VehicleMgr.FindVehicle(targetEntity);
652 LeaveSubgroup(agent,groupVehicle);
653
654 if (!vehicleUsageComp)
655 {
657 return;
658 }
659
660 m_FireteamMgr.OnAgentUnassignedFromVehicle(agent, vehicleUsageComp);
661 }
662
663 //------------------------------------------------------------------------------------------------
664 protected void OnAgentLifeStateChanged(AIAgent incapacitatedAgent, SCR_AIInfoComponent infoIncap, IEntity vehicle, ECharacterLifeState lifeState)
665 {
666 m_OnAgentLifeStateChanged.Invoke(incapacitatedAgent, infoIncap, vehicle, lifeState);
667 }
668
669 //------------------------------------------------------------------------------------------------
672 protected void OnCurrentWaypointChanged(AIWaypoint currentWp, AIWaypoint prevWp)
673 {
674 // Remove old wp state, if it existed
675 if (m_WaypointState && prevWp && m_WaypointState.GetWaypoint() == prevWp)
676 m_WaypointState.OnDeselected();
677
678 m_WaypointState = null;
679
680 // Create new wp state
681 if (currentWp)
682 {
683 SCR_AIWaypoint scrCurrentWp = SCR_AIWaypoint.Cast(currentWp);
684 if (scrCurrentWp)
685 {
686 SCR_AIWaypointState wpState = scrCurrentWp.CreateWaypointState(this);
687 if (wpState)
688 {
689 m_WaypointState = wpState;
690 m_WaypointState.OnSelected();
691 }
692 }
693 }
694 }
695
696 //------------------------------------------------------------------------------------------------
698 {
699 if (m_WaypointState)
700 m_WaypointState.OnExecuteWaypointTree();
701 }
702
703 //------------------------------------------------------------------------------------------------
705 void OnEnemyDetectedFiltered(SCR_AIGroup group, SCR_AITargetInfo target, AIAgent reporter)
706 {
707 SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(reporter);
708 if (!agent)
709 return;
710
711 SCR_AICommsHandler commsHandler = agent.m_UtilityComponent.m_CommsHandler;
712
713 // Ignore if the talk request can be optimized out
714 if (commsHandler.CanBypass())
715 return;
716
717 SCR_AITalkRequest rq = new SCR_AITalkRequest(ECommunicationType.REPORT_CONTACT, target.m_Entity, target.m_vWorldPos,
718 enumSignal: 0, transmitIfNoReceivers: true, transmitIfPassenger: true, preset: SCR_EAITalkRequestPreset.MEDIUM);
719 commsHandler.AddRequest(rq);
720 }
721
722 //------------------------------------------------------------------------------------------------
727 {
728 // If we've lost enemies at some place, make the assigned fireteams report that
729 if (newState == EAITargetClusterState.LOST &&
730 (prevState == EAITargetClusterState.INVESTIGATING || prevState == EAITargetClusterState.ATTACKING) &&
731 state.m_Activity)
732 {
733 SCR_AIFireteamsActivity ftActivity = SCR_AIFireteamsActivity.Cast(state.m_Activity);
734 if (ftActivity)
735 {
736 TFireteamLockRefArray fireteamLocks = {};
737 ftActivity.GetAssignedFireteams(fireteamLocks);
738 foreach (SCR_AIGroupFireteamLock ftLock : fireteamLocks)
739 {
740 AIAgent reporterAgent = ftLock.GetFireteam().GetMember(0);
741 if (!reporterAgent)
742 continue;
743 SCR_AICommsHandler commsHandler = SCR_AISoundHandling.FindCommsHandler(reporterAgent);
744 if (!commsHandler)
745 continue;
746 if (commsHandler.CanBypass())
747 continue;
748
749 SCR_AITalkRequest rq = new SCR_AITalkRequest(ECommunicationType.REPORT_CLEAR, null, vector.Zero, 0, false, false, SCR_EAITalkRequestPreset.MEDIUM);
750 commsHandler.AddRequest(rq);
751 }
752 }
753 }
754 }
755
756 //------------------------------------------------------------------------------------------------
761 void CancelActivitiesRelatedToWaypoint(notnull AIWaypoint waypoint, typename activityType = typename.Empty, bool doNotCompleteWaypoint = false)
762 {
763 array<ref AIActionBase> actions = {};
764 GetActions(actions);
765 bool checkType = activityType != typename.Empty;
766 foreach (AIActionBase action : actions)
767 {
768 SCR_AIActivityBase activity = SCR_AIActivityBase.Cast(action);
769
770 if (!activity || (checkType && !activity.IsInherited(activityType)))
771 continue;
772
773 EAIActionState state = activity.GetActionState();
774
775 if (state == EAIActionState.COMPLETED || state == EAIActionState.FAILED)
776 continue;
777
778 if (activity.m_RelatedWaypoint == waypoint)
779 activity.Fail(doNotCompleteWaypoint);
780 }
781 }
782
783 //------------------------------------------------------------------------------------------------
785 protected bool CanRebalanceFireteams()
786 {
787 // Can't rebalance fireteams if there is any fireteams-related activity
788 array<ref AIActionBase> allActions = {};
789 GetActions(allActions);
790
791 array<AIActionBase> subactions = {};
792 foreach (AIActionBase action : allActions)
793 {
794 if (SCR_AIFireteamsActivity.Cast(action))
795 return false;
797 if (parallel)
798 {
799 parallel.GetSubactions(subactions);
800 foreach (AIActionBase subaction : subactions)
801 {
802 if (SCR_AIFireteamsActivity.Cast(subaction))
803 return false;
804 }
805 }
806 }
807
808 return true;
809 }
810
811 //------------------------------------------------------------------------------------------------
812 void OnMoveFailed(int moveResult, IEntity vehicleUsed, bool isWaypointRelated, vector moveLocation)
813 {
814 if (m_OnMoveFailed)
815 m_OnMoveFailed.Invoke(moveResult, vehicleUsed, isWaypointRelated, moveLocation);
816 }
817
818 //------------------------------------------------------------------------------------------------
820 {
821 if (!m_OnMoveFailed)
822 m_OnMoveFailed = new ScriptInvoker_GroupMoveFailed();
823
824 return m_OnMoveFailed;
825 }
826
827 //---------------------------------------------------------------------------------------------------
828 override void EOnInit(IEntity owner)
829 {
830 super.EOnInit(owner);
831 m_Owner = SCR_AIGroup.Cast(owner);
832 if (!m_Owner)
833 return;
834
835 m_ConfigComponent = SCR_AIConfigComponent.Cast(m_Owner.FindComponent(SCR_AIConfigComponent));
836
837 m_ConfigComponent.AddDefaultActivities(this);
838 //AddAction(new SCR_AIIdleActivity(this))
839
840 m_Owner.GetOnAgentAdded().Insert(OnAgentAdded);
841 m_Owner.GetOnAgentRemoved().Insert(OnAgentRemoved);
842 m_Owner.GetOnWaypointCompleted().Insert(OnWaypointCompleted);
843 m_Owner.GetOnWaypointToRemove().Insert(OnWaypointToRemove);
844 m_Owner.GetOnCurrentWaypointChanged().Insert(OnCurrentWaypointChanged);
845
846 m_GroupInfo = SCR_AIGroupInfoComponent.Cast(m_Owner.FindComponent(SCR_AIGroupInfoComponent));
847
849 m_TargetClusterProcessor.m_OnClusterStateChanged.Insert(OnTargetClusterStateChanged);
850
852
853 m_Perception = new SCR_AIGroupPerception(this, m_Owner);
854 m_Perception.GetOnEnemyDetectedFiltered().Insert(OnEnemyDetectedFiltered);
855
857
858 m_Mailbox = SCR_MailboxComponent.Cast(m_Owner.FindComponent(SCR_MailboxComponent));
859
860 m_SettingsComponent = SCR_AIGroupSettingsComponent.Cast(m_Owner.FindComponent(SCR_AIGroupSettingsComponent));
861
863
864 m_GroupMovementComponent = SCR_AIGroupMovementComponent.Cast(owner.FindComponent(SCR_AIGroupMovementComponent));
865 }
866
867 //------------------------------------------------------------------------------------------------
868 override void EOnDiag(IEntity owner, float timeSlice)
869 {
870 if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_TARGET_CLUSTERS))
871 m_Perception.DiagDrawClusters();
872
873 if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_FIRETEAMS))
874 m_FireteamMgr.DiagDrawFireteams();
875
876 if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_SUBGROUPS))
877 m_GroupMovementComponent.DiagDrawSubgroups(m_Owner);
878 }
879
880 //------------------------------------------------------------------------------------------------
883 void UpdateGroupControlMode(SCR_AIActionBase currentAction)
884 {
885 #ifdef AI_DEBUG
886 AddDebugMessage("UpdateGroupControlMode");
887 #endif
888
889 if (m_GroupInfo)
890 {
891 SCR_AIActivityBase currentActivity = SCR_AIActivityBase.Cast(currentAction);
892
893 if (currentActivity && currentActivity.m_bIsWaypointRelated.m_Value)
894 m_GroupInfo.SetGroupControlMode(EGroupControlMode.FOLLOWING_WAYPOINT);
895 else if (SCR_AIIdleActivity.Cast(currentActivity))
896 m_GroupInfo.SetGroupControlMode(EGroupControlMode.IDLE);
897 else
898 m_GroupInfo.SetGroupControlMode(EGroupControlMode.AUTONOMOUS);
899 }
900 }
901
902 //------------------------------------------------------------------------------------------------
905 void UpdateClustersState(float deltaTime_ms)
906 {
907 foreach (SCR_AIGroupTargetCluster cluster : m_Perception.m_aTargetClusters)
908 {
909 m_TargetClusterProcessor.UpdateCluster(cluster, cluster.m_State, deltaTime_ms);
910 }
911 }
912
913 //------------------------------------------------------------------------------------------------
918 {
919 AIWaypoint wp = m_Owner.GetCurrentWaypoint();
920
921 SCR_DefendWaypoint defendWp = SCR_DefendWaypoint.Cast(wp);
922 if (defendWp)
923 {
924 // If we have a defend wp, we can go only inside of it
925 return vector.Distance(defendWp.GetOrigin(), pos) < defendWp.GetCompletionRadius();
926 }
927 else
928 {
929 // Either no waypoint, or not a defend waypoint
930 // We can go anywhere, but it depends on range
931
932 float distance = vector.Distance(m_Owner.GetCenterOfMass(), pos);
933
935 }
936
937 return true;
938 }
939
940 //------------------------------------------------------------------------------------------------
941 bool IsUsableVehicle(notnull SCR_AIVehicleUsageComponent vehicleUsageComp)
942 {
943 if (!vehicleUsageComp)
944 return false;
945
946 return m_VehicleMgr.FindVehicle(vehicleUsageComp) != null;
947 }
948
949 //------------------------------------------------------------------------------------------------
950 bool IsUsableVehicle(notnull IEntity vehicleEntity)
951 {
952 if (!vehicleEntity)
953 return false;
954
955 return m_VehicleMgr.FindVehicle(vehicleEntity) != null;
956 }
957
958 //------------------------------------------------------------------------------------------------
959 void AddUsableVehicle(notnull SCR_AIVehicleUsageComponent vehicleUsageComp)
960 {
961 m_VehicleMgr.TryAddVehicle(vehicleUsageComp);
962
963 // Events
964 vehicleUsageComp.GetOnDeleted().Remove(OnVehicleDeleted); // not to add it twice
965 vehicleUsageComp.GetOnDeleted().Insert(OnVehicleDeleted);
966 vehicleUsageComp.GetOnDamageStateChanged().Remove(OnVehicleDamageStateChanged); // not to add it twice
967 vehicleUsageComp.GetOnDamageStateChanged().Insert(OnVehicleDamageStateChanged);
968
969 // Create a vehicle combat activity as soon as first vehicle is added
971 {
972 auto activity = new SCR_AIVehicleCombatActivity(this, null);
973 AddAction(activity);
974 }
975 }
976
977 //------------------------------------------------------------------------------------------------
979 {
980 // Remove from vehicle mgr
981 m_VehicleMgr.RemoveVehicle(vehicleUsageComp);
982
983 // Remove from fireteam mgr
984 m_FireteamMgr.OnVehicleRemoved(vehicleUsageComp);
985
986 // Events
987 vehicleUsageComp.GetOnDeleted().Remove(OnVehicleDeleted);
988 vehicleUsageComp.GetOnDamageStateChanged().Remove(OnVehicleDamageStateChanged);
989
990 // Remove vehicle combat activity when we no longer have any vehicles
991 if (m_VehicleMgr.GetVehiclesCount() == 0)
992 {
993 auto vehicleCombatActivity = FindActionOfType(SCR_AIVehicleCombatActivity);
994 if (vehicleCombatActivity)
995 vehicleCombatActivity.Fail();
996 }
997 }
998
999 //------------------------------------------------------------------------------------------------
1001 {
1002 RemoveUsableVehicle(comp);
1003 }
1004
1005 //------------------------------------------------------------------------------------------------
1007 {
1008 if (state != EDamageState.DESTROYED)
1009 return;
1010
1011 RemoveUsableVehicle(comp);
1012 }
1013
1014 //------------------------------------------------------------------------------------------------
1017 {
1018 SCR_Faction f = SCR_Faction.Cast(m_Owner.GetFaction());
1019 if (!f)
1020 return false;
1021 return f.IsMilitary();
1022 }
1023
1024 // Diagnostics and debugging below
1025}
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
ECompartmentType
ArmaReforgerScripted GetGame()
Definition game.c:1398
void SCR_AIActivityBase(SCR_AIGroupUtilityComponent utility, AIWaypoint relatedWaypoint)
void DiagIncreaseCounter()
array< ref SCR_AIGroupFireteamLock > TFireteamLockRefArray
ScriptInvokerBase< ScriptInvoker_GroupMoveFailed_Callback > ScriptInvoker_GroupMoveFailed
func ScriptInvoker_GroupMoveFailed_Callback
enum EAIGroupCombatMode ComponentEditorProps(category:"GameScripted/AI", description:"Component for utility AI system for groups")
ECommunicationType
void SCR_AITalkRequest(ECommunicationType type, IEntity entity, vector pos, int enumSignal, bool transmitIfNoReceivers, bool transmitIfPassenger, SCR_EAITalkRequestPreset preset)
void SCR_AITargetClusterState(SCR_AIGroupTargetCluster cluster)
float distance
array< ref SCR_MenuActionPreset > GetActions()
Base class for all messages related to AI.
Definition AIMessage.c:14
Definition Debug.c:13
Diagnostic and developer menu system.
Definition DiagMenu.c:18
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
Definition Math.c:13
void AddRequest(SCR_AITalkRequest request)
bool CanBypass(SCR_AITalkRequest request=null)
void GetSubactions(notnull array< AIActionBase > outSubactions)
void GetAssignedFireteams(notnull TFireteamLockRefArray outFireteams)
ScriptInvoker GetOnAgentAdded()
void SetMaxAutonomousDistance(float dist)
Sets max distance for various autonomous behaviors.
EAIGroupCombatMode GetCombatModeActual()
Returns actual combat mode. See comment to EvaluateCombatMode method.
void AddUsableVehicle(notnull SCR_AIVehicleUsageComponent vehicleUsageComp)
void OnAgentCompartmentLeft(AIAgent agent, IEntity targetEntity, BaseCompartmentManagerComponent manager, int mgrID, int slotID, bool move)
ScriptInvoker_GroupMoveFailed GetOnMoveFailed()
void UpdateClustersState(float deltaTime_ms)
ref SCR_AITargetClusterState m_CurrentSuppressClusterState
void OnVehicleDeleted(SCR_AIVehicleUsageComponent comp)
void UpdateGroupControlMode(SCR_AIActionBase currentAction)
void SetFireRateCoef(float coef=1, bool overridePersistent=false)
bool IsUsableVehicle(notnull IEntity vehicleEntity)
void OnWaypointCompleted(AIWaypoint waypoint)
void OnEnemyDetectedFiltered(SCR_AIGroup group, SCR_AITargetInfo target, AIAgent reporter)
Called from m_Perception.
SCR_AIActionBase EvaluateActivity(out bool restartActivity)
void OnVehicleDamageStateChanged(SCR_AIVehicleUsageComponent comp, EDamageState state)
void RemoveUsableVehicle(notnull SCR_AIVehicleUsageComponent vehicleUsageComp)
void OnMoveFailed(int moveResult, IEntity vehicleUsed, bool isWaypointRelated, vector moveLocation)
void OnAgentRemoved(SCR_AIGroup group, AIAgent agent)
void OnTargetClusterStateChanged(SCR_AITargetClusterState state, EAITargetClusterState prevState, EAITargetClusterState newState)
void OnCurrentWaypointChanged(AIWaypoint currentWp, AIWaypoint prevWp)
void OnAgentCompartmentEntered(AIAgent agent, IEntity targetEntity, BaseCompartmentManagerComponent manager, int mgrID, int slotID, bool move)
void OnAgentLifeStateChanged(AIAgent incapacitatedAgent, SCR_AIInfoComponent infoIncap, IEntity vehicle, ECharacterLifeState lifeState)
bool ShouldSuppressCluster(SCR_AITargetClusterState clusterState)
bool CanRebalanceFireteams()
Determines when we can rebalance fireteams. We don't want to do that when fighting for example.
override void EOnInit(IEntity owner)
ref SCR_AIGroupFireteamManager m_FireteamMgr
ref SCR_AIGroupTargetClusterProcessor m_TargetClusterProcessor
SCR_AITargetClusterState GetCurrentSuppressClusterState()
bool OnJoinGroupFromVehicle(AIAgent agent, bool isDriving)
void CancelActivitiesRelatedToWaypoint(notnull AIWaypoint waypoint, typename activityType=typename.Empty, bool doNotCompleteWaypoint=false)
bool IsUsableVehicle(notnull SCR_AIVehicleUsageComponent vehicleUsageComp)
void LeaveSubgroup(AIAgent agent, SCR_AIGroupVehicle groupVehicle)
bool AnyMembersInsideBBox(vector bbMin, vector bbMax)
EAIGroupCombatMode GetCombatModeExternal()
See SetCombatMode.
bool IsMilitary()
Are we a military AI or not?
int CreateNewSubgroup(AIAgent agent, bool isSubgroupLeader, SCR_AIGroupVehicle groupVehicle)
void JoinSubgroup(AIAgent agent, int handleId, bool isSubgroupLeader)
override void EOnDiag(IEntity owner, float timeSlice)
void OnWaypointToRemove(AIWaypoint waypoint, bool isCurrentWaypoint)
void SetCombatMode(EAIGroupCombatMode combatMode)
Sets combat mode as desired by external means, such as scenario, or commanding, or game master.
ref SCR_AIGroupVehicleManager m_VehicleMgr
ref SCR_AIGroupPerception m_Perception
This class is used for keeping track of vehicles assigned to group.
void SetSubgroupHandleId(int handleId=-1)
bool HasUnitState(EUnitState state)
ref ScriptInvokerBase< SCR_AIOnCompartmentLeft > m_OnCompartmentLeft
ref ScriptInvokerBase< SCR_AIOnCompartmentEntered > m_OnCompartmentEntered
ref ScriptInvokerBase< SCR_AIOnAgentLifeStateChanged > m_OnAgentLifeStateChanged
Activity which handles combat of a group if it has vehicles.
static void ErrorIncorrectType(notnull IEntity entity)
static SCR_AIVehicleUsageComponent FindOnNearestParent(notnull IEntity ent, out IEntity componentOwner)
static void ErrorNoComponent(notnull IEntity entity)
SCR_AIWaypointState CreateWaypointState(SCR_AIGroupUtilityComponent groupUtilityComp)
bool IsMilitary()
proto external bool CallActionsOnMessage(AIMessage msg)
EAIActionState
proto external bool HasActionOfType(typename actionType)
Returns true when there is an action of exactly this type.
AIBaseUtilityComponentClass AIComponentClass EvaluateActions()
Evaluate all actions and return the highest evaluated action which is not suspended....
proto external void AddAction(AIActionBase action)
Adds an action.
proto external bool RemoveObsoleteActions()
Removes actions which are failed or completed.
proto external void SetCurrentAction(AIActionBase executed)
proto external AIActionBase FindActionOfType(typename actionType)
Finds action of exactly this type.
ECharacterLifeState
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
proto void PrintFormat(string fmt, void param1=NULL, void param2=NULL, void param3=NULL, void param4=NULL, void param5=NULL, void param6=NULL, void param7=NULL, void param8=NULL, void param9=NULL, LogLevel level=LogLevel.NORMAL)
EDamageState