Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AICombatComponent.c
Go to the documentation of this file.
1[ComponentEditorProps(category: "GameScripted/AI", description: "Component for utility AI system calculations")]
5
6enum EAISkill
7{
9 NOOB = 10,
10 ROOKIE = 20,
11 REGULAR = 50,
12 VETERAN = 70,
13 EXPERT = 80,
14 CYLON = 100
15}
16
17class SCR_AICombatComponent : ScriptComponent
18{
19 // Constants
20 static const int TARGET_ENDANGERED_TIMEOUT_S = 10;
21 static const float ENDANGERING_TARGET_SCORE_MULTIPLIER = 1.05;
22
23 // Score increments for assigned targets and endangering targets
24 protected static const float ASSIGNED_TARGETS_SCORE_INCREMENT = 0.5;
25 protected static const float ENDANGERING_TARGETS_SCORE_INCREMENT = 15.0;
26
27 // Constants related to weapon&target selector
28 static const float TARGET_MAX_LAST_SEEN_VISIBLE = 0.5;
29 protected static const float TARGET_MAX_LAST_SEEN_DIRECT_ATTACK = 1.6;
30 protected static const float TARGET_MAX_LAST_SEEN_DIRECT_ATTACK_CLOSE = 4.5;
31 static const float TARGET_MIN_LAST_SEEN_INDIRECT_ATTACK = 2.0;
32 static const float TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK = 7.0;
33 static const float TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK_MG = 14.0;
34 static const float TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK_CLOSE = 20.0;
35
36 static const float TARGET_MAX_LAST_SEEN = 11.0;
37 protected static const float TARGET_MIN_INDIRECT_TRACE_FRACTION_MIN = 0.5;
38 protected static const float TARGET_MAX_DISTANCE_INFANTRY = 9999.0;
39 protected static const float TARGET_MAX_DISTANCE_VEHICLE = 700.0;
40 protected static const float TARGET_MAX_DISTANCE_DISARMED = 0.0;
41 protected static const float TARGET_MAX_TIME_SINCE_ENDANGERED = 5.0;
42 protected static const float TARGET_SCORE_RETREAT = 75.0;
43 static const float TARGET_SCORE_HIGH_PRIORITY_ATTACK = 98.5;
44
45 protected static ref array<EWeaponType> s_aWeaponBlacklistFragGrenades = {EWeaponType.WT_FRAGGRENADE};
46
47 // Perception factors
48 protected const float PERCEPTION_FACTOR_SAFE = 1.0;
49 protected const float PERCEPTION_FACTOR_VIGILANT = 2.5;
50 protected const float PERCEPTION_FACTOR_ALERTED = 2.5;
51 protected const float PERCEPTION_FACTOR_THREATENED = 0.4; // We are suppressed and are bad at recognizing enemies
52
53 protected const float PERCEPTION_FACTOR_EQUIPMENT_BINOCULARS = 3.0;
54 protected const float PERCEPTION_FACTOR_EQUIPMENT_NONE = 1.0;
55
57 static const float CLOSE_RANGE_COMBAT_DISTANCE = 35.0;
58 static const float CLOSE_RANGE_COMBAT_DISTANCE_SQ = CLOSE_RANGE_COMBAT_DISTANCE * CLOSE_RANGE_COMBAT_DISTANCE;
59
61 static const float LONG_RANGE_COMBAT_DISTANCE = 280.0;
62
63 protected const float FRAG_GRENADE_MAX_THREAT = 0.6; // Max threat measure soldier is allowed to have to be able to use frag grenades
64
65 protected SCR_ChimeraAIAgent m_Agent;
66 protected SCR_CharacterControllerComponent m_CharacterController;
67 protected SCR_InventoryStorageManagerComponent m_InventoryManager;
68 protected BaseWeaponManagerComponent m_WpnManager;
69 protected SCR_CompartmentAccessComponent m_CompartmentAccess;
70 protected SCR_ExtendedDamageManagerComponent m_DamageManager;
71 protected PerceptionComponent m_Perception;
72 protected SCR_AIInfoComponent m_AIInfo;
73 protected SCR_AIUtilityComponent m_Utility;
74
75 // Cached data about current vehicle and compartment
76 protected IEntity m_CurrentVehicle;
77 protected bool m_bCurrentVehicleEvac;
78 protected SCR_BaseCompartmentManagerComponent m_CurrentVehicleCompartmentManager;
79 protected BaseCompartmentSlot m_CurrentCompartmentSlot;
80 protected TurretControllerComponent m_CurrentTurretController;
81
82 [Attribute("50", UIWidgets.ComboBox, "AI skill in combat", "", ParamEnumArray.FromEnum(EAISkill) )]
83 protected EAISkill m_eAISkillDefault;
84
85 protected EAISkill m_eAISkill;
86
87 // Belongs to friendly in aim check
88 protected bool m_bFriendlyAimLastResult;
89 #ifdef WORKBENCH
90 protected ref Shape m_FriendlyAimShape;
91 #endif
92
93 // Weapon and target selection
94 ref AIWeaponTargetSelector m_WeaponTargetSelector = new AIWeaponTargetSelector();
95 private ref BaseTarget m_SelectedTarget;
96
97 protected bool m_SelectedTargetVisible;
98 protected vector m_SelectedTargetDestinationPos;
99
100 protected ref BaseTarget m_SelectedRetreatTarget;
101 protected ref array<IEntity> m_aAssignedTargets = {};
103 protected EAIUnitType m_eExpectedEnemyType = EAIUnitType.UnitType_Infantry;
104 protected ResourceName m_SelectedWeaponResource;
105
106 // Weapon selection against a specific target and its properties
107 protected BaseWeaponComponent m_SelectedWeaponComp;
108 protected BaseMagazineComponent m_SelectedMagazineComp;
109 protected int m_iSelectedMuzzle;
110 protected float m_fSelectedWeaponMinDist;
111 protected float m_fSelectedWeaponMaxDist;
112 protected bool m_bSelectedWeaponDirectDamage;
113 protected EAIUnitType m_eUnitTypesCanAttack;
114 protected BaseCompartmentSlot m_WeaponEvaluationCompartment;
115
116 // Current soldier fire rate
117 protected float m_fFireRateCoef = 1;
118
119 // Is current soldier fire rate a result of direct/personal order. This is intented to be used
120 // when you want to set specific fire rate to specific soldier and not be changed by group, unless overriden
121 protected bool m_bIsFireRatePersistent = false;
122
123 // Weapon selection updates
124 protected float m_fNextWeaponTargetEvaluation_ms = 0;
125 protected const float WEAPON_TARGET_UPDATE_PERIOD_MS = 500;
126
127 protected SCR_AIConfigComponent m_ConfigComponent;
128
129 // Turret dismounting
130 protected float m_fDismountTurretTimer;
131 protected static const float DISMOUNT_TURRET_TARGET_LAST_SEEN_MAX_S = 100;
132 protected static const float TURRET_TARGET_EXCESS_ANGLE_THRESHOLD_DEG = 3.0;
133 protected const float DISMOUNT_TURRET_TIMER_MS = 1200;
134 protected static ref array<EVehicleType> s_aForbidDismountTurretsOfVehicleTypes = { EVehicleType.APC };
135 protected const float SECTOR_DANGER_TRESHOLD = 0.4;
136
137 // Perception
138 protected float m_fEquipmentPerceptionFactor = 1.0;
139 protected float m_fPerceptionFactor = 1.0;
140
141 protected EGadgetType m_eCurrentGadgetType = -1;
142 protected bool m_bGadgetFocus = false;
143
144 //------------------------------------------------------------------------------------------------
145 void SetGroupSuppressClusterState(SCR_AITargetClusterState state)
146 {
147 SCR_AISuppressGroupClusterBehavior behavior = SCR_AISuppressGroupClusterBehavior.Cast(m_Utility.FindActionOfType(SCR_AISuppressGroupClusterBehavior));
148
149 if (behavior)
150 {
151 // Same cluster as currently suppressed, no need to do anything
152 if (behavior.GetClusterState() == state)
153 return;
154
155 // Cluster changed, fail current behavior
156 behavior.Fail();
157 }
158
159 // New cluster is not null, create new behavior
160 if (state)
161 {
162 behavior = new SCR_AISuppressGroupClusterBehavior(m_Utility, null, null, 0, 1, SCR_AIActionBase.PRIORITY_LEVEL_NORMAL, state);
163 m_Utility.AddAction(behavior);
164 }
165 }
166
167 //------------------------------------------------------------------------------------------------
168 // Sets group's fire rate on agent
169 void SetGroupFireRateCoef(float coef = 1, bool overridePersistent = false)
170 {
171 // Exit if not override and current rate is persistent
172 if (m_bIsFireRatePersistent && !overridePersistent)
173 return;
174
175 // Group order is never persistent
176 SetFireRateCoef(coef, false);
177 }
178
179 //------------------------------------------------------------------------------------------------
180 // Sets agent fire rate
181 void SetFireRateCoef(float coef = 1, bool persistent = false)
182 {
183 m_fFireRateCoef = coef;
184 m_bIsFireRatePersistent = persistent;
185 }
186
187 //------------------------------------------------------------------------------------------------
188 // Get current agent fire rate coef
189 float GetFireRateCoef()
190 {
191 return m_fFireRateCoef;
192 }
193
194 //------------------------------------------------------------------------------------------------
196 EAISkill GetAISkill()
197 {
198 return m_eAISkill;
199 }
200
201 //------------------------------------------------------------------------------------------------
202 EAISkill GetAISkillDefault()
203 {
204 return m_eAISkillDefault;
205 }
206
207 //------------------------------------------------------------------------------------------------
210 void SetAISkill(EAISkill skill)
211 {
212 #ifdef AI_DEBUG
213 AddDebugMessage(string.Format("SetAISkill: %1", typename.EnumToString(EAISkill, skill)));
214 #endif
215
216 m_eAISkill = skill;
217 }
218
219 //------------------------------------------------------------------------------------------------
225 void SetPerceptionFactor(float value)
226 {
227 if (value < 0)
228 value = 0;
229 m_fPerceptionFactor = value;
230 UpdatePerceptionFactor(m_Perception, m_Utility.m_ThreatSystem);
231 }
232
233 //------------------------------------------------------------------------------------------------
234 float GetPerceptionFactor()
235 {
236 return m_fPerceptionFactor;
237 }
238
239 //------------------------------------------------------------------------------------------------
241 void ResetAISkill()
242 {
243 #ifdef AI_DEBUG
244 AddDebugMessage("ResetAISkill");
245 #endif
246
247 m_eAISkill = m_eAISkillDefault;
248 }
249
250 //------------------------------------------------------------------------------------------------
251 BaseWeaponComponent GetCurrentWeapon()
252 {
253 BaseWeaponManagerComponent wpnManger = GetCurrentWeaponManager();
254 if (wpnManger)
255 return wpnManger.GetCurrentWeapon();
256
257 return null;
258 }
259
260 //------------------------------------------------------------------------------------------------
261 BaseWeaponManagerComponent GetCurrentWeaponManager()
262 {
263 if (m_CurrentTurretController)
264 return m_CurrentTurretController.GetWeaponManager();
265
266 return m_WpnManager;
267 }
268
269 //------------------------------------------------------------------------------------------------
271 EWeaponType GetCurrentWeaponType(bool overrideWithMuzzle = false)
272 {
273 BaseWeaponComponent currentWeapon = GetCurrentWeapon();
274 if (currentWeapon)
275 return SCR_AIWeaponHandling.GetWeaponType(currentWeapon, overrideWithMuzzle);
276
277 return EWeaponType.WT_NONE;
278 }
279
280 //------------------------------------------------------------------------------------------------
282 BaseTarget GetCurrentTarget()
283 {
284 return m_SelectedTarget;
285 }
286
287 //------------------------------------------------------------------------------------------------
289 BaseTarget GetRetreatTarget()
290 {
291 return m_SelectedRetreatTarget;
292 }
293
294 //------------------------------------------------------------------------------------------------
296 BaseTarget GetLastSeenEnemy()
297 {
298 return m_Perception.GetLastSeenTarget(ETargetCategory.ENEMY, float.MAX);
299 }
300
301 //------------------------------------------------------------------------------------------------
303 // Finds out if some of my known enemies is aimng at me.
304 BaseTarget GetEndangeringEnemy()
305 {
306 array<BaseTarget> targets = {};
307 m_Perception.GetTargetsList(targets, ETargetCategory.ENEMY);
308
309 foreach (BaseTarget tgt : targets)
310 {
311 if (tgt.IsEndangering())
312 return tgt;
313 }
314
315 return null;
316 }
317
318 //------------------------------------------------------------------------------------------------
321 bool IsEnemyKnown(IEntity ent)
322 {
323 bool knownAsEnemy = m_Perception.GetTargetPerceptionObject(ent, ETargetCategory.ENEMY);
324 bool knownAsDetected = m_Perception.GetTargetPerceptionObject(ent, ETargetCategory.DETECTED);
325 return knownAsEnemy || knownAsDetected;
326 }
327
328 //------------------------------------------------------------------------------------------------
336 void EvaluateWeaponAndTarget(out bool outWeaponEvent, out bool outSelectedTargetChanged,
337 out BaseTarget outPrevTarget, out BaseTarget outCurrentTarget,
338 out bool outRetreatTargetChanged, out bool outCompartmentChanged)
339 {
340 float worldTime = GetGame().GetWorld().GetWorldTime();
341 if (worldTime < m_fNextWeaponTargetEvaluation_ms)
342 {
343 outWeaponEvent = false;
344 outSelectedTargetChanged = false;
345 return;
346 }
347
348 m_fNextWeaponTargetEvaluation_ms = worldTime + WEAPON_TARGET_UPDATE_PERIOD_MS;
349
350 SCR_ChimeraAIAgent myAgent = m_Agent;
351 if (!myAgent)
352 myAgent = GetAiAgent();
353
354 float agentThreat = m_Utility.m_ThreatSystem.GetThreatMeasure();
355
356 AIGroup myGroup = myAgent.GetParentGroup();
357 SCR_AIGroupInfoComponent groupInfoComp;
358 if (myGroup)
359 groupInfoComp = SCR_AIGroupInfoComponent.Cast(myGroup.FindComponent(SCR_AIGroupInfoComponent));
360
361 BaseTarget newTarget = null;
362 bool weaponEvent = false;
363 bool selectedTargetChanged = false;
364 bool retreatTargetChanged = false;
365 bool compartmentChanged = false;
366
367 // Resolve if we want to think of throwing grenade
368 // Grenade throw is synchronized via group
369 array<EWeaponType> weaponBlacklist;
370 if (groupInfoComp)
371 {
372 if (agentThreat > FRAG_GRENADE_MAX_THREAT || !groupInfoComp.IsGrenadeThrowAllowed(myAgent))
373 weaponBlacklist = s_aWeaponBlacklistFragGrenades;
374 }
375
376 bool useCompartmentWeapons = m_AIInfo.HasUnitState(EUnitState.IN_TURRET); // True when we are in a turret
377
378 // Which assigned targets array to use?
379 array<IEntity> assignedTargets;
380 if (m_TargetClusterState && m_TargetClusterState.m_Cluster && m_TargetClusterState.m_Cluster.m_aEntities)
381 assignedTargets = m_TargetClusterState.m_Cluster.m_aEntities;
382 else
383 assignedTargets = m_aAssignedTargets;
384
385 bool selectedWpnTarget = m_WeaponTargetSelector.SelectWeaponAndTarget(assignedTargets,
386 ASSIGNED_TARGETS_SCORE_INCREMENT, ENDANGERING_TARGETS_SCORE_INCREMENT,
387 useCompartmentWeapons, weaponTypesBlacklist: weaponBlacklist);
388
389 m_eUnitTypesCanAttack = m_WeaponTargetSelector.GetUnitTypesCanAttack();
390 if (selectedWpnTarget)
391 {
392 BaseWeaponComponent newWeaponComp;
393 BaseMagazineComponent newMagazineComp;
394 int newMuzzleId;
395
396 newTarget = m_WeaponTargetSelector.GetSelectedTarget();
397 m_WeaponTargetSelector.GetSelectedWeapon(newWeaponComp, newMuzzleId, newMagazineComp);
398 m_WeaponTargetSelector.GetSelectedWeaponProperties(m_fSelectedWeaponMinDist, m_fSelectedWeaponMaxDist, m_bSelectedWeaponDirectDamage);
399
400
401 weaponEvent = newWeaponComp != m_SelectedWeaponComp ||
402 newMuzzleId != m_iSelectedMuzzle ||
403 newMagazineComp != m_SelectedMagazineComp;
404
405 bool weaponOrMuzzleChanged = newWeaponComp != m_SelectedWeaponComp ||
406 newMuzzleId != m_iSelectedMuzzle;
407
408 if (weaponOrMuzzleChanged)
409 {
410 ref array<BaseMuzzleComponent> muzzles = {};
411 newWeaponComp.GetMuzzlesList(muzzles);
412 if (newMuzzleId >= muzzles.Count() || newMuzzleId < 0)
413 m_SelectedWeaponResource = m_ConfigComponent.GetTreeNameForWeaponType(newWeaponComp.GetWeaponType(),0);
414 else
415 m_SelectedWeaponResource = m_ConfigComponent.GetTreeNameForWeaponType(newWeaponComp.GetWeaponType(),muzzles[newMuzzleId].GetMuzzleType());
416
417 if (newWeaponComp)
418 {
419 EWeaponType weaponType = newWeaponComp.GetWeaponType();
420 if (groupInfoComp && weaponType == EWeaponType.WT_FRAGGRENADE)
421 {
422 // We want to throw a grenade
423 // Notify group immediately
424 groupInfoComp.OnAgentSelectedGrenade(myAgent);
425 }
426 }
427 }
428
429 m_SelectedWeaponComp = newWeaponComp;
430 m_iSelectedMuzzle = newMuzzleId;
431 m_SelectedMagazineComp = newMagazineComp;
432 }
433
434 BaseTarget prevTarget = m_SelectedTarget;
435 if (newTarget != m_SelectedTarget)
436 {
437 #ifdef AI_DEBUG
438 AddDebugMessage(string.Format("Target has changed. New: %1, Previous: %2", newTarget, m_SelectedTarget));
439 #endif
440 m_SelectedTarget = newTarget;
441 selectedTargetChanged = true;
442 }
443
444 // Check if we must retreat from some target
445 BaseTarget targetCantAttack;
446 float targetCantAttackScore;
447 m_WeaponTargetSelector.GetMostRelevantTargetCantAttack(targetCantAttack, targetCantAttackScore);
448 if (targetCantAttackScore < TARGET_SCORE_RETREAT)
449 targetCantAttack = null;
450 if (targetCantAttack != m_SelectedRetreatTarget)
451 {
452 m_SelectedRetreatTarget = targetCantAttack;
453 retreatTargetChanged = true;
454 }
455
456 // Check if compartment has changed
457 BaseCompartmentSlot currentCompartment = m_CompartmentAccess.GetCompartment();
458 if (currentCompartment != m_WeaponEvaluationCompartment)
459 {
460 compartmentChanged = true;
461 m_WeaponEvaluationCompartment = currentCompartment;
462 }
463
464 // Reset last velocity if target changed
465 if (selectedTargetChanged)
466 {
467 m_SelectedTargetVisible = false;
468 m_SelectedTargetDestinationPos = vector.Zero;
469 }
470
471 if (newTarget)
472 {
473 bool visible = IsTargetVisible(newTarget);
474 IEntity targetEntity = newTarget.GetTargetEntity();
475
476 if (visible != m_SelectedTargetVisible)
477 {
478 m_SelectedTargetVisible = visible;
479
480 // Save position (destination) of target pos at the time we figured we've lost LOS
481 // Note: this solution is dependent on update rate of EvaluateWeaponAndTarget
482 if (!visible && targetEntity)
483 m_SelectedTargetDestinationPos = targetEntity.GetOrigin();
484 }
485 }
486
487 outWeaponEvent = weaponEvent;
488 outSelectedTargetChanged = selectedTargetChanged;
489 outRetreatTargetChanged = retreatTargetChanged;
490 outCompartmentChanged = compartmentChanged;
491 outCurrentTarget = newTarget;
492 outPrevTarget = prevTarget;
493 }
494
495 //------------------------------------------------------------------------------------------------
496 // Returns selected target destination pos - pos where unit where just after we've lost LOS
497 vector GetTargetDestinationPos()
498 {
499 return m_SelectedTargetDestinationPos;
500 }
501
502 //------------------------------------------------------------------------------------------------
503 SCR_AIInfoComponent GetAIInfoComponent()
504 {
505 return m_AIInfo;
506 }
507
508 //------------------------------------------------------------------------------------------------
513 bool EvaluateLowAmmo(BaseWeaponComponent weaponComp, int muzzleId)
514 {
515 if (!weaponComp)
516 return false;
517 array<BaseMuzzleComponent> muzzles = {};
518 weaponComp.GetMuzzlesList(muzzles);
519 if (muzzleId >= muzzles.Count() || muzzleId < 0)
520 return false;
521
522 BaseMuzzleComponent muzzleComp = muzzles[muzzleId];
523 if (!muzzleComp)
524 return false;
525
526 // Ignore disposable weapons
527 if (muzzleComp.IsDisposable())
528 return false;
529
530 int magCount = m_InventoryManager.GetMagazineCountByWeapon(weaponComp);
531 int lowMagThreshold = GetWeaponLowMagThreshold(weaponComp);
532
533 return magCount < lowMagThreshold;
534 }
535
536 //------------------------------------------------------------------------------------------------
537 int GetWeaponLowMagThreshold(BaseWeaponComponent weapon)
538 {
539 EWeaponType wpType = SCR_AIWeaponHandling.GetWeaponType(weapon, true);
540 SCR_AIWeaponTypeHandlingConfig config = m_Utility.m_ConfigComponent.GetWeaponTypeHandlingConfig(wpType);
541 if (!config)
542 return SCR_AIWeaponTypeHandlingConfig.DEFAULT_LOW_MAG_THRESHOLD;
543
544 return config.m_iLowMagCountThreshold;
545 }
546
547 //------------------------------------------------------------------------------------------------
550 void Update(float timeSliceMs)
551 {
552 // Evaluate if we must dismount turret - only if we are already in turret
553 if (m_CurrentTurretController)
554 EvaluateDismountTurret(timeSliceMs);
555 }
556
557 //------------------------------------------------------------------------------------------------
564 bool DismountTurretCondition(inout vector targetPos, bool targetPosProvided, out float threatPriority)
565 {
566 // False if not in turret
567 if (!m_CurrentTurretController)
568 return false;
569
570 // False if we have a valid target to attack
571 if (m_SelectedTarget)
572 return false;
573
574 TurretComponent turretComp = m_CurrentTurretController.GetTurretComponent();
575 if (!turretComp)
576 return false;
577
578 // False if we have a driver in the vehicle
579 array<BaseCompartmentSlot> compartments = {};
580 m_CurrentVehicleCompartmentManager.GetCompartments(compartments);
581 foreach (BaseCompartmentSlot slot : compartments)
582 {
583 if (PilotCompartmentSlot.Cast(slot) && slot.GetOccupant())
584 return false;
585 }
586
587 // False if we are in a vehicle and we should not leave turret of this vehicle type
588 // Note that static turrets are not of Vehicle class.
589 Vehicle vehicle = Vehicle.Cast(m_CurrentVehicle);
590 if (vehicle && s_aForbidDismountTurretsOfVehicleTypes.Find(vehicle.m_eVehicleType) != -1)
591 return false;
592
593 // If target pos is not provided, find a target which we are going to check against
594 if (!targetPosProvided)
595 {
596 BaseTarget target = m_Perception.GetClosestTarget(ETargetCategory.DETECTED, DISMOUNT_TURRET_TARGET_LAST_SEEN_MAX_S, DISMOUNT_TURRET_TARGET_LAST_SEEN_MAX_S);
597 if (target)
598 {
599 targetPos = target.GetLastDetectedPosition();
600 threatPriority = SCR_AILookAction.PRIO_UNKNOWN_TARGET;
601 }
602 else
603 {
604 target = m_Perception.GetClosestTarget(ETargetCategory.ENEMY, DISMOUNT_TURRET_TARGET_LAST_SEEN_MAX_S, DISMOUNT_TURRET_TARGET_LAST_SEEN_MAX_S);
605 if (target)
606 {
607 targetPos = target.GetLastSeenPosition();
608 threatPriority = SCR_AILookAction.PRIO_ENEMY_TARGET;
609 }
610 }
611
612 if (!target)
613 {
614 // If there is no target, try to get data out of threat system
615 int sectorMajor, sectorMinor;
616 m_Utility.m_SectorThreatFilter.GetActiveSectors(sectorMajor, sectorMinor);
617
618 // False if there's nothing in threat system
619 if (sectorMajor == -1)
620 return false;
621
622 // If the danger is too small, we dont even dismount
623 if (m_Utility.m_SectorThreatFilter.GetSectorDanger(sectorMajor) < SECTOR_DANGER_TRESHOLD)
624 return false;
625
626 targetPos = m_Utility.m_SectorThreatFilter.GetSectorPos(sectorMajor);
627 threatPriority = SCR_AILookAction.PRIO_DANGER_EVENT;
628 }
629 else
630 {
631 IEntity targetEntity = target.GetTargetEntity();
632 if (!targetEntity)
633 return false;
634 else
635 {
636 vector bmin, bmax;
637 targetEntity.GetBounds(bmin, bmax);
638 targetPos = targetPos + 0.5 * (bmin + bmax);
639 threatPriority = SCR_AILookAction.PRIO_ENEMY_TARGET;
640 }
641 }
642 }
643
644 // Check angle excess of the target's position
645 vector angleExcess = turretComp.GetAimingAngleExcess(targetPos);
646
647 //PrintFormat("Excess angle: %1", angleExcess);
648
649 return angleExcess.Length() > TURRET_TARGET_EXCESS_ANGLE_THRESHOLD_DEG;
650 }
651
652 //------------------------------------------------------------------------------------------------
653 protected void EvaluateDismountTurret(float timeSliceMs)
654 {
655 vector targetPos;
656 float dangerLookPriority;
657 bool mustDismount = DismountTurretCondition(targetPos, false, dangerLookPriority);
658
659 if (!mustDismount)
660 {
661 m_fDismountTurretTimer = 0;
662 }
663 else
664 {
665 // Do nothing if already requested to dismount
666 if (m_fDismountTurretTimer == -1.0)
667 return;
668
669 m_fDismountTurretTimer += timeSliceMs;
670
671 if (m_fDismountTurretTimer > DISMOUNT_TURRET_TIMER_MS)
672 {
673 m_fDismountTurretTimer = -1.0;
674
675 TryAddDismountTurretActions(targetPos, dangerLookPriority: dangerLookPriority);
676 }
677 }
678 }
679
680 //------------------------------------------------------------------------------------------------
686 void TryAddDismountTurretActions(vector targetPos, bool addGetOut = true, bool addGetIn = true, float dangerLookPriority = 0)
687 {
688 BaseCompartmentSlot compartmentSlot = m_CurrentCompartmentSlot;
689 if (!compartmentSlot)
690 return;
691
692 if (dangerLookPriority > 0)
693 m_Utility.m_LookAction.LookAt(targetPos, dangerLookPriority);
694
695 if (addGetOut)
696 {
697 AIActionBase prevGetOutAction = m_Utility.FindActionOfType(SCR_AIGetOutVehicle);
698 if (prevGetOutAction)
699 return;
700
701 SCR_AIGetOutVehicle getOutAction = new SCR_AIGetOutVehicle(m_Utility, null, compartmentSlot.GetOwner(), priority: SCR_AIActionBase.PRIORITY_BEHAVIOR_DISMOUNT_TURRET);
702 m_Utility.AddAction(getOutAction);
703 }
704
705 if (addGetIn)
706 {
707 AIActionBase prevGetInAction = m_Utility.FindActionOfType(SCR_AIGetInVehicle);
708 if (!prevGetInAction)
709 {
710 SCR_AIGetInVehicle getInAction = new SCR_AIGetInVehicle(m_Utility, null, compartmentSlot.GetVehicle(), compartmentSlot, ECompartmentType.TURRET, SCR_AIActionBase.PRIORITY_BEHAVIOR_DISMOUNT_TURRET_GET_IN);
711 m_Utility.AddAction(getInAction);
712 }
713 }
714 }
715
716 //------------------------------------------------------------------------------------------------
717 protected void Event_OnInventoryChanged(IEntity item, BaseInventoryStorageComponent storageOwner)
718 {
719 // This event spams so much, especially at start, we want to process it only once
720 //ScriptCallQueue callQueue = GetGame().GetCallqueue();
721 //callQueue.Remove(Event_OnTimerAfterInventoryChanged);
722 //callQueue.CallLater(Event_OnTimerAfterInventoryChanged, 2500, false);
723 }
724 protected void Event_OnTimerAfterInventoryChanged()
725 {
726 //EvaluateAndReportOutOfAmmo();
727 }
728
729 //------------------------------------------------------------------------------------------------
735 protected void Event_OnCompartmentEntered( IEntity vehicle, BaseCompartmentManagerComponent manager, int mgrID, int slotID )
736 {
737 m_CurrentCompartmentSlot = manager.FindCompartment(slotID, mgrID);
738 IEntity compartmentEntity = m_CurrentCompartmentSlot.GetOwner();
739 m_CurrentTurretController = TurretControllerComponent.Cast(compartmentEntity.FindComponent(TurretControllerComponent));
740 m_CurrentVehicle = m_CurrentCompartmentSlot.GetVehicle();
741
742 // Ensure it's compartment mgr on root of entity, not on a bench
743 if (m_CurrentVehicle)
744 m_CurrentVehicleCompartmentManager = SCR_BaseCompartmentManagerComponent.Cast(m_CurrentVehicle.FindComponent(SCR_BaseCompartmentManagerComponent));
745 else
746 m_CurrentVehicleCompartmentManager = null;
747 }
748
749 //------------------------------------------------------------------------------------------------
755 protected void Event_OnCompartmentLeft( IEntity vehicle, BaseCompartmentManagerComponent manager, int mgrID, int slotID )
756 {
757 m_CurrentVehicle = null;
758 m_bCurrentVehicleEvac = false;
759 m_CurrentCompartmentSlot = null;
760 m_CurrentTurretController = null;
761 }
762
763 //------------------------------------------------------------------------------------------------
764 protected void Event_OnGadgetStateChanged(IEntity gadget, bool isInHand, bool isOnGround)
765 {
766 if (isInHand)
767 {
768 SCR_GadgetComponent gadgetComp = SCR_GadgetComponent.Cast(gadget.FindComponent(SCR_GadgetComponent));
769 if (gadgetComp)
770 m_eCurrentGadgetType = gadgetComp.GetType();
771 else
772 m_eCurrentGadgetType = -1;
773 }
774 else
775 m_eCurrentGadgetType = -1;
776
777 RecalculateEquipmentPerceptionFactor();
778 }
779
780 //------------------------------------------------------------------------------------------------
781 protected void Event_OnGadgetFocusStateChanged(IEntity gadget, bool isFocused)
782 {
783 m_bGadgetFocus = isFocused;
784 RecalculateEquipmentPerceptionFactor();
785 }
786
787 //------------------------------------------------------------------------------------------------
788 protected void Event_OnDamage(BaseDamageContext damageContext)
789 {
790 if (damageContext.damageType != EDamageType.FIRE || !m_Utility || !m_CurrentVehicle || m_bCurrentVehicleEvac)
791 return;
792
793 // Fire damage inside a vehicle - evac vehicle
794 m_bCurrentVehicleEvac = true;
795
796 SCR_AIGetOutVehicle behaviorGetOut = new SCR_AIGetOutVehicle(m_Utility, null, m_CurrentVehicle, priority: SCR_AIActionBase.PRIORITY_BEHAVIOR_GET_OUT_VEHICLE_HIGH_PRIORITY);
797 m_Utility.AddAction(behaviorGetOut);
798
799 SCR_AIMoveFromDangerBehavior behaviorMoveFromDanger = new SCR_AIMoveFromDangerBehavior(m_Utility, null, m_CurrentVehicle.GetOrigin(), m_CurrentVehicle);
800 m_Utility.AddAction(behaviorMoveFromDanger);
801 }
802
803 //------------------------------------------------------------------------------------------------
804 protected void Event_OnDamageEffectAdded(notnull SCR_DamageEffect dmgEffect)
805 {
806 if (!m_Utility || !m_Utility.m_AIInfo || dmgEffect.GetDamageType() != EDamageType.BLEEDING || !DotDamageEffect.Cast(dmgEffect))
807 return;
808
809 SCR_AIActionBase currentAction = SCR_AIActionBase.Cast(m_Utility.GetCurrentAction());
810 if (!currentAction)
811 return;
812
813 float priorityLevelClamped = currentAction.GetRestrictedPriorityLevel();
814
815 if (!m_Utility.m_AIInfo.HasUnitState(EUnitState.UNCONSCIOUS) && m_Utility.m_AIInfo.HasRole(EUnitRole.MEDIC))
816 {
817 if (!m_Utility.HasActionOfType(SCR_AIHealBehavior))
818 {
819 // If we can heal ourselves, add Heal Behavior.
820 SCR_AIHealBehavior behavior = new SCR_AIHealBehavior(m_Utility, null, m_Utility.m_OwnerEntity, true, priorityLevel: priorityLevelClamped);
821 m_Utility.AddAction(behavior);
822 }
823 }
824 else if (m_Agent)
825 {
826 // If we immediately know that we can't heal ourselves, report to group
827 AIGroup myGroup = m_Agent.GetParentGroup();
828 if (myGroup)
829 {
830 // Hack: we use group's comms component, and send from it to it directly, because if this AI is unconscious, his mailbox is already disabled.
831 AICommunicationComponent comms = myGroup.GetCommunicationComponent();
832 if (comms)
833 {
834 SCR_AIMessage_Wounded msg = SCR_AIMessage_Wounded.Create(m_Utility.m_OwnerEntity);
835
836 #ifdef AI_DEBUG
837 msg.m_sSentFromBt = "SCR_AICombatComponent.Event_OnDamageEffectAdded";
838 #endif
839
840 msg.SetReceiver(myGroup);
841 comms.RequestBroadcast(msg, myGroup);
842 }
843 }
844 }
845 }
846
847 //------------------------------------------------------------------------------------------------
849 EAIGroupCombatMode GetCombatMode()
850 {
851 SCR_AIGroup myGroup = SCR_AIGroup.Cast(GetAiAgent().GetParentGroup());
852 if (!myGroup)
853 return EAIGroupCombatMode.FIRE_AT_WILL;
854
856 }
857
858 //------------------------------------------------------------------------------------------------
860 bool IsFriendlyInAim()
861 {
862 IEntity friendlyEntInAim = m_Perception.GetFriendlyInLineOfFire();
863#ifdef WORKBENCH
864 if (friendlyEntInAim && DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_SHOW_FRIENDLY_IN_AIM))
865 m_FriendlyAimShape = Shape.CreateSphere(COLOR_RED, ShapeFlags.NOOUTLINE|ShapeFlags.NOZBUFFER|ShapeFlags.TRANSP, friendlyEntInAim.GetOrigin() + Vector(0, 2, 0), 0.1);
866 else
867 m_FriendlyAimShape = null;
868#endif
869 m_bFriendlyAimLastResult = friendlyEntInAim != null;
870
871 return m_bFriendlyAimLastResult;
872 }
873
874 //------------------------------------------------------------------------------------------------
878 bool IsTargetVisible(notnull BaseTarget target)
879 {
880 // When m_lastSeenMax value is lower than perception component update interval,
881 // which might be large at higher LOD levels, the decorator might periodically return false,
882 // even when target is still visible.
883 // Therefore we must prevent the threshold from being smaller than update interval of perception component.
884 float lastSeenMax = Math.Max(TARGET_MAX_LAST_SEEN_VISIBLE, m_Perception.GetUpdateInterval()) + 0.02;
885 bool visible = target.GetTimeSinceSeen() < lastSeenMax;
886 return visible;
887 }
888
889 //------------------------------------------------------------------------------------------------
893 void SetAssignedTargets(array<IEntity> assignedTargets, SCR_AITargetClusterState clusterState)
894 {
895 m_aAssignedTargets.Clear();
896
897 if (assignedTargets)
898 m_aAssignedTargets.Copy(assignedTargets);
899 else if (clusterState)
900 m_TargetClusterState = clusterState;
901 }
902
903 //------------------------------------------------------------------------------------------------
905 void ResetAssignedTargets()
906 {
907 m_aAssignedTargets.Clear();
909 }
910
911 //------------------------------------------------------------------------------------------------
913 void SetExpectedEnemyType(EAIUnitType expectedEnemyType)
914 {
915 m_eExpectedEnemyType = expectedEnemyType;
916 }
917
918 //------------------------------------------------------------------------------------------------
920 void ResetExpectedEnemyType()
921 {
922 m_eExpectedEnemyType = EAIUnitType.UnitType_Infantry;
923 }
924
925 //------------------------------------------------------------------------------------------------
932 void GetSelectedWeaponAndMagazine(out BaseWeaponComponent outWeaponComp, out int outMuzzleId, out BaseMagazineComponent outMagazineComp)
933 {
934 if (m_SelectedWeaponComp)
935 {
936 outWeaponComp = m_SelectedWeaponComp;
937 outMagazineComp = m_SelectedMagazineComp;
938 outMuzzleId = m_iSelectedMuzzle;
939 }
940 else
941 {
942 outWeaponComp = null;
943 outMuzzleId = -1;
944 outMagazineComp = null;
945 }
946 }
947
948 //------------------------------------------------------------------------------------------------
951 void GetSelectedWeapon(out BaseWeaponComponent outWeaponComp, out int outMuzzleId)
952 {
953 if (m_SelectedWeaponComp)
954 {
955 outWeaponComp = m_SelectedWeaponComp;
956 outMuzzleId = m_iSelectedMuzzle;
957 }
958 else
959 {
960 outWeaponComp = null;
961 outMuzzleId = -1;
962 }
963 }
964
965 //------------------------------------------------------------------------------------------------
967 EWeaponType GetSelectedWeaponType()
968 {
969 if (m_SelectedWeaponComp)
970 return m_SelectedWeaponComp.GetWeaponType();
971 else
972 return EWeaponType.WT_NONE;
973 }
974
975 //------------------------------------------------------------------------------------------------
979 void GetSelectedWeaponProperties(out float outMinDistance, out float outMaxDistance, out bool outDirectDamage)
980 {
981 outMinDistance = m_fSelectedWeaponMinDist;
982 outMaxDistance = m_fSelectedWeaponMaxDist;
983 outDirectDamage = m_bSelectedWeaponDirectDamage;
984 }
985
986 //------------------------------------------------------------------------------------------------
988 bool GetSelectedWeaponDirectDamage()
989 {
990 return m_bSelectedWeaponDirectDamage;
991 }
992
993 //------------------------------------------------------------------------------------------------
995 float GetSelectedWeaponMinDist()
996 {
997 return m_fSelectedWeaponMinDist;
998 }
999
1000 //------------------------------------------------------------------------------------------------
1002 float GetSelectedWeaponMaxDist()
1003 {
1004 return m_fSelectedWeaponMaxDist;
1005 }
1006
1007 //------------------------------------------------------------------------------------------------
1009 ResourceName GetSelectedWeaponResource()
1010 {
1011 return m_SelectedWeaponResource;
1012 }
1013
1014 //------------------------------------------------------------------------------------------------
1016 EAIUnitType GetUnitTypesCanAttack()
1017 {
1018 return m_eUnitTypesCanAttack;
1019 }
1020
1021 //------------------------------------------------------------------------------------------------
1027 void EvaluateExpectedWeapon(out BaseWeaponComponent outWeaponComp, out int outMuzzleId, out BaseMagazineComponent outMagazineComp)
1028 {
1029 bool useCompartmentWeapons = m_AIInfo.HasUnitState(EUnitState.IN_TURRET);
1030 bool success = m_WeaponTargetSelector.SelectWeaponAgainstUnitType(m_eExpectedEnemyType, useCompartmentWeapons);
1031
1032 if (success)
1033 {
1034 BaseWeaponComponent weaponComp;
1035 int muzzleId;
1036 BaseMagazineComponent magazineComp;
1037
1038 m_WeaponTargetSelector.GetSelectedWeapon(weaponComp, muzzleId, magazineComp);
1039
1040 outWeaponComp = weaponComp;
1041 outMuzzleId = muzzleId;
1042 outMagazineComp = magazineComp;
1043 }
1044 else
1045 {
1046 outWeaponComp = null;
1047 outMuzzleId = -1;
1048 outMagazineComp = null;
1049 }
1050 }
1051
1052 //------------------------------------------------------------------------------------------------
1055 void EvaluateSuppressionWeapon(EAIUnitType targetUnitType, float distanceToTarget, out BaseWeaponComponent outWeaponComp, out int outMuzzleId, out BaseMagazineComponent outMagazineComp)
1056 {
1057 bool useCompartmentWeapons = m_AIInfo.HasUnitState(EUnitState.IN_TURRET);
1058 bool success = m_WeaponTargetSelector.SelectWeaponAgainstUnitTypeAndDistance(targetUnitType, distanceToTarget, false, useCompartmentWeapons,
1059 weaponTypesWhitelist: null,
1060 weaponTypesBlacklist: s_aWeaponBlacklistFragGrenades,
1061 minMagCountSpec: m_ConfigComponent.m_aMinSuppressiveMagCountSpec);
1062
1063 if (success)
1064 {
1065 BaseWeaponComponent weaponComp;
1066 int muzzleId;
1067 BaseMagazineComponent magazineComp;
1068
1069 m_WeaponTargetSelector.GetSelectedWeapon(weaponComp, muzzleId, magazineComp);
1070
1071 outWeaponComp = weaponComp;
1072 outMuzzleId = muzzleId;
1073 outMagazineComp = magazineComp;
1074 }
1075 else
1076 {
1077 outWeaponComp = null;
1078 outMuzzleId = -1;
1079 outMagazineComp = null;
1080 }
1081 }
1082
1083 //------------------------------------------------------------------------------------------------
1086 bool HasWeaponOfType(EWeaponType weaponType)
1087 {
1088 return m_WeaponTargetSelector.HasWeaponOfType(weaponType, false);
1089 }
1090
1091 //------------------------------------------------------------------------------------------------
1095 BaseWeaponComponent FindWeaponOfType(EWeaponType weaponType)
1096 {
1097 return m_WeaponTargetSelector.FindWeaponOfType(weaponType, false);
1098 }
1099
1100 //------------------------------------------------------------------------------------------------
1104 int GetMagazineCount(typename magazineWell, bool countMagazineInWeapon = false)
1105 {
1106 int count = m_WeaponTargetSelector.GetMagazineCount(magazineWell, false);
1107 return count - 1 + countMagazineInWeapon;
1108 }
1109
1110 //------------------------------------------------------------------------------------------------
1111 override void OnPostInit(IEntity owner)
1112 {
1113 super.OnPostInit(owner);
1114 SetEventMask(owner, EntityEvent.INIT);
1115 }
1116
1117 //------------------------------------------------------------------------------------------------
1118 override void EOnInit(IEntity owner)
1119 {
1120 m_eAISkill = m_eAISkillDefault;
1121
1122 ChimeraCharacter character = ChimeraCharacter.Cast(owner);
1123 if (character)
1124 {
1125 m_CharacterController = SCR_CharacterControllerComponent.Cast(character.GetCharacterController());
1127 {
1128 m_CharacterController.m_OnGadgetStateChangedInvoker.Insert(Event_OnGadgetStateChanged);
1129 m_CharacterController.m_OnGadgetFocusStateChangedInvoker.Insert(Event_OnGadgetFocusStateChanged);
1130
1131 m_WpnManager = m_CharacterController.GetWeaponManagerComponent();
1132
1133 m_InventoryManager = SCR_InventoryStorageManagerComponent.Cast(m_CharacterController.GetInventoryStorageManager());
1135 {
1136 m_InventoryManager.m_OnItemAddedInvoker.Insert(Event_OnInventoryChanged);
1137 m_InventoryManager.m_OnItemRemovedInvoker.Insert(Event_OnInventoryChanged);
1138 }
1139 }
1140
1141 m_CompartmentAccess = SCR_CompartmentAccessComponent.Cast(character.GetCompartmentAccessComponent());
1142 if (m_CompartmentAccess)
1143 {
1144 m_CompartmentAccess.GetOnCompartmentEntered().Insert(Event_OnCompartmentEntered);
1145 m_CompartmentAccess.GetOnCompartmentLeft().Insert(Event_OnCompartmentLeft);
1146 }
1147 }
1148
1149 m_Perception = PerceptionComponent.Cast(owner.FindComponent(PerceptionComponent));
1150
1151 BaseWorld world = GetGame().GetWorld();
1152 if (world)
1153 {
1154 float worldTime = world.GetWorldTime();
1155 m_fNextWeaponTargetEvaluation_ms = worldTime + Math.RandomFloat(0, WEAPON_TARGET_UPDATE_PERIOD_MS);
1156 }
1157
1158 if (owner)
1159 {
1160 InitWeaponTargetSelector(owner);
1161 }
1162
1163 m_DamageManager = SCR_ExtendedDamageManagerComponent.Cast(owner.FindComponent(SCR_ExtendedDamageManagerComponent));
1164 if (m_DamageManager)
1165 {
1166 m_DamageManager.GetOnDamageEffectAdded().Insert(Event_OnDamageEffectAdded);
1167 m_DamageManager.GetOnDamage().Insert(Event_OnDamage);
1168 }
1169
1170 AIControlComponent ctrl = AIControlComponent.Cast(owner.FindComponent(AIControlComponent));
1171 if (ctrl)
1172 {
1173 SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(ctrl.GetAIAgent());
1174 if (agent)
1175 {
1176 m_AIInfo = agent.m_InfoComponent;
1177 m_ConfigComponent = SCR_AIConfigComponent.Cast(agent.FindComponent(SCR_AIConfigComponent));
1178 m_Utility = SCR_AIUtilityComponent.Cast(agent.FindComponent(SCR_AIUtilityComponent));
1179 }
1180 }
1181 }
1182
1183 //------------------------------------------------------------------------------------------------
1184 override void OnDelete(IEntity owner)
1185 {
1186 if (m_CompartmentAccess)
1187 {
1188 m_CompartmentAccess.GetOnCompartmentEntered().Remove(Event_OnCompartmentEntered);
1189 m_CompartmentAccess.GetOnCompartmentLeft().Remove(Event_OnCompartmentLeft);
1190 }
1191
1193 {
1194 m_CharacterController.m_OnGadgetStateChangedInvoker.Remove(Event_OnGadgetStateChanged);
1195 m_CharacterController.m_OnGadgetFocusStateChangedInvoker.Remove(Event_OnGadgetFocusStateChanged);
1196 }
1197
1198 if (m_DamageManager)
1199 {
1200 m_DamageManager.GetOnDamageEffectAdded().Remove(Event_OnDamageEffectAdded);
1201 m_DamageManager.GetOnDamage().Remove(Event_OnDamage);
1202 }
1203 }
1204
1205 //------------------------------------------------------------------------------------------------
1206 protected void InitWeaponTargetSelector(IEntity owner)
1207 {
1208 m_WeaponTargetSelector.Init(owner);
1209
1210 // maxLastSeenIndirect must be consistent with conditions in ShouldAttackEndForTarget, therefore we use TARGET_INVISIBLE_TIME here
1211 SetTargetSelectionProperties(false);
1212
1213
1214 m_WeaponTargetSelector.SetTargetScoreConstants(EAIUnitType.UnitType_Infantry, 100.0, -0.1); // At short range we prefer to shoot enemies in vehicle
1215 m_WeaponTargetSelector.SetTargetScoreConstants(EAIUnitType.UnitType_VehicleUnarmored, 99.0, -0.08);
1216 m_WeaponTargetSelector.SetTargetScoreConstants(EAIUnitType.UnitType_VehicleMedium, 150.0, -0.15);
1217 m_WeaponTargetSelector.SetTargetScoreConstants(EAIUnitType.UnitType_VehicleHeavy, 200.0, -0.15);
1218 m_WeaponTargetSelector.SetTargetScoreConstants(EAIUnitType.UnitType_Aircraft, 90.0, -0.015);
1219 m_WeaponTargetSelector.SetTargetScoreConstants(EAIUnitType.UnitType_Fortification, 40.0, -0.1); // Fortifications are not used ATM
1220 }
1221
1222 //------------------------------------------------------------------------------------------------
1223 void SetTargetSelectionProperties(bool closeCombat)
1224 {
1225 if (closeCombat)
1226 {
1227 // Here TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK_CLOSE is used instead of TARGET_MAX_LAST_SEEN, since it's largest of the two
1228 m_WeaponTargetSelector.SetSelectionProperties(TARGET_MAX_LAST_SEEN_DIRECT_ATTACK_CLOSE, TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK_CLOSE, TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK_CLOSE,
1229 TARGET_MIN_INDIRECT_TRACE_FRACTION_MIN, TARGET_MAX_DISTANCE_INFANTRY, TARGET_MAX_DISTANCE_VEHICLE, TARGET_MAX_TIME_SINCE_ENDANGERED, TARGET_MAX_DISTANCE_DISARMED);
1230 return;
1231 }
1232
1233 m_WeaponTargetSelector.SetSelectionProperties(TARGET_MAX_LAST_SEEN_DIRECT_ATTACK, TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK, TARGET_MAX_LAST_SEEN,
1234 TARGET_MIN_INDIRECT_TRACE_FRACTION_MIN, TARGET_MAX_DISTANCE_INFANTRY, TARGET_MAX_DISTANCE_VEHICLE, TARGET_MAX_TIME_SINCE_ENDANGERED, TARGET_MAX_DISTANCE_DISARMED);
1235 }
1236
1237 //------------------------------------------------------------------------------------------------
1241 void UpdatePerceptionFactor(PerceptionComponent perceptionComp, SCR_AIThreatSystem threatSystem)
1242 {
1243 EAIThreatState threatState = threatSystem.GetState();
1244 float perceptionFactor;
1245 switch (threatState)
1246 {
1247 case EAIThreatState.SAFE:
1248 perceptionFactor = PERCEPTION_FACTOR_SAFE; break;
1249 case EAIThreatState.VIGILANT:
1250 perceptionFactor = PERCEPTION_FACTOR_VIGILANT; break;
1251 case EAIThreatState.ALERTED:
1252 perceptionFactor = PERCEPTION_FACTOR_ALERTED; break;
1253 case EAIThreatState.THREATENED:
1254 perceptionFactor = PERCEPTION_FACTOR_THREATENED; break;
1255 }
1256
1257 perceptionFactor *= m_fEquipmentPerceptionFactor;
1258 perceptionFactor *= m_fPerceptionFactor;
1259
1260 perceptionComp.SetPerceptionFactor(perceptionFactor);
1261 }
1262
1263 //------------------------------------------------------------------------------------------------
1264 protected void RecalculateEquipmentPerceptionFactor()
1265 {
1266 if (m_eCurrentGadgetType == EGadgetType.BINOCULARS && m_bGadgetFocus)
1267 m_fEquipmentPerceptionFactor = PERCEPTION_FACTOR_EQUIPMENT_BINOCULARS;
1268 else
1269 m_fEquipmentPerceptionFactor = PERCEPTION_FACTOR_EQUIPMENT_NONE;
1270 }
1271
1272 //------------------------------------------------------------------------------------------------
1273 // destructor
1274 void ~SCR_AICombatComponent()
1275 {
1276 GetGame().GetCallqueue().Remove(Event_OnTimerAfterInventoryChanged);
1277 }
1278
1279 //------------------------------------------------------------------------------------------------
1281 void DebugPrintToWidget(TextWidget w)
1282 {
1283 string str = string.Format("\n%1", m_SelectedTarget.ToString());
1284 if (m_SelectedTarget)
1285 str = str + string.Format("\n%1", m_SelectedTarget.GetTimeSinceSeen());
1286 else
1287 str = str + "\n";
1288 w.SetText(str);
1289 }
1290
1291 //------------------------------------------------------------------------------------------------
1293 SCR_ChimeraAIAgent GetAiAgent()
1294 {
1295 if (m_Agent)
1296 return m_Agent;
1297
1298 AIControlComponent controlComp = AIControlComponent.Cast(GetOwner().FindComponent(AIControlComponent));
1299 m_Agent = SCR_ChimeraAIAgent.Cast(controlComp.GetAIAgent());
1300 return m_Agent;
1301 }
1302
1303 //------------------------------------------------------------------------------------------------
1307 BaseTarget FindTargetByEntity(IEntity ent)
1308 {
1309 BaseTarget tgt = m_Perception.FindTargetPerceptionObject(ent);
1310 if (tgt)
1311 return tgt;
1312
1313 #ifdef AI_DEBUG
1314 AddDebugMessage(string.Format("FindTargetByEntity: did not find target: %1", ent), LogLevel.WARNING);
1315 Print(string.Format("FindTargetByEntity: did not find target: %1", ent), LogLevel.WARNING);
1316 #endif
1317 return null;
1318 }
1319
1320 //------------------------------------------------------------------------------------------------
1324 void UpdateLastSeenPosition(IEntity entity, notnull SCR_AITargetInfo targetInfo)
1325 {
1326 BaseTarget tgt = m_Perception.FindTargetPerceptionObject(entity);
1327 if (!tgt)
1328 return;
1329
1330 if (targetInfo.m_fTimestamp > tgt.GetTimeLastSeen())
1331 tgt.UpdateLastSeenPosition(targetInfo.m_vWorldPos, targetInfo.m_fTimestamp);
1332 }
1333
1334 //------------------------------------------------------------------------------------------------
1338 void UpdateLastSeenPosition(notnull BaseTarget tgt, notnull SCR_AITargetInfo targetInfo)
1339 {
1340 if (targetInfo.m_fTimestamp > tgt.GetTimeLastSeen())
1341 tgt.UpdateLastSeenPosition(targetInfo.m_vWorldPos, targetInfo.m_fTimestamp);
1342 }
1343
1344 //------------------------------------------------------------------------------------------------
1345 // Obsolete methods
1346 [Obsolete("Outdated functionality which is not used anywhere")]
1347 bool IsActionAllowed(int action) { return true; }
1348
1349 [Obsolete("Outdated functionality which is not used anywhere")]
1350 int GetAllowedActions() { return 0; }
1351
1352 [Obsolete("Outdated method, does not affect anything in AI")]
1353 private void SetActionAllowed(int action, bool isAllowed) {}
1354
1355 [Obsolete("Use Combat Modes instead")]
1356 void SetHoldFire(bool isHoldFire) {}
1357
1358 [Obsolete("Outdated functionality which is not used anywhere")]
1359 int GetCombatType() { return 0; }
1360
1361 [Obsolete("Use Combat Modes instead")]
1362 void SetCombatType(int combatType) {}
1363
1364 [Obsolete()]
1365 void SetDefaultCombatType(int combatType) {}
1366
1367 [Obsolete()]
1368 void ResetCombatType() {}
1369
1370 #ifdef AI_DEBUG
1371 //------------------------------------------------------------------------------------------------
1372 protected void AddDebugMessage(string str, LogLevel logLevel = LogLevel.NORMAL)
1373 {
1374 AIControlComponent controlComp = AIControlComponent.Cast(GetOwner().FindComponent(AIControlComponent));
1375 AIAgent agent = controlComp.GetAIAgent();
1376 if (!agent)
1377 return;
1378 SCR_AIInfoBaseComponent infoComp = SCR_AIInfoBaseComponent.Cast(agent.FindComponent(SCR_AIInfoBaseComponent));
1379 infoComp.AddDebugMessage(str, msgType: EAIDebugMsgType.COMBAT, logLevel);
1380 }
1381 #endif
1382}
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
ECompartmentType
ArmaReforgerScripted GetGame()
Definition game.c:1398
enum SCR_EAIActivityCause m_Utility
SCR_ChimeraAIAgent m_Agent
SCR_AICombatComponentClass EXPERT
SCR_AICombatComponentClass VETERAN
SCR_AICombatComponentClass NOOB
SCR_AICombatComponentClass ROOKIE
EAIDebugMsgType
void SCR_AIGroup(IEntitySource src, IEntity parent)
enum EAIGroupCombatMode ComponentEditorProps(category:"GameScripted/AI", description:"Component for utility AI system for groups")
SCR_AITargetClusterState m_TargetClusterState
void SCR_AITargetClusterState(SCR_AIGroupTargetCluster cluster)
DamageManagerComponent m_DamageManager
SCR_AIInfoComponent m_AIInfo
SCR_AIConfigComponent m_ConfigComponent
SCR_InventoryStorageManagerComponent m_InventoryManager
SCR_CharacterPerceivableComponentClass m_CharacterController
EVehicleType
Definition Vehicle.c:4
enum EVehicleType IEntity
proto external int SetEventMask(notnull IEntity owner, int mask)
proto external GenericComponent FindComponent(typename typeName)
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
proto external void GetBounds(out vector mins, out vector maxs)
SCR_AIGroupUtilityComponent GetGroupUtilityComponent()
EAIGroupCombatMode GetCombatModeActual()
Returns actual combat mode. See comment to EvaluateCombatMode method.
bool HasUnitState(EUnitState state)
SCR_AITargetClusterState GetClusterState()
ScriptInvoker GetOnCompartmentEntered(bool createNew=true)
ScriptInvoker GetOnCompartmentLeft(bool createNew=true)
proto external GenericEntity GetOwner()
Get owner entity.
void EOnInit(IEntity owner)
enum EPhysicsLayerPresets Vehicle
Definition gameLib.c:24
proto external AIGroup GetParentGroup()
EAIUnitType
Definition EAIUnitType.c:13
const int COLOR_RED
Definition constants.c:21
proto external void SetPerceptionFactor(float value)
Sets perception factor - how good this AI can recognize enemy, independent of enemy state....
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
ShapeFlags
Definition ShapeFlags.c:13
@ NONE
When Shape is created and not initialized yet.
Definition ShapeType.c:15
SCR_FieldOfViewSettings Attribute
EntityEvent
Various entity events.
Definition EntityEvent.c:14
EDamageType
Definition EDamageType.c:13
ETargetCategory
proto native vector Vector(float x, float y, float z)
@ REGULAR
EWeaponType
Definition EWeaponType.c:13
proto external BaseWeaponComponent GetCurrentWeapon()