Dayz Explorer 1.28.160049
Loading...
Searching...
No Matches
effectarea.c
Go to the documentation of this file.
1// #define EFFECT_AREA_VISUAL_DEBUG
2
3// Mostly used for better readability
5{
6 STATIC = 1,
7 DYNAMIC = 2
9
10enum EEffectAreaType
11{
14 VOLCANIC = 4
15}
16
17// Class used for parameter transfer, all of the params are shadows of EffectArea member variables
18class EffectAreaParams
19{
20 string m_ParamName = "Default setup";
21 string m_ParamTriggerType = "ContaminatedTrigger";
22 float m_ParamRadius = 100;
23 float m_ParamPosHeight = 25;
24 float m_ParamNegHeight = 10;
25 int m_ParamInnerRings = 1;
26 int m_ParamInnerSpace = 35;
27 bool m_ParamOuterToggle = true;
28 int m_ParamOuterSpace = 20;
29 int m_ParamOuterOffset = -5;
30 int m_ParamVertLayers = 0;
31 int m_ParamVerticalOffset = 10;
32 int m_ParamEffectInterval = 0;
33 int m_ParamEffectDuration = 0;
34 int m_ParamEffectModifier = 0;
35 /*
36 int m_ParamPartId = ParticleList.CONTAMINATED_AREA_GAS_BIGASS;
37 int m_ParamAroundPartId = ParticleList.CONTAMINATED_AREA_GAS_AROUND;
38 int m_ParamTinyPartId = ParticleList.CONTAMINATED_AREA_GAS_TINY;
39 */
40 int m_ParamPartId = 0;
41 int m_ParamAroundPartId = 0;
42 int m_ParamTinyPartId = 0;
43
44 string m_ParamPpeRequesterType = "PPERequester_ContaminatedAreaTint";
45}
46
47// Base class for contaminated and other "Effect" areas
48class EffectArea : House
49{
50 // Area Data
51 string m_Name = "Default setup"; // The user defined name of the area
52 int m_Type = eZoneType.STATIC; // If the zone is static or dynamic
53 vector m_Position; // World position of area snapped to ground on creation (see: EffectAreaLoader)
54 vector m_PositionTrigger; // World position adjusted according to trigger pivot (pivot is cylinder center)
55 int m_EffectInterval; // If non persisent effect: determines intervals between effect activation
56 int m_EffectDuration; // If non persisent effect: determines duration of effect
57 bool m_EffectModifier; // Flag for modification of internal behavior of the effect
58
59 // Trigger Data
60 float m_Radius = 100; // Radius of the Contaminated Area
61 float m_PositiveHeight = 25; // Distance between center and maximum height
62 float m_NegativeHeight = 10; // Distance between center and minimum height
63
64 // PCG parameters
65 // Inner Particle data
66 int m_InnerRings = 1; // The amount of inner rings one wants
67 int m_InnerSpacing = 35; // Distance between two particles placed on inner rings of the area
68 // Outer particle data
69 bool m_OuterRingToggle = true; // Allow disabling outer ring if undesired
70 int m_OuterRingOffset = -5; // Distance between the outermost ring of particles and area radius
71 int m_OuterSpacing = 20; // Distance between two particles placed on the outer most ring of the area
72 // Verticality handling
73 int m_VerticalLayers = 0; // Used to add multiple layers vertically and set vertical spacing ( 0 don't do anything )
74 int m_VerticalOffset = 10; // Used to determine vertical offset between two vertical layers
75
76 // Particles and visual effects
77 int m_ParticleID = ParticleList.CONTAMINATED_AREA_GAS_BIGASS;
78 int m_AroundParticleID = ParticleList.CONTAMINATED_AREA_GAS_AROUND;
79 int m_TinyParticleID = ParticleList.CONTAMINATED_AREA_GAS_TINY;
80 string m_PPERequesterType;
81 int m_PPERequesterIdx = -1;
82 int m_EffectsPriority; // When multiple areas overlap, only the area with the highest priority will play its effects
83
84 const int PARTICLES_MAX = 1000; // Better safe than sorry
85
86 // Other values and storage
87 string m_TriggerType = "ContaminatedTrigger"; // The trigger class used by this zone
88 EffectTrigger m_Trigger; // The trigger used to determine if player is inside toxic area
89
90 ref array<Particle> m_ToxicClouds; // All static toxic clouds in ContaminatedArea
91
92 // ----------------------------------------------
93 // INITIAL SETUP
94 // ----------------------------------------------
95
96 void EffectArea()
97 {
98 RegisterNetSyncVariableFloat("m_Radius", 0, 0, 2);
99 RegisterNetSyncVariableFloat("m_PositiveHeight", 0, 0, 2);
100 RegisterNetSyncVariableFloat("m_NegativeHeight", 0, 0, 2);
101
102 RegisterNetSyncVariableInt("m_InnerRings");
103 RegisterNetSyncVariableInt("m_InnerSpacing");
104 RegisterNetSyncVariableInt("m_OuterRingOffset");
105 RegisterNetSyncVariableInt("m_OuterSpacing");
106 RegisterNetSyncVariableInt("m_VerticalLayers");
107 RegisterNetSyncVariableInt("m_VerticalOffset");
108
109 RegisterNetSyncVariableInt("m_ParticleID");
110 /*
111 RegisterNetSyncVariableInt("m_AroundParticleID");
112 RegisterNetSyncVariableInt("m_TinyParticleID");
113 RegisterNetSyncVariableInt("m_PPERequesterIdx");
114 */
115 RegisterNetSyncVariableBool("m_OuterRingToggle");
116 }
117
118 void ~EffectArea()
119 {
120
121 }
122
123 void SetupZoneData( EffectAreaParams params )
124 {
125 // A lot of branching, allowing to use default values on specified params
126 if ( params.m_ParamName != "" )
127 m_Name = params.m_ParamName;
128 if ( params.m_ParamTriggerType != "" )
129 m_TriggerType = params.m_ParamTriggerType;
130
131 if ( params.m_ParamRadius > 0 )
132 m_Radius = params.m_ParamRadius;
133 if ( params.m_ParamPosHeight > -1 )
134 m_PositiveHeight = params.m_ParamPosHeight;
135 if ( params.m_ParamNegHeight > -1 )
136 m_NegativeHeight = params.m_ParamNegHeight;
137
138 m_InnerRings = params.m_ParamInnerRings;
139 if ( params.m_ParamInnerSpace > -1 )
140 m_InnerSpacing = params.m_ParamInnerSpace;
141
142 m_OuterRingToggle = params.m_ParamOuterToggle;
143 if ( params.m_ParamOuterSpace > -1 )
144 m_OuterSpacing = params.m_ParamOuterSpace;
145 m_OuterRingOffset = params.m_ParamOuterOffset;
146
147 if ( params.m_ParamVertLayers > 0 )
148 m_VerticalLayers = params.m_ParamVertLayers;
149 if ( params.m_ParamVerticalOffset > 0 )
150 m_VerticalOffset = params.m_ParamVerticalOffset;
151
152 m_EffectInterval = params.m_ParamEffectInterval;
153 m_EffectDuration = params.m_ParamEffectDuration;
154 m_EffectModifier = params.m_ParamEffectModifier;
155
156 m_ParticleID = params.m_ParamPartId;
157 m_AroundParticleID = params.m_ParamAroundPartId;
158 m_TinyParticleID = params.m_ParamTinyPartId;
159
160 if ( params.m_ParamPpeRequesterType != "" )
161 {
162 m_PPERequesterType = params.m_ParamPpeRequesterType;
163 m_PPERequesterIdx = GetRequesterIndex(m_PPERequesterType);
164 }
165 // We get the PPE index for future usage and synchronization
166
167 // DEVELOPER NOTE :
168 // If you cannot register a new requester, add your own indexation and lookup methods to get an index and synchronize it
169 // EXAMPLE : m_PPERequesterIdx = MyLookupMethod()
170
171 #ifdef ENABLE_LOGGING
172 Debug.Log(">>>> SetupZoneData: Finished: " + m_Name);
173 #endif
174
175 // We sync our data
176 SetSynchDirty();
177 }
178
179 void Tick()
180 {
181 #ifdef DIAG_DEVELOPER
182 #ifdef EFFECT_AREA_VISUAL_DEBUG
183 CleanupDebugShapes(m_DebugTargets);
184 #endif
185 #endif
186 }
187
188
189 // Through this we will evaluate the resize of particles
190 override void OnCEUpdate()
191 {
192 super.OnCEUpdate();
193 Tick();
194 }
195
196 void InitZone()
197 {
198 // Debug.Log("------------------------------------------");
199 // Debug.Log("InitZone: " + m_Name);
200
202 m_PositionTrigger = m_Position;
203 m_PositionTrigger[1] = m_Position[1] + ((m_PositiveHeight - m_NegativeHeight) * 0.5); // Cylinder trigger pivot correction
204
205 if (!GetGame().IsDedicatedServer())
207
208 if (GetGame().IsServer())
210
211 // Debug.Log("------------------------------------------");
212 }
213
214 // The following methods are to be overriden to execute specifc logic
215 void InitZoneServer();
216 void InitZoneClient();
217
218 // ----------------------------------------------
219 // INTERACTION SETUP
220 // ----------------------------------------------
221
222 override bool CanPutInCargo( EntityAI parent )
223 {
224 return false;
225 }
226
227 override bool CanPutIntoHands( EntityAI parent )
228 {
229 return false;
230 }
231
232 override bool DisableVicinityIcon()
233 {
234 return true;
235 }
236
237 override bool CanBeTargetedByAI( EntityAI ai )
238 {
239 return false;
240 }
241
242 // ----------------------------------------------
243 // PARTICLE GENERATION
244 // ----------------------------------------------
245 // Used to position all particles procedurally
246 void PlaceParticles( vector pos, float radius, int nbRings, int innerSpacing, bool outerToggle, int outerSpacing, int outerOffset, int partId )
247 {
248 // Debug.Log("PlaceParticles: " + pos);
249
250 #ifdef NO_GUI
251 return; // do not place any particles if there is no GUI
252 #endif
253 if (partId == 0)
254 {
255 Error("[WARNING] :: [EffectArea PlaceParticles] :: no particle defined, skipping area particle generation" );
256 return;
257 }
258 if ( radius == 0 )
259 {
260 Error("[WARNING] :: [EffectArea PlaceParticles] :: Radius of contaminated zone is set to 0, this should not happen");
261 return;
262 }
263 if ( outerToggle && radius == outerOffset )
264 {
265 Error("[WARNING] :: [EffectArea PlaceParticles] :: Your outerOffset is EQUAL to your Radius, this will result in division by 0");
266 return;
267 }
268
269 int partCount = 0; // Number of spawned emitters
270
271 ParticlePropertiesArray props = new ParticlePropertiesArray();
272
273 // Inner spacing of 0 would cause infinite loops as no increment would happen
274 if (innerSpacing == 0)
275 innerSpacing = 1;
276
277 // For each concentric ring, we place a particle emitter at a set offset
278 for ( int i = 0; i <= nbRings + outerToggle; ++i )
279 {
280 if (i == 0) // Skipping 0, we want to start by placing a particle at center of area
281 {
282 SpawnParticles(props, pos, pos, partCount);
283 }
284 else
285 {
286 // We prepare the variables to use later in calculation
287 float angleIncrement; // The value added to the offset angle to place following particle
288 float ab; // Length of a side of triangle used to calculate particle positionning
289 vector temp = vector.Zero; // Vector we rotate to position next spawn point
290
291 // The particle density is not the same on the final ring which will only happen if toggled
292 // Toggle uses bool parameter treated as int, thus i > nbRings test ( allows to limit branching )
293 if ( i > nbRings )
294 {
295 ab = radius - outerOffset; // We want to leave some space to better see area demarcation
296
297 // We calculate the rotation angle depending on particle spacing and distance from center
298 angleIncrement = Math.Acos( 1 - ( ( outerSpacing * outerSpacing ) / ( 2 * Math.SqrInt(ab) ) ) );
299 temp[2] = temp[2] + ab;
300
301 //Debug.Log("Radius of last circle " + i + " is : " + ab);
302 }
303 else
304 {
305 ab = ( radius / ( nbRings + 1 ) ) * i; // We add the offset from one ring to another
306
307 // We calculate the rotation angle depending on particle spacing and distance from center
308 angleIncrement = Math.Acos( 1 - ( ( innerSpacing * innerSpacing ) / ( 2 * Math.SqrInt(ab) ) ) );
309 temp[2] = temp[2] + ab;
310
311 //Debug.Log("Radius of inner circle " + i + " is : " + ab);
312 }
313
314 for ( int j = 0; j <= ( Math.PI2 / angleIncrement ); j++ )
315 {
316 // Determine position of particle emitter
317 // Use offset of current ring for vector length
318
319 float sinAngle = Math.Sin(angleIncrement * j);
320 float cosAngle = Math.Cos(angleIncrement * j);
321
322 vector partPos = vector.RotateAroundZero( temp, vector.Up, cosAngle, sinAngle );
323 partPos += pos;
324
325 SpawnParticles(props, pos, partPos, partCount);
326 }
327 }
328 }
329
330 InsertParticles(props, partCount, partId);
331 }
332
333 // Fill the radius with particle emitters using the Circle packing in a circle method
334 void FillWithParticles(vector pos, float areaRadius, float outwardsBleed, float partSize, int partId)
335 {
336 // Debug.Log("FillWithParticles: " + pos);
337
338 #ifdef NO_GUI
339 return; // do not place any particles if there is no GUI
340 #endif
341 if (partId == 0)
342 return;
343
344 if (partSize <= 0)
345 partSize = 1;
346
347 int partCount = 0; // Number of spawned emitters
348 int ringCount = 0; // Number of area rings
349 float ringDist = 0; // Distance between rings
350
351 float radiusMax = areaRadius + outwardsBleed; // Visual radius of the area
352 float radiusPart = partSize / 2; // Particle radius
353
354 bool centerPart = true; // Spawn central particle?
355
356 ParticlePropertiesArray props = new ParticlePropertiesArray();
357
358 // Debug.Log("Area radius: " + radiusMax + "m, Particle radius: " + radiusPart + "m");
359
360 if (radiusMax > radiusPart * 1.5) // Area has to be larger than particle, plus some margin
361 {
362 if (radiusMax < radiusPart * 2.5) // Area fits one ring of particles, but no center particle (minus some overlap margin)
363 {
364 ringDist = radiusMax - radiusPart; // Snap the particles to outer edge
365 ringCount = 1;
366 centerPart = false;
367 }
368 else // Area fits all
369 {
370 radiusMax -= radiusPart; // Snap the particles to outer edge
371 ringCount = Math.Ceil(radiusMax / partSize); // Get number of inner rings
372 ringDist = radiusMax / ringCount; // Adjust ring distance after rounding
373 }
374 }
375
376 // Debug.Log("We have : " + ringCount + " rings, " + ringDist + "m apart, center: " + centerPart);
377 // Debug.Log("We have : " + m_VerticalLayers + " layers, " + m_VerticalOffset + "m apart");
378
379 // For each concentric ring, we place a particle emitter at a set offset
380 for (int ring = 0; ring <= ringCount; ++ring)
381 {
382 if (ring == 0 && centerPart) // We start by placing particle at center of area
383 {
384 SpawnParticles(props, pos, pos, partCount);
385 }
386 else if (ring > 0)
387 {
388 float ringRadius = ringDist * ring;
389 float circumference = 2 * Math.PI2 * ringRadius;
390
391 int count = Math.Floor(circumference / partSize); // Get number of particles on ring (roughly)
392 float angleInc = Math.PI2 / count; // Get angle between particles on ring
393
394 for (int i = 0; i < count; ++i) // Insert particles around the ring
395 {
396 vector partPos = pos;
397 float x = ringRadius * Math.Sin(angleInc * i);
398 float z = ringRadius * Math.Cos(angleInc * i);
399
400 partPos[0] = partPos[0] + x;
401 partPos[2] = partPos[2] + z;
402
403 SpawnParticles(props, pos, partPos, partCount);
404 }
405 }
406 }
407
408 InsertParticles(props, partCount, partId);
409 }
410
411 protected void SpawnParticles(ParticlePropertiesArray props, vector centerPos, vector partPos, inout int count)
412 {
413 partPos[1] = GetGame().SurfaceY(partPos[0], partPos[2]); // Snap particles to ground
414
415 // We also populate vertically, layer 0 will be snapped to ground, subsequent layers will see particles floating by m_VerticalOffset
416 for (int layer = 0; layer <= m_VerticalLayers; ++layer)
417 {
418 partPos[1] = partPos[1] + (m_VerticalOffset * layer);
419
420 // We check that spawned particle is inside the trigger
421 if (count < PARTICLES_MAX && Math.IsInRange(partPos[1], centerPos[1] - m_NegativeHeight, centerPos[1] + m_PositiveHeight))
422 {
423 props.Insert(ParticleProperties(partPos, ParticlePropertiesFlags.PLAY_ON_CREATION, null, GetGame().GetSurfaceOrientation( partPos[0], partPos[2] ), this));
424 ++count;
425 }
426 }
427 }
428
429 private void InsertParticles(ParticlePropertiesArray props, int count, int partId)
430 {
431 m_ToxicClouds.Reserve(count);
432
433 ParticleManager gPM = ParticleManager.GetInstance();
434
435 array<ParticleSource> createdParticles = gPM.CreateParticlesByIdArr(partId, props, count);
436 if (createdParticles.Count() != count)
437 {
438 if (gPM.IsFinishedAllocating())
439 {
440 ErrorEx(string.Format("Not enough particles in pool for EffectArea: %1", m_Name));
441 OnParticleAllocation(gPM, createdParticles);
442 }
443 else
444 {
445 gPM.GetEvents().Event_OnAllocation.Insert(OnParticleAllocation);
446 }
447 }
448 else
449 {
450 OnParticleAllocation(gPM, createdParticles);
451 }
452
453 // Debug.Log("Emitter count: " + count);
454 }
455
457 {
458 foreach (ParticleSource p : particles)
459 {
460 if (p.GetOwner() == this) // Safety, since it can be unrelated particles when done through event
461 {
462 m_ToxicClouds.Insert(p);
463 }
464 }
465 }
466
467 int GetRequesterIndex(string type)
468 {
469 typename t = type.ToType();
470 if (!t)
471 return - 1;
472 PPERequesterBase req = PPERequesterBank.GetRequester(t);
473 if (req)
474 return req.GetRequesterIDX();
475 return -1;
476 }
477
478
479 // ----------------------------------------------
480 // TRIGGER SETUP
481 // ----------------------------------------------
482 void CreateTrigger(vector pos, int radius)
483 {
484 #ifdef DIAG_DEVELOPER
485 #ifdef EFFECT_AREA_VISUAL_DEBUG
486 Shape dbgShape;
487 CleanupDebugShapes(m_DebugTargets);
488 #endif
489 #endif
490
491 // Create new trigger of specified type
492 if (Class.CastTo(m_Trigger, GetGame().CreateObjectEx(m_TriggerType, pos, ECE_NONE)))
493 {
494 // We finalize trigger dimension setup
495 float centerHeightCorrection = (m_PositiveHeight - m_NegativeHeight) * 0.5;
496
497 m_Trigger.SetCollisionCylinderTwoWay(radius, -(m_NegativeHeight + centerHeightCorrection), (m_PositiveHeight - centerHeightCorrection));
498 m_Trigger.SetPosition(pos);
499 m_Trigger.Update();
500
501 #ifdef DIAG_DEVELOPER
502 #ifdef EFFECT_AREA_VISUAL_DEBUG
503 /*
504 vector cubePos = pos;
505 cubePos[0] = cubePos[0] + radius;
506 cubePos[1] = cubePos[1] + (m_PositiveHeight - centerHeightCorrection);
507 cubePos[2] = cubePos[2] + radius;
508 m_DebugTargets.Insert(Debug.DrawCube(cubePos, 0.5, 0x1fff0000));
509 */
510
511 vector colliderPosDebug = pos;
513 colliderPosDebug[1] = pos[1] + (m_PositiveHeight - centerHeightCorrection);
514 m_DebugTargets.Insert(Debug.DrawSphere(colliderPosDebug, 0.15, 0x1f0000ff, ShapeFlags.NOZWRITE));
515 //m_DebugTargets.Insert(Debug.DrawLine(cubePos, colliderPosDebug, 0x1fff0000, ShapeFlags.NOZWRITE)); // connector
517 m_DebugTargets.Insert(Debug.DrawSphere(pos, 0.15, 0x1fff0000, ShapeFlags.NOZWRITE));
518 //m_DebugTargets.Insert(Debug.DrawLine(cubePos, pos, 0x1fff0000, ShapeFlags.NOZWRITE)); // connector
519
521 colliderPosDebug[1] = pos[1] - (m_NegativeHeight + centerHeightCorrection);
522 m_DebugTargets.Insert(Debug.DrawSphere(colliderPosDebug, 0.15, 0x1f00ff00, ShapeFlags.NOZWRITE));
523 //m_DebugTargets.Insert(Debug.DrawLine(cubePos, colliderPosDebug, 0x1fff0000, ShapeFlags.NOZWRITE)); // connector
524
525 float triggerHeight = (m_PositiveHeight + m_NegativeHeight);
526 m_DebugTargets.Insert(Debug.DrawCylinder(pos, radius, triggerHeight, 0x1f0000ff, ShapeFlags.TRANSP|ShapeFlags.NOZWRITE));
527 #endif
528 #endif
529
530 // If the trigger is lower in hierarchy and can see it's local effects customized, we pass the new parameters
531 if ( m_Trigger.IsInherited(EffectTrigger))
532 {
533 //Debug.Log("We override area local effects");
534 EffectTrigger.Cast(m_Trigger).SetLocalEffects(m_AroundParticleID, m_TinyParticleID, m_PPERequesterIdx);
535 }
536 m_Trigger.Init(this, m_EffectsPriority);
537 //Debug.Log("We created the trigger at : " + m_Trigger.GetWorldPosition() );
538 }
539 }
540
541 // ----------------------------------------------
542 // AREA DELETION
543 // ----------------------------------------------
544
545 override void EEDelete( EntityAI parent )
546 {
547 if ( m_Trigger )
548 {
549 GetGame().ObjectDelete( m_Trigger );
550 }
551
552 // We stop playing particles on this client when the base object is deleted ( out of range for example )
553 if ( (GetGame().IsClient() || !GetGame().IsMultiplayer()) && m_ToxicClouds )
554 {
555 foreach ( Particle p : m_ToxicClouds )
556 {
557 p.Stop();
558 }
559 }
560
561 super.EEDelete( parent );
562 }
563
564 void OnPlayerEnterServer(PlayerBase player, EffectTrigger trigger)
565 {
566 player.IncreaseEffectAreaCount();
567 }
568 void OnPlayerExitServer(PlayerBase player, EffectTrigger trigger)
569 {
570 player.DecreaseEffectAreaCount();
571 }
572
573 #ifdef DIAG_DEVELOPER
574 #ifdef EFFECT_AREA_VISUAL_DEBUG
575 protected ref array<Shape> m_DebugTargets = new array<Shape>();
576
577 protected void CleanupDebugShapes(array<Shape> shapes)
578 {
579 foreach (Shape shape : shapes)
580 Debug.RemoveShape(shape);
581
582 shapes.Clear();
583 }
584 #endif
585 #endif
586}
eBleedingSourceType m_Type
float m_Radius
const int ECE_NONE
proto native float SurfaceY(float x, float z)
Super root of all classes in Enforce script.
Definition enscript.c:11
Definition debug.c:2
Definition enmath.c:7
int GetRequesterIDX()
Returns requester index.
Legacy way of using particles in the game.
Definition particle.c:7
Entity which has the particle instance as an ObjectComponent.
Result for an object found in CGame.IsBoxCollidingGeometryProxy.
override void OnPlayerExitServer(PlayerBase player, EffectTrigger trigger)
override void OnPlayerEnterServer(PlayerBase player, EffectTrigger trigger)
override void InitZoneServer()
override void InitZoneClient()
override void EEDelete(EntityAI parent)
override void Tick()
override void SetupZoneData(EffectAreaParams params)
override void OnParticleAllocation(ParticleManager pm, array< ParticleSource > particles)
override void InitZone()
override bool DisableVicinityIcon()
Definition dayzanimal.c:75
vector m_Position
Cached world position.
Definition effect.c:43
enum eZoneType UNDEFINED
enum eZoneType HOT_SPRING
eZoneType
Definition effectarea.c:5
override void OnCEUpdate()
override bool CanPutInCargo(EntityAI parent)
override bool CanPutIntoHands(EntityAI parent)
proto native CGame GetGame()
void Error(string err)
Messagebox with error message.
Definition endebug.c:90
enum ShapeType ErrorEx
ShapeFlags
Definition endebug.c:126
class DiagMenu Shape
don't call destructor directly. Use Destroy() instead
@ DYNAMIC
Definition effectarea.c:7
@ STATIC
Definition effectarea.c:6
class JsonUndergroundAreaTriggerData GetPosition
Icon x
void ParticleManager(ParticleManagerSettings settings)
Constructor (ctor)
class SyncedValue m_Name
void CreateTrigger()
Definition trapbase.c:475