8 protected static const string PORT_BASE_TARGET =
"BaseTarget";
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;
17 protected EAIThreatState m_eThreatState;
21 protected float m_fTargetDist;
22 protected float m_fWeaponMinDist;
23 protected bool m_bCloseRangeCombat;
25 protected float m_fNextUpdate_ms;
27 protected float m_fUpdateInterval_ms;
30 protected const float COVER_QUERY_SECTOR_ANGLE_RAD = 0.3 * Math.PI;
33 protected override void OnInit(AIAgent owner)
35 m_Utility = SCR_AIUtilityComponent.Cast(owner.FindComponent(SCR_AIUtilityComponent));
37 m_State = m_Utility.m_CombatMoveState;
39 m_MyEntity = owner.GetControlledEntity();
43 m_CharacterController = CharacterControllerComponent.Cast(m_MyEntity.FindComponent(CharacterControllerComponent));
44 m_CombatComp = SCR_AICombatComponent.Cast(m_MyEntity.FindComponent(SCR_AICombatComponent));
48 protected override ENodeResult EOnTaskSimulate(AIAgent owner,
float dt)
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;
56 GetVariableIn(PORT_BASE_TARGET, target);
58 return ENodeResult.FAIL;
62 if (executedBehavior && !executedBehavior.m_bUseCombatMove)
63 return ENodeResult.RUNNING;
67 m_fTargetDist = target.GetDistance();
69 m_eThreatState = m_Utility.m_ThreatSystem.GetState();
71 m_fWeaponMinDist = m_CombatComp.GetSelectedWeaponMinDist();
95 if (SuppressedInCoverCondition())
97 SuppressedInCoverLogic();
99 else if (MoveFromTargetCondition())
103 if (MoveFromTargetNewRequestCondition())
104 PushRequestMoveFromTarget();
106 else if (CurrentCoverUselessCondition())
110 PushRequestLeaveUselessCover();
117 m_State.ApplyRequestChangeStanceInCover(
false);
122 m_State.ApplyRequestChangeStanceInCover(
true);
124 else if (FFAvoidanceCondition())
126 if (FFAvoidanceNewRequestCondition())
127 PushRequestFFAvoidance();
129 else if (MoveToNextPosCondition())
137 ECharacterStance newStance = ResolveStanceOutsideCover(m_bCloseRangeCombat, m_eThreatState);
141 m_State.ApplyRequestChangeStanceOutsideCover(newStance);
145 return ENodeResult.RUNNING;
153 protected bool FFAvoidanceCondition()
170 return m_Utility.m_CombatComponent.IsFriendlyInAim();
174 protected bool FFAvoidanceNewRequestCondition()
176 if (!
m_State.IsExecutingRequest())
185 return rq.m_eReason != SCR_EAICombatMoveReason.FF_AVOIDANCE;
188 protected void PushRequestFFAvoidance()
192 rq.m_eReason = SCR_EAICombatMoveReason.FF_AVOIDANCE;
197 if (prevRequest && prevRequest.m_eReason == SCR_EAICombatMoveReason.FF_AVOIDANCE)
199 rq.m_eDirection = prevRequest.m_eDirection;
203 if (Math.RandomIntInclusive(0, 1) == 1)
204 rq.m_eDirection = SCR_EAICombatMoveDirection.RIGHT;
206 rq.m_eDirection = SCR_EAICombatMoveDirection.LEFT;
210 rq.m_eStanceEnd = rq.m_eStanceMoving;
211 rq.m_vMovePos = ResolveRequestTargetPos();
213 rq.m_fMoveDistance = 1.5;
214 rq.m_bAimAtTarget =
SCR_AICombatMoveUtils.IsAimingAndMovementPossible(rq.m_eStanceMoving, rq.m_eMovementType);
215 rq.m_bAimAtTargetEnd =
true;
224 protected bool MoveToNextPosCondition()
228 float optimalDist = ResolveOptimalDistance(m_fWeaponMinDist);
229 if (m_fTargetDist < optimalDist &&
m_Target.GetTimeSinceSeen() < 15)
232 if (
m_State.IsExecutingRequest())
237 if (IsFirstExecution() && !
m_State.m_bInCover)
240 float stoppedWaitTime = ResolveStoppedWaitTime(
m_State.m_bInCover, m_eThreatState,
m_eWeaponType);
241 return m_State.m_fTimerStopped_s > stoppedWaitTime;
244 protected void PushRequestMove()
248 rq.m_eReason = SCR_EAICombatMoveReason.STANDARD;
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;
257 float coverSearchDistMin = 0;
258 float coverSearchDistMax = 30;
259 float moveDistanceMax = 30;
260 if (m_bCloseRangeCombat)
264 switch (m_eThreatState)
266 case EAIThreatState.THREATENED:
270 coverSearchDistMin = 2.0;
271 coverSearchDistMax = 10.0;
272 moveDistanceMax = 8.0;
279 coverSearchDistMin = 5.0;
280 coverSearchDistMax = 15.0;
281 moveDistanceMax = 10.0;
286 rq.m_bFailIfNoCover =
false;
288 rq.m_bAimAtTarget =
SCR_AICombatMoveUtils.IsAimingAndMovementPossible(rq.m_eStanceMoving, rq.m_eMovementType) &&
290 rq.m_bAimAtTargetEnd =
true;
296 switch (m_eThreatState)
298 case EAIThreatState.THREATENED:
300 coverSearchDistMin = 2.0;
301 coverSearchDistMax = 20.0;
302 moveDistanceMax = 10.0;
309 coverSearchDistMin = 10.0;
310 coverSearchDistMax = 30.0;
311 moveDistanceMax = 15.0;
318 if (IsFirstExecution())
319 rq.m_bFailIfNoCover =
true;
321 rq.m_bFailIfNoCover =
m_State.m_bInCover;
324 rq.m_bAimAtTarget =
false;
325 rq.m_bAimAtTargetEnd =
true;
330 coverSearchDistMin = 0;
332 rq.m_fCoverSearchDistMin = coverSearchDistMin;
333 rq.m_fCoverSearchDistMax = coverSearchDistMax;
334 rq.m_fMoveDistance = Math.RandomFloat(0.5, 1.0) * moveDistanceMax;
338 rq.GetOnMovementStarted().Insert(OnMovementStarted);
339 rq.GetOnCompleted().Insert(OnMovementCompleted);
344 protected static void OnMovementStarted(SCR_AIUtilityComponent utility,
SCR_AICombatMoveRequest_Move rq, vector pos,
bool destinationIsCover)
346 if (!utility.m_CommsHandler.CanBypass())
349 utility.m_CommsHandler.AddRequest(talkRq);
355 if (!utility.m_CommsHandler.CanBypass())
358 utility.m_CommsHandler.AddRequest(talkRq);
364 protected void ResolveMoveRequestMovePosAndDir(vector targetPos, out vector outMovePos, out SCR_EAICombatMoveDirection outDirection, out
float outCoverSearchSectorHalfAngleRad)
366 AIWaypoint wp =
null;
367 AIAgent agent = m_Utility.GetAIAgent();
368 AIGroup group = agent.GetParentGroup();
370 wp = group.GetCurrentWaypoint();
373 SCR_EAICombatMoveDirection eDirection;
374 float coverSearchSectorHalfAngleRad;
380 eDirection = SCR_EAICombatMoveDirection.FORWARD;
382 if (IsFirstExecution())
383 coverSearchSectorHalfAngleRad = Math.PI;
385 coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
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());
394 if (myDistToWp > wpRadius)
398 eDirection = SCR_EAICombatMoveDirection.CUSTOM_POS;
399 coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
401 else if (myDistToWp > 0.5 * wpRadius)
409 eDirection = SCR_EAICombatMoveDirection.FORWARD;
410 coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
416 eDirection = SCR_EAICombatMoveDirection.ANYWHERE;
417 coverSearchSectorHalfAngleRad = -1.0;
425 eDirection = SCR_EAICombatMoveDirection.FORWARD;
427 coverSearchSectorHalfAngleRad = COVER_QUERY_SECTOR_ANGLE_RAD;
431 outMovePos = movePos;
432 outDirection = eDirection;
433 outCoverSearchSectorHalfAngleRad = coverSearchSectorHalfAngleRad;
439 protected bool SuppressedInCoverCondition()
441 return m_State.m_bInCover && m_eThreatState == EAIThreatState.THREATENED;
444 protected void SuppressedInCoverLogic()
454 waitTime_s = rq.m_f_UserTimer_s;
463 if (
m_State.m_bExposedInCover &&
m_State.m_fTimerRequest_s > waitTime_s)
465 float newWaitTime = Math.RandomFloat(3.5, 7.0);
466 PushRequestChangeStanceInCover(
false, SCR_EAICombatMoveReason.SUPPRESSED_IN_COVER, newWaitTime);
468 else if (!
m_State.m_bExposedInCover &&
m_State.m_fTimerRequest_s > waitTime_s)
470 float newWaitTime = Math.RandomFloat(1.7, 2.5);
471 PushRequestChangeStanceInCover(
true, SCR_EAICombatMoveReason.SUPPRESSED_IN_COVER, newWaitTime);
475 protected void PushRequestChangeStanceInCover(
bool exposed, SCR_EAICombatMoveReason reason,
float waitTime)
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;
492 protected bool CurrentCoverUselessCondition()
497 vector tgtPos =
m_Target.GetLastSeenPosition();
499 float cosAngle =
m_State.GetAssignedCover().CosAngleToThreat(tgtPos);
500 return (cosAngle < 0.5);
503 protected void PushRequestLeaveUselessCover()
507 rq.m_eReason = SCR_EAICombatMoveReason.STANDARD;
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;
518 rq.m_eDirection = SCR_EAICombatMoveDirection.BACKWARD;
519 rq.m_fCoverSearchSectorHalfAngleRad = -1.0;
520 rq.m_bAimAtTarget =
SCR_AICombatMoveUtils.IsAimingAndMovementPossible(rq.m_eStanceMoving, rq.m_eMovementType);
521 rq.m_bAimAtTargetEnd =
true;
523 rq.m_fCoverSearchDistMin = 0;
524 rq.m_fCoverSearchDistMax = 20;
534 protected bool MoveFromTargetCondition()
536 float weaponMinDist = Math.Max(3.0, m_fWeaponMinDist);
538 return m_fTargetDist < weaponMinDist;
541 protected bool MoveFromTargetNewRequestCondition()
543 if (!
m_State.IsExecutingRequest())
552 return rq.m_eReason != SCR_EAICombatMoveReason.MOVE_FROM_TARGET;
555 protected void PushRequestMoveFromTarget()
559 rq.m_eReason = SCR_EAICombatMoveReason.MOVE_FROM_TARGET;
561 rq.m_vMovePos = ResolveRequestTargetPos();
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;
574 protected vector ResolveRequestTargetPos()
576 if (m_CombatComp.IsTargetVisible(
m_Target))
578 IEntity tgtEntity =
m_Target.GetTargetEntity();
580 ChimeraCharacter character = ChimeraCharacter.Cast(tgtEntity);
583 vector eyePos = character.EyePosition();
588 vector pos = tgtEntity.GetOrigin();
589 pos = pos + Vector(0, 2.0, 0);
594 vector lastSeenPos =
m_Target.GetLastSeenPosition();
595 lastSeenPos = lastSeenPos + Vector(0, 1.8, 0);
600 protected float ResolveStoppedWaitTime(
bool inCover, EAIThreatState threat,
EWeaponType weaponType)
609 case EAIThreatState.THREATENED:
621 case EAIThreatState.THREATENED:
631 bool longWaitTime =
false;
649 protected static ECharacterStance ResolveStanceOutsideCover(
bool closeRange, EAIThreatState threat)
661 case EAIThreatState.THREATENED:
673 protected static float ResolveOptimalDistance(
float weaponMinDist)
675 return Math.Max(weaponMinDist + 5.0, 15.0);
680 protected static bool IsAimingAndMovingAllowedForWeapon(
EWeaponType weaponType)
702 protected bool IsFirstExecution()
708 override bool VisibleInPalette() {
return true; }
710 protected static ref TStringArray s_aVarsIn = {
713 override TStringArray GetVariablesIn() {
return s_aVarsIn; }