Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_AIDangerReaction_WeaponFired.c
Go to the documentation of this file.
3{
4 protected static const float SOUND_SPEED_MS = 343.0; // At Earth, sea level, 20 deg.c
5 protected static const float ENDANGERING_FOR_GROUP_RADIUS = 15; // Gunshot is endangering for group at this distance and lower, even if it's not a fly-by
6 protected static const float PROJECTILE_FLYBY_RADIUS = 13;
8 protected static const float PROJECTILE_FLYBY_DELAY_S = 0.05;
9 protected static const float AUDIBLE_DISTANCE_NORMAL = 500.0; // Audible distance for normal gunshots
10 protected static const float AUDIBLE_DISTANCE_SUPPRESSED = 100.0; // Audible distance for suppressed gunshots
11
12 override bool PerformReaction(notnull SCR_AIUtilityComponent utility, notnull SCR_AIThreatSystem threatSystem, AIDangerEvent dangerEvent, int dangerEventCount)
13 {
14 AIDangerEventWeaponFire eventWeaponFire = AIDangerEventWeaponFire.Cast(dangerEvent);
15 IEntity shooter = eventWeaponFire.GetObject();
16
17 if (!shooter || !eventWeaponFire)
18 return false;
19
20 IEntity instigatorEntity = eventWeaponFire.GetInstigatorEntity();
21 if (!instigatorEntity)
22 return false;
23
24 // Check faction relations, ignore if not enemy or there is no faction
25 Faction instigatorFaction = SCR_AIFactionHandling.GetEntityPerceivedFaction(instigatorEntity);
26
27 if (!instigatorFaction)
28 return false;
29
30 SCR_ChimeraAIAgent agent = SCR_ChimeraAIAgent.Cast(utility.GetOwner());
31
32 bool myFactionIsMilitary = utility.IsMilitary();
33 if (myFactionIsMilitary && !agent.IsEnemy(instigatorFaction))
34 return false;
35
36 // Get root entity of shooter, in case it is turret in vehicle hierarchy
37 vector shotPos = eventWeaponFire.GetPosition();
38 vector shotDir = eventWeaponFire.GetDirection();
39 bool isShotSuppressed = eventWeaponFire.IsSuppressed();
40
41 vector myOrigin = utility.m_OwnerEntity.GetOrigin();
42 float distance = vector.Distance(myOrigin, shotPos);
43
44 // Is it a flyby?
45 bool isFlyby = IsFlyby(myOrigin, shotPos, shotDir, distance);
46
47 // Is it audible?
48 float maxAudibleDistance;
49 if (isShotSuppressed)
50 maxAudibleDistance = AUDIBLE_DISTANCE_SUPPRESSED;
51 else
52 maxAudibleDistance = AUDIBLE_DISTANCE_NORMAL;
53 bool isAudible = distance < maxAudibleDistance;
54
55
56 float timeTillFlyby_s = float.MAX;
57 float timeTillGunshotHeard_s = float.MAX;
58 if (isFlyby)
59 {
60 float projectileSpeed = eventWeaponFire.GetInitialSpeed();
61 WorldTimestamp eventTimestamp = eventWeaponFire.GetTimestamp();
62 float flightTime_s = distance / projectileSpeed;
63 WorldTimestamp flybyTimestamp = eventTimestamp.PlusSeconds(flightTime_s + PROJECTILE_FLYBY_DELAY_S);
64 timeTillFlyby_s = flybyTimestamp.DiffSeconds(GetGame().GetWorld().GetTimestamp());
65
66 if (timeTillFlyby_s < 0)
67 OnProjectileFlyby(utility, dangerEventCount, shotPos);
68 else
69 {
70 utility.GetCallqueue().CallLater(OnProjectileFlyby, 1000*timeTillFlyby_s, false,
71 utility, dangerEventCount, shotPos);
72 }
73 }
74
75 if (isAudible)
76 {
77 WorldTimestamp eventTimestamp = eventWeaponFire.GetTimestamp();
78 float wavefrontTravelTime_s = distance / SOUND_SPEED_MS;
79 WorldTimestamp wavefrontArrivalTimestamp = eventTimestamp.PlusSeconds(wavefrontTravelTime_s);
80 timeTillGunshotHeard_s = wavefrontArrivalTimestamp.DiffSeconds(GetGame().GetWorld().GetTimestamp());
81
82 // Ignore the gunshot sound if projectile flies by sooner than sound of gunshot.
83 // This is general case for majority of weapons. We don't want to notify the threat system twice.
84 // Threat system was not tuned to be notified twice in such cases.
85 bool ignoreGunshotHeard = isFlyby && timeTillFlyby_s < timeTillGunshotHeard_s;
86
87 if (!ignoreGunshotHeard)
88 {
89 if (timeTillGunshotHeard_s < 0)
90 OnGunshotHeard(utility, distance, dangerEventCount, shotPos);
91 else
92 {
93 utility.GetCallqueue().CallLater(OnGunshotHeard, 1000*timeTillGunshotHeard_s, false,
94 utility, distance, dangerEventCount, shotPos);
95 }
96 }
97 }
98
99 if (isFlyby || isAudible)
100 {
101 // Notify our group, only if we are a leader
102 bool endangeringForGroup = isFlyby || distance < ENDANGERING_FOR_GROUP_RADIUS;
103
104 AIGroup myGroup = utility.GetOwner().GetParentGroup();
105 if (myGroup && myGroup.GetLeaderAgent() == agent)
106 {
107 float timeTillGroupNotified_s = Math.Min(timeTillFlyby_s, timeTillGunshotHeard_s);
108
109 if (timeTillGroupNotified_s < 0)
110 NotifyGroup(myGroup, shooter, instigatorEntity, instigatorFaction, shotPos, endangeringForGroup);
111 else
112 {
113 utility.GetCallqueue().CallLater(NotifyGroup, 1000*timeTillGroupNotified_s, false,
114 myGroup, shooter, instigatorEntity, instigatorFaction, shotPos, endangeringForGroup);
115 }
116 }
117 }
118
119 return true;
120 }
121
122 void OnGunshotHeard(notnull SCR_AIUtilityComponent utility, float distance, int dangerEventCount, vector pos)
123 {
124 utility.m_ThreatSystem.ThreatShotFired(distance, dangerEventCount);
125 utility.m_SectorThreatFilter.OnShotsFired(pos, dangerEventCount, false);
126 }
127
128 void OnProjectileFlyby(notnull SCR_AIUtilityComponent utility, int dangerEventCount, vector pos)
129 {
130 utility.m_ThreatSystem.ThreatProjectileFlyby(dangerEventCount);
131 utility.m_SectorThreatFilter.OnShotsFired(pos, dangerEventCount, true);
132 }
133
134 void NotifyGroup(AIGroup group, IEntity shooterEntity, IEntity instigatorEntity, Faction faction, vector posWorld, bool endangering)
135 {
136 // This can be called delayed, so check if group still exists
137 if (!group)
138 return;
139
140 PerceptionManager pm = GetGame().GetPerceptionManager();
141 SCR_AIGroupUtilityComponent groupUtilityComp = SCR_AIGroupUtilityComponent.Cast(group.FindComponent(SCR_AIGroupUtilityComponent));
142
143 if (!groupUtilityComp || !pm)
144 return;
145
146 // Resolve which entity to report to group perception
147 // For static turrets it's Instigator entity (character),
148 // For turrets in vehicles it's root of vehicle
149
150 IEntity targetEntity; // Target entity for group perception
151
152 if (ChimeraCharacter.Cast(shooterEntity))
153 targetEntity = shooterEntity; // Character
154 else if (Turret.Cast(shooterEntity))
155 {
156 IEntity shooterEntityRoot = shooterEntity.GetRootParent();
157 if (Vehicle.Cast(shooterEntityRoot))
158 targetEntity = shooterEntityRoot; // Root of vehicle
159 else
160 targetEntity = instigatorEntity; // Character in turret
161 }
162
163 // Bail if can't resolve the target entity for group perception
164 if (!targetEntity)
165 return;
166
167 float timestamp = pm.GetTime();
168 groupUtilityComp.m_Perception.AddOrUpdateGunshot(targetEntity, posWorld, faction, timestamp, endangering);
169 }
170
171 bool IsFlyby(vector myPos, vector shotPos, vector shotDir, float distance)
172 {
173 return Math3D.IntersectionPointCylinder(myPos, shotPos, shotDir, PROJECTILE_FLYBY_RADIUS);
174 }
175};
ArmaReforgerScripted GetGame()
Definition game.c:1398
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
float distance
Event which gets broadcasted from danger-causing places to AI.
proto external IEntity GetRootParent()
Definition Math.c:13
void NotifyGroup(AIGroup group, IEntity shooterEntity, IEntity instigatorEntity, Faction faction, vector posWorld, bool endangering)
void OnGunshotHeard(notnull SCR_AIUtilityComponent utility, float distance, int dangerEventCount, vector pos)
bool IsFlyby(vector myPos, vector shotPos, vector shotDir, float distance)
override bool PerformReaction(notnull SCR_AIUtilityComponent utility, notnull SCR_AIThreatSystem threatSystem, AIDangerEvent dangerEvent, int dangerEventCount)
void OnProjectileFlyby(notnull SCR_AIUtilityComponent utility, int dangerEventCount, vector pos)
void AddOrUpdateGunshot(notnull IEntity shooter, vector worldPos, Faction faction, float timestamp, bool endangering)
ref SCR_AIGroupPerception m_Perception
bool IsEnemy(IEntity entity)
enum EPhysicsLayerPresets Vehicle
Definition gameLib.c:24