Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AIUpdateTargetAttackData.c
Go to the documentation of this file.
2{
3 // Inputs
4 protected static const string BASE_TARGET_PORT = "BaseTarget";
5 protected static const string WEAPON_IS_READY = "WeaponReady";
6
7 // Outputs
8 protected static const string PORT_LAST_SEEN_POSITION = "LastSeenPosition";
9 protected static const string PORT_THREAT_POSITION = "ThreatPosition";
10 protected static const string PORT_VISIBLE = "Visible";
11 protected static const string PORT_FIRE_TREE_ID = "FireTreeId";
12 protected static const string PORT_FIRE_RATE = "FireRate";
13 static const string PORT_AIMPOINT_TYPE_0 = "AimpointType0";
14 static const string PORT_AIMPOINT_TYPE_1 = "AimpointType1";
15
16
17 // These IDs must match to actual trees in attack tree
18 protected const int FIRE_TREE_INVALID = -1; // No aiming or firing is allowed at all
19 protected const int FIRE_TREE_LOOK = 0; // Looking at target without firing
20 protected const int FIRE_TREE_BURST = 1;
21 protected const int FIRE_TREE_SINGLE = 2;
22 protected const int FIRE_TREE_SUPPRESSIVE = 3;
23 protected const int FIRE_TREE_MELEE = 4;
24 protected const int FIRE_TREE_LOOK_THREATS = 5; // Looking at data from threat system
25
26 protected const float MELEE_MAX_DISTANCE = 2.0;
27 protected const float BURST_FIRE_MAX_DISTANCE = 50.0;
28
30 protected SCR_AICombatComponent m_CombatComponent;
31 protected CharacterControllerComponent m_CharacterController;
32 protected PerceptionComponent m_PerceptionComponent;
33 protected SCR_AIUtilityComponent m_UtilityComponent;
36
37 // Flag for executing some logic only once at start
38 protected bool m_bFirstSimulate = true;
39
40 protected bool m_bWeaponHasBurstOrAuto; // Cached on first run
41
42 // State of looking at threat system
43 protected bool m_bLookAtThreats;
46
47 //-----------------------------------------------------------------------------------------------------
48 override void OnInit(AIAgent owner)
49 {
50 m_UtilityComponent = SCR_AIUtilityComponent.Cast(owner.FindComponent(SCR_AIUtilityComponent));
51 m_Agent = SCR_ChimeraAIAgent.Cast(owner);
52
53 IEntity myEntity = owner.GetControlledEntity();
54 if (myEntity)
55 {
56 m_CombatComponent = SCR_AICombatComponent.Cast(myEntity.FindComponent(SCR_AICombatComponent));
57
58 m_PerceptionComponent = PerceptionComponent.Cast(myEntity.FindComponent(PerceptionComponent));
59 m_CharacterController = CharacterControllerComponent.Cast(myEntity.FindComponent(CharacterControllerComponent));
60 }
61
63 {
64 m_ThreatFilter = m_UtilityComponent.m_SectorThreatFilter;
65
66 m_ThreatFilter.GetOnEscalationInvoker().Remove(OnThreatSectorEscalation);
67 m_ThreatFilter.GetOnEscalationInvoker().Insert(OnThreatSectorEscalation);
68
69 m_ThreatFilter.GetOnDamageTaken().Remove(OnThreatSectorDamageTaken);
70 m_ThreatFilter.GetOnDamageTaken().Insert(OnThreatSectorDamageTaken);
71 }
72
73 m_bFirstSimulate = true;
74 }
75
76 //-----------------------------------------------------------------------------------------------------
78 {
80 {
81 m_ThreatFilter.GetOnEscalationInvoker().Remove(OnThreatSectorEscalation);
82 m_ThreatFilter.GetOnDamageTaken().Remove(OnThreatSectorDamageTaken);
83 }
84 }
85
86 //-----------------------------------------------------------------------------------------------------
87 override void OnAbort(AIAgent owner, Node nodeCausingAbort)
88 {
89 m_bFirstSimulate = true;
90 }
91
92 //-----------------------------------------------------------------------------------------------------
93 override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
94 {
95 BaseTarget target;
97 m_Target = target;
98
99 bool weaponReady;
100 GetVariableIn(WEAPON_IS_READY, weaponReady);
101
103 return ENodeResult.FAIL;
104
106 {
107 BaseWeaponComponent selectedWeaponComp;
108 int selectedMuzzleId;
109 m_CombatComponent.GetSelectedWeapon(selectedWeaponComp, selectedMuzzleId);
110 m_bWeaponHasBurstOrAuto = WeaponHasBurstOrAutoMode(selectedWeaponComp, selectedMuzzleId);
111 }
112
113 // Reset looking at threats if time is out
115 {
116 WorldTimestamp timeNow = GetGame().GetWorld().GetTimestamp();
117 if (timeNow.Greater(m_LookAtThreatsEndTime))
118 m_bLookAtThreats = false;
119 }
120
121 // Last seen pos
122 SetVariableOut(PORT_LAST_SEEN_POSITION, target.GetLastSeenPosition());
123
124 // Threat pos (only relevant if we're in threat observation state)
126
127 // Visible?
128 bool visible = m_CombatComponent.IsTargetVisible(target);
130
131 // Fire rate modifier (modified in ResolveFireTree)
132 float fireRate = 1;
133
134 // Which fire tree to use?
135 // This must be reevaluated periodically
136 int fireTreeId = ResolveFireTree(target, visible, weaponReady, fireRate);
139
140 // Which aimpoints to use?
141 // We run this only once, since it's not going to change much
143 {
144 EAimPointType aimpointType0;
145 EAimPointType aimpointType1;
146 ResolveAimpointTypes(target, aimpointType0, aimpointType1);
147 SetVariableOut(PORT_AIMPOINT_TYPE_0, aimpointType0);
148 SetVariableOut(PORT_AIMPOINT_TYPE_1, aimpointType1);
149 }
150
151 // Reset m_bFirstSimulate flag
152 m_bFirstSimulate = false;
153
154 return ENodeResult.SUCCESS;
155 }
156
157 //-----------------------------------------------------------------------------------------------------
158 // Evaluates which fire tree should be used
159 int ResolveFireTree(BaseTarget target, bool visible, bool weaponReady, out float fireRate)
160 {
161 // Is aiming forbidden by combat move?
162 SCR_AIBehaviorBase executedBehavior = SCR_AIBehaviorBase.Cast(m_UtilityComponent.GetExecutedAction());
163 if (executedBehavior && executedBehavior.m_bUseCombatMove && !m_UtilityComponent.m_CombatMoveState.m_bAimAtTarget)
164 return FIRE_TREE_INVALID;
165
166 // Is looking at threats activated?
169
170 // Is weapon not ready?
171 if (!weaponReady)
172 return FIRE_TREE_LOOK;
173
174 BaseWeaponComponent selectedWeaponComp;
175 int selectedMuzzleId;
176 m_CombatComponent.GetSelectedWeapon(selectedWeaponComp, selectedMuzzleId);
177
178 bool directDamage
179 float weaponMinDist, weaponMaxDist;
180 m_CombatComponent.GetSelectedWeaponProperties(weaponMinDist, weaponMaxDist, directDamage);
181
182 if (!selectedWeaponComp)
183 return FIRE_TREE_LOOK;
184
185 // Hold fire?
186 if (m_CombatComponent.GetCombatMode() == EAIGroupCombatMode.HOLD_FIRE)
187 return FIRE_TREE_LOOK;
188
189 EWeaponType weaponType = selectedWeaponComp.GetWeaponType();
190
191 float targetDistance = target.GetDistance();
192
193 // Use melee?
194 if (targetDistance < MELEE_MAX_DISTANCE &&
195 !m_CharacterController.CanFire() &&
196 m_CharacterController.GetStance() != ECharacterStance.PRONE)
197 {
198 return FIRE_TREE_MELEE;
199 }
200
201 // Friendly in aim?
202 if (m_PerceptionComponent.GetFriendlyInLineOfFire())
203 {
204 return FIRE_TREE_LOOK;
205 }
206
207
208 // Not melee
209
210 if (targetDistance < weaponMinDist || targetDistance > weaponMaxDist)
211 {
212 // Outside weapon usage range
213 // Look at target
214
215 return FIRE_TREE_LOOK;
216 }
217
218 // Within weapon usage range
219
220 if (visible)
221 {
222 // Visible
223 // If machinegun, always use burst at any range
224 // For regular weapons, use burst at short range if available, otherwise single
225 if (weaponType == EWeaponType.WT_MACHINEGUN)
226 return FIRE_TREE_BURST;
227 else if (targetDistance < BURST_FIRE_MAX_DISTANCE && m_bWeaponHasBurstOrAuto)
228 return FIRE_TREE_BURST;
229 else
230 return FIRE_TREE_SINGLE;
231
232 }
233 else
234 {
235 // Invisible
236 // Use suppressive fire or don't fire at all
237
238 // If weapon is configured to deal indirect damage, then use it for invisible target
239 // Otherwise regular weapons can also be used to fire at hidden target,
240 // except for rocket launchers, their ammo is too valuable
241 // Also ensure we don't do suppressive fire into a wall in front of us
242
243 float threat = m_UtilityComponent.m_ThreatSystem.GetThreatMeasure();
244 float lastSeenThreshold;
245 if (weaponType == EWeaponType.WT_MACHINEGUN)
246 lastSeenThreshold = SCR_AICombatComponent.TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK_MG;
247 else
248 {
249 if (targetDistance < SCR_AICombatComponent.CLOSE_RANGE_COMBAT_DISTANCE)
250 lastSeenThreshold = SCR_AICombatComponent.TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK_CLOSE;
251 else
252 lastSeenThreshold = SCR_AICombatComponent.TARGET_MAX_LAST_SEEN_INDIRECT_ATTACK;
253 }
254
255 lastSeenThreshold = Math.Max(SCR_AICombatComponent.TARGET_MIN_LAST_SEEN_INDIRECT_ATTACK, lastSeenThreshold * threat);
256
257 if ((!directDamage || weaponType != EWeaponType.WT_ROCKETLAUNCHER) &&
258 target.GetTimeSinceSeen() < lastSeenThreshold &&
259 target.GetTraceFraction() > 0.5)
260 {
261 float maxFireRate = Math.Max(1, Math.Map(targetDistance, 0, SCR_AICombatComponent.LONG_RANGE_COMBAT_DISTANCE, 2, 1));
262 fireRate = maxFireRate * threat;
263
265 }
266 else
267 return FIRE_TREE_LOOK;
268 }
269
270 return FIRE_TREE_LOOK;
271 }
272
273 //-----------------------------------------------------------------------------------------------------
274 static bool WeaponHasBurstOrAutoMode(notnull BaseWeaponComponent weaponComp, int muzzleId)
275 {
276 array<BaseMuzzleComponent> muzzles = {};
277 weaponComp.GetMuzzlesList(muzzles);
278
279 if (!muzzles.IsIndexValid(muzzleId))
280 return false;
281
282 BaseMuzzleComponent muzzle = muzzles[muzzleId];
283 if (!muzzle)
284 return false;
285
286 array<BaseFireMode> fireModes = {};
287 muzzle.GetFireModesList(fireModes);
288 foreach (BaseFireMode mode : fireModes)
289 {
290 EWeaponFiremodeType modeType = mode.GetFiremodeType();
291 if (modeType == EWeaponFiremodeType.Burst || modeType == EWeaponFiremodeType.Auto)
292 return true;
293 }
294 return false;
295 }
296
297 //-----------------------------------------------------------------------------------------------------
298 void ResolveAimpointTypes(notnull BaseTarget target, out EAimPointType aimpointType0, out EAimPointType aimpointType1)
299 {
300 IEntity targetEntity = target.GetTargetEntity();
301 if (!targetEntity)
302 {
303 aimpointType0 = -1;
304 aimpointType1 = -1;
305 return;
306 }
307
308 ChimeraCharacter character = ChimeraCharacter.Cast(targetEntity);
309 if (character)
310 {
311 // Characters
312
313 if (character.IsInVehicle())
314 {
315 // Aim at head
316 aimpointType0 = EAimPointType.WEAK;
317 aimpointType1 = EAimPointType.NORMAL;
318 return;
319 }
320
321 aimpointType0 = EAimPointType.NORMAL;
322 aimpointType1 = EAimPointType.WEAK;
323 return;
324 }
325 else
326 {
327 // Vehicles and the rest
328
329 EWeaponType weaponType = m_CombatComponent.GetSelectedWeaponType();
330
331 if (weaponType == EWeaponType.WT_ROCKETLAUNCHER)
332 {
333 // Rocket launcher, aim at weak point
334 aimpointType0 = EAimPointType.WEAK;
335 aimpointType1 = EAimPointType.NORMAL;
336 return;
337 }
338
339 aimpointType0 = EAimPointType.NORMAL;
340 aimpointType1 = EAimPointType.WEAK;
341 return;
342 }
343 }
344
345 //-----------------------------------------------------------------------------------------------------
346 // Handling of Look At Threats state
347 // If we are attacking a target and then something more horrible happens sideways, we look at it briefly.
348
349 //-----------------------------------------------------------------------------------------------------
350 protected void OnThreatSectorEscalation(SCR_AISectorThreatFilter ts, int sectorId, float dangerValue)
351 {
352 vector sectorPos = ts.GetSectorPos(sectorId);
353 m_vLookAtThreatsPos = sectorPos;
354
355 // Ignore if it's already in our view cone
356 if (PosSameDirectionAsTarget(sectorPos))
357 return;
358
360 float lookDuration = CalculateLookAtThreatDuration(m_Target, flags);
361 LookAtThreats(lookDuration);
362 }
363
364 //-----------------------------------------------------------------------------------------------------
366 {
367 vector sectorPos = ts.GetSectorPos(sectorId);
368 m_vLookAtThreatsPos = sectorPos;
369
370 // Ignore if it's already in our view cone
371 if (PosSameDirectionAsTarget(sectorPos))
372 return;
373
375 float lookDuration = CalculateLookAtThreatDuration(m_Target, flags);
376 LookAtThreats(lookDuration);
377 }
378
379 //-----------------------------------------------------------------------------------------------------
381 protected void LookAtThreats(float duration_s)
382 {
383 m_bLookAtThreats = true;
384 WorldTimestamp timeNow = GetGame().GetWorld().GetTimestamp();
385 m_LookAtThreatsEndTime = timeNow.PlusSeconds(2.0);
386
387 // Also allow aiming, even if it was forbidden temporary.
388 // Character might not be able to look around now, but this is very important to look elsewhere.
389 m_UtilityComponent.m_CombatMoveState.EnableAiming(true);
390 }
391
392 //-----------------------------------------------------------------------------------------------------
393 protected float CalculateLookAtThreatDuration(BaseTarget currentTarget, SCR_EAIThreatSectorFlags threatFlags)
394 {
395 // Time depends on how far our target is.
396 // The further the target, the more time we can spare to look at something else.
397 float tgtDist = currentTarget.GetDistance();
398 float baseDuration = -5.3 + 4*Math.Log10(tgtDist + 21.2);
399
400 if (threatFlags & SCR_EAIThreatSectorFlags.CAUSED_DAMAGE)
401 baseDuration *= 2.0;
402 else if (threatFlags & SCR_EAIThreatSectorFlags.DIRECTED_AT_ME)
403 baseDuration *= 1.5;
404
405 float duration = Math.RandomFloat(baseDuration, 1.5*baseDuration);
406
407 return duration;
408 }
409
410 //-----------------------------------------------------------------------------------------------------
411 protected static const float COS_VIEW_CONE = 0.707; // Cos 45, should be same as view cone as perception
412 protected bool PosSameDirectionAsTarget(vector threatPos)
413 {
414 IEntity controlledEntity = m_Agent.GetControlledEntity();
415
416 if (!controlledEntity || !m_Target)
417 return false;
418
419 vector myPos = controlledEntity.GetOrigin();
420 vector targetPos = m_Target.GetLastSeenPosition();
421
422 vector dirToTarget = (targetPos - myPos).Normalized();
423 vector dirToThreat = (threatPos - myPos).Normalized();
424 float cosAngle = vector.Dot(dirToTarget, dirToThreat);
425
426 return cosAngle > COS_VIEW_CONE;
427 }
428
429 //-----------------------------------------------------------------------------------------------------
430 protected ref TStringArray s_aVarsIn = {
433 };
434 override TStringArray GetVariablesIn() { return s_aVarsIn; }
435
446
447 static override bool VisibleInPalette() { return true; }
448
449 static override string GetOnHoverDescription() { return "Special node which is used in attack behavior"; };
450
451}
SCR_EAIThreatSectorFlags flags
ArmaReforgerScripted GetGame()
Definition game.c:1398
void SCR_AIBehaviorBase(SCR_AIUtilityComponent utility, SCR_AIActivityBase groupActivity)
SCR_EAIThreatSectorFlags
proto external Managed FindComponent(typename typeName)
proto external vector GetOrigin()
Definition Math.c:13
Definition Node.c:13
proto void SetVariableOut(string name, void val)
proto bool GetVariableIn(string name, out void val)
SCR_EAIThreatSectorFlags GetSectorFlags(int sectorId)
float CalculateLookAtThreatDuration(BaseTarget currentTarget, SCR_EAIThreatSectorFlags threatFlags)
int ResolveFireTree(BaseTarget target, bool visible, bool weaponReady, out float fireRate)
CharacterControllerComponent m_CharacterController
void OnThreatSectorEscalation(SCR_AISectorThreatFilter ts, int sectorId, float dangerValue)
void ResolveAimpointTypes(notnull BaseTarget target, out EAimPointType aimpointType0, out EAimPointType aimpointType1)
override void OnInit(AIAgent owner)
void LookAtThreats(float duration_s)
Activates state when we are looking at threats.
void OnThreatSectorDamageTaken(SCR_AISectorThreatFilter ts, int sectorId)
override void OnAbort(AIAgent owner, Node nodeCausingAbort)
static override string GetOnHoverDescription()
static bool WeaponHasBurstOrAutoMode(notnull BaseWeaponComponent weaponComp, int muzzleId)
override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
ENodeResult
Definition ENodeResult.c:13
EAimPointType
ECharacterStance
array< string > TStringArray
Definition Types.c:385
EWeaponType
Definition EWeaponType.c:13
EWeaponFiremodeType