Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_AIActivitySmokeCoverFeature.c
Go to the documentation of this file.
2 {
3  NONE = 0,
6 };
7 
8 // Class used for weighting of target clusters for smoke cover
10 {
11  SCR_AIGroupTargetCluster m_Cluster;
12  vector m_vClusterCenterPos;
13 
14  // More weight, less likely to be considered
15  [SortAttribute()]
16  float m_fWeight = 0;
17 
18  //------------------------------------------------------------------------------------------------
20  {
21  m_Cluster = cluster;
22  m_vClusterCenterPos = m_Cluster.m_State.GetCenterPosition();
23  m_fWeight = vector.DistanceSq(targetPos, m_vClusterCenterPos);
24 
25  // More targets in a cluster, bigger probability we'll consider it
26  m_fWeight -= m_Cluster.m_aTargets.Count() * 16;
27  }
28 
29  //------------------------------------------------------------------------------------------------
30  vector GetCenterPosition()
31  {
32  return m_vClusterCenterPos;
33  }
34 }
35 
36 // Class used for weighting of group agents for smoke cover
38 {
39  static const float CLOSE_DISTANCE_TRESHOLD_SQ = 11*11;
40  static const float MAX_CLOSE_DISTANCE_WEIGHT = 256;
41 
43 
44  // More weight, less likely to get picked
45  [SortAttribute()]
46  float m_fWeight = 0;
47 
48  //------------------------------------------------------------------------------------------------
49  float GetCloseTargetPenalty(float distanceToTargetSq) {
50  return ((-1 * MAX_CLOSE_DISTANCE_WEIGHT / (CLOSE_DISTANCE_TRESHOLD_SQ * CLOSE_DISTANCE_TRESHOLD_SQ)) * (distanceToTargetSq * distanceToTargetSq)) + MAX_CLOSE_DISTANCE_WEIGHT;
51  }
52 
53  //------------------------------------------------------------------------------------------------
54  void SCR_AIActivitySmokeCoverFeatureAgent(SCR_ChimeraAIAgent agent, float distanceToTargetSq, bool avoid)
55  {
56  m_Agent = agent;
57  m_fWeight = distanceToTargetSq;
58  SCR_AIInfoComponent infoComp = agent.m_InfoComponent;
59 
60  if (infoComp.HasRole(EUnitRole.MEDIC))
61  m_fWeight += 64;
62 
63  if (infoComp.HasRole(EUnitRole.MACHINEGUNNER))
64  m_fWeight += 256;
65 
66  if (infoComp.HasUnitState(EUnitState.WOUNDED))
67  m_fWeight += 256;
68 
69  if (avoid)
70  m_fWeight += 512;
71 
72  if (distanceToTargetSq < CLOSE_DISTANCE_TRESHOLD_SQ)
73  m_fWeight += GetCloseTargetPenalty(distanceToTargetSq);
74  }
75 
76  //------------------------------------------------------------------------------------------------
77  void SendMessage(AICommunicationComponent comms, SCR_AIActivityBase activity, vector position, float delay)
78  {
80  msg.m_RelatedGroupActivity = activity;
81  msg.m_fPriorityLevel = SCR_AIActionBase.PRIORITY_BEHAVIOR_THROW_GRENADE;
82  msg.SetReceiver(m_Agent);
83  comms.RequestBroadcast(msg, m_Agent);
84  }
85 }
86 
88 {
89  static const int MAX_DISTANCE_TO_TARGET_POS_SQ = 40*40;
90  static const int MAX_SMOKE_POSITION_COUNT = 3; // Max number of smoke grenades that can be thrown at one time
91  static const int SMOKE_WALL_GAPS_SIZE = 5; // Width in meters of gaps between smokes in smoke walls
92 
93  //-------------------------------------------------------------------------------------
94  protected bool IsAgentAvailable(SCR_ChimeraAIAgent agent)
95  {
96  SCR_AIInfoComponent infoComp = agent.m_InfoComponent;
97 
98  return agent && infoComp.GetAIState() == EUnitAIState.AVAILABLE &&
99  !infoComp.HasUnitState(EUnitState.IN_TURRET) && !infoComp.HasUnitState(EUnitState.IN_VEHICLE) &&
100  !infoComp.HasUnitState(EUnitState.UNCONSCIOUS);
101  }
102 
103  //-------------------------------------------------------------------------------------
104  protected void GetConsideredAgents(SCR_AIGroupUtilityComponent groupUtility, vector targetPosition, array<AIAgent> avoidAgents, array<AIAgent> excludeAgents,
105  notnull array<ref SCR_AIActivitySmokeCoverFeatureAgent> outConsideredAgents, out int combatReadyAgentsCount)
106  {
107  combatReadyAgentsCount = 0;
108 
109  foreach (SCR_AIInfoComponent infoComp: groupUtility.m_aInfoComponents)
110  {
111  SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(infoComp.GetOwner());
112  if (!agent || excludeAgents.Contains(agent))
113  continue;
114 
115  // Ignore agents that are not "available", considered combat ready
116  if (!IsAgentAvailable(agent))
117  continue;
118 
119  // Count how many agents in combat-ready condition group currently has
120  combatReadyAgentsCount++;
121 
122  IEntity controlledEntity = agent.GetControlledEntity();
123  // Don't consider agents that are not capable of throwing smoke grenades
124  if (!controlledEntity || !infoComp.HasRole(EUnitRole.HAS_SMOKE_GRENADE))
125  continue;
126 
127  // Don't consider agents that are too far or too close to target position
128  float distanceToTargetSq = vector.DistanceSq(targetPosition, controlledEntity.GetOrigin());
129  if (distanceToTargetSq > MAX_DISTANCE_TO_TARGET_POS_SQ)
130  continue;
131 
132  outConsideredAgents.Insert(new SCR_AIActivitySmokeCoverFeatureAgent(agent, distanceToTargetSq, avoidAgents.Contains(agent)));
133  }
134  }
135 
136  //-------------------------------------------------------------------------------------
137  protected void GetClusterBasedSmokePositions(array<ref SCR_AIGroupTargetCluster> targetClusters, vector targetPosition, int maxSmokePositions, out array<vector> smokePositions)
138  {
139  int clustersCount = targetClusters.Count();
140 
141  array<ref SCR_AIActivitySmokeCoverFeatureCluster> clusters = {};
142  foreach (SCR_AIGroupTargetCluster cluster: targetClusters)
143  clusters.Insert(new SCR_AIActivitySmokeCoverFeatureCluster(cluster, targetPosition));
144 
145  // More clusters than max smoke positions - sort clusters by weight
146  if (clustersCount > maxSmokePositions)
147  clusters.Sort(); // Sort clusters by weight, ascending
148 
149  float maxClustersCount = Math.Min(maxSmokePositions, clustersCount);
150 
151  vector lastDirection;
152 
153  // Gather positions to cover from clusters
154  for (int i = 0; i < maxClustersCount; i++)
155  {
156  SCR_AIActivitySmokeCoverFeatureCluster cluster = clusters[i];
157  vector clusterPos = cluster.GetCenterPosition();
158 
159  lastDirection = vector.Direction(targetPosition, clusterPos).Normalized();
160  smokePositions.Insert(targetPosition + (lastDirection * (5 + (maxClustersCount * 3))));
161  }
162 
163  int smokesLeft = maxSmokePositions - maxClustersCount;
164 
165  // Exit if no more smokes left to be thrown
166  if (smokesLeft <= 0)
167  return;
168 
169  // If only one cluster, create a wall oriented torwards the cluster
170  if (maxClustersCount == 1)
171  {
172  float distance = 0;
173 
174  vector pos = smokePositions[0];
175  vector relDir = (lastDirection * vector.Up).Normalized();
176 
177  for (int i = 0; i < smokesLeft; i++)
178  {
179  int pair = i % 2;
180  if (pair == 0)
181  distance += SMOKE_WALL_GAPS_SIZE;
182 
183  vector relVector = relDir * distance;
184 
185  if (pair == 0)
186  smokePositions.Insert(pos + relVector);
187  else
188  smokePositions.Insert(pos - relVector);
189  }
190 
191  return;
192  }
193 
194  // If more clusters, fill gaps between already picked smoke positions
195  int maxPositions = Math.Min(maxClustersCount / 2, smokesLeft);
196 
197  for (int i = 0; i < maxPositions; i++)
198  {
199  vector pos1 = smokePositions[i];
200  vector pos2 = smokePositions[i + 1];
201  vector direction = vector.Direction(pos1, pos2).Normalized();
202  float distance = vector.Distance(pos1, pos2);
203 
204  smokePositions.Insert(pos1 + (direction * (distance / 2)));
205  }
206  }
207 
208 
209  //-------------------------------------------------------------------------------------
210  bool Execute(
211  notnull SCR_AIGroupUtilityComponent groupUtility,
212  vector targetPosition,
213  SCR_AIActivitySmokeCoverFeatureProperties smokeCoverProperties,
214  notnull array<AIAgent> avoidAgents,
215  notnull array<AIAgent> excludeAgents,
216  SCR_AIActivityBase contextActivity
217  ) {
218  // Number of agents that are considered combat-ready. Will be used to calculate how many
219  // grenade throwers can be picked to not impare group's ability to continue fire fight
220  int combatReadyAgentsCount;
221  // Array of agents that will be considered as smoke grenade throwers
222  array<ref SCR_AIActivitySmokeCoverFeatureAgent> consideredAgents = {};
223 
224  GetConsideredAgents(groupUtility, targetPosition, excludeAgents, avoidAgents, consideredAgents, combatReadyAgentsCount);
225 
226  int consideredAgentsCount = consideredAgents.Count();
227 
228  // Early exit if no agents to consider for throwing
229  if (!consideredAgentsCount)
230  return false;
231 
232  // Get max count of smoke positions to cover
233  // Don't allow more than half of combat-ready agents to throw, other half must cover/fight
234  int maxSmokePositions = Math.Min(Math.Floor(combatReadyAgentsCount / 2), Math.Min(MAX_SMOKE_POSITION_COUNT, consideredAgentsCount));
235 
236  // Early exit if we can't smoke any position
237  if (maxSmokePositions <= 0)
238  return false;
239 
240  // Sort considered agents by weights if we have more agents than potential positions
241  if (consideredAgentsCount > maxSmokePositions)
242  consideredAgents.Sort(); // Sort considered agents by weight, ascending
243 
244  array<vector> smokePositions = {};
245 
246  // Smoke to protect position, we won't be throwing exactly at target position but to cover it
247  if (smokeCoverProperties & SCR_AIActivitySmokeCoverFeatureProperties.PROTECT_POS)
248  {
249  array<ref SCR_AIGroupTargetCluster> targetClusters = groupUtility.m_Perception.m_aTargetClusters;
250 
251  // Pick positions based on target clusters if enabled and available
252  if ((smokeCoverProperties & SCR_AIActivitySmokeCoverFeatureProperties.PROTECT_FROM_CLUSTERS) && targetClusters.Count() > 0)
253  GetClusterBasedSmokePositions(targetClusters, targetPosition, maxSmokePositions, smokePositions);
254  // If not, randomize positions around target position
255  else
256  {
257  float angle = Math.RandomFloat(0, 360);
258  for (int i = 0; i < maxSmokePositions; i++)
259  {
260  vector direction = {Math.Cos(angle * Math.DEG2RAD), 0, Math.Sin(angle * Math.DEG2RAD)};
261  // More smoke positions, greater the distance
262  float distance = 1 + maxSmokePositions + Math.RandomFloat(0, maxSmokePositions * 1.5);
263  smokePositions.Insert(targetPosition + (direction * distance));
264 
265  angle += (360 / maxSmokePositions);
266  if (angle > 360)
267  angle -= 360;
268  }
269  }
270  }
271  // Just simply smoke target position
272  else
273  smokePositions.Insert(targetPosition);
274 
275  AICommunicationComponent comms = groupUtility.m_Owner.GetCommunicationComponent();
276  if (!comms)
277  return false;
278 
279  int smokePositionsCount = smokePositions.Count();
280 
281  // Send throw grenade messages to all picked agents
282  for (int i = 0; i < smokePositionsCount; i++)
283  {
284  // Randomize small delay to prevent unnatural perfect sync between throws of multiple soldiers
285  float delay = 0.3 + Math.RandomFloat(0, 1.5);
286  consideredAgents[i].SendMessage(comms, contextActivity, smokePositions[i], delay);
287  }
288 
289  return true;
290  }
291 
292  //-------------------------------------------------------------------------------------
293  bool ExecuteForActivity(SCR_AIActivityBase activity)
294  {
295  SCR_AIGroupUtilityComponent groupUtility = activity.m_Utility;
296  if (!groupUtility)
297  return false;
298 
299  return Execute(
300  groupUtility,
301  GetActivityTargetPosition(activity),
302  GetActivityProperties(activity),
303  GetActivityAvoidedAgents(activity),
304  GetActivityExcludedAgents(activity),
305  activity
306  );
307  }
308 
309  //-------------------------------------------------------------------------------------
310  // Returns position that should be covered by smoke grenades for given activity
311  vector GetActivityTargetPosition(SCR_AIActivityBase activity)
312  {
313  return vector.Zero;
314  }
315 
316  //-------------------------------------------------------------------------------------
317  // Returns smoke cover properties for given activity
318  SCR_AIActivitySmokeCoverFeatureProperties GetActivityProperties(SCR_AIActivityBase activity)
319  {
321  }
322 
323  //-------------------------------------------------------------------------------------
324  // Returns list of agents that should be avoided during selection for smoke grenade throwing for given activity
325  // Those agents still can be picked, we'll just try what we can to avoid it
326  array<AIAgent> GetActivityAvoidedAgents(SCR_AIActivityBase activity)
327  {
328  return null;
329  }
330 
331  //-------------------------------------------------------------------------------------
332  // Returns list of agents that should be excluded from smoke grenade throwing for given activity
333  // Those agents won't be picked, no matter the circumstances
334  array<AIAgent> GetActivityExcludedAgents(SCR_AIActivityBase activity)
335  {
336  return null;
337  }
338 }
339 
340 class SCR_AIHealActivitySmokeCoverFeature: SCR_AIActivitySmokeCoverFeature
341 {
342  //-------------------------------------------------------------------------------------
344  {
345  SCR_AIHealActivity healActivity = SCR_AIHealActivity.Cast(activity);
346 
347  if (healActivity.m_EntityToHeal.m_Value)
348  return healActivity.m_EntityToHeal.m_Value.GetOrigin();
349 
350  return vector.Zero;
351  }
352 
353  //-------------------------------------------------------------------------------------
355  {
357  }
358 
359  //-------------------------------------------------------------------------------------
360  override array<AIAgent> GetActivityAvoidedAgents(SCR_AIActivityBase activity)
361  {
362  SCR_AIHealActivity healActivity = SCR_AIHealActivity.Cast(activity);
363 
364  ChimeraCharacter character = ChimeraCharacter.Cast(healActivity.m_MedicEntity.m_Value);
365  if (!character)
366  return null;
367 
368  CharacterControllerComponent charCtrl = character.GetCharacterController();
369  if (!charCtrl)
370  return null;
371 
372  AIControlComponent aiCtrl = charCtrl.GetAIControlComponent();
373  if (!aiCtrl)
374  return null;
375 
376  return {aiCtrl.GetAIAgent()};
377  }
378 
379  //-------------------------------------------------------------------------------------
380  override array<AIAgent> GetActivityExcludedAgents(SCR_AIActivityBase activity)
381  {
382  SCR_AIHealActivity healActivity = SCR_AIHealActivity.Cast(activity);
383 
384  ChimeraCharacter character = ChimeraCharacter.Cast(healActivity.m_EntityToHeal.m_Value);
385  if (!character)
386  return null;
387 
388  CharacterControllerComponent charCtrl = character.GetCharacterController();
389  if (!charCtrl)
390  return null;
391 
392  AIControlComponent aiCtrl = charCtrl.GetAIControlComponent();
393  if (!aiCtrl)
394  return null;
395 
396  return {aiCtrl.GetAIAgent()};
397  }
398 }
SCR_AIActivitySmokeCoverFeatureAgent
void SCR_AIActivitySmokeCoverFeatureAgent(SCR_ChimeraAIAgent agent, float distanceToTargetSq, bool avoid)
Definition: SCR_AIActivitySmokeCoverFeature.c:54
NONE
@ NONE
Gadget anim variable.
Definition: SCR_AIActivitySmokeCoverFeature.c:3
direction
vector direction
Definition: SCR_DestructibleTreeV2.c:31
SCR_AIActivitySmokeCoverFeature
Definition: SCR_AIActivitySmokeCoverFeature.c:87
m_Cluster
enum EAITargetClusterState m_Cluster
GetActivityAvoidedAgents
array< AIAgent > GetActivityAvoidedAgents(SCR_AIActivityBase activity)
Definition: SCR_AIActivitySmokeCoverFeature.c:315
GetActivityTargetPosition
SCR_AIActivitySmokeCoverFeature SCR_AIActivityFeatureBase GetActivityTargetPosition(SCR_AIActivityBase activity)
Definition: SCR_AIActivitySmokeCoverFeature.c:343
m_Agent
SCR_ChimeraAIAgent m_Agent
Definition: SCR_AIActivitySmokeCoverFeature.c:42
SCR_AIActionBase
Definition: SCR_AIAction.c:1
SCR_AIHealActivity
Definition: SCR_AIHealActivity.c:1
SCR_AIActivityBase
Definition: SCR_AIActivity.c:1
SCR_AIActivityFeatureBase
Definition: SCR_AIActivityFeatureBase.c:1
SCR_AIActivitySmokeCoverFeatureCluster
Definition: SCR_AIActivitySmokeCoverFeature.c:9
SCR_ChimeraAIAgent
Definition: SCR_ChimeraAIAgent.c:5
distance
float distance
Definition: SCR_DestructibleTreeV2.c:29
SCR_AIGroupTargetCluster
Definition: SCR_AIGroupTargetCluster.c:41
m_fWeight
float m_fWeight
Definition: SCR_AIActivitySmokeCoverFeature.c:46
PROTECT_POS
@ PROTECT_POS
Definition: SCR_AIActivitySmokeCoverFeature.c:4
SCR_AIActivitySmokeCoverFeatureProperties
SCR_AIActivitySmokeCoverFeatureProperties
Definition: SCR_AIActivitySmokeCoverFeature.c:1
SendMessage
void SendMessage(AICommunicationComponent comms, SCR_AIActivityBase activity, vector position, float delay)
Definition: SCR_AIActivitySmokeCoverFeature.c:77
GetCloseTargetPenalty
float GetCloseTargetPenalty(float distanceToTargetSq)
Definition: SCR_AIActivitySmokeCoverFeature.c:49
CLOSE_DISTANCE_TRESHOLD_SQ
SCR_AIActivitySmokeCoverFeatureCluster CLOSE_DISTANCE_TRESHOLD_SQ
EWeaponType
EWeaponType
Definition: EWeaponType.c:12
position
vector position
Definition: SCR_DestructibleTreeV2.c:30
GetActivityProperties
SCR_AIActivitySmokeCoverFeatureProperties GetActivityProperties(SCR_AIActivityBase activity)
Definition: SCR_AIActivitySmokeCoverFeature.c:307
GetActivityExcludedAgents
array< AIAgent > GetActivityExcludedAgents(SCR_AIActivityBase activity)
Definition: SCR_AIActivitySmokeCoverFeature.c:323
SCR_AIMessage_ThrowGrenadeTo
Definition: SCR_AIMessage.c:598
PROTECT_FROM_CLUSTERS
@ PROTECT_FROM_CLUSTERS
Definition: SCR_AIActivitySmokeCoverFeature.c:5