Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_AICombatMoveLogic_Attack.c
Go to the documentation of this file.
1 /*
2 This analyzes combat move state and issues new requests for combat move tree.
3 */
4 
5 class SCR_AICombatMoveLogic_Attack : AITaskScripted
6 {
7  // Inputs
8  protected static const string PORT_BASE_TARGET = "BaseTarget";
9 
10  protected SCR_AICombatMoveState m_State;
11  protected SCR_AIUtilityComponent m_Utility;
12  protected SCR_AICombatComponent m_CombatComp;
13  protected IEntity m_MyEntity;
14  protected CharacterControllerComponent m_CharacterController;
15 
16  // Values updated on each update, to avoid passing them through calls
17  protected EAIThreatState m_eThreatState;
18  protected ECharacterStance m_eStance;
19  protected EWeaponType m_eWeaponType;
20  protected BaseTarget m_Target;
21  protected float m_fTargetDist;
22  protected float m_fWeaponMinDist;
23  protected bool m_bCloseRangeCombat; // True if we consider it's a close range fight
24 
25  protected float m_fNextUpdate_ms;
26  [Attribute("500")]
27  protected float m_fUpdateInterval_ms;
28 
29  // Half-angle of cover query sector when directed cover search is used
30  protected const float COVER_QUERY_SECTOR_ANGLE_RAD = 0.3 * Math.PI;
31 
32  //--------------------------------------------------------------------------------------------
33  protected override void OnInit(AIAgent owner)
34  {
35  m_Utility = SCR_AIUtilityComponent.Cast(owner.FindComponent(SCR_AIUtilityComponent));
36  if (m_Utility)
37  m_State = m_Utility.m_CombatMoveState;
38 
39  m_MyEntity = owner.GetControlledEntity();
40 
41  if (m_MyEntity)
42  {
43  m_CharacterController = CharacterControllerComponent.Cast(m_MyEntity.FindComponent(CharacterControllerComponent));
44  m_CombatComp = SCR_AICombatComponent.Cast(m_MyEntity.FindComponent(SCR_AICombatComponent));
45  }
46  }
47 
48  protected override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
49  {
50  float currentTime_ms = GetGame().GetWorld().GetWorldTime();
51  if (currentTime_ms < m_fNextUpdate_ms)
52  return ENodeResult.RUNNING;
53  m_fNextUpdate_ms = currentTime_ms + m_fUpdateInterval_ms;
54 
55  BaseTarget target;
56  GetVariableIn(PORT_BASE_TARGET, target);
57  if (!target || !target.GetTargetEntity() || !m_State || !m_MyEntity || !m_Utility || !m_CombatComp || !m_CharacterController)
58  return ENodeResult.FAIL;
59 
60  // Don't run combat movement logic if CombatMove BT is not used now (like in turret)
61  SCR_AIBehaviorBase executedBehavior = SCR_AIBehaviorBase.Cast(m_Utility.GetExecutedAction());
62  if (executedBehavior && !executedBehavior.m_bUseCombatMove)
63  return ENodeResult.RUNNING;
64 
65  // Update cached variables
66  m_Target = target;
67  m_fTargetDist = target.GetDistance();
68  m_bCloseRangeCombat = m_fTargetDist < SCR_AICombatMoveUtils.CLOSE_RANGE_COMBAT_DIST;
69  m_eThreatState = m_Utility.m_ThreatSystem.GetState();
70  m_eStance = m_CharacterController.GetStance();
71  m_fWeaponMinDist = m_CombatComp.GetSelectedWeaponMinDist();
72  m_eWeaponType = m_CombatComp.GetSelectedWeaponType();
73 
74 
75  /*
76  //------------------------------------------------------------------------------------
77  Combat movement logic
78 
79  Conditions represent states inside which we want to remain.
80 
81  Conditions are organized based on their priority, highest first.
82 
83  Within each state there can be extra logic which decides if it's worth to
84  send a new request, because even though we have selected a state, we should avoid
85  spamming same request over and over.
86 
87  Conditions for states mostly depend on Combat Move State and its timers.
88 
89  It is important to write logic in such a way that it doesn't depend on state
90  of this node. In this case the state flow also doesn't depend on it, and AI
91  does movement is more fluent when switching to a new behavior which also utilizes
92  combat movement, including attacking a different target.
93  */
94 
95  if (SuppressedInCoverCondition())
96  {
97  SuppressedInCoverLogic();
98  }
99  else if (MoveFromTargetCondition())
100  {
101  // Too close to target
102  // Step away
103  if (MoveFromTargetNewRequestCondition())
104  PushRequestMoveFromTarget();
105  }
106  else if (CurrentCoverUselessCondition())
107  {
108  // Current cover has been compromised, it's not directed at enemy any more
109  // Find a new cover nearby
110  PushRequestLeaveUselessCover();
111  }
112  else if (m_State.m_bInCover && m_CharacterController.IsReloading())
113  {
114  // We're reloading and can't do much else now
115  // Hide in cover
116  if (m_State.m_bExposedInCover)
117  m_State.ApplyRequestChangeStanceInCover(false);
118  }
119  else if (m_State.m_bInCover && !m_State.m_bExposedInCover)
120  {
121  // We're in cover but we are still hiding in it, unhide
122  m_State.ApplyRequestChangeStanceInCover(true);
123  }
124  else if (FFAvoidanceCondition())
125  {
126  if (FFAvoidanceNewRequestCondition())
127  PushRequestFFAvoidance();
128  }
129  else if (MoveToNextPosCondition())
130  {
131  // We've waited here too long, move to next place
132  PushRequestMove();
133  }
134  else if (!m_State.IsExecutingRequest() && !m_State.m_bInCover)
135  {
136  // We are stopped and not in cover, manage our stance
137  ECharacterStance newStance = ResolveStanceOutsideCover(m_bCloseRangeCombat, m_eThreatState);
138  if (newStance > m_eStance)
139  {
140  // Only let stance go down, no need to get back up
141  m_State.ApplyRequestChangeStanceOutsideCover(newStance);
142  }
143  }
144 
145  return ENodeResult.RUNNING;
146  }
147 
148 
149  //--------------------------------------------------------------------------------------------
150  // Friendly fire avoidance
151 
152  // Friendly fire avoidance condition
153  protected bool FFAvoidanceCondition()
154  {
155  // Should we deal with friendly fire avoidance?
156 
157  // Ignore if we don't want to aim at all
158  if (!m_State.m_bAimAtTarget)
159  return false;
160 
161  // True only when not in cover
162  if (m_State.m_bInCover)
163  return false;
164 
165  // True when not moving to cover
166  if (m_State.IsMovingToCover())
167  return false;
168 
169  // True when friendly is in aim
170  return m_Utility.m_CombatComponent.IsFriendlyInAim();
171  }
172 
173  // Friendly fire avoidance condition for pushing new request
174  protected bool FFAvoidanceNewRequestCondition()
175  {
176  if (!m_State.IsExecutingRequest())
177  return true;
178 
179  // Still executing ...
180  // Send new request only if we are executing NOT side-step
182  if (!rq)
183  return true;
184 
185  return rq.m_eReason != SCR_EAICombatMoveReason.FF_AVOIDANCE;
186  }
187 
188  protected void PushRequestFFAvoidance()
189  {
191 
192  rq.m_eReason = SCR_EAICombatMoveReason.FF_AVOIDANCE;
193 
194  // If prev. request was FF avoidance too, keep direction.
195  // Otherwise choose a new direction.
196  SCR_AICombatMoveRequest_Move prevRequest = SCR_AICombatMoveRequest_Move.Cast(m_State.GetRequest());
197  if (prevRequest && prevRequest.m_eReason == SCR_EAICombatMoveReason.FF_AVOIDANCE)
198  {
199  rq.m_eDirection = prevRequest.m_eDirection;
200  }
201  else
202  {
203  if (Math.RandomIntInclusive(0, 1) == 1)
204  rq.m_eDirection = SCR_EAICombatMoveDirection.RIGHT;
205  else
206  rq.m_eDirection = SCR_EAICombatMoveDirection.LEFT;
207  }
208 
209  rq.m_eStanceMoving = m_CharacterController.GetStance(); // Don't change stance
210  rq.m_eStanceEnd = rq.m_eStanceMoving;
211  rq.m_vMovePos = ResolveRequestTargetPos();
212  rq.m_eMovementType = EMovementType.WALK;
213  rq.m_fMoveDistance = 1.5;
214  rq.m_bAimAtTarget = SCR_AICombatMoveUtils.IsAimingAndMovementPossible(rq.m_eStanceMoving, rq.m_eMovementType);
215  rq.m_bAimAtTargetEnd = true;
216 
217  m_State.ApplyNewRequest(rq);
218  }
219 
220 
221  //--------------------------------------------------------------------------------------------
222  // Movement
223 
224  protected bool MoveToNextPosCondition()
225  {
226  // Don't get any more closer
227  // Except we should still move closer if we haven't seen target for a long time
228  float optimalDist = ResolveOptimalDistance(m_fWeaponMinDist);
229  if (m_fTargetDist < optimalDist && m_Target.GetTimeSinceSeen() < 15)
230  return false;
231 
232  if (m_State.IsExecutingRequest())
233  return false;
234 
235  // If it's first run, ignore timers, only if:
236  // - If we are not in cover.
237  if (IsFirstExecution() && !m_State.m_bInCover)
238  return true;
239 
240  float stoppedWaitTime = ResolveStoppedWaitTime(m_State.m_bInCover, m_eThreatState, m_eWeaponType);
241  return m_State.m_fTimerStopped_s > stoppedWaitTime;
242  }
243 
244  protected void PushRequestMove()
245  {
247 
248  rq.m_eReason = SCR_EAICombatMoveReason.STANDARD;
249 
250  // Common values
251  rq.m_vTargetPos = ResolveRequestTargetPos();
252  ResolveMoveRequestMovePosAndDir(rq.m_vTargetPos, rq.m_vMovePos, rq.m_eDirection, rq.m_fCoverSearchSectorHalfAngleRad);
253  rq.m_bTryFindCover = true;
254  rq.m_bUseCoverSearchDirectivity = true;
255  rq.m_bCheckCoverVisibility = true;
256 
257  float coverSearchDistMin = 0;
258  float coverSearchDistMax = 30;
259  float moveDistanceMax = 30;
260  if (m_bCloseRangeCombat)
261  {
262  // Close range combat
263 
264  switch (m_eThreatState)
265  {
266  case EAIThreatState.THREATENED:
267  {
268  rq.m_eStanceMoving = ECharacterStance.CROUCH;
269  rq.m_eStanceEnd = ECharacterStance.CROUCH;
270  coverSearchDistMin = 2.0;
271  coverSearchDistMax = 10.0;
272  moveDistanceMax = 8.0;
273  break;
274  }
275  default:
276  {
277  rq.m_eStanceMoving = ECharacterStance.STAND;
278  rq.m_eStanceEnd = ECharacterStance.CROUCH;
279  coverSearchDistMin = 5.0;
280  coverSearchDistMax = 15.0;
281  moveDistanceMax = 10.0;
282  break;
283  }
284  }
285 
286  rq.m_bFailIfNoCover = false;
287  rq.m_eMovementType = EMovementType.RUN;
288  rq.m_bAimAtTarget = SCR_AICombatMoveUtils.IsAimingAndMovementPossible(rq.m_eStanceMoving, rq.m_eMovementType) &&
289  IsAimingAndMovingAllowedForWeapon(m_eWeaponType);
290  rq.m_bAimAtTargetEnd = true;
291  }
292  else
293  {
294  // Long range combat
295 
296  switch (m_eThreatState)
297  {
298  case EAIThreatState.THREATENED:
299  {
300  coverSearchDistMin = 2.0;
301  coverSearchDistMax = 20.0;
302  moveDistanceMax = 10.0;
303  rq.m_eStanceMoving = ECharacterStance.CROUCH;
304  rq.m_eStanceEnd = ECharacterStance.PRONE;
305  break;
306  }
307  default:
308  {
309  coverSearchDistMin = 10.0;
310  coverSearchDistMax = 30.0;
311  moveDistanceMax = 15.0; // Shouldn't be so large because we are sprinting and can't shoot
312  rq.m_eStanceMoving = ECharacterStance.CROUCH;
313  rq.m_eStanceEnd = ECharacterStance.CROUCH;
314  break;
315  }
316  }
317 
318  if (IsFirstExecution())
319  rq.m_bFailIfNoCover = true; // On first run we want to move to cover, or stay where we are if there is no cover, and shoot.
320  else
321  rq.m_bFailIfNoCover = m_State.m_bInCover; // Don't leave cover if there is no next cover
322 
323  rq.m_eMovementType = EMovementType.SPRINT;
324  rq.m_bAimAtTarget = false; // Can't aim at tgt while sprinting
325  rq.m_bAimAtTargetEnd = true;
326  }
327 
328  // If we are not in cover, min cover search distance is overridden to 0, we should find any cover ASAP
329  if (!m_State.m_bInCover)
330  coverSearchDistMin = 0;
331 
332  rq.m_fCoverSearchDistMin = coverSearchDistMin;
333  rq.m_fCoverSearchDistMax = coverSearchDistMax;
334  rq.m_fMoveDistance = Math.RandomFloat(0.5, 1.0) * moveDistanceMax; // Move distance if cover is not found, randomized
335 
336  // Subscribe to events
337  // We will pronounce voice lines once we start or end moving
338  rq.GetOnMovementStarted().Insert(OnMovementStarted);
339  rq.GetOnCompleted().Insert(OnMovementCompleted);
340 
341  m_State.ApplyNewRequest(rq);
342  }
343 
344  protected static void OnMovementStarted(SCR_AIUtilityComponent utility, SCR_AICombatMoveRequest_Move rq, vector pos, bool destinationIsCover)
345  {
346  if (!utility.m_CommsHandler.CanBypass())
347  {
348  SCR_AITalkRequest talkRq = new SCR_AITalkRequest(ECommunicationType.REPORT_MOVING, null, vector.Zero, 0, false, false, SCR_EAITalkRequestPreset.IRRELEVANT_IMMEDIATE);
349  utility.m_CommsHandler.AddRequest(talkRq);
350  }
351  }
352 
353  protected static void OnMovementCompleted(SCR_AIUtilityComponent utility, SCR_AICombatMoveRequestBase rq)
354  {
355  if (!utility.m_CommsHandler.CanBypass())
356  {
357  SCR_AITalkRequest talkRq = new SCR_AITalkRequest(ECommunicationType.REPORT_COVERING, null, vector.Zero, 0, false, false, SCR_EAITalkRequestPreset.IRRELEVANT_IMMEDIATE);
358  utility.m_CommsHandler.AddRequest(talkRq);
359  }
360  }
361 
362  // Resolves which move pos and dir. we should use for _MOVE_ request
363  // By now rq.m_vTargetPos must be already calculated!
364  protected void ResolveMoveRequestMovePosAndDir(vector targetPos, out vector outMovePos, out SCR_EAICombatMoveDirection outDirection, out float outCoverSearchSectorHalfAngleRad)
365  {
366  AIWaypoint wp = null;
367  AIAgent agent = m_Utility.GetAIAgent();
368  AIGroup group = agent.GetParentGroup();
369  if (group)
370  wp = group.GetCurrentWaypoint();
371 
372  vector movePos;
373  SCR_EAICombatMoveDirection eDirection;
374  float coverSearchSectorHalfAngleRad;
375 
376  if (!wp)
377  {
378  // No waypoint, standard move logic
379  movePos = targetPos;
380  eDirection = SCR_EAICombatMoveDirection.FORWARD;
381 
382  if (IsFirstExecution())
383  coverSearchSectorHalfAngleRad = Math.PI; // Full circle, on first run we just want any cover if possible
384  else
385  coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
386  }
387  else
388  {
389  vector wpPos = wp.GetOrigin();
390  float wpRadius = wp.GetCompletionRadius();
391  bool tgtInWaypoint = vector.DistanceXZ(wpPos, targetPos) < wpRadius;
392  float myDistToWp = vector.DistanceXZ(wpPos, m_MyEntity.GetOrigin());
393 
394  if (myDistToWp > wpRadius)
395  {
396  // We are outside WP, move towards center
397  movePos = wpPos;
398  eDirection = SCR_EAICombatMoveDirection.CUSTOM_POS;
399  coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
400  }
401  else if (myDistToWp > 0.5 * wpRadius)
402  {
403  // We are between 50% and 100% of wp radius
404 
405  if (tgtInWaypoint)
406  {
407  // Towards target
408  movePos = targetPos;
409  eDirection = SCR_EAICombatMoveDirection.FORWARD;
410  coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
411  }
412  else
413  {
414  // Move around current pos.
415  movePos = targetPos;
416  eDirection = SCR_EAICombatMoveDirection.ANYWHERE;
417  coverSearchSectorHalfAngleRad = -1.0;
418  }
419  }
420  else
421  {
422  // We are within 50% radius of wp,
423  // Move towards tgt, regardless where tgt is
424  movePos = targetPos;
425  eDirection = SCR_EAICombatMoveDirection.FORWARD;
426 
427  coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
428  }
429  }
430 
431  outMovePos = movePos;
432  outDirection = eDirection;
433  outCoverSearchSectorHalfAngleRad = coverSearchSectorHalfAngleRad;
434  }
435 
436  //--------------------------------------------------------------------------------------------
437  // Hide in cover when suppressed
438 
439  protected bool SuppressedInCoverCondition()
440  {
441  return m_State.m_bInCover && m_eThreatState == EAIThreatState.THREATENED;
442  }
443 
444  protected void SuppressedInCoverLogic()
445  {
446  // We're pinned in cover and can't do much else now
447  // Alternate hiding in cover and unhiding
448 
449  // How long to wait here? Depends on timer value from previous request.
450 
451  float waitTime_s;
452  SCR_AICombatMoveRequestBase rq = m_State.GetRequest();
453  if (SCR_AICombatMoveRequest_ChangeStanceInCover.Cast(rq) && rq.m_eReason == SCR_EAICombatMoveReason.SUPPRESSED_IN_COVER)
454  waitTime_s = rq.m_f_UserTimer_s;
455  else
456  {
457  if (m_State.m_bExposedInCover)
458  waitTime_s = 1.7;
459  else
460  waitTime_s = 3.0;
461  }
462 
463  if (m_State.m_bExposedInCover && m_State.m_fTimerRequest_s > waitTime_s)
464  {
465  float newWaitTime = Math.RandomFloat(3.5, 7.0); // Hide in cover for this time
466  PushRequestChangeStanceInCover(false, SCR_EAICombatMoveReason.SUPPRESSED_IN_COVER, newWaitTime);
467  }
468  else if (!m_State.m_bExposedInCover && m_State.m_fTimerRequest_s > waitTime_s)
469  {
470  float newWaitTime = Math.RandomFloat(1.7, 2.5); // Expose out of cover for this time
471  PushRequestChangeStanceInCover(true, SCR_EAICombatMoveReason.SUPPRESSED_IN_COVER, newWaitTime);
472  }
473  }
474 
475  protected void PushRequestChangeStanceInCover(bool exposed, SCR_EAICombatMoveReason reason, float waitTime)
476  {
478 
479  rq.m_bExposedInCover = exposed;
480  rq.m_bAimAtTarget = true;
481  rq.m_bAimAtTargetEnd = true;
482  rq.m_f_UserTimer_s = waitTime;
483  rq.m_eReason = reason;
484 
485  m_State.ApplyNewRequest(rq);
486  }
487 
488  //--------------------------------------------------------------------------------------------
489  // Cover not useful any more
490  // It means that it doesn't provide cover in direction of enemy
491 
492  protected bool CurrentCoverUselessCondition()
493  {
494  if (!m_State.m_bInCover || !m_State.IsAssignedCoverValid())
495  return false;
496 
497  vector tgtPos = m_Target.GetLastSeenPosition();
498 
499  float cosAngle = m_State.GetAssignedCover().CosAngleToThreat(tgtPos);
500  return (cosAngle < 0.5); // cos 60 deg = 0.5
501  }
502 
503  protected void PushRequestLeaveUselessCover()
504  {
506 
507  rq.m_eReason = SCR_EAICombatMoveReason.STANDARD;
508 
509  rq.m_vTargetPos = ResolveRequestTargetPos();
510  rq.m_vMovePos = rq.m_vTargetPos;
511  rq.m_bTryFindCover = true;
512  rq.m_bUseCoverSearchDirectivity = true;
513  rq.m_bCheckCoverVisibility = true;
514  rq.m_bFailIfNoCover = true;
515  rq.m_eStanceMoving = ECharacterStance.CROUCH;
516  rq.m_eStanceEnd = ECharacterStance.CROUCH;
517  rq.m_eMovementType = EMovementType.RUN;
518  rq.m_eDirection = SCR_EAICombatMoveDirection.BACKWARD; // Move back from target
519  rq.m_fCoverSearchSectorHalfAngleRad = -1.0;
520  rq.m_bAimAtTarget = SCR_AICombatMoveUtils.IsAimingAndMovementPossible(rq.m_eStanceMoving, rq.m_eMovementType);
521  rq.m_bAimAtTargetEnd = true;
522 
523  rq.m_fCoverSearchDistMin = 0;
524  rq.m_fCoverSearchDistMax = 20;
525 
526  m_State.ApplyNewRequest(rq);
527  }
528 
529 
530  //--------------------------------------------------------------------------------------------
531  // Moving away from target if we are too close
532  // This is meant for very stepping backwards a very short distance
533 
534  protected bool MoveFromTargetCondition()
535  {
536  float weaponMinDist = Math.Max(3.0, m_fWeaponMinDist);
537 
538  return m_fTargetDist < weaponMinDist;
539  }
540 
541  protected bool MoveFromTargetNewRequestCondition()
542  {
543  if (!m_State.IsExecutingRequest())
544  return true;
545 
546  // Still executing ...
547  // Send new request only if we are executing NOT side-step
549  if (!rq)
550  return true;
551 
552  return rq.m_eReason != SCR_EAICombatMoveReason.MOVE_FROM_TARGET;
553  }
554 
555  protected void PushRequestMoveFromTarget()
556  {
558 
559  rq.m_eReason = SCR_EAICombatMoveReason.MOVE_FROM_TARGET;
560 
561  rq.m_vMovePos = ResolveRequestTargetPos();
562  rq.m_eMovementType = EMovementType.RUN;
563  rq.m_eStanceMoving = ECharacterStance.STAND;
564  rq.m_eStanceEnd = ECharacterStance.STAND;
565  rq.m_eDirection = SCR_EAICombatMoveDirection.BACKWARD;
566  rq.m_fMoveDistance = 3.0;
567  rq.m_bAimAtTarget = SCR_AICombatMoveUtils.IsAimingAndMovementPossible(rq.m_eStanceMoving, rq.m_eMovementType);
568  rq.m_bAimAtTargetEnd = true;
569 
570  m_State.ApplyNewRequest(rq);
571  }
572 
573  //--------------------------------------------------------------------------------------------
574  protected vector ResolveRequestTargetPos()
575  {
576  if (m_CombatComp.IsTargetVisible(m_Target))
577  {
578  IEntity tgtEntity = m_Target.GetTargetEntity(); // We've checked above that it's not null
579 
580  ChimeraCharacter character = ChimeraCharacter.Cast(tgtEntity);
581  if (character)
582  {
583  vector eyePos = character.EyePosition();
584  return eyePos;
585  }
586 
587  // It's a vehicle
588  vector pos = tgtEntity.GetOrigin();
589  pos = pos + Vector(0, 2.0, 0);
590  return pos;
591  }
592 
593  // Target is not visible, use last seen position
594  vector lastSeenPos = m_Target.GetLastSeenPosition();
595  lastSeenPos = lastSeenPos + Vector(0, 1.8, 0);
596  return lastSeenPos;
597  }
598 
599  //--------------------------------------------------------------------------------------------
600  protected float ResolveStoppedWaitTime(bool inCover, EAIThreatState threat, EWeaponType weaponType)
601  {
602  float waitTime;
603 
604  if (inCover)
605  {
606  // In cover
607  switch (threat)
608  {
609  case EAIThreatState.THREATENED:
610  waitTime = 20.0; // Stay in cover for a long time, until we are not suppressed any more
611  break;
612  default:
613  waitTime = 5.0;
614  }
615  }
616  else
617  {
618  // Not in cover
619  switch (threat)
620  {
621  case EAIThreatState.THREATENED:
622  waitTime = 6.0;
623  break;
624  default:
625  waitTime = 3.0;
626  break;
627  }
628  }
629 
630  // When using those weapons we want to move much less
631  bool longWaitTime = false;
632  switch (weaponType)
633  {
634  case EWeaponType.WT_MACHINEGUN:
635  case EWeaponType.WT_ROCKETLAUNCHER:
636  case EWeaponType.WT_GRENADELAUNCHER:
637  case EWeaponType.WT_SNIPERRIFLE:
638  longWaitTime = true;
639  }
640 
641  if (longWaitTime)
642  waitTime *= 2.0;
643 
644  return waitTime;
645  }
646 
647  //--------------------------------------------------------------------------------------------
648  // Returns stance when stopped outside cover
649  protected static ECharacterStance ResolveStanceOutsideCover(bool closeRange, EAIThreatState threat)
650  {
651  if (closeRange)
652  {
653  // Close range combat
654  return ECharacterStance.CROUCH;
655  }
656  else
657  {
658  // Long range combat
659  switch (threat)
660  {
661  case EAIThreatState.THREATENED:
662  return ECharacterStance.PRONE;
663  default:
664  return ECharacterStance.CROUCH;
665  }
666  }
667  return ECharacterStance.STAND;
668  }
669 
670  //--------------------------------------------------------------------------------------------
671  // Returns 'optimal' distance
672  // If we are between weaponMinDist and 'optimal' dist, we don't need to move closer to tgt
673  protected static float ResolveOptimalDistance(float weaponMinDist)
674  {
675  return Math.Max(weaponMinDist + 5.0, 15.0);
676  }
677 
678  //--------------------------------------------------------------------------------------------
679  // Returns true if we are allowed to aim and move with that weapon
680  protected static bool IsAimingAndMovingAllowedForWeapon(EWeaponType weaponType)
681  {
682  switch (weaponType)
683  {
684  // Most common case is fast
685  case EWeaponType.WT_RIFLE:
686  return true;
687 
688  case EWeaponType.WT_ROCKETLAUNCHER:
689  case EWeaponType.WT_GRENADELAUNCHER:
690  return false;
691 
692  // Everything else
693  default:
694  return true;
695  }
696 
697  return true;
698  }
699 
700  //--------------------------------------------------------------------------------------------
701  // Returns true if it's first of combat movement logic. Doesn't mean first execution of this node.
702  protected bool IsFirstExecution()
703  {
704  return !m_State.GetRequest();
705  }
706 
707  //--------------------------------------------------------------------------------------------
708  override bool VisibleInPalette() { return true; }
709 
710  protected static ref TStringArray s_aVarsIn = {
711  PORT_BASE_TARGET
712  };
713  override TStringArray GetVariablesIn() { return s_aVarsIn; }
714 }
SCR_AICombatMoveUtils
Definition: SCR_AICombatMoveUtils.c:5
EMovementType
EMovementType
Definition: EMovementType.c:12
ECharacterStance
ECharacterStance
Definition: ECharacterStance.c:12
SCR_AICombatMoveRequestBase
Definition: SCR_AICombatMoveRequest.c:37
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
m_Target
class SCR_AIPolar m_Target
m_CharacterController
SCR_CharacterPerceivableComponentClass m_CharacterController
BaseTarget
Definition: BaseTarget.c:12
SCR_AICombatMoveLogic_Attack
Definition: SCR_AICombatMoveLogic_Attack.c:5
SCR_AICombatMoveRequest_ChangeStanceInCover
Definition: SCR_AICombatMoveRequest.c:123
m_eStance
SCR_AICombatMoveRequest_ChangeStanceInCover m_eStance
Attribute
typedef Attribute
Post-process effect of scripted camera.
SCR_AITalkRequest
void SCR_AITalkRequest(ECommunicationType type, IEntity entity, vector pos, int enumSignal, bool transmitIfNoReceivers, bool transmitIfPassenger, SCR_EAITalkRequestPreset preset)
Definition: SCR_AITalkRequest.c:37
ECommunicationType
ECommunicationType
Definition: SCR_AISoundHandling.c:1
m_eWeaponType
EWeaponType m_eWeaponType
Definition: SendOrder.c:143
SCR_AICombatMoveRequest_Move
Definition: SCR_AICombatMoveRequest.c:78
SCR_AICombatMoveRequest_Move
void SCR_AICombatMoveRequest_Move()
Definition: SCR_AICombatMoveRequest.c:95
m_State
private EEditableEntityState m_State
Definition: SCR_BaseEntitiesEditorUIEffect.c:3
EWeaponType
EWeaponType
Definition: EWeaponType.c:12
SCR_AIBehaviorBase
Definition: SCR_AIBehavior.c:1