Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_RandomParticleSpawnable.c
Go to the documentation of this file.
3{
4 override bool _WB_GetCustomTitle(BaseContainer source, out string title)
5 {
6 title = "Random Particle Effect";
7 return true;
8 }
9}
10
14class SCR_RandomParticleSpawnable : SCR_ParticleSpawnable
15{
17 [Attribute("60", UIWidgets.Slider, "Maximum duration for particle spawning loop [seconds]", "0 600 1")]
18 protected float m_fMaxDuration;
19
21 [Attribute("1", UIWidgets.Slider, "Minimum interval between particle spawns [seconds]", "0.1 30 0.1")]
22 protected float m_fMinInterval;
23
25 [Attribute("5", UIWidgets.Slider, "Maximum interval between particle spawns [seconds]", "0.1 30 0.1")]
26 protected float m_fMaxInterval;
27
29 [Attribute("", UIWidgets.EditBox, "Sound event name to play with particles")]
30 protected string m_sSoundEvent;
31
33 [Attribute("0", UIWidgets.Slider, "Minimum distance from camera to trigger effects [meters]", "0 1000 1")]
34 protected float m_fTriggerDistanceMin;
35
37 [Attribute("5", UIWidgets.Slider, "Maximum distance from camera to trigger effects [meters] (0 = no limit)", "0 1000 1")]
38 protected float m_fTriggerDistanceMax;
39
41 [Attribute("1", UIWidgets.Slider, "Number of repetitions in a sequence", "1 10 1")]
42 protected int m_iRepetitionCount;
43
45 [Attribute("0", UIWidgets.Slider, "Repetition count randomization", "0 5 1")]
46 protected int m_iRepetitionCountRnd;
47
49 [Attribute("10", UIWidgets.Slider, "Time between sequences [seconds]", "1 60 0.1")]
50 protected float m_fSequenceRepetitionTime;
51
53 [Attribute("5", UIWidgets.Slider, "Sequence repetition time randomization [seconds]", "0 30 0.1")]
54 protected float m_fSequenceRepetitionTimeRnd;
55
56 // Runtime variables
57 protected float m_fElapsedTime = 0;
58 protected ref RandomGenerator m_prng = null;
59 protected ScriptCallQueue m_CallQueue;
60 protected int m_iCurrentRepCount;
61 protected bool m_bInSequence = false;
62
63#ifdef WORKBENCH
64 //------------------------------------------------------------------------------------------------
65 override bool CompareAttributes(SCR_BaseSpawnable other)
66 {
67 SCR_RandomParticleSpawnable otherRandom = SCR_RandomParticleSpawnable.Cast(other);
68
69 if (!super.CompareAttributes(other))
70 return false;
71
72 if (!otherRandom)
73 return false;
74
75 if (otherRandom.m_fMaxDuration != m_fMaxDuration)
76 return false;
77
78 if (otherRandom.m_fMinInterval != m_fMinInterval)
79 return false;
80
81 if (otherRandom.m_fMaxInterval != m_fMaxInterval)
82 return false;
83
84 if (otherRandom.m_sSoundEvent != m_sSoundEvent)
85 return false;
86
87 if (otherRandom.m_fTriggerDistanceMin != m_fTriggerDistanceMin)
88 return false;
89
90 if (otherRandom.m_fTriggerDistanceMax != m_fTriggerDistanceMax)
91 return false;
92
93 if (otherRandom.m_iRepetitionCount != m_iRepetitionCount)
94 return false;
95
96 if (otherRandom.m_iRepetitionCountRnd != m_iRepetitionCountRnd)
97 return false;
98
99 if (otherRandom.m_fSequenceRepetitionTime != m_fSequenceRepetitionTime)
100 return false;
101
102 if (otherRandom.m_fSequenceRepetitionTimeRnd != m_fSequenceRepetitionTimeRnd)
103 return false;
104
105 return true;
106 }
107
108 //------------------------------------------------------------------------------------------------
109 override void SetVariables(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path, int index)
110 {
111 super.SetVariables(api, source, path, index);
112
113 // Set all variables of the spawn object
114 api.SetVariableValue(source, path, "m_fMaxDuration", m_fMaxDuration.ToString());
115 api.SetVariableValue(source, path, "m_fMinInterval", m_fMinInterval.ToString());
116 api.SetVariableValue(source, path, "m_fMaxInterval", m_fMaxInterval.ToString());
117 api.SetVariableValue(source, path, "m_sSoundEvent", m_sSoundEvent);
118 api.SetVariableValue(source, path, "m_fTriggerDistanceMin", m_fTriggerDistanceMin.ToString());
119 api.SetVariableValue(source, path, "m_fTriggerDistanceMax", m_fTriggerDistanceMax.ToString());
120 api.SetVariableValue(source, path, "m_iRepetitionCount", m_iRepetitionCount.ToString());
121 api.SetVariableValue(source, path, "m_iRepetitionCountRnd", m_iRepetitionCountRnd.ToString());
122 api.SetVariableValue(source, path, "m_fSequenceRepetitionTime", m_fSequenceRepetitionTime.ToString());
123 api.SetVariableValue(source, path, "m_fSequenceRepetitionTimeRnd", m_fSequenceRepetitionTimeRnd.ToString());
124 }
125
126 //------------------------------------------------------------------------------------------------
127 override bool CreateObject(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path, int index)
128 {
129 if (!AlreadyExists(api, source, index))
130 {
131 api.CreateObjectArrayVariableMember(source, path, "m_aPhaseDestroySpawnObjects", "SCR_RandomParticleSpawnable", index);
132 return true;
133 }
134
135 return false;
136 }
137#endif // WORKBENCH
138
139 //------------------------------------------------------------------------------------------------
141 protected int GetRepCount()
142 {
143 return Math.RandomIntInclusive(Math.Max(1, m_iRepetitionCount - m_iRepetitionCountRnd), m_iRepetitionCount + m_iRepetitionCountRnd);
144 }
145
146 //------------------------------------------------------------------------------------------------
148 protected float GetRandomInterval()
149 {
150 if (m_fMinInterval >= m_fMaxInterval)
151 return m_fMinInterval;
152
153 return Math.RandomFloatInclusive(m_fMinInterval, m_fMaxInterval);
154 }
155
156 //------------------------------------------------------------------------------------------------
158 protected float GetSequenceRepTime()
159 {
160 if (m_fSequenceRepetitionTimeRnd < 0.001)
161 return m_fSequenceRepetitionTime;
162
163 return Math.RandomFloatInclusive(Math.Max(0.1, m_fSequenceRepetitionTime - m_fSequenceRepetitionTimeRnd), m_fSequenceRepetitionTime + m_fSequenceRepetitionTimeRnd);
164 }
165
166 //------------------------------------------------------------------------------------------------
168 protected bool IsInRange(IEntity owner)
169 {
170 // If no distance limits are set, always trigger
171 if (m_fTriggerDistanceMax <= 0 && m_fTriggerDistanceMin <= 0)
172 return true;
173
174 vector ownerTransform[4];
175 vector cameraTransform[4];
176
177 owner.GetTransform(ownerTransform);
178 owner.GetWorld().GetCurrentCamera(cameraTransform);
179
180 float distance = vector.Distance(ownerTransform[3], cameraTransform[3]);
181
182 // Check minimum distance
183 if (m_fTriggerDistanceMin > 0 && distance < m_fTriggerDistanceMin)
184 return false;
185
186 // Check maximum distance
187 if (m_fTriggerDistanceMax > 0 && distance > m_fTriggerDistanceMax)
188 return false;
189
190 return true;
191 }
192
193 //------------------------------------------------------------------------------------------------
195 protected void OnTimer(IEntity owner, SCR_HitInfo hitInfo)
196 {
197 // Check if owner still exists and hasn't been deleted
198 if (!owner || owner.IsDeleted())
199 {
200 CleanupTimer();
201 return;
202 }
203
204 // Check if we've exceeded maximum duration
205 if (m_fMaxDuration > 0 && m_fElapsedTime >= m_fMaxDuration)
206 {
207 CleanupTimer();
208 return;
209 }
210
211 // Check distance-based triggering
212 if (!IsInRange(owner))
213 {
214 // Still schedule next timer even if out of range
215 ScheduleNextTimer(owner, hitInfo);
216 return;
217 }
218
219 // Spawn particle effect using parent's method
220 if (hitInfo)
221 {
222 super.Spawn(owner, null, hitInfo, false);
223 }
224
225 // Play sound if specified
226 if (!m_sSoundEvent.IsEmpty())
227 {
228 SoundComponent soundComponent = SoundComponent.Cast(owner.FindComponent(SoundComponent));
229 if (soundComponent)
230 {
231 soundComponent.SoundEvent(m_sSoundEvent);
232 }
233 }
234
235 // Schedule next timer
236 ScheduleNextTimer(owner, hitInfo);
237 }
238
239 //------------------------------------------------------------------------------------------------
241 protected void ScheduleNextTimer(IEntity owner, SCR_HitInfo hitInfo)
242 {
243 if (!m_CallQueue)
244 return;
245
246 float nextInterval;
247
248 if (m_bInSequence && m_iCurrentRepCount > 1)
249 {
250 // We're in a sequence, use regular interval
251 m_iCurrentRepCount--;
252 nextInterval = GetRandomInterval();
253 }
254 else
255 {
256 // Start new sequence or use sequence interval
257 m_iCurrentRepCount = GetRepCount();
258 m_bInSequence = true;
259
260 if (m_iCurrentRepCount > 1)
261 {
262 m_iCurrentRepCount--;
263 nextInterval = GetRandomInterval();
264 }
265 else
266 {
267 // Single shot, use sequence time for next
268 m_bInSequence = false;
269 nextInterval = GetSequenceRepTime();
270 }
271 }
272
273 // Update elapsed time
274 m_fElapsedTime += nextInterval;
275
276 // Schedule next call
277 m_CallQueue.CallLater(OnTimer, nextInterval * 1000, false, owner, hitInfo);
278 }
279
280 //------------------------------------------------------------------------------------------------
282 protected void CleanupTimer()
283 {
284 if (m_CallQueue)
285 {
286 m_CallQueue.Remove(OnTimer);
287 m_CallQueue = null;
288 }
289
290 m_fElapsedTime = 0;
291 m_iCurrentRepCount = 0;
292 m_bInSequence = false;
293 }
294
295
296 //------------------------------------------------------------------------------------------------
298 override ParticleEffectEntity Spawn(IEntity owner, Physics parentPhysics, SCR_HitInfo hitInfo, bool snapToTerrain = false)
299 {
300 if (!owner || !hitInfo)
301 return null;
302
303 // Initialize pseudo random numbers generator with seed based on owner's ID for deterministic behavior
304 if (!m_prng)
305 {
306 m_prng = new RandomGenerator;
307 }
308 int seed = owner.GetID();
309 m_prng.SetSeed(seed);
310
311 // Initialize timing variables
312 m_fElapsedTime = 0;
313 m_CallQueue = GetGame().GetCallqueue();
314 m_iCurrentRepCount = GetRepCount();
315 m_bInSequence = true;
316
317 // Spawn initial particle effect
318 ParticleEffectEntity initialParticle = ParticleEffectEntity.Cast(super.Spawn(owner, parentPhysics, hitInfo, snapToTerrain));
319
320 // Play initial sound if specified
321 if (!m_sSoundEvent.IsEmpty())
322 {
323 SoundComponent soundComponent = SoundComponent.Cast(owner.FindComponent(SoundComponent));
324 if (soundComponent)
325 {
326 vector spawnMat[4];
327 GetSpawnTransform(owner, spawnMat);
328 soundComponent.SoundEventTransform(m_sSoundEvent, spawnMat);
329 }
330 }
331
332 // Schedule first timer for subsequent spawns
333 if (m_CallQueue && m_fMaxDuration > 0)
334 {
335 float firstInterval;
336
337 if (m_iCurrentRepCount > 1)
338 {
339 m_iCurrentRepCount--;
340 firstInterval = GetRandomInterval();
341 }
342 else
343 {
344 m_bInSequence = false;
345 firstInterval = GetSequenceRepTime();
346 }
347
348 m_fElapsedTime += firstInterval;
349 m_CallQueue.CallLater(OnTimer, firstInterval * 1000, false, owner, hitInfo);
350 }
351
352 return initialParticle;
353 }
354
355 //------------------------------------------------------------------------------------------------
357 void OnDelete(IEntity owner)
358 {
359 CleanupTimer();
360 }
361
362 //------------------------------------------------------------------------------------------------
364 void SCR_RandomParticleSpawnable()
365 {
366 // Ensure valid interval range
367 if (m_fMinInterval > m_fMaxInterval)
368 {
369 float temp = m_fMinInterval;
370 m_fMinInterval = m_fMaxInterval;
371 m_fMaxInterval = temp;
372 }
373
374 // Ensure minimum values
375 if (m_fMinInterval < 0.1)
376 m_fMinInterval = 0.1;
377
378 if (m_fMaxInterval < 0.1)
379 m_fMaxInterval = 0.1;
380 }
381}
string path
ArmaReforgerScripted GetGame()
Definition game.c:1398
SCR_AIGroupClass snapToTerrain
void GetSpawnTransform(IEntity owner, out vector outMat[4], bool localCoords=false)
Calculates the spawn tranformation matrix for the object.
bool IsInRange(notnull IEntity actionOwner, vector actionPosition)
int m_iRepetitionCount
float distance
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
SCR_Spawnable_RandomParticleTitle BaseContainerCustomTitle BaseContainerProps()
void ParticleEffectEntity(IEntitySource src, IEntity parent)
string m_sSoundEvent
enum EVehicleType IEntity
proto external Managed FindComponent(typename typeName)
proto external BaseWorld GetWorld()
proto external EntityID GetID()
proto external void GetTransform(out vector mat[])
proto external bool IsDeleted()
Custom title for Workbench editor.
ScriptCallQueue Class provide "lazy" calls - when we don't want to execute function immediately but l...
Definition tools.c:53
AISpawnerGroupClass AIGroupClass Spawn()
Spawns a new group entity, sets its transformation and then calls OnSpawn.
SCR_FieldOfViewSettings Attribute