Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_AIGetAimErrorOffset.c
Go to the documentation of this file.
2 {
3  NONE,
7 };
8 
9 class SCR_AIGetAimErrorOffset: AITaskScripted
10 {
11  static const string PORT_ERROR_OFFSET = "ErrorOffset";
12  static const string PORT_BASE_TARGET = "BaseTargetIn";
13  static const string PORT_AIM_POINT = "AimPoint";
14  static const string PORT_TOLERANCE = "AimingTolerance";
15  static const string PORT_AIMPOINT_TYPE_0 = "AimpointType0"; // Primary aimpoint type
16  static const string PORT_AIMPOINT_TYPE_1 = "AimpointType1"; // Secondary aimpoint type
17  static const float CLOSE_RANGE_THRESHOLD = 15.0;
18  static const float LONG_RANGE_THRESHOLD = 200.0;
19  static const float AIMING_ERROR_SCALE = 1.0; // TODO: game master and server option
20  static const float AIMING_ERROR_FACTOR_MIN = 0.4;
21  static const float AIMING_ERROR_CLOSE_RANGE_FACTOR_MIN = 0.05;
22  static const float AIMING_ERROR_FACTOR_MAX = 1.4;
23  static const float MAXIMAL_TOLERANCE = 10.0;
24  static const float MINIMAL_TOLERANCE = 0.003;
25 
26  protected SCR_AICombatComponent m_CombatComponent;
27  // private SCR_AIInfoComponent m_InfoComponent; temporary removed (adding threat effect later)
28 
29 #ifdef AI_DEBUG
30  protected ref array<ref Shape> m_aDebugShapes = {};
31 #endif
32  [Attribute("2", UIWidgets.ComboBox, "Hit zone selection", "", ParamEnumArray.FromEnum(EAimingPreference) )]
33  protected EAimingPreference m_eAimingPreference;
34 
35  [Attribute("0", UIWidgets.ComboBox, "AimPoint type for fixed option", "", ParamEnumArray.FromEnum(EAimPointType) )]
36  protected EAimPointType m_eAimPointType;
37 
38  [Attribute("0.03", UIWidgets.EditBox, "Default PrecisionXY")]
39  protected float m_fDefaultPrecisionXY;
40 
41  private int m_iTorsoCount = 0;
42 
43  //------------------------------------------------------------------------------------------------
44  override void OnInit(AIAgent owner)
45  {
46  IEntity ent = owner.GetControlledEntity();
47  if (ent)
48  m_CombatComponent = SCR_AICombatComponent.Cast(ent.FindComponent(SCR_AICombatComponent));
49  //m_InfoComponent = SCR_AIInfoComponent.Cast(owner.FindComponent(SCR_AIInfoComponent));
50  }
51 
52  //------------------------------------------------------------------------------------------------
53  protected static ref TStringArray s_aVarsOut = {
54  PORT_ERROR_OFFSET,
55  PORT_AIM_POINT,
56  PORT_TOLERANCE
57  };
58  override array<string> GetVariablesOut()
59  {
60  return s_aVarsOut;
61  }
62 
63  //------------------------------------------------------------------------------------------------
64  protected static ref TStringArray s_aVarsIn = {
65  PORT_BASE_TARGET,
66  PORT_AIMPOINT_TYPE_0,
67  PORT_AIMPOINT_TYPE_1
68  };
69  override array<string> GetVariablesIn()
70  {
71  return s_aVarsIn;
72  }
73 
74  //------------------------------------------------------------------------------------------------
75  override ENodeResult EOnTaskSimulate(AIAgent owner, float dt)
76  {
77  //if (!m_CombatComponent || !m_InfoComponent)
78  if (!m_CombatComponent)
79  return ENodeResult.FAIL;
80 
81  IEntity entity = owner.GetControlledEntity();
82  if (!entity)
83  return ENodeResult.FAIL;
84 
85 #ifdef AI_DEBUG
86  m_aDebugShapes.Clear();
87 
88 #endif
89 
90  BaseTarget target;
91  GetVariableIn(PORT_BASE_TARGET, target);
92 
93  // Bail if target is invalid
94  if (!target)
95  {
96  ClearPorts();
97  return ENodeResult.FAIL;
98  }
99 
100  // Bail if target is invalid
101  IEntity targetEntity = target.GetTargetEntity();
102  if (!targetEntity)
103  {
104  ClearPorts();
105  return ENodeResult.FAIL;
106  }
107 
108  // Resolve which aimpoint types to use
109  EAimPointType aimpointTypes[3];
110  EAimPointType aimpointType0, aimpointType1;
111  if (!GetVariableIn(PORT_AIMPOINT_TYPE_0, aimpointType0))
112  aimpointType0 = -1;
113  if (!GetVariableIn(PORT_AIMPOINT_TYPE_1, aimpointType1))
114  aimpointType1 = -1;
115  aimpointTypes[0] = aimpointType0;
116  aimpointTypes[1] = aimpointType1;
117  aimpointTypes[2] = m_eAimPointType;
118 
119  // Try to find aimpoint
120  AimPoint aimPoint = GetAimPoint(target, aimpointTypes);
121 
122  // Bail if aimpoint was not found
123  if (!aimPoint)
124  {
125  ClearPorts();
126  return ENodeResult.FAIL;
127  }
128 
129  EWeaponType weaponType = m_CombatComponent.GetCurrentWeaponType();
130 
131 #ifdef AI_DEBUG
132  if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_SHOW_TARGET_AIMPOINT))
133  m_aDebugShapes.Insert(Shape.CreateSphere(COLOR_YELLOW_A, ShapeFlags.NOZBUFFER | ShapeFlags.TRANSP, aimPoint.GetPosition(),aimPoint.GetDimension()));
134 #endif
135 
136  // Calculate angular bounds
137  vector offsetX, offsetY;
138  float angularSize, distance, tolerance;
139  GetTargetAngularBounds(entity, aimPoint, offsetX, offsetY, angularSize, distance);
140 
141  // Correct aim point size based on factors
142  float distanceFactor = GetDistanceFactor(distance);
143  float offsetWeaponFactor = GetOffsetWeaponTypeFactor(weaponType);
144 
145  EAISkill currentSkill = m_CombatComponent.GetAISkill();
146  offsetX = GetRandomFactor(currentSkill, 0) * offsetX * AIMING_ERROR_SCALE * distanceFactor * offsetWeaponFactor;
147  offsetY = GetRandomFactor(currentSkill, 0) * offsetY * AIMING_ERROR_SCALE * distanceFactor * offsetWeaponFactor;
148 
149  tolerance = GetTolerance(entity, targetEntity, angularSize, distance, weaponType);
150 
151  SetVariableOut(PORT_ERROR_OFFSET, offsetX + offsetY);
152  SetVariableOut(PORT_AIM_POINT, aimPoint);
153  SetVariableOut(PORT_TOLERANCE, tolerance);
154 
155 #ifdef WORKBENCH
156  // PrintFormat("Target size - used in tolerance: %1 target aimpointPosition: %2", distance * Math.Tan(tolerance * Math.DEG2RAD), aimPoint.GetPosition());
157 #endif
158 
159  return ENodeResult.SUCCESS;
160  }
161 
162  void ClearPorts()
163  {
164  ClearVariable(PORT_ERROR_OFFSET);
165  ClearVariable(PORT_AIM_POINT);
166  ClearVariable(PORT_TOLERANCE);
167  }
168 
169  //----------------------------------------------------------------------------------------------------------------------------------------------
171  void GetTargetAngularBounds(IEntity shooter, AimPoint targetAimpoint, out vector sizeVectorX, out vector sizeVectorY, out float angularSize, out float distance)
172  {
173 
174  vector shooterCenter, joinNorm, join, min, max;
175  float dimension = 2 * targetAimpoint.GetDimension();
176  shooter.GetWorldBounds(min,max);
177 
178  // we wanted to use muzzle transform but that is unrelyable (when weapon switches, on init of fight, and so on)
179  shooterCenter = (min + max) * 0.5;
180  join = targetAimpoint.GetPosition() - shooterCenter;
181  distance = join.Length();
182  angularSize = Math.Atan2(dimension, distance) * Math.RAD2DEG;
183 
184  joinNorm = join.Normalized();
185  sizeVectorX = (vector.Up * joinNorm).Normalized();
186  sizeVectorY = (joinNorm * sizeVectorX).Normalized();
187 
188  sizeVectorX *= dimension;
189  sizeVectorY *= dimension;
190 
191 #ifdef AI_DEBUG
192  if (DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_AI_SHOW_TARGET_PROJECTED_SIZE))
193  {
194  m_aDebugShapes.Insert(Shape.CreateArrow(shooterCenter, shooterCenter + sizeVectorX, 0.1, COLOR_BLUE, ShapeFlags.NOZBUFFER));
195  m_aDebugShapes.Insert(Shape.CreateArrow(shooterCenter, shooterCenter + sizeVectorY, 0.1, COLOR_RED, ShapeFlags.NOZBUFFER));
196  m_aDebugShapes.Insert(Shape.CreateArrow(shooterCenter, shooterCenter + joinNorm * dimension, 0.1, COLOR_YELLOW, ShapeFlags.NOZBUFFER));
197  }
198 #endif
199  }
200 
201  //----------------------------------------------------------------------------------------------------------------------------------------------
203  float GetDistanceFactor(float distance)
204  {
205  if (distance < CLOSE_RANGE_THRESHOLD)
206  return Math.Map(distance, 0, CLOSE_RANGE_THRESHOLD, AIMING_ERROR_CLOSE_RANGE_FACTOR_MIN, AIMING_ERROR_FACTOR_MIN);
207 
208  float distanceCl = Math.Clamp((distance - CLOSE_RANGE_THRESHOLD) / LONG_RANGE_THRESHOLD, 0, 1);
209  return Math.Lerp(AIMING_ERROR_FACTOR_MIN, AIMING_ERROR_FACTOR_MAX, distanceCl);
210  }
211 
212  //------------------------------------------------------------------------------------------------
213  // returns random factor based on AI skill
214  float GetRandomFactor(EAISkill skill,float mu)
215  {
216  float sigma;
217  switch (skill)
218  {
219  case EAISkill.ROOKIE :
220  {
221  sigma = 2;
222  break;
223  }
224  case EAISkill.REGULAR :
225  {
226  sigma = 1;
227  break;
228  }
229  case EAISkill.VETERAN :
230  {
231  sigma = 0.5;
232  break;
233  }
234  case EAISkill.EXPERT :
235  {
236  sigma = 0.25;
237  break;
238  }
239  case EAISkill.CYLON :
240  {
241  return 0;
242  }
243  }
244  // PrintFormat("Gauss: %1, sigma: %2, skill: %3",result,sigma,typename.EnumToString(EAISkill,skill));
245  return Math.RandomGaussFloat(sigma,mu);
246  }
247 
248  //------------------------------------------------------------------------------------------------
249  EAimPointType SelectAimPointType(EAimingPreference aimingPreference, EAimPointType currentSelection)
250  {
251  switch (aimingPreference)
252  {
253  case EAimingPreference.AUTOMATIC:
254  {
255  switch(currentSelection)
256  {
257  case EAimPointType.WEAK:
258  {
259  return EAimPointType.INCAPACITATE;
260  }
261  case EAimPointType.INCAPACITATE:
262  {
263  return EAimPointType.NORMAL;
264  }
265  case EAimPointType.NORMAL:
266  {
267  if (m_iTorsoCount < 2)
268  {
269  m_iTorsoCount += 1;
270  return EAimPointType.NORMAL;
271  }
272  else
273  {
274  m_iTorsoCount = 0;
275  return EAimPointType.WEAK;
276  }
277  }
278  };
279  break;
280  }
281  case EAimingPreference.RANDOM:
282  {
283  return Math.RandomInt(EAimPointType.NORMAL,EAimPointType.INCAPACITATE);
284  }
285  case EAimingPreference.FIXED:
286  {
287  return m_eAimPointType;
288  }
289  };
290  return currentSelection;
291  }
292 
293  //------------------------------------------------------------------------------------------------
294  // returns skill corrected by current threat level and if AI can shoot under such suppression
295  EAISkill GetSkillFromThreat(EAISkill inSkill, EAIThreatState threat)
296  {
297  switch (threat)
298  {
299  case EAIThreatState.THREATENED :
300  {
301  switch (inSkill)
302  {
303  case EAISkill.ROOKIE :
304  {
305  return EAISkill.ROOKIE;
306  }
307  case EAISkill.REGULAR :
308  {
309  return EAISkill.ROOKIE;
310  }
311  case EAISkill.VETERAN :
312  {
313  return EAISkill.REGULAR;
314  }
315  case EAISkill.EXPERT :
316  {
317  return EAISkill.VETERAN;
318  }
319  case EAISkill.CYLON :
320  {
321  return EAISkill.CYLON;
322  }
323  };
324  break;
325  }
326  case EAIThreatState.ALERTED :
327  {
328  switch (inSkill)
329  {
330  case EAISkill.ROOKIE :
331  {
332  return EAISkill.REGULAR;
333  }
334  case EAISkill.REGULAR :
335  {
336  return EAISkill.VETERAN;
337  }
338  case EAISkill.VETERAN :
339  {
340  return EAISkill.EXPERT;
341  }
342  case EAISkill.EXPERT :
343  {
344  return EAISkill.CYLON;
345  }
346  case EAISkill.CYLON :
347  {
348  return EAISkill.CYLON;
349  }
350  };
351  break;
352  }
353  default :
354  {
355  return inSkill;
356  break;
357  }
358  }
359  return EAISkill.NONE;
360  }
361 
362  //------------------------------------------------------------------------------------------------
364  float GetTolerance(IEntity observer, IEntity target, float angularSize, float distance, EWeaponType weaponType)
365  {
366  float tolerance;
367  bool setMaxTolerance;
368 
369  // Always use max tolerance in close range
370  if (distance < CLOSE_RANGE_THRESHOLD)
371  return MAXIMAL_TOLERANCE;
372 
373  tolerance = angularSize / 2; // half of the size
374  // angular speed
375  tolerance *= GetAngularSpeedFactor(observer, target, setMaxTolerance);
376 
377  if (setMaxTolerance)
378  tolerance = MAXIMAL_TOLERANCE;
379  else
380  {
381  // weapon type tolerance modifier
382  tolerance *= GetWeaponTypeFactor(weaponType);
383  };
384  return Math.Clamp(tolerance, MINIMAL_TOLERANCE, MAXIMAL_TOLERANCE);
385  }
386 
387  //------------------------------------------------------------------------------------------------
388  float GetAngularSpeedFactor(IEntity observer, IEntity enemy, out bool setBigTolerance)
389  {
390  IEntity parent = enemy.GetParent(); // getting the vehicle for character inside vehicle
391  if (parent)
392  {
393  enemy = parent; // case of driver
394  parent = enemy.GetParent();
395  if (parent) // case of turret
396  enemy = parent;
397  }
398  Physics ph = enemy.GetPhysics();
399  if (ph)
400  {
401  vector positionVector = enemy.GetOrigin() - observer.GetOrigin();
402  vector angularVelocity = positionVector * ph.GetVelocity() / positionVector.LengthSq(); // omega = (r x v) / ||r||^2
403  float angularSpeed = angularVelocity.Length();
404 
405  if (angularSpeed < 0.07) // rougly 4 degs in radians
406  return 1.0;
407  else if (angularSpeed < 0.17) // roughly 10 degs in radians
408  return 2;
409  }
410  setBigTolerance = true;
411  return 0;
412  }
413 
414  //------------------------------------------------------------------------------------------------
415  float GetSuppressionFactor(EAIThreatState threat, out bool setBigTolerance)
416  {
417  switch (threat)
418  {
419  case EAIThreatState.THREATENED :
420  {
421  setBigTolerance = true;
422  return 0;
423  break;
424  }
425  case EAIThreatState.ALERTED :
426  {
427  return 2.0;
428  break;
429  }
430  default :
431  {
432  return 1.0;
433  break;
434  }
435  }
436  return 0;
437  }
438 
439  //------------------------------------------------------------------------------------------------
440  float GetWeaponTypeFactor(EWeaponType weaponType)
441  {
442  switch(weaponType)
443  {
444  case EWeaponType.WT_RIFLE:
445  {
446  return 1.0;
447  }
448  case EWeaponType.WT_MACHINEGUN:
449  {
450  return 2.0;
451  }
452  case EWeaponType.WT_HANDGUN:
453  {
454  return 1.5;
455  }
456  case EWeaponType.WT_FRAGGRENADE:
457  {
458  return 3.0;
459  }
460  case EWeaponType.WT_SMOKEGRENADE:
461  {
462  return 4.0;
463  }
464  case EWeaponType.WT_ROCKETLAUNCHER:
465  {
466  return 0.5;
467  }
468  case EWeaponType.WT_SNIPERRIFLE:
469  {
470  return 0.5;
471  }
472  }
473  return 1.0;
474  }
475 
476  //------------------------------------------------------------------------------------------------
478  float GetOffsetWeaponTypeFactor(EWeaponType weaponType)
479  {
480  if (weaponType == EWeaponType.WT_ROCKETLAUNCHER)
481  return 0;
482  else
483  return 1.0;
484  }
485 
486  //------------------------------------------------------------------------------------------------
487  AimPoint GetAimPoint(BaseTarget target, EAimPointType aimpointTypes[3])
488  {
489  PerceivableComponent targetPerceivable = target.GetPerceivableComponent();
490  if (!targetPerceivable)
491  return null;
492 
493  array<ref AimPoint> aimPoints = {};
494 
495  // Try to find an aimpoint
496  for (int i = 0; i < 3; i++)
497  {
498  EAimPointType aimpointType = aimpointTypes[i];
499  if (aimpointType == -1)
500  continue;
501 
502  targetPerceivable.GetAimpointsOfType(aimPoints, aimpointType);
503  if (!aimPoints.IsEmpty())
504  break;
505  }
506 
507  int index = aimPoints.GetRandomIndex(); // more aimpoints of same type -> pick one at random
508 
509  if (index != -1)
510  return aimPoints[index];
511  else
512  return null;
513  }
514 
515  //------------------------------------------------------------------------------------------------
516  override bool VisibleInPalette() {return true;}
517 
518  //------------------------------------------------------------------------------------------------
519  protected override string GetOnHoverDescription() {return "Get aiming error position from the center of the target";}
520 };
521 
NONE
@ NONE
Definition: SCR_AIGetAimErrorOffset.c:3
RANDOM
@ RANDOM
Definition: SCR_AIGetAimErrorOffset.c:4
FIXED
@ FIXED
Definition: SCR_AIGetAimErrorOffset.c:6
s_aVarsOut
SCR_AIPickupInventoryItemsBehavior s_aVarsOut
Definition: SCR_AIGetCombatMoveRequestParameters.c:149
BaseTarget
Definition: BaseTarget.c:12
Attribute
typedef Attribute
Post-process effect of scripted camera.
distance
float distance
Definition: SCR_DestructibleTreeV2.c:29
m_CombatComponent
SCR_AICombatComponent m_CombatComponent
Definition: SCR_AIUtilityComponent.c:12
SCR_AIGetAimErrorOffset
Definition: SCR_AIGetAimErrorOffset.c:9
index
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
Definition: SCR_DestructionSynchronizationComponent.c:17
AimPoint
Definition: AimPoint.c:12
m_aDebugShapes
protected ref array< ref Shape > m_aDebugShapes
Definition: SCR_PowerlineGeneratorEntity.c:64
EAimingPreference
EAimingPreference
Definition: SCR_AIGetAimErrorOffset.c:1
EWeaponType
EWeaponType
Definition: EWeaponType.c:12
AUTOMATIC
@ AUTOMATIC
Definition: SCR_AIGetAimErrorOffset.c:5
SCR_DebugMenuID
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition: DebugMenuID.c:3
EAimPointType
EAimPointType
Definition: EAimPointType.c:12