Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_MotorExhaustEffectGeneralComponent.c
Go to the documentation of this file.
5
6class SCR_MotorExhaustEffectGeneralComponent : MotorExhaustEffectComponent
7{
8// protected Particles m_ExhaustParticles;
13 protected float m_fRPMScaled; // 0 to 1 scale of RPM between current and max RPM
14 protected float m_fRPMScaledOld; // RPM scale in previous frame to calculate acceleration
15 protected float m_fThrust; // 0 to 1. Represents how much pedal to the metal is the driver pushing.
16 protected float m_fEngineLoad; // How much load is the engine being pushed through
17 protected float m_fPreviousLoad = -1; // Previous load state to detect necessary change
18 protected float m_fLifetimeScale; // Lifetime of the exhaust particles
19 protected int m_iIsExhaustUnderWaterSignalIdx; // IsExhaustUnderWater signal index
20 protected int m_iEngineLoadIdx; // engine load singal index
21 protected bool m_bIsUnderwater;
22 protected bool m_bIsDefective;
23
24 // There are some heavy calculations with particles going on per frame. The following variables solve the performance impact by calling them less often when they happen far away from the camera.
25 protected float m_fUpdateDelay; // Desired delay between each particle calculations in seconds. 0 means per frame.
26 protected float m_fCurrentUpdateDelay; // The time of current delay
27 protected const float TICK_TIME_DELAY_MAX = 2; // Max acceptable delay in seconds
28 protected const float TICK_DELAY_RANGE_START = 15; // Starting range at which the delay begins to increase (from 0 to TICK_TIME_DELAY_MAX)
29 protected const float TICK_DELAY_RANGE_END = 100; // End range at which delay reaches its maximum (TICK_TIME_DELAY_MAX)
30
31 // Arrays of arrays of emitter indices (one array of indices per stage).
32 // E.g. m_aExhaustStageEmitters[1][0] gets emitter index of the emitter #0 of stage #1
33 protected ref array<ref array<int>> m_aExhaustStagesEmitters;
34 protected ref array<ref array<int>> m_aDamageStagesEmitters;
35
36 protected float m_fStartupTimeLeft; // Remaining time to keep playing the startup particle effect after startup
37
38 [Attribute("{AEA751F0BE7FE821}Particles/Vehicle/Vehicle_smoke_car_exhaust_damage.ptc", UIWidgets.ResourceNamePicker, desc: "Particle effect for damaged engine", params: "ptc")]
40
41 [Attribute("1", UIWidgets.Auto, desc: "Time to play damaged exhaust particle after startup")]
42 protected float m_fStartupTime;
43
44 //------------------------------------------------------------------------------------------------
45 override void OnInit(IEntity owner)
46 {
47 if (System.IsConsoleApp())
48 {
49 Deactivate(owner);
50 return;
51 }
52
55
57 {
58 m_iIsExhaustUnderWaterSignalIdx = m_SignalsManager.AddOrFindSignal("IsExhaustUnderWater");
59 m_iEngineLoadIdx = m_SignalsManager.AddOrFindSignal("engineLoad");
60 }
61 }
62
63 //------------------------------------------------------------------------------------------------
64 override void OnDelete(IEntity owner)
65 {
67
68 super.OnDelete(owner);
69 }
70
71 //------------------------------------------------------------------------------------------------
72 // Parses an emitter name and returns index of a stage the emitter belongs to.
73 // Returns -1 if the emitter's name does not meet the stage naming WeatherWindPattern
74 // (s<X>_<name> where <X> is a number of a stage and <name> is any string, e.g. s0_smoke, s1_smoke, s10_debris etc.)
75 protected static int GetStageIndex(string emitterName)
76 {
77 static const string EMITTER_STAGE_PREFIX = "s";
78 static const string EMITTER_STAGE_SUFIX = "_";
79
80 int offset = EMITTER_STAGE_PREFIX.Length();
81 if (!emitterName.StartsWith(EMITTER_STAGE_PREFIX) || !emitterName.IsDigitAt(offset))
82 return -1;
83
84 int numberLen;
85 int stageNo = emitterName.ToInt(offset: offset, parsed: numberLen);
86 offset += numberLen;
87 if (!emitterName.ContainsAt(EMITTER_STAGE_SUFIX, offset))
88 return -1;
89
90 return stageNo;
91 }
92
93 //------------------------------------------------------------------------------------------------
94 // Creates an index of emitters in stages of Particles of ParticleEffectEntity,
95 // i.e. array of arrays of emitter indices - one array of emitter indices for each stage.
96 // E.g. for stageEmitters = CreateStageIndex(effectEntity),
97 // at stageEmitters[2] there is a an array of indices of emitters belonging to the the stage #2.
98 protected static array<ref array<int>> CreateStageIndexes(notnull ParticleEffectEntity effectEntity)
99 {
100 Particles particles = effectEntity.GetParticles();
101 if (!particles)
102 return {};
103
104 array<ref array<int>> ret = {{}};
105
106 array<string> emitterNames = {};
107 int emitterNamesCount = particles.GetEmitterNames(emitterNames);
108
109 for (int i; i < emitterNamesCount; i++)
110 {
111 int stage = GetStageIndex(emitterNames[i]);
112 if (stage < 0)
113 continue;
114
115 if (stage >= ret.Count())
116 ret.Resize(stage + 1);
117
118 if (!ret[stage])
119 ret[stage] = {};
120
121 ret[stage].Insert(i);
122 }
123
124 return ret;
125 }
126
127 //------------------------------------------------------------------------------------------------
128 void Update(float timeSlice)
129 {
130 // Check if delay treshold was crossed. This prevents calling intense calculations per frame for vehicles which are far away from camera as they have low priority
131 m_fCurrentUpdateDelay += timeSlice;
133 return;
134
136
137 // Check if effect exists. If not then try to get it.
138 if (!m_ExhaustEmitter)
140
141 if (!m_ExhaustEmitter)
142 return;
143
144 // Check if exhaust is underwater
145 bool isUnderwater = SCR_WorldTools.IsObjectUnderwater(m_ExhaustEmitter);
146 if (m_bIsUnderwater != isUnderwater)
147 {
148 m_bIsUnderwater = isUnderwater;
149 m_SignalsManager.SetSignalValue(m_iIsExhaustUnderWaterSignalIdx, isUnderwater);
150 }
151
152 // Check if engine is damaged
153 m_bIsDefective = !isUnderwater && m_DamageManager && m_DamageManager.GetEngineEfficiency() <= m_DamageManager.GetEngineMalfunctionThreshold();
154
155 // Update emitters
156 UpdateExhaustEmitter(timeSlice);
157
159 UpdateDamagedEmitter(timeSlice);
160
161 // Calculate distance between owner and camera
162 CameraBase camera = GetGame().GetCameraManager().CurrentCamera();
163 if (!camera)
164 return;
165
166 float distance = vector.Distance(camera.GetOrigin(), m_ExhaustEmitter.GetOrigin());
167
168 // Prolong update interval based on the distance between camera and effect
171 else
172 m_fUpdateDelay = 0;
173 }
174
175 //------------------------------------------------------------------------------------------------
176 void OnGamePauseChanged(bool paused)
177 {
178 UpdateExhaustEmitter(0.001, paused);
179 }
180 //------------------------------------------------------------------------------------------------
181 protected void UpdateExhaustParticles(float timeSlice)
182 {
183 // We are about to call relatively performance heavy update of particle in OnUpdateEffect(...). This will be done per frame but only if the effect is very close to camera. Otherwise the update will be separated by time intervals to save performance.
185 if (m_fRPMScaled > 0 || m_fPreviousLoad < 0)
186 {
187 // Prepare values which will be used for particle calculations later in OnUpdateEffect(...)
189 float RPM_acceleration = (m_fRPMScaled - m_fRPMScaledOld) * 1000 * timeSlice;
191 }
192
193 // Startup force full engine load
194 if (m_fStartupTimeLeft > 0)
195 m_fEngineLoad = 1;
196 else if (m_SignalsManager)
197 m_fEngineLoad = Math.Max(m_SignalsManager.GetSignalValue(m_iEngineLoadIdx), 0);
198
199 if (float.AlmostEqual(m_fEngineLoad, m_fPreviousLoad))
200 return;
201
203
204 // Update exhaust stages
206 m_aExhaustStagesEmitters = CreateStageIndexes(m_ExhaustEmitter);
207
209 }
210
211 //------------------------------------------------------------------------------------------------
213 protected void UpdateExhaustEmitter(float timeSlice, bool paused = false)
214 {
215 if (!m_ExhaustEmitter)
216 return;
217
218 bool shouldBePlaying = !paused && !m_bIsUnderwater && !(m_bIsDefective && m_DamagedEmitter);
219 bool isPlaying = m_ExhaustEmitter.GetState() == EParticleEffectState.PLAYING;
220
221 // Enable only if no damage particles are being played
222 if (shouldBePlaying)
223 {
224 if (!isPlaying)
225 m_ExhaustEmitter.Play();
226
227 UpdateExhaustParticles(timeSlice);
228 }
229 else
230 {
231 // Pause so that emitter does not get deleted until engine is stopped
232 if (isPlaying)
233 m_ExhaustEmitter.Pause();
234 }
235 }
236
237 //------------------------------------------------------------------------------------------------
239 protected void UpdateDamagedEmitter(float timeSlice)
240 {
241 bool isEmitting = m_DamagedEmitter && m_DamagedEmitter.GetState() == EParticleEffectState.PLAYING;
242 if (m_bIsDefective == isEmitting)
243 return;
244
245 if (m_bIsDefective)
246 {
248 m_DamagedEmitter.Play();
249 else if (!m_sDamagedParticle.IsEmpty())
251 }
252 else
253 {
255 m_DamagedEmitter.StopEmission();
256 }
257 }
258
259 //------------------------------------------------------------------------------------------------
261 protected void CreateDamageEffect()
262 {
264
265 EntitySlotInfo effectPosition = GetEffectPosition();
266 if (effectPosition)
267 {
268 spawnParams.PivotID = effectPosition.GetNodeId();
269 effectPosition.GetLocalTransform(spawnParams.Transform);
270 }
271
272 spawnParams.UseFrameEvent = true;
273 spawnParams.Parent = GetOwner();
274
275 m_DamagedEmitter = ParticleEffectEntity.SpawnParticleEffect(m_sDamagedParticle, spawnParams);
276 }
277
278 //------------------------------------------------------------------------------------------------
279 protected void AdjustEngineEffects(notnull ParticleEffectEntity effectEntity, array<ref array<int>> stageIndexes)
280 {
281 Particles particles = effectEntity.GetParticles();
282 if (!particles)
283 return;
284
285 if (stageIndexes && !stageIndexes.IsEmpty()) // Check if the assigned particle effect supports staging (Staging divides effect's emittors into groups which are selectively enabled/disabled according to our needs)
286 {
287 // Staging is supported, so disable all emittors...
288 particles.SetParam(-1, EmitterParam.BIRTH_RATE, 0.0);
289 particles.SetParam(-1, EmitterParam.BIRTH_RATE_RND, 0.0);
290
291 // ... now enable only the relevant emittors and work with them
292 int iMaxStage = stageIndexes.Count();
293 int stage = Math.ClampInt(Math.Ceil(iMaxStage * m_fEngineLoad), 1, iMaxStage) - 1;
294 array<int> stageEmitterIDs = stageIndexes[stage];
295
296 // Now we work only with the relevant stage (group) of emitters
297 for (int i; i < stageEmitterIDs.Count(); i++)
298 {
299 particles.MultParam(stageEmitterIDs[i], EmitterParam.BIRTH_RATE, m_fRPMScaled* 0.5 + 0.5);
300 particles.MultParam(stageEmitterIDs[i], EmitterParam.BIRTH_RATE_RND, m_fRPMScaled* 0.5 + 0.5);
301 particles.MultParam(stageEmitterIDs[i], EmitterParam.VELOCITY, (m_fRPMScaled* 3));
302 particles.MultParam(stageEmitterIDs[i], EmitterParam.VELOCITY_RND, (m_fRPMScaled* 3));
303 particles.MultParam(stageEmitterIDs[i], EmitterParam.AIR_RESISTANCE, m_fRPMScaled);
304 particles.MultParam(stageEmitterIDs[i], EmitterParam.AIR_RESISTANCE_RND, m_fRPMScaled);
305 }
306 }
307 else
308 {
309 // Staging is not supported so apply changes to all emittors.
310 particles.MultParam(-1, EmitterParam.BIRTH_RATE, m_fRPMScaled* 1);
311 particles.MultParam(-1, EmitterParam.BIRTH_RATE_RND, m_fRPMScaled* 1);
312 particles.MultParam(-1, EmitterParam.LIFETIME, m_fLifetimeScale);
313 particles.MultParam(-1, EmitterParam.LIFETIME_RND, m_fLifetimeScale);
314 particles.MultParam(-1, EmitterParam.VELOCITY, (m_fRPMScaled* 7));
315 particles.MultParam(-1, EmitterParam.VELOCITY_RND, (m_fRPMScaled* 7));
316 particles.MultParam(-1, EmitterParam.AIR_RESISTANCE, m_fRPMScaled);
317 particles.MultParam(-1, EmitterParam.AIR_RESISTANCE_RND, m_fRPMScaled);
318 }
319 }
320
321 //------------------------------------------------------------------------------------------------
323 void OnEngineStart(bool startup)
324 {
326 m_fPreviousLoad = -1;
327 m_bIsUnderwater = false;
328
329 if (startup)
331 else
333
334 TurnOn(GetOwner());
336 }
337
338 //------------------------------------------------------------------------------------------------
341 {
342 TurnOff();
344
346 m_DamagedEmitter.StopEmission();
347 }
348
349 //------------------------------------------------------------------------------------------------
351 {
352 World world = GetOwner().GetWorld();
353 MotorExhaustSystem updateSystem = MotorExhaustSystem.Cast(world.FindSystem(MotorExhaustSystem));
354 if (!updateSystem)
355 return;
356
357 updateSystem.Register(this);
358 }
359
360 //------------------------------------------------------------------------------------------------
362 {
363 World world = GetOwner().GetWorld();
364 MotorExhaustSystem updateSystem = MotorExhaustSystem.Cast(world.FindSystem(MotorExhaustSystem));
365 if (!updateSystem)
366 return;
367
368 updateSystem.Unregister(this);
369 }
370}
ArmaReforgerScripted GetGame()
Definition game.c:1398
enum EAIGroupCombatMode ComponentEditorProps(category:"GameScripted/AI", description:"Component for utility AI system for groups")
DamageManagerComponent m_DamageManager
void Deactivate()
float distance
void TurnOn()
ResourceName m_sDamagedParticle
const float TICK_TIME_DELAY_MAX
ref array< ref array< int > > m_aDamageStagesEmitters
SCR_MotorExhaustEffectGeneralComponentClass m_ExhaustEmitter
void UpdateExhaustEmitter(float timeSlice, bool paused=false)
Ignition engine exhaust.
void UpdateExhaustParticles(float timeSlice)
void OnGamePauseChanged(bool paused)
ref array< ref array< int > > m_aExhaustStagesEmitters
ParticleEffectEntity m_DamagedEmitter
const float TICK_DELAY_RANGE_START
void CreateDamageEffect()
Create damaged exhaust particles.
const float TICK_DELAY_RANGE_END
void UpdateDamagedEmitter(float timeSlice)
Damaged engine exhaust.
void OnEngineStop()
Turn off the effect and pause the damaged exhaust effect.
void AdjustEngineEffects(notnull ParticleEffectEntity effectEntity, array< ref array< int > > stageIndexes)
void ParticleEffectEntity(IEntitySource src, IEntity parent)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
SignalsManagerComponent m_SignalsManager
void SCR_VehicleDamageManagerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
override void OnEngineStart()
Is called every time the engine starts.
Adds ability to attach an object to a slot.
proto external Managed FindComponent(typename typeName)
proto external BaseWorld GetWorld()
Definition Math.c:13
void Register(SCR_MotorExhaustEffectGeneralComponent component)
void Unregister(SCR_MotorExhaustEffectGeneralComponent component)
Definition World.c:16
IEntity GetOwner()
Owner entity of the fuel tank.
HYBRID_COMPONENT_ICON
Default icon for all components written in script that don't inherit ScriptComponent.
SCR_FieldOfViewSettings Attribute
BaseEffectComponentClass GameComponentClass GetParticleEntity()
Returns the particle entity associated with this effect component.
EParticleEffectState
proto external float GetSignalThrust()
proto external float GetRpmScaled()
proto external EntitySlotInfo GetEffectPosition()
void TurnOff()
Remove every particle associed with this effect.
EmitterParam