Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AIActivitySmokeCoverFeature.c
Go to the documentation of this file.
7
8// Class used for weighting of target clusters for smoke cover
9class SCR_AIActivitySmokeCoverFeatureCluster : Managed
10{
12 vector m_vClusterCenterPos;
13
14 // More weight, less likely to be considered
16 float m_fWeight = 0;
17
18 //------------------------------------------------------------------------------------------------
19 void SCR_AIActivitySmokeCoverFeatureCluster(SCR_AIGroupTargetCluster cluster, vector targetPos)
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 //------------------------------------------------------------------------------------------------
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
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 SMOKE_WALL_GAPS_SIZE = 5; // Width in meters of gaps between smokes in smoke walls
91
92 //-------------------------------------------------------------------------------------
93 protected void GetConsideredAgents(SCR_AIGroupUtilityComponent groupUtility, vector targetPosition, array<AIAgent> avoidAgents, array<AIAgent> excludeAgents,
94 notnull array<ref SCR_AIActivitySmokeCoverFeatureAgent> outConsideredAgents, out int combatReadyAgentsCount)
95 {
96 combatReadyAgentsCount = 0;
97
98 foreach (SCR_AIInfoComponent infoComp: groupUtility.m_aInfoComponents)
99 {
100 SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(infoComp.GetOwner());
101 if (!agent || excludeAgents.Contains(agent))
102 continue;
103
104 // Ignore agents that are not "available", considered combat ready
105 if (!IsAgentAvailable(agent))
106 continue;
107
108 // Count how many agents in combat-ready condition group currently has
109 combatReadyAgentsCount++;
110
111 IEntity controlledEntity = agent.GetControlledEntity();
112 // Don't consider agents that are not capable of throwing smoke grenades
113 if (!controlledEntity || !infoComp.HasRole(EUnitRole.HAS_SMOKE_GRENADE))
114 continue;
115
116 // Don't consider agents that are too far or too close to target position
117 float distanceToTargetSq = vector.DistanceSq(targetPosition, controlledEntity.GetOrigin());
118 if (distanceToTargetSq > MAX_DISTANCE_TO_TARGET_POS_SQ)
119 continue;
120
121 outConsideredAgents.Insert(new SCR_AIActivitySmokeCoverFeatureAgent(agent, distanceToTargetSq, avoidAgents.Contains(agent)));
122 }
123 }
124
125 //-------------------------------------------------------------------------------------
126 protected void GetClusterBasedSmokePositions(array<ref SCR_AIGroupTargetCluster> targetClusters, vector targetPosition, int maxSmokePositions, out array<vector> smokePositions)
127 {
128 int clustersCount = targetClusters.Count();
129
130 array<ref SCR_AIActivitySmokeCoverFeatureCluster> clusters = {};
131 foreach (SCR_AIGroupTargetCluster cluster: targetClusters)
132 clusters.Insert(new SCR_AIActivitySmokeCoverFeatureCluster(cluster, targetPosition));
133
134 // More clusters than max smoke positions - sort clusters by weight
135 if (clustersCount > maxSmokePositions)
136 clusters.Sort(); // Sort clusters by weight, ascending
137
138 float maxClustersCount = Math.Min(maxSmokePositions, clustersCount);
139
140 vector lastDirection;
141
142 // Gather positions to cover from clusters
143 for (int i = 0; i < maxClustersCount; i++)
144 {
145 SCR_AIActivitySmokeCoverFeatureCluster cluster = clusters[i];
146 vector clusterPos = cluster.GetCenterPosition();
147
148 lastDirection = vector.Direction(targetPosition, clusterPos).Normalized();
149 smokePositions.Insert(targetPosition + (lastDirection * (5 + (maxClustersCount * 3))));
150 }
151
152 int smokesLeft = maxSmokePositions - maxClustersCount;
153
154 // Exit if no more smokes left to be thrown
155 if (smokesLeft <= 0)
156 return;
157
158 // If only one cluster, create a wall oriented torwards the cluster
159 if (maxClustersCount == 1)
160 {
161 float distance = 0;
162
163 vector pos = smokePositions[0];
164 vector relDir = (lastDirection * vector.Up).Normalized();
165
166 for (int i = 0; i < smokesLeft; i++)
167 {
168 int pair = i % 2;
169 if (pair == 0)
170 distance += SMOKE_WALL_GAPS_SIZE;
171
172 vector relVector = relDir * distance;
173
174 if (pair == 0)
175 smokePositions.Insert(pos + relVector);
176 else
177 smokePositions.Insert(pos - relVector);
178 }
179
180 return;
181 }
182
183 // If more clusters, fill gaps between already picked smoke positions
184 int maxPositions = Math.Min(maxClustersCount / 2, smokesLeft);
185
186 for (int i = 0; i < maxPositions; i++)
187 {
188 vector pos1 = smokePositions[i];
189 vector pos2 = smokePositions[i + 1];
190 vector direction = vector.Direction(pos1, pos2).Normalized();
191 float distance = vector.Distance(pos1, pos2);
192
193 smokePositions.Insert(pos1 + (direction * (distance / 2)));
194 }
195 }
196
197
198 //-------------------------------------------------------------------------------------
200 notnull SCR_AIGroupUtilityComponent groupUtility,
201 vector targetPosition,
203 notnull array<AIAgent> avoidAgents,
204 notnull array<AIAgent> excludeAgents,
205 int maxPositionCount = 1,
206 SCR_AIActivityBase contextActivity = null
207 ) {
208 // Number of agents that are considered combat-ready. Will be used to calculate how many
209 // grenade throwers can be picked to not impare group's ability to continue fire fight
210 int combatReadyAgentsCount;
211 // Array of agents that will be considered as smoke grenade throwers
212 array<ref SCR_AIActivitySmokeCoverFeatureAgent> consideredAgents = {};
213
214 GetConsideredAgents(groupUtility, targetPosition, excludeAgents, avoidAgents, consideredAgents, combatReadyAgentsCount);
215
216 int consideredAgentsCount = consideredAgents.Count();
217
218 // Early exit if no agents to consider for throwing
219 if (!consideredAgentsCount)
220 return false;
221
222
223 // Get max count of smoke positions to cover
224 // Don't allow more than half of combat-ready agents to throw, other half must cover/fight
225 int maxSmokePositions = Math.Min(Math.Floor(combatReadyAgentsCount / 2), Math.Min(maxPositionCount, consideredAgentsCount));
226
227 // Early exit if we can't smoke any position
228 if (maxSmokePositions <= 0)
229 return false;
230
231 // Sort considered agents by weights if we have more agents than potential positions
232 if (consideredAgentsCount > maxSmokePositions)
233 consideredAgents.Sort(); // Sort considered agents by weight, ascending
234
235 array<vector> smokePositions = {};
236
237 // Smoke to protect position, we won't be throwing exactly at target position but to cover it
238 if (smokeCoverProperties & SCR_AIActivitySmokeCoverFeatureProperties.PROTECT_POS)
239 {
240 array<ref SCR_AIGroupTargetCluster> targetClusters = groupUtility.m_Perception.m_aTargetClusters;
241
242 // Pick positions based on target clusters if enabled and available
243 if ((smokeCoverProperties & SCR_AIActivitySmokeCoverFeatureProperties.PROTECT_FROM_CLUSTERS) && targetClusters.Count() > 0)
244 GetClusterBasedSmokePositions(targetClusters, targetPosition, maxSmokePositions, smokePositions);
245 // If not, randomize positions around target position
246 else
247 {
248 float angle = Math.RandomFloat(0, 360);
249 for (int i = 0; i < maxSmokePositions; i++)
250 {
251 vector direction = {Math.Cos(angle * Math.DEG2RAD), 0, Math.Sin(angle * Math.DEG2RAD)};
252 // More smoke positions, greater the distance
253 float distance = 1 + maxSmokePositions + Math.RandomFloat(0, maxSmokePositions * 1.5);
254 smokePositions.Insert(targetPosition + (direction * distance));
255
256 angle += (360 / maxSmokePositions);
257 if (angle > 360)
258 angle -= 360;
259 }
260 }
261 }
262 // Just simply smoke target position
263 else
264 smokePositions.Insert(targetPosition);
265
266 AICommunicationComponent comms = groupUtility.m_Owner.GetCommunicationComponent();
267 if (!comms)
268 return false;
269
270 int smokePositionsCount = smokePositions.Count();
271
272 // Send throw grenade messages to all picked agents
273 for (int i = 0; i < smokePositionsCount; i++)
274 {
275 // Randomize small delay to prevent unnatural perfect sync between throws of multiple soldiers
276 float delay = 0.3 + Math.RandomFloat(0, 1.5);
277 consideredAgents[i].SendMessage(comms, contextActivity, smokePositions[i], delay);
278 }
279
280 return true;
281 }
282
283 //-------------------------------------------------------------------------------------
284 bool ExecuteForActivity(SCR_AIActivityBase activity, int maxPositionCount = 3)
285 {
286 SCR_AIGroupUtilityComponent groupUtility = activity.m_Utility;
287 if (!groupUtility)
288 return false;
289
290 return Execute(
291 groupUtility,
293 GetActivityProperties(activity),
294 GetActivityAvoidedAgents(activity),
296 maxPositionCount,
297 activity
298 );
299 }
300
301 //-------------------------------------------------------------------------------------
302 // Returns position that should be covered by smoke grenades for given activity
304 {
305 return vector.Zero;
306 }
307
308 //-------------------------------------------------------------------------------------
309 // Returns smoke cover properties for given activity
314
315 //-------------------------------------------------------------------------------------
316 // Returns list of agents that should be avoided during selection for smoke grenade throwing for given activity
317 // Those agents still can be picked, we'll just try what we can to avoid it
319 {
320 return null;
321 }
322
323 //-------------------------------------------------------------------------------------
324 // Returns list of agents that should be excluded from smoke grenade throwing for given activity
325 // Those agents won't be picked, no matter the circumstances
327 {
328 return null;
329 }
330}
331
332
333class SCR_AIHealActivitySmokeCoverFeature: SCR_AIActivitySmokeCoverFeature
334{
335 //-------------------------------------------------------------------------------------
336 override vector GetActivityTargetPosition(SCR_AIActivityBase activity)
337 {
338 SCR_AIHealActivity healActivity = SCR_AIHealActivity.Cast(activity);
339
340 if (healActivity.m_EntityToHeal.m_Value)
341 return healActivity.m_EntityToHeal.m_Value.GetOrigin();
342
343 return vector.Zero;
344 }
345
346 //-------------------------------------------------------------------------------------
348 {
350 }
351
352 //-------------------------------------------------------------------------------------
353 override array<AIAgent> GetActivityAvoidedAgents(SCR_AIActivityBase activity)
354 {
355 SCR_AIHealActivity healActivity = SCR_AIHealActivity.Cast(activity);
356
357 ChimeraCharacter character = ChimeraCharacter.Cast(healActivity.m_MedicEntity.m_Value);
358 if (!character)
359 return null;
360
361 CharacterControllerComponent charCtrl = character.GetCharacterController();
362 if (!charCtrl)
363 return null;
364
365 AIControlComponent aiCtrl = charCtrl.GetAIControlComponent();
366 if (!aiCtrl)
367 return null;
368
369 return {aiCtrl.GetAIAgent()};
370 }
371
372 //-------------------------------------------------------------------------------------
373 override array<AIAgent> GetActivityExcludedAgents(SCR_AIActivityBase activity)
375 SCR_AIHealActivity healActivity = SCR_AIHealActivity.Cast(activity);
376
377 ChimeraCharacter character = ChimeraCharacter.Cast(healActivity.m_EntityToHeal.m_Value);
378 if (!character)
379 return null;
380
381 CharacterControllerComponent charCtrl = character.GetCharacterController();
382 if (!charCtrl)
383 return null;
384
385 AIControlComponent aiCtrl = charCtrl.GetAIControlComponent();
386 if (!aiCtrl)
387 return null;
388
389 return {aiCtrl.GetAIAgent()};
390 }
391}
void SCR_AIActivityBase(SCR_AIGroupUtilityComponent utility, AIWaypoint relatedWaypoint)
SCR_AIActivitySmokeCoverFeatureCluster CLOSE_DISTANCE_TRESHOLD_SQ
SCR_ChimeraAIAgent m_Agent
SCR_AIActivitySmokeCoverFeatureProperties
void SCR_AIActivitySmokeCoverFeatureAgent(SCR_ChimeraAIAgent agent, float distanceToTargetSq, bool avoid)
float GetCloseTargetPenalty(float distanceToTargetSq)
void SendMessage(AICommunicationComponent comms, SCR_AIActivityBase activity, vector position, float delay)
vector GetCenterPosition()
float distance
vector direction
vector position
proto external vector GetOrigin()
Definition Math.c:13
bool IsAgentAvailable(SCR_ChimeraAIAgent agent)
void GetClusterBasedSmokePositions(array< ref SCR_AIGroupTargetCluster > targetClusters, vector targetPosition, int maxSmokePositions, out array< vector > smokePositions)
bool Execute(notnull SCR_AIGroupUtilityComponent groupUtility, vector targetPosition, SCR_AIActivitySmokeCoverFeatureProperties smokeCoverProperties, notnull array< AIAgent > avoidAgents, notnull array< AIAgent > excludeAgents, int maxPositionCount=1, SCR_AIActivityBase contextActivity=null)
array< AIAgent > GetActivityAvoidedAgents(SCR_AIActivityBase activity)
array< AIAgent > GetActivityExcludedAgents(SCR_AIActivityBase activity)
bool ExecuteForActivity(SCR_AIActivityBase activity, int maxPositionCount=3)
SCR_AIActivitySmokeCoverFeatureProperties GetActivityProperties(SCR_AIActivityBase activity)
void GetConsideredAgents(SCR_AIGroupUtilityComponent groupUtility, vector targetPosition, array< AIAgent > avoidAgents, array< AIAgent > excludeAgents, notnull array< ref SCR_AIActivitySmokeCoverFeatureAgent > outConsideredAgents, out int combatReadyAgentsCount)
vector GetActivityTargetPosition(SCR_AIActivityBase activity)
bool HasUnitState(EUnitState state)
bool HasRole(EUnitRole role)
@ NONE
When Shape is created and not initialized yet.
Definition ShapeType.c:15
EWeaponType
Definition EWeaponType.c:13