Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AICombatMoveLogic_MovingCommander.c
Go to the documentation of this file.
1/*
2This issues turret operator requests to move to driver that is performing the move itself.
3*/
4
7{
8 protected SCR_AIUtilityComponent m_Utility;
9 protected SCR_AICombatComponent m_CombatComp;
11 protected BaseWeaponManagerComponent m_WeaponManagerComponent;
15
16 protected SCR_AICombatMoveState m_DriverState;
17 protected SCR_AIUtilityComponent m_DriverUtility;
18
19 [Attribute("500", UIWidgets.EditBox, "Update interval of the node")]
20 protected float m_fUpdateInterval_ms;
21 protected float m_fNextUpdate_ms;
22
23 //--------------------------------------------------------------------------------------------
24 protected override void OnInit(AIAgent owner)
25 {
26 m_Utility = SCR_AIUtilityComponent.Cast(owner.FindComponent(SCR_AIUtilityComponent));
27 m_MyEntity = owner.GetControlledEntity();
28
30 m_CombatComp = SCR_AICombatComponent.Cast(m_MyEntity.FindComponent(SCR_AICombatComponent));
31
33 {
34 IEntity turretEnt = m_CompartmentAccessComponent.GetCompartment().GetOwner();
35 if (turretEnt)
36 {
38 if (contr)
39 m_TurretComponent = contr.GetTurretComponent();
40 m_WeaponManagerComponent = BaseWeaponManagerComponent.Cast(turretEnt.FindComponent(BaseWeaponManagerComponent));
41 }
43 }
44 }
45
46 //--------------------------------------------------------------------------------------------
49 {
50 rq.m_eUnitType = SCR_EAICombatMoveUnitType.GROUND_VEHICLE;
51 m_DriverState.ApplyNewRequest(rq);
52 }
53
54 //--------------------------------------------------------------------------------------------
55 protected override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
56 {
57 // Timer logic
58 float currentTime_ms = GetGame().GetWorld().GetWorldTime();
59 if (currentTime_ms < m_fNextUpdate_ms)
60 return ENodeResult.RUNNING;
61 m_fNextUpdate_ms = currentTime_ms + m_fUpdateInterval_ms;
62
63 // Update variables for driver, turret, ...
64 if (!UpdateDriver(owner))
65 return ENodeResult.FAIL;
66
67 // Verify variables
69 return ENodeResult.FAIL;
70
71 // Is driver awake?
72 if (m_DriverUtility.m_AIInfo.HasUnitState(EUnitState.UNCONSCIOUS))
73 return ENodeResult.FAIL;
74
75 // Can driver execute our requests?
76 SCR_AIBehaviorBase executedBehavior = SCR_AIBehaviorBase.Cast(m_DriverUtility.GetExecutedAction());
77 if (executedBehavior && !executedBehavior.m_bUseCombatMove)
78 return ENodeResult.FAIL;
79
80 // Run child class logic
81 bool result = UpdateCombatMoveLogic();
82
83 if (result)
84 return ENodeResult.RUNNING;
85 else
86 return ENodeResult.FAIL;
87 }
88
89 //--------------------------------------------------------------------------------------------
90 protected bool UpdateDriver(AIAgent owner)
91 {
92 if (!m_MyVehicle)
93 return false;
94
95 IEntity driverEntity = m_MyVehicle.GetPilot();
96 if (!driverEntity)
97 return false;
98
99 AIControlComponent driverControlComp = AIControlComponent.Cast(driverEntity.FindComponent(AIControlComponent));
100
101 if (!driverControlComp)
102 return false;
103
104 AIAgent driverAgent = driverControlComp.GetAIAgent();
105
106 if (!driverAgent)
107 return false;
108
109 m_DriverUtility = SCR_AIUtilityComponent.Cast(driverAgent.FindComponent(SCR_AIUtilityComponent));
110
111 if (!m_DriverUtility)
112 return false;
113
114 m_DriverState = m_DriverUtility.m_CombatMoveState;
115
116 return true;
117 }
118
119 //--------------------------------------------------------------------------------------------
122
123 //--------------------------------------------------------------------------------------------
124 // Returns true if it's first of combat movement logic. Doesn't mean first execution of this node.
125 protected bool IsFirstExecution()
126 {
127 return !m_DriverState.GetRequest();
128 }
129
130 //--------------------------------------------------------------------------------------------
135 {
136 // Check turret horizontal limits
137 vector turretLimitsHoriz_Rad, turretLimitsVert_Rad;
138 m_TurretComponent.GetAimingLimits(turretLimitsHoriz_Rad, turretLimitsVert_Rad); // Initially this returns in degrees, we convert it later
139 if (m_TurretComponent.HasMoveableBase())
140 turretLimitsHoriz_Rad = Vector(-180, 180, 0);
141 turretLimitsHoriz_Rad = Math.DEG2RAD * turretLimitsHoriz_Rad;
142 turretLimitsVert_Rad = Math.DEG2RAD * turretLimitsVert_Rad;
143
144 vector targetPosLocal = m_TurretComponent.GetOwner().CoordToLocal(targetPos); // Target pos in turret space
145 vector dirToTargetLocalXZ = targetPosLocal; // Dir to target in turret space, projected on XZ plane.
146 dirToTargetLocalXZ[1] = 0;
147 dirToTargetLocalXZ.Normalize();
148 float angleDirToTarget_Rad = Math.Atan2(dirToTargetLocalXZ[0], 1.0); // Angle of direction to target, in turret space
149
150 // We want to start rotating car sooner than target reaches limits of the turret, that's why we shrink the horizontal limits.
151 vector safeTurretLimitsHoriz_Rad = 0.5 * turretLimitsHoriz_Rad;
152 return (angleDirToTarget_Rad > safeTurretLimitsHoriz_Rad[0]) && (angleDirToTarget_Rad < safeTurretLimitsHoriz_Rad[1]);
153 }
154
155 //--------------------------------------------------------------------------------------------
156 static override bool VisibleInPalette() { return true; }
157}
158
160class SCR_AICombatMoveLogicVehicleGunner_Attack : SCR_AICombatMoveLogicVehicleGunnerBase
161{
162 // Inputs
163 protected static const string PORT_BASE_TARGET = "BaseTarget";
164
166
167 protected const float WEAPON_MIN_DIST = 2.0;
168
169 // minimal distance Driver should be from enemy
170 protected const float MIN_ENGAGEMENT_DISTANCE_TO_TARGET_SQ = 60.0 * 60.0; // when too close to target, try to move backwards
171 protected const float MAX_MOVE_DURATION_TO_TARGET_S = 9; // max step to move towards target
172 protected const float MAX_MOVE_DURATION_TO_TARGET_THREATENED_S = 5; // move less under threat
173 protected const float REVERSE_MOVE_DURATION_S = 1.2; // step to move from target. This must be consistent with reverse distance in car movement component, so that car moves backwards.
174
175 // Values updated on each update, to avoid passing them through calls
176 protected EAIThreatState m_eThreatState;
177 protected float m_fTargetDist;
179
180 //------------------------------------------------------------------------------------
181 override bool UpdateCombatMoveLogic()
182 {
183 // Read target data
185 if (!m_Target || !m_Target.GetTargetEntity())
186 return false;
187
188 // Update cached variables
190 m_eThreatState = m_Utility.m_ThreatSystem.GetState();
191 m_fWeaponMinDist = 2.0;
192
193 // Conditions and states of combat movement
195 {
196 // Target out of reach for turret or too close
197 // Step backwards
200 }
201 else if (FFAvoidanceCondition())
202 {
205 }
206 else if (MoveToNextPosCondition())
207 {
208 // We've waited here too long, move to next place
210 }
211 else if (!m_DriverState.IsExecutingRequest())
212 {
213 // TODO: We are stopped keep distance, scan the perimeter
214 }
215
216 return true;
217 }
218
219 //--------------------------------------------------------------------------------------------
221 protected bool MoveFromTargetCondition()
222 {
223 if (!m_WeaponManagerComponent)
224 return false;
225
226 vector mat[4];
227 m_WeaponManagerComponent.GetCurrentMuzzleTransform(mat);
228 vector muzzlePos = mat[3];
229 vector muzzleDir = mat[2].Normalized();
230 vector targetPos = m_Target.GetLastSeenPosition();
231 vector targetDir = (targetPos - muzzlePos).Normalized();
232
233 // Target is too close in front -> we move backward
234 if (vector.DistanceSq(targetPos, muzzlePos) < MIN_ENGAGEMENT_DISTANCE_TO_TARGET_SQ)
235 return true;
236
237 // Target is behind vehicle -> we move backward
238 //vector targetPosVehicleSpaceLocal = m_MyVehicle.CoordToLocal(targetPos); // Target pos in vehicle's space
239 //if (targetPosVehicleSpaceLocal[2] < 0)
240 // return true;
241
242 // Check turret horizontal limits
244 return true;
245
246 return false;
247 }
248
249 //--------------------------------------------------------------------------------------------
251 {
252 if (!m_DriverState.IsExecutingRequest())
253 return true;
254
255 // Still executing ...
256 // Send new request only if we are executing NOT move_from_target
257 SCR_AICombatMoveRequest_Move rq = SCR_AICombatMoveRequest_Move.Cast(m_DriverState.GetRequest());
258 if (!rq)
259 return true;
260
261 return rq.m_eReason != SCR_EAICombatMoveReason.MOVE_FROM_TARGET;
262 }
263
264 //--------------------------------------------------------------------------------------------
266 {
268
269 rq.m_eReason = SCR_EAICombatMoveReason.MOVE_FROM_TARGET;
270
271 rq.m_vMovePos = ResolveRequestTargetPos();
272 rq.m_eDirection = SCR_EAICombatMoveDirection.BACKWARD;
273 rq.m_fMoveDuration_s = REVERSE_MOVE_DURATION_S;
274 rq.m_bAimAtTarget = false;
275 rq.m_bAimAtTargetEnd = false;
276
277 ApplyNewRequest(rq);
278 }
279
280 //--------------------------------------------------------------------------------------------
281 // Movement
282
283 protected void PushRequestMove()
284 {
286
287 rq.m_eReason = SCR_EAICombatMoveReason.STANDARD;
288
289 // Common values
290 rq.m_vTargetPos = ResolveRequestTargetPos();
291 ResolveMoveRequestMovePosAndDir(rq.m_vTargetPos, rq.m_vMovePos, rq.m_eDirection);
292 rq.m_bTryFindCover = false;
293 rq.m_bUseCoverSearchDirectivity = false;
294 rq.m_bCheckCoverVisibility = false;
295
296
297 float moveDurationMax = MAX_MOVE_DURATION_TO_TARGET_S;
298
299 // Long range combat
300
301 switch (m_eThreatState)
302 {
303 case EAIThreatState.THREATENED:
304 {
306 break;
307 }
308 default:
309 {
310 moveDurationMax = MAX_MOVE_DURATION_TO_TARGET_S;
311 break;
312 }
313 }
314
315 rq.m_bAimAtTarget = false;
316 rq.m_bAimAtTargetEnd = false; // turn towards the target should be true!
317 rq.m_bFailIfNoCover = false;
318
319 rq.m_fMoveDuration_s = Math.RandomFloat(0.5, 1.0) * moveDurationMax; // Move distance randomized
320
321 // Subscribe to events
322 // We will pronounce voice lines once we start or end moving
323 rq.GetOnMovementStarted().Insert(OnMovementStarted);
324 rq.GetOnCompleted().Insert(OnMovementCompleted);
325
326 ApplyNewRequest(rq);
327 }
328
329 //--------------------------------------------------------------------------------------------
330 // Resolves which move pos and dir. we should use for _MOVE_ request
331 // By now rq.m_vTargetPos must be already calculated!
332 protected void ResolveMoveRequestMovePosAndDir(vector targetPos, out vector outMovePos, out SCR_EAICombatMoveDirection outDirection)
333 {
334 AIWaypoint wp = null;
335 AIAgent agent = m_DriverUtility.GetAIAgent();
336 AIGroup group = agent.GetParentGroup();
337 if (group)
338 wp = group.GetCurrentWaypoint();
339
340 vector movePos;
341 SCR_EAICombatMoveDirection eDirection;
342
343 if (!wp)
344 {
345 // No waypoint, standard move logic
346 eDirection = SCR_EAICombatMoveDirection.FORWARD;
347 movePos = targetPos;
348 }
349 else
350 {
351 vector wpPos = wp.GetOrigin();
352 float wpRadius = wp.GetCompletionRadius();
353 bool tgtInWaypoint = vector.DistanceXZ(wpPos, targetPos) < wpRadius;
354 float myDistToWp = vector.DistanceXZ(wpPos, m_MyEntity.GetOrigin());
355
356 if (myDistToWp > wpRadius)
357 {
358 // We are outside WP, move towards center
359 movePos = wpPos;
360 eDirection = SCR_EAICombatMoveDirection.CUSTOM_POS;
361 }
362 else if (myDistToWp > 0.5 * wpRadius)
363 {
364 // We are between 50% and 100% of wp radius
365
366 if (tgtInWaypoint)
367 {
368 // Towards target
369 movePos = targetPos;
370 eDirection = SCR_EAICombatMoveDirection.FORWARD;
371 }
372 else
373 {
374 // Move around current pos.
375 movePos = targetPos;
376 eDirection = SCR_EAICombatMoveDirection.ANYWHERE;
377 }
378 }
379 else
380 {
381 // We are within 50% radius of wp,
382 // Move towards tgt, regardless where tgt is
383 movePos = targetPos;
384 eDirection = SCR_EAICombatMoveDirection.FORWARD;
385 }
386 }
387
388 outMovePos = movePos;
389 outDirection = eDirection;
390 }
391
392 //--------------------------------------------------------------------------------------------
393 // Friendly fire avoidance
394
395 // Friendly fire avoidance condition
396 protected bool FFAvoidanceCondition()
397 {
398 return m_Utility.m_CombatComponent.IsFriendlyInAim();
399 }
400
401 //--------------------------------------------------------------------------------------------
403 {
404 if (!m_DriverState.IsExecutingRequest())
405 return true;
406
407 // Still executing ...
408 // Send new request only if we are executing NOT side-step
409 SCR_AICombatMoveRequest_Move rq = SCR_AICombatMoveRequest_Move.Cast(m_DriverState.GetRequest());
410 if (!rq)
411 return true;
412
413 return rq.m_eReason != SCR_EAICombatMoveReason.FF_AVOIDANCE;
414 }
415
416 //--------------------------------------------------------------------------------------------
417 protected void PushRequestFFAvoidance()
418 {
420
421 rq.m_eReason = SCR_EAICombatMoveReason.FF_AVOIDANCE;
422
423 // If prev. request was FF avoidance too, keep direction.
424 // Otherwise choose a new direction.
425 SCR_AICombatMoveRequest_Move prevRequest = SCR_AICombatMoveRequest_Move.Cast(m_DriverState.GetRequest());
426 if (prevRequest && prevRequest.m_eReason == SCR_EAICombatMoveReason.FF_AVOIDANCE)
427 {
428 rq.m_eDirection = prevRequest.m_eDirection;
429 }
430 else
431 {
432 if (Math.RandomIntInclusive(0, 1) == 1)
433 rq.m_eDirection = SCR_EAICombatMoveDirection.RIGHT;
434 else
435 rq.m_eDirection = SCR_EAICombatMoveDirection.LEFT;
436 }
437
438 rq.m_vMovePos = ResolveRequestTargetPos();
439 rq.m_fMoveDuration_s = REVERSE_MOVE_DURATION_S;
440 rq.m_bAimAtTarget = false;
441 rq.m_bAimAtTargetEnd = false;
442
443 ApplyNewRequest(rq);
444 }
445
446 //--------------------------------------------------------------------------------------------
447 protected float GetTargetDistance()
448 {
449 return m_Target.GetDistance();
450 }
451
452 //--------------------------------------------------------------------------------------------
454 {
455 IEntity tgtEntity = m_Target.GetTargetEntity();
456 if (tgtEntity && m_CombatComp.IsTargetVisible(m_Target))
457 {
458 ChimeraCharacter character = ChimeraCharacter.Cast(tgtEntity);
459 if (character)
460 {
461 vector eyePos = character.EyePosition();
462 return eyePos;
463 }
464
465 // It's a vehicle
466 vector pos = tgtEntity.GetOrigin();
467 pos = pos + Vector(0, 2.0, 0);
468 return pos;
469 }
470
471 // Target is either not visible or tgtEntity is null, use last seen position
472 vector lastSeenPos = m_Target.GetLastSeenPosition();
473 lastSeenPos = lastSeenPos + Vector(0, 1.8, 0);
474 return lastSeenPos;
475 }
476
477 //--------------------------------------------------------------------------------------------
478 protected float ResolveStoppedWaitTime(EAIThreatState threat)
479 {
480 float waitTime;
481
482 // based on threat we wait longer
483 switch (threat)
484 {
485 case EAIThreatState.THREATENED:
486 waitTime = 12.0;
487 break;
488 default:
489 waitTime = 6.0;
490 break;
491 }
492
493 return waitTime;
494 }
495
496 //--------------------------------------------------------------------------------------------
497 protected bool MoveToNextPosCondition()
498 {
499 // Don't get any more closer
500 // Except we should still move closer if we haven't seen target for a long time
501 float optimalDist = ResolveOptimalDistance(m_fWeaponMinDist);
502 if (m_fTargetDist < optimalDist && m_Target.GetTimeSinceSeen() < 15)
503 return false;
504
505 if (m_DriverState.IsExecutingRequest())
506 return false;
507
508 // If it's first run, ignore timers
509 // TODO: add effect of explosion from threat system, i.e. if BOOM -> move away asap
510 if (IsFirstExecution())
511 return true;
512
513 float stoppedWaitTime = ResolveStoppedWaitTime(m_eThreatState);
514 return m_DriverState.m_fTimerStopped_s > stoppedWaitTime;
515 }
516
517 //--------------------------------------------------------------------------------------------
518 // Returns 'optimal' distance
519 // If we are between weaponMinDist and 'optimal' dist, we don't need to move closer to tgt
520 protected static float ResolveOptimalDistance(float weaponMinDist)
521 {
522 return Math.Max(weaponMinDist + 5.0, 200);
523 }
524
525 //--------------------------------------------------------------------------------------------
527 protected static void OnMovementStarted(SCR_AIUtilityComponent utility, SCR_AICombatMoveRequest_Move rq, vector pos, bool destinationIsCover)
528 {
529 if (!utility.m_CommsHandler.CanBypass())
530 {
531 SCR_AITalkRequest talkRq = new SCR_AITalkRequest(ECommunicationType.REPORT_MOVING, null, vector.Zero, 0, false, false, SCR_EAITalkRequestPreset.IRRELEVANT_IMMEDIATE);
532 utility.m_CommsHandler.AddRequest(talkRq);
533 }
534 }
535
536 protected static void OnMovementCompleted(SCR_AIUtilityComponent utility, SCR_AICombatMoveRequestBase rq)
537 {
538 if (!utility.m_CommsHandler.CanBypass())
539 {
540 SCR_AITalkRequest talkRq = new SCR_AITalkRequest(ECommunicationType.REPORT_COVERING, null, vector.Zero, 0, false, false, SCR_EAITalkRequestPreset.IRRELEVANT_IMMEDIATE);
541 utility.m_CommsHandler.AddRequest(talkRq);
542 }
543 }
544
545 //--------------------------------------------------------------------------------------------
546 protected static ref TStringArray s_aVarsIn = {
548 };
549 override TStringArray GetVariablesIn() { return s_aVarsIn; }
550}
551
552// Combat move logic for Suppressive Fire behavior of gunner. It receives Suppression Volume.
554{
555 protected static const string PORT_SUPPRESSION_VOLUME = "SuppressionVolume";
556
558
559 //-------------------------------------------------------------------------------------------
560 override bool UpdateCombatMoveLogic()
561 {
564 return false;
565
566 // Logic for suppressive fire is very simple. We only want to rotate the car until we can aim the turret at target.
568 {
570 {
572 }
573 }
574
575 return true;
576 }
577
578 //-------------------------------------------------------------------------------------------
580 {
581 vector targetPos = m_SuppressionVolume.GetCenterPosition();
583 return true;
584
585 return false;
586 }
587
588 //-------------------------------------------------------------------------------------------
590 {
591 return !m_DriverState.IsExecutingRequest();
592 }
593
594 //-------------------------------------------------------------------------------------------
596 {
598
599 rq.m_eReason = SCR_EAICombatMoveReason.STANDARD;
600
601 rq.m_vMovePos = m_SuppressionVolume.GetCenterPosition();
602 rq.m_eDirection = SCR_EAICombatMoveDirection.FORWARD;
603 rq.m_fMoveDuration_s = 3;
604 rq.m_bAimAtTarget = false;
605 rq.m_bAimAtTargetEnd = false;
606
607 ApplyNewRequest(rq);
608 }
609
610 //-------------------------------------------------------------------------------------------
611 protected static ref TStringArray s_aVarsIn = { PORT_SUPPRESSION_VOLUME };
612 override TStringArray GetVariablesIn() { return s_aVarsIn; }
613}
ArmaReforgerScripted GetGame()
Definition game.c:1398
enum SCR_EAIActivityCause m_Utility
void SCR_AIBehaviorBase(SCR_AIUtilityComponent utility, SCR_AIActivityBase groupActivity)
SCR_AICombatMoveLogicBase PORT_BASE_TARGET
Combat movement node for attack behavior, which is aimed at BaseTarget.
const float REVERSE_MOVE_DURATION_S
float ResolveStoppedWaitTime(EAIThreatState threat)
const float MAX_MOVE_DURATION_TO_TARGET_THREATENED_S
override TStringArray GetVariablesIn()
void ResolveMoveRequestMovePosAndDir(vector targetPos, out vector outMovePos, out SCR_EAICombatMoveDirection outDirection)
const float MIN_ENGAGEMENT_DISTANCE_TO_TARGET_SQ
bool MoveFromTargetNewRequestCondition()
bool MoveFromTargetCondition()
decides if we should move backwards from target
EAIThreatState m_eThreatState
const float MAX_MOVE_DURATION_TO_TARGET_S
void SCR_AICombatMoveRequest_Move()
class SCR_AIPolar m_Target
ECommunicationType
ref TStringArray s_aVarsIn
void SCR_AITalkRequest(ECommunicationType type, IEntity entity, vector pos, int enumSignal, bool transmitIfNoReceivers, bool transmitIfPassenger, SCR_EAITalkRequestPreset preset)
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
Definition Math.c:13
proto bool GetVariableIn(string name, out void val)
Base class for combat movement logic executed by turret operator of vehicle.
bool UpdateCombatMoveLogic()
Logic of the child class.
override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
void ApplyNewRequest(notnull SCR_AICombatMoveRequestBase rq)
Applies combat move request to driver's mind.
ScriptInvokerBase< SCR_AICombatMoveRequest_Move_MovementEvent > GetOnMovementStarted(bool createInvoker=true)
enum EPhysicsLayerPresets Vehicle
Definition gameLib.c:24
ENodeResult
Definition ENodeResult.c:13
SCR_FieldOfViewSettings Attribute
array< string > TStringArray
Definition Types.c:385
proto native vector Vector(float x, float y, float z)