Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_DestructibleBuildingComponent.c
Go to the documentation of this file.
1//------------------------------------------------------------------------------------------------
3{
4 [Attribute()]
5 ref array<ref SCR_TimedEffect> m_aEffects;
6
7 [Attribute(desc: "Useful for complex buildings. Leave empty to use entity's bounding box.")]
8 ref array<ref SCR_InteriorBoundingBox> m_aInteriorQueryBoundingBoxes;
9
10 [Attribute(defvalue: "0", desc: "Delay in seconds between damage and the beginning of the building transition")]
11 float m_fDelay;
12
13 [Attribute(defvalue: "1", desc: "Meters per second")]
14 float m_fSinkingSpeed;
15
16 [Attribute(defvalue: "150", desc: "This speeds up the sinking gradually % per second.", params: "0 10000 1")]
17 float m_fSinkingSpeedGradualMultiplier;
18
19 [Attribute(defvalue: "0.1", desc: "Degrees per second")]
20 float m_fRotationSpeed;
21
22 [Attribute(defvalue: "0.8", desc: "Time between rotation changes when building is collapsing in seconds")]
23 float m_fRotationTime;
24
25 [Attribute(defvalue: "50", desc: "Rotation time randomizer in % - can both shorten/prolong the time", params: "0 10000 1")]
26 float m_fRotationTimeRandom;
27
28 [Attribute(defvalue: "1", desc: "Max rotations count while sinking")]
29 int m_iMaxRotations;
30
31 [Attribute(defvalue : vector.Zero.ToString(), desc: "This vector defines offset for the final position after destruction.")]
32 vector m_vSinkVector;
33
34 [Attribute("", UIWidgets.Auto, desc: "Slow down event audio source configuration")]
35 ref SCR_AudioSourceConfiguration m_AudioSourceConfiguration;
36
37 [Attribute(uiwidget: UIWidgets.CurveDialog)]
38 ref Curve m_CameraShakeCurve;
39
40 [Attribute(uiwidget: UIWidgets.Flags, enums: ParamEnumArray.FromEnum(SCR_EDestructionRotationEnum))]
41 SCR_EDestructionRotationEnum m_eAllowedRotations;
42
43 [Attribute()]
44 ref DestructionHeatmapEntry m_EntryData;
45
46};
47
48//------------------------------------------------------------------------------------------------
49[BaseContainerProps(namingConvention: NamingConvention.NC_CAN_HAVE_NAME)]
51{
52 [Attribute()]
53 ref PointInfo m_vCenter;
54
55 [Attribute(defvalue: "1 1 1", params: "inf inf purpose=sizes space=custom", uiwidget: UIWidgets.BoundingVolumeEditor)]
56 protected vector m_vScale;
57
58 [Attribute(defvalue: "0 1 1 1")]
59 protected ref Color m_Color;
60
61 //------------------------------------------------------------------------------------------------
62 bool GetCenter(out vector center)
63 {
64 if (!m_vCenter)
65 return false;
66
67 vector centerTransform[4];
68 m_vCenter.GetTransform(centerTransform);
69
70 center = centerTransform[3];
71 return true;
72 }
73
74 //------------------------------------------------------------------------------------------------
76 {
77 return m_vScale;
78 }
79
80 //------------------------------------------------------------------------------------------------
81 void GetBounds(out vector mins, out vector maxs)
82 {
83 vector center;
84 GetCenter(center);
85
86 mins = {center[0] - m_vScale[0] * 0.5, center[1] - m_vScale[1] * 0.5, center[2] - m_vScale[2] * 0.5};
87 maxs = {center[0] + m_vScale[0] * 0.5, center[1] + m_vScale[1] * 0.5, center[2] + m_vScale[2] * 0.5};
88 }
89
90#ifdef WORKBENCH
91 //------------------------------------------------------------------------------------------------
92 void DrawDebug(vector ownerTransform[4])
93 {
94 vector center;
95 GetCenter(center);
96 vector halfScale = m_vScale * 0.5;
97
98 vector points[8] = {{-halfScale[0], -halfScale[1], -halfScale[2]} + center,
99 {halfScale[0], halfScale[1], halfScale[2]} + center,
100 {halfScale[0], -halfScale[1], -halfScale[2]} + center,
101 {-halfScale[0], halfScale[1], halfScale[2]} + center,
102 {-halfScale[0], halfScale[1], -halfScale[2]} + center,
103 {halfScale[0], -halfScale[1], halfScale[2]} + center,
104 {-halfScale[0], -halfScale[1], halfScale[2]} + center,
105 {halfScale[0], halfScale[1], -halfScale[2]} + center};
106
107 // Before you freak out, this is all just workbench debug visuals :)
108 vector p0[] = {points[0].Multiply4(ownerTransform), points[4].Multiply4(ownerTransform)};
109 vector p1[] = {points[2].Multiply4(ownerTransform), points[7].Multiply4(ownerTransform)};
110 vector p2[] = {points[5].Multiply4(ownerTransform), points[1].Multiply4(ownerTransform)};
111 vector p3[] = {points[6].Multiply4(ownerTransform), points[3].Multiply4(ownerTransform)};
112
113 vector p4[] = {points[0].Multiply4(ownerTransform), points[2].Multiply4(ownerTransform)};
114 vector p5[] = {points[2].Multiply4(ownerTransform), points[5].Multiply4(ownerTransform)};
115 vector p6[] = {points[5].Multiply4(ownerTransform), points[6].Multiply4(ownerTransform)};
116 vector p7[] = {points[6].Multiply4(ownerTransform), points[0].Multiply4(ownerTransform)};
117
118 vector p8[] = {points[4].Multiply4(ownerTransform), points[7].Multiply4(ownerTransform)};
119 vector p9[] = {points[7].Multiply4(ownerTransform), points[1].Multiply4(ownerTransform)};
120 vector p10[] = {points[1].Multiply4(ownerTransform), points[3].Multiply4(ownerTransform)};
121 vector p11[] = {points[3].Multiply4(ownerTransform), points[4].Multiply4(ownerTransform)};
122
123 ShapeFlags shapeFlags = ShapeFlags.ONCE | ShapeFlags.NOZBUFFER | ShapeFlags.TRANSP | ShapeFlags.DOUBLESIDE | ShapeFlags.NOOUTLINE;
124
125 if (!m_Color)
126 m_Color = Color.Blue;
127 int color = m_Color.PackToInt();
128
129 // Bounding Box markers
130 Shape.CreateLines(color, shapeFlags, p0, 2);
131 Shape.CreateLines(color, shapeFlags, p1, 2);
132 Shape.CreateLines(color, shapeFlags, p2, 2);
133 Shape.CreateLines(color, shapeFlags, p3, 2);
134
135 Shape.CreateLines(color, shapeFlags, p4, 2);
136 Shape.CreateLines(color, shapeFlags, p5, 2);
137 Shape.CreateLines(color, shapeFlags, p6, 2);
138 Shape.CreateLines(color, shapeFlags, p7, 2);
139
140 Shape.CreateLines(color, shapeFlags, p8, 2);
141 Shape.CreateLines(color, shapeFlags, p9, 2);
142 Shape.CreateLines(color, shapeFlags, p10, 2);
143 Shape.CreateLines(color, shapeFlags, p11, 2);
144
145 // Calculate min and max points for the box
146 vector mins = {center[0] - halfScale[0], center[1] - halfScale[1], center[2] - halfScale[2]};
147 vector maxs = {center[0] + halfScale[0], center[1] + halfScale[1], center[2] + halfScale[2]};
148
149 // Create a semi-transparent box using our new AddBoxWithTransform method that transforms all points by owner transform
150 color = Color(m_Color.R(), m_Color.G(), m_Color.B(), 0.25).PackToInt();
151 SCR_DebugShapeHelperComponent.AddBoxWithTransform(mins, maxs, ownerTransform, color, ShapeFlags.ONCE | ShapeFlags.TRANSP | ShapeFlags.DOUBLESIDE);
152 }
153#endif
154}
155
156//------------------------------------------------------------------------------------------------
157enum SCR_EDestructionRotationEnum
158{
159 NONE = 0,
162 ROTATION_Z = 4
163}
164
165//------------------------------------------------------------------------------------------------
166class SCR_BuildingDestructionCameraShakeProgress : SCR_NoisyCameraShakeProgress
167{
168 protected const float MAX_MULTIPLIER = 1.5;
169 protected float m_fMaxDistance = 50;
170 protected float m_fSizeMultiplier = 1;
171
172 protected ref Curve m_CameraShakeCurve;
173 protected vector m_vStartOrigin;
174
175 //------------------------------------------------------------------------------------------------
176 override void Update(IEntity owner, float timeSlice)
177 {
178 if (!IsRunning())
179 return;
180
181 super.Update(owner, timeSlice);
182
183 CameraBase camera = GetGame().GetCameraManager().CurrentCamera();
184 if (!camera)
185 return;
186
187 float distanceSq = vector.DistanceSqXZ(m_vStartOrigin, camera.GetOrigin());
188 float multiplier = 1 - Math.Clamp(distanceSq / (m_fMaxDistance * m_fMaxDistance * m_fSizeMultiplier), 0, 1);
189 float curveMultiplier = LegacyCurve.Curve(ECurveType.CatmullRom, 1 - multiplier, m_CameraShakeCurve)[1];
190
191 m_vTranslation *= Math.Min(multiplier * m_fSizeMultiplier * curveMultiplier, MAX_MULTIPLIER);
192 m_vRotation *= Math.Min(multiplier * m_fSizeMultiplier * curveMultiplier, MAX_MULTIPLIER);
193 }
194
195 //------------------------------------------------------------------------------------------------
196 void SetStartOrigin(vector startOrigin)
197 {
198 m_vStartOrigin = startOrigin
199 }
200
201 //------------------------------------------------------------------------------------------------
202 void SetCurve(Curve curve)
203 {
204 m_CameraShakeCurve = curve;
205 }
206
207 //------------------------------------------------------------------------------------------------
208 void SetSizeMultiplier(float sizeMultiplier)
209 {
210 m_fSizeMultiplier = Math.Min(sizeMultiplier, MAX_MULTIPLIER);
211 }
212}
213
214//------------------------------------------------------------------------------------------------
215[BaseContainerProps(namingConvention: NamingConvention.NC_MUST_HAVE_NAME)]
216class SCR_TimedDebris : SCR_TimedEffect
217{
218 [Attribute("0 0 0", UIWidgets.Coords, desc: "Positional offset (in local space to the destructible)")]
219 protected vector m_vOffsetPosition;
220
221 [Attribute("0 0 0", UIWidgets.Coords, desc: "Yaw, pitch & roll offset (in local space to the destructible)")]
222 protected vector m_vOffsetRotation;
223
224 [Attribute(ResourceName.Empty, UIWidgets.ResourcePickerThumbnail, "Debris model prefabs to spawn (spawns ALL of them)", "et xob")]
225 ref array<ResourceName> m_ModelPrefabs;
226
227 [Attribute("10", UIWidgets.Slider, "Mass of the debris", "0.01 1000 0.01")]
228 float m_fMass;
229
230 [Attribute("5", UIWidgets.Slider, "Minimum lifetime value for the debris (in s)", "0 3600 0.5")]
231 float m_fLifetimeMin;
232
233 [Attribute("10", UIWidgets.Slider, "Maximum lifetime value for the debris (in s)", "0 3600 0.5")]
234 float m_fLifetimeMax;
235
236 [Attribute("200", UIWidgets.Slider, "Maximum distance from camera above which the debris is not spawned (in m)", "0 3600 0.5")]
237 float m_fDistanceMax;
238
239 [Attribute("0", UIWidgets.Slider, "Higher priority overrides lower priority if at or over debris limit", "0 100 1")]
240 int m_fPriority;
241
242 [Attribute("0.1", UIWidgets.Slider, "Damage received to physics impulse (speed / mass) multiplier", "0 10000 0.01")]
243 float m_fDamageToImpulse;
244
245 [Attribute("2", UIWidgets.Slider, "Damage to speed multiplier, used when objects get too much damage to impulse", "0 10000 0.01")]
246 float m_fMaxDamageToSpeedMultiplier;
247
248 [Attribute("0.5", UIWidgets.Slider, "Random linear velocity multiplier (m/s)", "0 200 0.1")]
250
251 [Attribute("180", UIWidgets.Slider, "Random angular velocity multiplier (deg/s)", "0 3600 0.1")]
253
254 [Attribute("0", uiwidget: UIWidgets.ComboBox, "Type of material for debris sound", "", ParamEnumArray.FromEnum(SCR_EMaterialSoundTypeDebris))]
255 SCR_EMaterialSoundTypeDebris m_eMaterialSoundType;
256
257 //------------------------------------------------------------------------------------------------
259 void GetSpawnTransform(IEntity owner, out vector outMat[4], bool localCoords = false)
260 {
261 if (localCoords)
262 {
263 Math3D.AnglesToMatrix(m_vOffsetRotation, outMat);
264 // TODO: Remove hotfix for sleeping/static object
265 if (m_vOffsetPosition == vector.Zero)
266 outMat[3] = vector.Up * 0.001;
267 else
268 outMat[3] = m_vOffsetPosition;
269 }
270 else
271 {
272 vector localMat[4], parentMat[4];
273 owner.GetWorldTransform(parentMat);
274 Math3D.AnglesToMatrix(m_vOffsetRotation, localMat);
275 localMat[3] = m_vOffsetPosition;
276
277 Math3D.MatrixMultiply4(parentMat, localMat, outMat);
278 }
279 }
280
281 //------------------------------------------------------------------------------------------------
283 override void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
284 {
285 int numModelPrefabs = 0;
286 if (m_ModelPrefabs)
287 numModelPrefabs = m_ModelPrefabs.Count();
288
289 for (int i = 0; i < numModelPrefabs; i++)
290 {
291 ResourceName prefabPath = m_ModelPrefabs[i];
292
293 ResourceName modelPath;
294 string remap;
295 SCR_Global.GetModelAndRemapFromResource(prefabPath, modelPath, remap);
296
297 if (modelPath == ResourceName.Empty)
298 continue;
299
300 vector spawnMat[4];
301 GetSpawnTransform(owner, spawnMat);
302
303 SCR_DestructionDamageManagerComponent destructionComponent = SCR_DestructionDamageManagerComponent.Cast(owner.FindComponent(SCR_DestructionDamageManagerComponent));
304
305 float dmgSpeed = Math.Clamp(hitInfo.m_HitDamage * m_fDamageToImpulse / m_fMass, 0, m_fMaxDamageToSpeedMultiplier);
306
307 vector linearVelocity = hitInfo.m_HitDirection * Math.RandomFloat(0, 1);
308 linearVelocity += Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) * m_fRandomVelocityLinear;
309 linearVelocity *= dmgSpeed;
310 vector angularVelocity = Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) * Math.RandomFloat(0.25, 4) * m_fRandomVelocityAngular;
311 angularVelocity *= dmgSpeed;
312
313 SCR_DebrisSmallEntity.SpawnDebris(owner.GetWorld(), spawnMat, modelPath, m_fMass, Math.RandomFloat(m_fLifetimeMin, m_fLifetimeMax), m_fDistanceMax, m_fPriority, linearVelocity, angularVelocity, remap, false, m_eMaterialSoundType);
314 }
315 }
316}
317
318//------------------------------------------------------------------------------------------------
319[BaseContainerProps(namingConvention: NamingConvention.NC_MUST_HAVE_NAME)]
320class SCR_TimedEffect : Managed
321{
322 [Attribute("0", UIWidgets.Slider, "Set time in % of sinking the building. 0 = Immediately, can happen before the sinking starts if delay is used", "0 1 0.01")]
323 float m_fSpawnTime;
324
325 [Attribute()]
326 bool m_bSnapToTerrain;
327
328 [Attribute()]
329 bool m_bAttachToParent;
330
331 [Attribute("0", desc: "Does this effect remain after destruction? F. E. ruins.")]
332 bool m_bPersistent;
333
334 //------------------------------------------------------------------------------------------------
336 void SnapToTerrain(inout vector origin, IEntity owner)
337 {
338 if (!m_bSnapToTerrain)
339 return;
340
341 float y = SCR_TerrainHelper.GetTerrainY(origin, owner.GetWorld());
342 origin[1] = y;
343 }
344
345 //------------------------------------------------------------------------------------------------
347 void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
348 {
349 }
351
352//------------------------------------------------------------------------------------------------
353[BaseContainerProps(namingConvention: NamingConvention.NC_MUST_HAVE_NAME)]
354class SCR_TimedSound : SCR_TimedEffect
355{
356 [Attribute("", UIWidgets.Auto, desc: "Audio Source Configuration")]
357 protected ref SCR_AudioSourceConfiguration m_AudioSourceConfiguration;
358
359 //------------------------------------------------------------------------------------------------
361 override void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
362 {
363 super.ExecuteEffect(owner, hitInfo, data);
364
365 if (!System.IsConsoleApp())
366 {
367 SCR_SoundManagerModule soundManager = SCR_SoundManagerModule.GetInstance(owner.GetWorld());
368 if (!soundManager || !m_AudioSourceConfiguration || !m_AudioSourceConfiguration.IsValid())
369 return;
370
372 if (!destructibleComponent)
373 return;
374
375 SCR_AudioSource audioSource = soundManager.CreateAudioSource(owner, m_AudioSourceConfiguration, data.m_vStartMatrix[3]);
376 if (!audioSource)
377 return;
378
379 destructibleComponent.SetAudioSource(audioSource);
380
381 soundManager.PlayAudioSource(audioSource);
382 }
383 }
384
385#ifdef WORKBENCH
386 //------------------------------------------------------------------------------------------------
387 SCR_AudioSourceConfiguration GetAudioSourceConfiguration()
388 {
390 }
391#endif
392};
393
394//------------------------------------------------------------------------------------------------
395[BaseContainerProps(namingConvention: NamingConvention.NC_MUST_HAVE_NAME)]
396class SCR_TimedPrefab : SCR_TimedEffect
397{
398 [Attribute(desc: "Defines what remains after the building is destroyed.", params: "et")]
400
401 [Attribute("1", desc: "If this prefab has an RplComponent then it should be only spawned by the server")]
402 protected bool m_bSpawnedByTheServer;
403
404 //------------------------------------------------------------------------------------------------
406 override void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
407 {
408 super.ExecuteEffect(owner, hitInfo, data);
409
410 RplComponent rplComp = SCR_EntityHelper.GetEntityRplComponent(owner);
411 if (m_bSpawnedByTheServer && (!rplComp || rplComp.IsProxy()))
412 return;
413
414 Resource resource = Resource.Load(m_sRuinsPrefab);
415 if (!resource || !resource.IsValid())
416 return;
417
418 vector mat[4];
419 mat = data.m_vStartMatrix;
420
421 if (m_bSnapToTerrain)
422 {
423 vector origin = mat[3];
424 SnapToTerrain(origin, owner);
425 mat[3] = origin;
426 }
427
429 params.Transform = mat;
430 params.TransformMode = ETransformMode.WORLD;
431
432 if (m_bAttachToParent)
433 params.Parent = owner;
434
435 IEntity spawnedEntity = GetGame().SpawnEntityPrefab(resource, owner.GetWorld(), params);
436 if (!spawnedEntity)
437 {
438 Debug.Error(string.Format("Could not spawn prefab %1 in SCR_TimedPrefab.ExecuteEffect()", m_sRuinsPrefab));
439 return;
440 }
441
442 auto persistence = PersistenceSystem.GetInstance();
443 if (persistence)
444 persistence.StartTracking(spawnedEntity);
445
446 data.m_aExcludeList.Insert(spawnedEntity);
447 }
448};
449
450//------------------------------------------------------------------------------------------------
451[BaseContainerProps(namingConvention: NamingConvention.NC_MUST_HAVE_NAME)]
452class SCR_TimedParticle : SCR_TimedEffect
453{
454 [Attribute()]
456
457 [Attribute("1", desc: "Overall multiplier for particle effect intensity. Higher values multiplies particle count (Birt Rate + Birth Rate RND), size (Size Multiplier and Size RND) , and velocity (Velocity and Velocity RND).")]
458 protected float m_fParticlesMultiplier;
459
461 {
462 if (!m_Particle)
463 {
464 Print("No SCR_ParticleSpawnable defined on SCR_TimedParticle", LogLevel.WARNING);
465 }
466 }
467
468 //------------------------------------------------------------------------------------------------
470 override void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
471 {
472 super.ExecuteEffect(owner, hitInfo, data);
473
474 if (m_Particle == null)
475 return;
476
477 ParticleEffectEntity emitter;
478 if (m_bAttachToParent)
479 emitter = m_Particle.SpawnAsChild(owner, hitInfo, m_bSnapToTerrain);
480 else
481 emitter = ParticleEffectEntity.Cast(m_Particle.Spawn(owner, owner.GetPhysics(), hitInfo, m_bSnapToTerrain));
482
483 if (!emitter)
484 {
485 Debug.Error("No emitter was spawned in SCR_TimedParticle.ExecuteEffect()");
486 return;
487 }
488
489 SetParticleParams(emitter, data);
490
491 data.m_aExcludeList.Insert(emitter);
492 }
493
494 //------------------------------------------------------------------------------------------------
495 void SetParticleParams(ParticleEffectEntity emitter, inout notnull SCR_BuildingDestructionData data)
496 {
497 Particles particles = emitter.GetParticles();
498 if (!particles)
499 return;
500
501 particles.MultParam(-1, EmitterParam.BIRTH_RATE, Math.Clamp(data.m_fSizeMultiplier, 0.5, 2) * m_fParticlesMultiplier);
502 particles.MultParam(-1, EmitterParam.BIRTH_RATE_RND, Math.Clamp(data.m_fSizeMultiplier, 0.5, 2) * m_fParticlesMultiplier);
503 particles.MultParam(-1, EmitterParam.SIZE, Math.Clamp(data.m_fSizeMultiplier, 0.5, 1) * Math.Clamp(m_fParticlesMultiplier, 0.1, 2));
504 particles.MultParam(-1, EmitterParam.VELOCITY, Math.Clamp(data.m_fSizeMultiplier, 1, 2) * m_fParticlesMultiplier);
505 particles.MultParam(-1, EmitterParam.VELOCITY_RND, Math.Clamp(data.m_fSizeMultiplier, 1, 2) * m_fParticlesMultiplier);
506 }
507
508#ifdef WORKBENCH
509 //------------------------------------------------------------------------------------------------
511 SCR_ParticleSpawnable GetParticle()
512 {
513 return m_Particle;
514 }
515 //------------------------------------------------------------------------------------------------
517 float GetParticlesMultiplier()
518 {
520 }
521#endif
522};
523
524//------------------------------------------------------------------------------------------------
525class SCR_DestructibleBuildingComponent : SCR_DamageManagerComponent
526{
527 // In project settings you can see the physics response indices & their interactions matrix
528 protected const int NO_COLLISION_RESPONSE_INDEX = 11;
529 protected const int MAX_CHECKS_PER_FRAME = 20;
530 protected const float BUILDING_SIZE = 5000;
531 protected const vector TRACE_DIRECTIONS[3] = {vector.Right, vector.Up, vector.Forward};
532
533 private int m_iDataIndex = -1;
534
535 protected bool m_bDestroyed = false;
536
537 protected ref array<vector> m_aPowerPolePositions = {};
538
539 //------------------------------------------------------------------------------------------------
541 protected SCR_BuildingDestructionData GetData()
542 {
543 SCR_BuildingDestructionManagerComponent manager = GetGame().GetBuildingDestructionManager();
544 if (!manager)
545 {
546 Print("SCR_BuildingDestructionManagerComponent not found! Building destruction won't work.", LogLevel.ERROR);
547 return null;
548 }
549
550 return manager.GetData(m_iDataIndex);
551 }
552
553 //------------------------------------------------------------------------------------------------
555 protected void FreeData()
556 {
557 SCR_BuildingDestructionManagerComponent manager = GetGame().GetBuildingDestructionManager();
558 if (!manager)
559 {
560 Print("SCR_BuildingDestructionManagerComponent not found! Building destruction won't work.", LogLevel.ERROR);
561 return;
562 }
563
564 manager.FreeData(m_iDataIndex);
565 m_iDataIndex = -1;
566 }
567
568 //------------------------------------------------------------------------------------------------
572 protected void DestroyChildDestructibles(notnull IEntity owner)
573 {
574 // Pre-allocate shared resources to avoid repeated allocations
575 Instigator sharedInstigator = GetInstigator();
576 if (!sharedInstigator)
577 sharedInstigator = Instigator.CreateInstigator(null);
578
579 HitZone defaultHitZone;
580 vector hitPosDirNorm[3]; // Empty for TRUE damage type
581 // Trigger destruction - child will handle its own sub-children
582 SCR_DamageContext damageContext = new SCR_DamageContext(
583 EDamageType.TRUE,
584 1,
585 hitPosDirNorm,
586 null,
587 defaultHitZone,
588 sharedInstigator,
589 null,
590 -1,
591 -1
592 );
593
594 // Iterate through direct children and destroy those with destructible components
595 IEntity child = owner.GetChildren();
596 IEntity nextChild;
597 while (child)
598 {
599 // Store next sibling BEFORE any modifications to be safe
600 nextChild = child.GetSibling();
601
604 );
605
606 if (childDestructibleComp)
607 {
608 defaultHitZone = childDestructibleComp.GetDefaultHitZone();
609
610 // Detach from parent before triggering destruction
611 owner.RemoveChild(child, true);
612
613 damageContext.damageValue = defaultHitZone.GetMaxHealth();
614 damageContext.struckHitZone = defaultHitZone;
615 damageContext.hitEntity = child;
616
617 childDestructibleComp.HandleDamage(damageContext);
618 }
619
620 child = nextChild;
621 }
622 }
623
624 //------------------------------------------------------------------------------------------------
627 {
628 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_fSinkingSpeedGradualMultiplier;
629 }
630
631 //------------------------------------------------------------------------------------------------
633 {
634 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_fRotationTimeRandom;
635 }
636
637 //------------------------------------------------------------------------------------------------
638 protected float GetRotationSpeed()
639 {
641 }
642
643 //------------------------------------------------------------------------------------------------
644 protected float GetRotationTime()
645 {
647 }
648
649 //------------------------------------------------------------------------------------------------
650 protected int GetMaxRotations()
651 {
653 }
654
655 //------------------------------------------------------------------------------------------------
657 protected float GetSpeed()
658 {
660 }
661
662 //------------------------------------------------------------------------------------------------
664 {
666 }
667
668 //------------------------------------------------------------------------------------------------
670 protected float GetDelay()
671 {
673 }
674
675 //------------------------------------------------------------------------------------------------
676 protected ref array<ref SCR_InteriorBoundingBox> GetInteriorBoundingBoxes()
677 {
678 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_aInteriorQueryBoundingBoxes;
679 }
680
681 //------------------------------------------------------------------------------------------------
682 protected bool GetAllowRotationX()
683 {
684 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_eAllowedRotations & SCR_EDestructionRotationEnum.ROTATION_X;
685 }
686
687 //------------------------------------------------------------------------------------------------
688 protected bool GetAllowRotationY()
689 {
690 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_eAllowedRotations & SCR_EDestructionRotationEnum.ROTATION_Y;
691 }
692
693 //------------------------------------------------------------------------------------------------
694 protected bool GetAllowRotationZ()
695 {
696 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_eAllowedRotations & SCR_EDestructionRotationEnum.ROTATION_Z;
697 }
698
699 //------------------------------------------------------------------------------------------------
701 {
702 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_CameraShakeCurve;
703 }
704
705 //------------------------------------------------------------------------------------------------
707 protected array<ref SCR_TimedEffect> GetEffects()
708 {
710 }
711
712 //------------------------------------------------------------------------------------------------
713 protected SCR_AudioSourceConfiguration GetSlowDownAudioSourceConfiguration()
714 {
715 return SCR_DestructibleBuildingComponentClass.Cast(GetComponentData(GetOwner())).m_AudioSourceConfiguration;
716 }
717
718 //------------------------------------------------------------------------------------------------
720 {
721 SCR_BuildingDestructionData data = GetData();
722 if (!data)
723 return;
724
725 if (data.m_AudioSource)
726 {
727 // Kill previous sound
728 SCR_SoundManagerModule soundManager = SCR_SoundManagerModule.GetInstance(GetOwner().GetWorld());
729 if (soundManager)
730 soundManager.TerminateAudioSource(data.m_AudioSource);
731 }
732
733 data.m_AudioSource = audioSource;
734
735 if (audioSource)
736 data.m_AudioSource.SetSignalValue(SCR_AudioSource.ENTITY_SIZE_SIGNAL_NAME, data.m_fBuildingVolume);
737 }
738
739 //------------------------------------------------------------------------------------------------
741 {
742 SCR_BuildingDestructionData data = GetData();
743 if (!data)
744 return null;
745
746 return data.m_AudioSource;
747 }
748
749 //------------------------------------------------------------------------------------------------
750 protected void SetSeed(int seed)
751 {
752 SCR_BuildingDestructionData data = GetData();
753 if (!data)
754 return;
755
756 data.m_RandomGenerator.SetSeed(seed);
757 }
758
759 //------------------------------------------------------------------------------------------------
761 protected override void OnDamageStateChanged(EDamageState newState, EDamageState previousDamageState, bool isJIP)
762 {
763 super.OnDamageStateChanged(newState, previousDamageState, isJIP);
764
765 if (IsProxy())
766 return;
767
768 // Already destroyed
769 if (m_bDestroyed)
770 return;
771
772 // Called only on the server
773 if (newState == EDamageState.DESTROYED)
774 {
775 int seed = Math.RandomInt(0, 10000);
778 Rpc(RPC_GoToDestroyedState, seed);
779 }
780 }
781
782 //------------------------------------------------------------------------------------------------
784 [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
785 protected void RPC_GoToDestroyedState(int seed)
786 {
787 SetSeed(seed);
788 DestroyInteriorInit(false);
790 SpawnEffects(0, GetOwner(), false);
791 GetGame().GetCallqueue().CallLater(GoToDestroyedState, 1000 * GetDelay(), param1: false, param2: true);
792 }
793
794 //------------------------------------------------------------------------------------------------
796 protected void DestroyInteriorInit(bool immediate)
797 {
798 // Verify that both data and destruction manager are present before doing anything
799 SCR_BuildingDestructionData data = GetData();
800 if (!data)
801 return;
802
803 SCR_BuildingDestructionManagerComponent manager = GetGame().GetBuildingDestructionManager();
804 if (!manager)
805 return;
806
807 //exclude spawned effects
808 IEntity owner = GetOwner();
809 BaseWorld world = owner.GetWorld();
810 vector mins, maxs;
811
812 data.m_aQueriedEntities = {};
813
814 owner.GetWorldTransform(data.m_vStartMatrix);
815
816 // Destroy child entities with SCR_DestructibleBuildingComponent before querying
818
819 // We'll query for static and dynamic entities only.
820 // Proxies are also excluded so we don't even consider:
821 // - items attached to players
822 // - players attached to vehicles
823 // - anything (most likely buildings parts) using EntitySlots
824 EQueryEntitiesFlags queryFlags = EQueryEntitiesFlags.STATIC | EQueryEntitiesFlags.DYNAMIC | EQueryEntitiesFlags.NO_PROXIES;
825
826 // Run the queries and fetch entities that we'll be interacting with.
827 // In theory, we could run two separate queries, one for static and and for dynamic entities.
828 // However, the logic for both is almost the same so there is little to no gain to doing that.
829 array<ref SCR_InteriorBoundingBox> boundingBoxes = GetInteriorBoundingBoxes();
830 if (!boundingBoxes || boundingBoxes.IsEmpty())
831 {
832
833 owner.GetBounds(mins, maxs);
834 world.QueryEntitiesByOBB(mins, maxs, data.m_vStartMatrix, AddEntityCallback, QueryFilterCallback, queryFlags);
835 return;
836 }
837
838 SCR_DestructionManager destrManager = SCR_DestructionManager.GetDestructionManagerInstance();
839 SCR_RegionalDestructionManager regionalManager;
840 if (destrManager)
841 regionalManager = SCR_RegionalDestructionManager.Cast(destrManager.GetOrCreateRegionalDestructionManager(GetOwner().GetOrigin()));
842
843 RplComponent rplComponent = RplComponent.Cast(owner.FindComponent(RplComponent));
844
845 ref array<ref vector> boxesPositions = new array<ref vector>;
846
847 foreach(SCR_InteriorBoundingBox boundingBox : boundingBoxes)
848 {
849 vector center;
850 if (!boundingBox || !boundingBox.GetCenter(center) || boundingBox.GetScale() == vector.One)
851 {
852 Print("SCR_DestructibleBuildingComponent: Interior Bounding Box has been added but not set up!", LogLevel.WARNING);
853 continue;
854 }
855
856 boundingBox.GetBounds(mins, maxs);
857 if (regionalManager && !rplComponent.IsProxy())
858 regionalManager.RegisterInteriorBoundingBox(data.m_vStartMatrix, mins, maxs, center);
859
860
861 //on clients set it handled. We dont check for destrManager because we obtain regionalManager from it
862 if (rplComponent.IsProxy() && regionalManager)
863 {
864 if (destrManager.m_RegionalManagerHandledBoxes.Find(regionalManager.GetRplID(), boxesPositions))
865 {
866 boxesPositions.Insert(data.m_vStartMatrix[3]+center);
867 }
868 else
869 {
870 boxesPositions = new array<ref vector>;
871 boxesPositions.Insert(data.m_vStartMatrix[3]+center);
872 destrManager.m_RegionalManagerHandledBoxes.Insert(regionalManager.GetRplID(), boxesPositions);
873 }
874 }
875
876 world.QueryEntitiesByOBB(mins, maxs, data.m_vStartMatrix, AddEntityCallback, QueryFilterCallback, queryFlags);
877 }
878 }
879
880 //------------------------------------------------------------------------------------------------
881 // Uses data queried by DestroyInteriorInit method
882 protected void DestroyInterior(bool immediate)
883 {
884 SCR_BuildingDestructionData data = GetData();
885 if (!data)
886 return;
887
888 int count = data.m_aQueriedEntities.Count();
889 SCR_DestructibleEntity destructibleEntity;
890 SCR_DestructionDamageManagerComponent destructionComponent;
891 SCR_EditableEntityComponent editableEntity;
892 vector hitPosDirNorm[3];
893 RplComponent rplComponent;
894 IEntity childEntity;
895 array<IEntity> handledEntities = {};
896 for (int i = count - 1; i >= 0; i--)
897 {
898
899 if (!data.m_aQueriedEntities[i])
900 {
901 data.m_aQueriedEntities.Remove(i);
902 continue;
903 }
904
905 //Has children, will be handled later
906 childEntity = data.m_aQueriedEntities[i].GetChildren();
907 if (childEntity)
908 {
909 bool wasHandled = handledEntities.Contains(childEntity);
910
911 // Child entity was not queried for some reason && has not been handled yet, add it to the list of queried entities
912 if (!wasHandled && !data.m_aQueriedEntities.Contains(childEntity))
913 {
914 data.m_aQueriedEntities.Insert(childEntity);
915 i++;
916 }
917
918 // If child was already handled, we can consider this entity a non-parent
919 if (!wasHandled)
920 {
921 continue;
922 }
923 }
924
925 // Following order of operations is crucial, so we don't end up with null pointers at some point!
926
927 // Any entity that reaches this point is perceived as handled
928 handledEntities.Insert(data.m_aQueriedEntities[i]);
929
930 // Ignore entities outside the building
931 data.m_iChecksThisFrame++;
932 if (data.m_iChecksThisFrame >= MAX_CHECKS_PER_FRAME)
933 {
934 data.m_iChecksThisFrame = 0;
935 // Callqueue used here, because it shouldn't rely on running EOnFrame
936 GetGame().GetCallqueue().CallLater(DestroyInterior, param1: immediate);
937 return;
938 }
939
940 // Interior check for the object
941 // Disabled for now, this check now happens under ground, so it's pointless here
942 /*if (!IsInside(data.m_aQueriedEntities[i]))
943 {
944 data.m_aQueriedEntities.Remove(i);
945 i--;
946 count--;
947
948 continue;
949 }*/
950
951 destructibleEntity = SCR_DestructibleEntity.Cast(data.m_aQueriedEntities[i]);
952 destructionComponent = SCR_DestructionDamageManagerComponent.Cast(data.m_aQueriedEntities[i].FindComponent(SCR_DestructionDamageManagerComponent));
953 editableEntity = SCR_EditableEntityComponent.Cast(data.m_aQueriedEntities[i].FindComponent(SCR_EditableEntityComponent));
954
955 // Non-destructible object, just delete it later, so destruction happens on frame
956 if (!destructibleEntity && !destructionComponent && !editableEntity)
957 RplComponent.DeleteRplEntity(data.m_aQueriedEntities[i], false);
958
959 // Is destructible entity
960 if (destructibleEntity)
961 destructibleEntity.HandleDamage(EDamageType.TRUE, destructibleEntity.GetCurrentHealth() * 11, hitPosDirNorm);
962
963 // Uses destruction component
964 if (destructionComponent)
965 destructionComponent.DeleteDestructibleDelayed();
966
967 // Is editable entity
968 if (editableEntity)
969 editableEntity.Delete();
970
971 data.m_aQueriedEntities.Remove(i);
972 }
973
974 if(!IsProxy())
975 {
978 }
979
980
982 }
983
984 //------------------------------------------------------------------------------------------------
986 protected void DeleteBuilding()
987 {
988 RplComponent.DeleteRplEntity(GetOwner(), false);
989 }
990
991 //------------------------------------------------------------------------------------------------
993 [RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
994 protected void FinishDestruction()
995 {
996 SCR_BuildingDestructionData data = GetData();
997 if (!data)
998 return;
999
1000 if (data.m_CameraShake != null)
1001 {
1002 //data.m_CameraShake.Stop();
1003 data.m_CameraShake.SetParams(0.15, 0.15, 0.01, 0.3, 0.24);
1004 data.m_CameraShake = null;
1005 }
1006
1007 data.m_aQueriedEntities = null;
1008 FreeData();
1009 }
1010
1011 //------------------------------------------------------------------------------------------------
1012 protected bool PerformTrace(notnull TraceParam param, vector start, vector direction, notnull BaseWorld world, float lengthMultiplier = 1)
1013 {
1014 param.Start = start - direction * lengthMultiplier;
1015 param.End = start + direction * lengthMultiplier;
1016 world.TraceMove(param, TraceFilter);
1017
1018 return param.TraceEnt != null;
1019 }
1020
1021 //------------------------------------------------------------------------------------------------
1023 protected bool IsInside(notnull IEntity entity)
1024 {
1025 IEntity owner = GetOwner();
1026 BaseWorld world = owner.GetWorld();
1027 vector start = entity.GetOrigin();
1028
1029 TraceParam param = new TraceParam();
1030 param.Flags = TraceFlags.ENTS;
1031 param.LayerMask = EPhysicsLayerDefs.Projectile;
1032 param.Include = owner; // Include only the building for performance reasons
1033
1034 bool result;
1035 for (int i = 0; i < 3; i++)
1036 {
1037 float lengthMultiplier = 1;
1038 if (i == 1)
1039 lengthMultiplier = 100; // Vertical traces can and must be long to detect roof, where there is no floor, also they are internally optimized
1040
1041 result = PerformTrace(param, start, TRACE_DIRECTIONS[i], world, lengthMultiplier);
1042
1043 if (result)
1044 return true;
1045 }
1046
1047 return false;
1048 }
1049
1050 //------------------------------------------------------------------------------------------------
1052 protected bool TraceFilter(notnull IEntity e, vector start = "0 0 0", vector dir = "0 0 0")
1053 {
1054 return e == GetOwner();
1055 }
1056
1057 //------------------------------------------------------------------------------------------------
1058 protected bool HasPhysicalChildren(notnull IEntity e)
1059 {
1060 IEntity children = e.GetChildren();
1061 while (children)
1062 {
1063 if (children.GetPhysics())
1064 return true;
1065
1066 children = children.GetSibling();
1067 }
1068
1069 return false;
1070 }
1071
1073 protected bool QueryFilterCallback(notnull IEntity entity)
1074 {
1075 IEntity owner = GetOwner();
1076 IEntity ownerParent = owner.GetParent();
1077 IEntity entityParent = entity.GetParent();
1078
1079 if (
1080 // Exclude the owner...
1081 entity == owner ||
1082 // ... && avoid reparenting loops
1083 entity == ownerParent ||
1084 // ... && exclude children of other objects
1085 (entityParent && entityParent != owner)
1086 )
1087 return false;
1088
1089 return true;
1090 }
1091
1093 protected bool AddEntityCallback(notnull IEntity e)
1094 {
1095 // Exclude characters and vehicles right away.
1096 // Note, we query with NO_PROXY flags so characters inside vehicles or inventory
1097 // items of characters won't even make it here.
1098
1099 ChimeraCharacter character = ChimeraCharacter.Cast(e);
1100 if (character)
1101 {
1102 SCR_DamageManagerComponent damageManager = character.GetDamageManager();
1103 if (damageManager && damageManager.GetState() != EDamageState.DESTROYED)
1104 {
1105 // Kill occupants after destruction starts determined by delay set in prefab data, so it appears as if they died from the building falling on top of them
1106 GetGame().GetCallqueue().CallLater(DamageOccupantsDelayed, GetDelay() * 1000, param1: e);
1107 return true;
1108 }
1109 // Occupants who are already dead (ragdolls) will be pulled downwards to ensure there will be no flying bodies after the physics freeze.
1110 }
1111 else if (Vehicle.Cast(e))
1112 {
1113 //destroy the vehicle after the destruction delay
1114 GetGame().GetCallqueue().CallLater(HandleVehicle, GetDelay() * 1000, param1: e);
1115 return true;
1116 }
1117
1118 // Since characters manning a turret wont be found in our query (and only the turrets themselves) we must deal with them seperately
1119 SCR_BaseCompartmentManagerComponent baseCompartmentManagerComp = SCR_BaseCompartmentManagerComponent.Cast(e.FindComponent(SCR_BaseCompartmentManagerComponent));
1120 if (baseCompartmentManagerComp)
1121 {
1122 array<IEntity> turretOccupants = {};
1123 baseCompartmentManagerComp.GetOccupants(turretOccupants);
1124 foreach (IEntity occupant : turretOccupants)
1125 {
1126 // Kill occupants after destruction starts determined by delay set in prefab data, so it appears as if they died from the building falling on top of them
1127 GetGame().GetCallqueue().CallLater(DamageOccupantsDelayed, GetDelay() * 1000, param1: occupant);
1128 }
1129 }
1130
1131 SCR_BuildingDestructionData data = GetData();
1132
1133 //write debug here to see where Villa_E_2I01_FIA_01.e is failing to get dragged down
1134
1135 // Exclude entity if already included.
1136 // NOTE: This place can be a bottleneck when running a big amount of queries.
1137 // The number grows bigger progressively and if there are too many things
1138 // to query, checking against e.g. 200 of them might be an issue.
1139 if (data.m_aQueriedEntities.Contains(e))
1140 return true;
1141
1142 // Exclude entities in exclude list
1143 // Disabled for now, because it might not be necessary
1144 // Keeping it for later if we decide to use it in some other way than originally planned
1145 /*if (data.m_aExcludeList.Contains(e))
1146 return true;*/
1147
1148 // Exclude static entities such as trees, rocks, ruins etc. and other destructible buildings
1149 SCR_BuildingDestructionManagerComponent manager = GetGame().GetBuildingDestructionManager();
1150 foreach (typename typeName : manager.GetExcludedQueryTypes())
1151 {
1152 if (e.IsInherited(typeName))
1153 {
1154 if (!e.Type().IsInherited(SCR_PowerPole) && !e.Type().IsInherited(SCR_JunctionPowerPole))
1155 return true;
1156
1157 // Cast to SCR_PowerPole to access slot data
1158 SCR_PowerPole powerPole = SCR_PowerPole.Cast(e);
1159 if (!powerPole)
1160 {
1161 // Fallback to entity origin if cast fails
1162 m_aPowerPolePositions.Insert(e.GetOrigin());
1163 continue;
1164 }
1165
1166 // Get prefab data to access cable slot groups
1167 SCR_PowerPoleClass prefabData = SCR_PowerPoleClass.Cast(powerPole.GetPrefabData());
1168 if (!prefabData || !prefabData.m_aCableSlotGroups)
1169 {
1170 // Fallback to entity origin if no cable slot groups
1171 m_aPowerPolePositions.Insert(e.GetOrigin());
1172 continue;
1173 }
1174
1175 // Iterate through cable slot groups and average slot positions per group
1176 foreach (SCR_PoleCableSlotGroup slotGroup : prefabData.m_aCableSlotGroups)
1177 {
1178 if (!slotGroup || !slotGroup.m_aSlots)
1179 continue;
1180
1181 vector avgLocalPos = vector.Zero;
1182 int validSlotCount = 0;
1183
1184 // Sum all slot positions in this group
1185 foreach (SCR_PoleCableSlot slot : slotGroup.m_aSlots)
1186 {
1187 if (!slot)
1188 continue;
1189
1190 avgLocalPos += slot.m_vPosition;
1191 validSlotCount++;
1192 }
1193
1194 // Calculate average and transform to world space
1195 if (validSlotCount > 0)
1196 {
1197 avgLocalPos = avgLocalPos / validSlotCount;
1198 vector avgWorldPos = powerPole.CoordToParent(avgLocalPos);
1199 m_aPowerPolePositions.Insert(avgWorldPos);
1200 }
1201 }
1202 }
1203 }
1204
1205 // There shouldn't really be any physical hierchies created on buildings and alike,
1206 // so more than anything, this check is just a precaution.
1207 // I wish it was just a precaution - Mour
1208 if (!e.GetPhysics() && !HasPhysicalChildren(e))
1209 return true;
1210
1211 SCR_EditorLinkComponent linkComp = SCR_EditorLinkComponent.Cast(e.FindComponent(SCR_EditorLinkComponent));
1212 if (linkComp)
1213 return true;
1214
1215 // In order to sink entities within the building along with it when sinking, we need to make sure
1216 // they are attached to the building.
1217 // We already filtered out all entities that have a parent different than this building so it
1218 // is okay to simply attach the queried entity as a child directly.
1219 // TODO: On headless, we should probably just delete non-replicated things right away so we don't
1220 // waste performance updating the entire building with all of its furniture. Players and
1221 // vehicles inside the building are getting destroyed so no collisiton desyncs are probable.
1222 // And even if they happened, characters have client-authority collision and vehicles are
1223 // server-corrected.
1224 IEntity owner = GetOwner();
1225 if(e.GetParent() != owner)
1226 {
1227 //e.SetFlags(EntityFlags.USER3);
1228 owner.AddChild(e, -1, EAddChildFlags.AUTO_TRANSFORM | EAddChildFlags.RECALC_LOCAL_TRANSFORM);
1229 }
1230
1231 // If entity is editable, prevent game master interaction
1233 if (editableEntity)
1234 editableEntity.SetEntityState(EEditableEntityState.INTERACTIVE, false);
1235
1236 data.m_aQueriedEntities.Insert(e);
1237 return true;
1238 }
1239
1240 //------------------------------------------------------------------------------------------------
1241 protected void DamageOccupantsDelayed(IEntity targetEntity)
1242 {
1243 // The character is outside the building
1244 if (!IsInside(targetEntity))
1245 return;
1246
1248 if (!damageManager)
1249 return;
1250
1251 HitZone headHitzone = damageManager.GetHeadHitZone();
1252 if (headHitzone)
1253 headHitzone.HandleDamage(1000, EDamageType.TRUE, null);
1254 }
1255
1256 //------------------------------------------------------------------------------------------------
1257 protected void HandleVehicle(IEntity targetEntity)
1258 {
1259 if (!IsInside(targetEntity))
1260 return;
1261
1263 if (!damageManager)
1264 return;
1265
1266 HitZone defaultHitzone = damageManager.GetDefaultHitZone();
1267 defaultHitzone.HandleDamage(defaultHitzone.GetMaxHealth(), EDamageType.TRUE, null);
1268 }
1269
1270 //------------------------------------------------------------------------------------------------
1272 {
1273 BaseWorld world = GetGame().GetWorld();
1274
1275 foreach (vector polePosition : m_aPowerPolePositions)
1276 {
1277 world.QueryEntitiesBySphere(polePosition, 3, ProcessFoundPowerline);
1278 }
1279 }
1280
1281 //------------------------------------------------------------------------------------------------
1282 protected bool ProcessFoundPowerline(notnull IEntity e)
1283 {
1284 if (e.Type() != PowerlineEntity)
1285 return true;
1286
1287 delete e;
1288 return true;
1289 }
1290
1291 //------------------------------------------------------------------------------------------------
1292 void GoToDestroyedStateLoad(bool spawnEffects = true)
1293 {
1295 GoToDestroyedState(true, spawnEffects);
1296 }
1297
1298 //------------------------------------------------------------------------------------------------
1300 {
1301 SCR_BuildingDestructionData data = GetData();
1302 if (!data)
1303 return;
1304
1305 vector mins, maxs;
1306 GetOwner().GetBounds(mins, maxs);
1307
1308 float x = Math.AbsFloat(mins[0]) + maxs[0];
1309 float y = Math.AbsFloat(mins[1]) + maxs[1];
1310 float z = Math.AbsFloat(mins[2]) + maxs[2];
1311
1312 float buildingVolume = x * y * z;
1313
1314 data.m_fBuildingVolume = buildingVolume;
1315
1316 data.m_fSizeMultiplier = data.m_fBuildingVolume / BUILDING_SIZE; // BUILDING_SIZE constant is value for the average building size
1317 }
1318
1319 //------------------------------------------------------------------------------------------------
1323 protected void GoToDestroyedState(bool immediate, bool spawnEffects = true)
1324 {
1325 SCR_BuildingDestructionData data = GetData();
1326 if (!data)
1327 return;
1328
1329 m_bDestroyed = true;
1330
1331 if (IsMaster())
1333
1334 IEntity owner = GetOwner();
1335
1336 // Ensure registration of destroyed building as they are otherwise not tracked by default
1337 auto persistence = PersistenceSystem.GetInstance();
1338 if (persistence)
1339 persistence.StartTracking(owner, false);
1340
1341 vector mins, maxs;
1342 owner.GetBounds(mins, maxs);
1343
1344 maxs[0] = 0;
1345 maxs[2] = 0;
1346
1347 vector sinkVector = GetSinkVector();
1348 if (sinkVector == vector.Zero)
1349 sinkVector = -maxs;
1350
1351 data.m_vTargetOrigin = owner.GetOrigin() + sinkVector;
1352 data.m_vStartAngles = owner.GetLocalAngles();
1353
1354 StaticModelEntity.Cast(owner).DestroyOccluders();
1355
1356 // Don't animate, JIP happened
1357 if (immediate)
1358 {
1359 DestroyInteriorInit(immediate);
1360 FinishLerp(owner, immediate, true, spawnEffects);
1361 }
1362 else // Animate sinking, play particles, sounds etc...
1363 {
1364 owner.GetPhysics().SetResponseIndex(NO_COLLISION_RESPONSE_INDEX);
1365
1366 auto shake = data.m_CameraShake;
1367 if (shake != null)
1368 {
1369 shake.SetParams(0.15, 0.15, 0.01, 400, 0.24);
1370 shake.SetCurve(GetCameraShakeCurve());
1371 shake.SetStartOrigin(data.m_vStartMatrix[3]);
1372 shake.SetSizeMultiplier(data.m_fSizeMultiplier);
1373
1375 if (character && character.IsInVehicle())
1376 {
1377 Vehicle vehicle = Vehicle.Cast(character.GetParent());
1378 if (vehicle)
1379 {
1380 VehicleBaseSimulation simulation = VehicleBaseSimulation.Cast(vehicle.FindComponent(VehicleBaseSimulation));
1381 if (simulation && simulation.HasAnyGroundContact())
1382 SCR_CameraShakeManagerComponent.AddCameraShake(shake);
1383 }
1384 }
1385 else
1386 {
1387 SCR_CameraShakeManagerComponent.AddCameraShake(shake);
1388 }
1389
1390 //SCR_CameraShakeManagerComponent.AddCameraShake(shake);
1391 }
1392
1393 //SetEventMask(owner, EntityEvent.FRAME);
1394 EnableDamageSystemOnFrame();
1395 }
1396 }
1397
1398 //------------------------------------------------------------------------------------------------
1402 protected void SpawnEffects(float percentDone, IEntity owner, bool immediateDestruction)
1403 {
1404 SCR_BuildingDestructionData data = GetData();
1405 if (!data)
1406 return;
1407
1408 if (!data.m_aExcludeList)
1409 data.m_aExcludeList = {};
1410
1411 SCR_HitInfo hitInfo = new SCR_HitInfo();
1412 hitInfo.m_DamageType = EDamageType.KINETIC; // Todo properly store damage type
1413
1414 array<ref SCR_TimedEffect> effects = GetEffects();
1415 SCR_TimedEffect currentEffect;
1416 for (int i = effects.Count() - 1; i >= 0; i--)
1417 {
1418 if (data.m_aExecutedEffectIndices && data.m_aExecutedEffectIndices.Contains(i))
1419 continue;
1420
1421 currentEffect = effects[i]; // Store it, because each effects[i] is effects.Get(i) call internally
1422
1423 if (immediateDestruction && !currentEffect.m_bPersistent)
1424 continue; // Skip because destruction happened immediately & effect isn't persistent
1425
1426 if (currentEffect.m_fSpawnTime <= percentDone)
1427 {
1428 currentEffect.ExecuteEffect(owner, hitInfo, data);
1429
1430 // Create the set if it doesn't exist yet
1431 if (!data.m_aExecutedEffectIndices)
1432 {
1433 data.m_aExecutedEffectIndices = new set<int>();
1434 // Max size of the set is known beforehand, because m_aEffects is in prefab data
1435 data.m_aExecutedEffectIndices.Reserve(effects.Count());
1436 }
1437
1438 data.m_aExecutedEffectIndices.Insert(i);
1439 }
1440 }
1441 }
1442
1443 //------------------------------------------------------------------------------------------------
1448 protected void FinishLerp(IEntity owner, bool immediate, bool updateEntity, bool spawnEffects = true)
1449 {
1450 owner.SetObject(null, ""); // Hide the building
1451 //ClearEventMask(owner, EntityEvent.FRAME);
1452 DisableDamageSystemOnFrame();
1453
1454 if (spawnEffects)
1455 SpawnEffects(1, owner, immediate); // Ensure all effects get played
1456
1457 SCR_BuildingDestructionData data = GetData();
1458 if (!data)
1459 return;
1460
1461 owner.SetOrigin(data.m_vTargetOrigin);
1462
1463 if (updateEntity)
1464 owner.Update();
1465 else
1466 {
1467 // Rather than updating entities one by one, let the damage system
1468 // know it is supposed to perform a parallel update.
1469 owner.SetFlags(EntityFlags.USER3);
1470 }
1471
1473 DestroyInterior(immediate);
1474 }
1475
1476 //------------------------------------------------------------------------------------------------
1478 protected void RegenerateNavmesh()
1479 {
1480 if (Replication.IsClient())
1481 return;
1482
1483 SCR_BuildingDestructionData data = GetData();
1484 if (!data)
1485 return;
1486
1487 SCR_AIWorld aiWorld = SCR_AIWorld.Cast(GetGame().GetAIWorld());
1488 if (!aiWorld)
1489 return;
1490
1491 aiWorld.RequestNavmeshRebuildAreas(data.m_aNavmeshAreas, data.m_aRedoRoads);
1492 }
1493
1494 //------------------------------------------------------------------------------------------------
1496 protected void StoreNavmeshData()
1497 {
1498 if (Replication.IsClient())
1499 return;
1500
1501 SCR_BuildingDestructionData data = GetData();
1502 if (!data)
1503 return;
1504
1505 SCR_AIWorld aiWorld = SCR_AIWorld.Cast(GetGame().GetAIWorld());
1506 if (!aiWorld)
1507 return;
1508
1509 data.m_aNavmeshAreas = {};
1510 data.m_aRedoRoads = {};
1511 aiWorld.GetNavmeshRebuildAreas(GetOwner(), data.m_aNavmeshAreas, data.m_aRedoRoads); // Get area with current phase
1512 }
1513
1514 //------------------------------------------------------------------------------------------------
1515 protected void ClampVector(inout vector currentOrigin, vector startOrigin, vector endOrigin)
1516 {
1517 bool targetSmaller;
1518 for (int i = 0; i < 3; i++)
1519 {
1520 targetSmaller = endOrigin[i] < startOrigin[i];
1521 if (targetSmaller)
1522 {
1523 if (currentOrigin[i] < endOrigin[i])
1524 currentOrigin[i] = endOrigin[i];
1525 }
1526 else
1527 {
1528 if (currentOrigin[i] > endOrigin[i])
1529 currentOrigin[i] = endOrigin[i];
1530 }
1531 }
1532 }
1533
1534 //------------------------------------------------------------------------------------------------
1535 protected void LerpRotation(IEntity owner, float timeSlice)
1536 {
1537 if (!owner)
1538 return;
1539
1540 SCR_BuildingDestructionData data = GetData();
1541 if (!data)
1542 return;
1543
1544 if (data.m_iRotatedTimes > GetMaxRotations())
1545 return;
1546
1547 // When it's time to reset the target rotation
1548 if (data.m_iRotationStart + data.m_iRotationTime < owner.GetWorld().GetWorldTime() && data.m_iRotatedTimes < GetMaxRotations())
1549 {
1550 // Generate next rotation time offset
1551 float rotationTimeRandomizer = GetRotationTimeRandomizer() * 0.01; // * 0.01 to 0-1 range
1552 data.m_iRotationTime = GetRotationTime() * (1 + data.m_RandomGenerator.RandFloatXY(0, rotationTimeRandomizer)) * 1000; // * 1000 = to ms
1553
1554 // Slow down the sinking to make it seem like it crashed into something
1555 data.m_fSpeedMultiplier *= 0.05;
1556
1557 // Only call OnSlowDown if it's not initial rotation setting
1558 if (data.m_iRotatedTimes != 0)
1559 OnSlowDown();
1560
1561 bool allowRotationX = GetAllowRotationX();
1562 bool allowRotationY = GetAllowRotationY();
1563 bool allowRotationZ = GetAllowRotationZ();
1564
1565 // Generate new random angles
1566 vector newTargetAngles = Vector(data.m_RandomGenerator.RandFloatXY(5, 20) * allowRotationX * data.m_iRotationMultiplier + data.m_vStartAngles[0]
1567 , data.m_RandomGenerator.RandFloatXY(5, 20) * allowRotationY * data.m_iRotationMultiplier + data.m_vStartAngles[1]
1568 , data.m_RandomGenerator.RandFloatXY(5, 20) * allowRotationZ * data.m_iRotationMultiplier + data.m_vStartAngles[2]);
1569
1570 // This ensures the rotation will always be going to the opposite side of the previous one
1571 data.m_iRotationMultiplier *= -1;
1572
1573 // Reset the speed multiplier
1574 data.m_fRotationSpeedMultiplier = 0.5;
1575
1576 // Save the new target angles
1577 data.m_vTargetAngles = newTargetAngles;
1578
1579 // Cache world
1580 BaseWorld world = owner.GetWorld();
1581
1582 int pauseTime = data.m_RandomGenerator.RandIntInclusive(0, 500);
1583
1584 // Save the current timestamp as the rotation start
1585 // Add the pause time to it
1586 data.m_iRotationStart = world.GetWorldTime() + pauseTime;
1587
1588 // Set pause time to stop the building for a while
1589 data.m_iPauseTime = world.GetWorldTime() + pauseTime;
1590
1591 data.m_iRotatedTimes++;
1592 }
1593
1594 // This is the actual lerp
1595 data.m_fRotationSpeedMultiplier += timeSlice;
1596 //vector newAngles = vector.Lerp(owner.GetAngles(), data.m_vTargetAngles, timeSlice * Math.Pow(data.m_fRotationSpeedMultiplier, 3));
1597 vector newAngles = LerpAngles(data.m_vStartAngles, owner.GetLocalAngles(), data.m_vTargetAngles, GetRotationSpeed(), timeSlice, data);
1598 owner.SetAngles(newAngles);
1599 }
1600
1601 //------------------------------------------------------------------------------------------------
1602 protected void PlaySlowDownSound()
1603 {
1604 const IEntity owner = GetOwner();
1605 SCR_SoundManagerModule soundManager = SCR_SoundManagerModule.GetInstance(owner.GetWorld());
1606 if (!soundManager)
1607 return;
1608
1609 SCR_AudioSourceConfiguration audioSourceConfiguration = GetSlowDownAudioSourceConfiguration();
1610 if (!audioSourceConfiguration || !audioSourceConfiguration.IsValid())
1611 return;
1612
1613 SCR_AudioSource audioSource = soundManager.CreateAudioSource(owner, audioSourceConfiguration, GetData().m_vStartMatrix[3]);
1614 if (!audioSource)
1615 return;
1616
1617 SetAudioSource(audioSource);
1618
1619 soundManager.PlayAudioSource(audioSource);
1620 }
1621
1622 //------------------------------------------------------------------------------------------------
1623 protected void OnSlowDown()
1624 {
1626 }
1627
1628 //------------------------------------------------------------------------------------------------
1629 protected vector LerpAngles(vector start, vector current, vector target, float rotationSpeed, float timeSlice, notnull SCR_BuildingDestructionData data)
1630 {
1631 if (target == vector.Zero)
1632 return vector.Zero;
1633
1634 float percent = timeSlice / (data.m_iRotationTime * 0.001);
1635 vector diff = target - start;
1636 vector nextRotation = current + percent * diff * rotationSpeed * data.m_fRotationSpeedMultiplier;
1637
1638 ClampVector(nextRotation, start, target);
1639 return nextRotation;
1640 }
1641
1642 //------------------------------------------------------------------------------------------------
1645 protected void LerpPosition(IEntity owner, float timeSlice)
1646 {
1647 if (!owner)
1648 return;
1649
1650 SCR_BuildingDestructionData data = GetData();
1651 if (!data)
1652 return;
1653
1654 vector currentOrigin = owner.GetOrigin();
1655 vector direction = (data.m_vTargetOrigin - currentOrigin).Normalized();
1656
1657 data.m_fSpeedMultiplier += (GetSpeedGradualMultiplier() * timeSlice) * 0.01;
1658 vector mat[4];
1659 owner.GetTransform(mat);
1660 vector newOrigin = currentOrigin + direction * GetSpeed() * timeSlice * data.m_fSpeedMultiplier;
1661 ClampVector(newOrigin, data.m_vStartMatrix[3], data.m_vTargetOrigin);
1662
1663 // No need to clear the parallel update flag.
1664 // The damage system automatically clears the flag before each entity update.
1665 // -> owner.ClearFlags(EntityFlags.USER3);
1666
1667 bool isFinalState =
1668 float.AlmostEqual(newOrigin[0], data.m_vTargetOrigin[0]) &&
1669 float.AlmostEqual(newOrigin[1], data.m_vTargetOrigin[1]) &&
1670 float.AlmostEqual(newOrigin[2], data.m_vTargetOrigin[2]);
1671
1672 float difY = data.m_vTargetOrigin[1] - data.m_vStartMatrix[3][1];
1673 float curY = newOrigin[1] - data.m_vStartMatrix[3][1];
1674 float percentDone = curY/difY;
1675 SpawnEffects(percentDone, owner, false);
1676
1677 // Final state
1678 if (isFinalState)
1679 FinishLerp(owner, false, false);
1680 else
1681 // Getting to the final state
1682 {
1683 vector worldMat[4];
1684 owner.GetWorldTransform(worldMat);
1685 worldMat[3] = newOrigin;
1686 owner.SetWorldTransform(worldMat);
1687
1688 // Rather than updating entities one by one, let the damage system
1689 // know it is supposed to perform a parallel update.
1690 owner.SetFlags(EntityFlags.USER3);
1691 }
1692 }
1693
1694 //------------------------------------------------------------------------------------------------
1695 override bool HijackDamageHandling(notnull BaseDamageContext damageContext)
1696 {
1697 if (damageContext.damageEffect && damageContext.damageEffect.Type() == SCR_VehicleFireDamageEffect)
1698 return true;
1699
1700 return false;
1701 }
1702
1703 //------------------------------------------------------------------------------------------------
1704 protected override bool HasDataToReplicate()
1705 {
1706 if(m_bDestroyed == true)
1707 return true;
1708
1709 return false;
1710 }
1711
1712 //------------------------------------------------------------------------------------------------
1714 protected override event bool OnRplSave(ScriptBitWriter writer)
1715 {
1716 super.OnRplSave(writer);
1717
1718 writer.WriteBool(m_bDestroyed);
1719
1720 return true;
1721 }
1722
1723 //------------------------------------------------------------------------------------------------
1725 protected override event bool OnRplLoad(ScriptBitReader reader)
1726 {
1727 super.OnRplLoad(reader);
1728
1729 reader.ReadBool(m_bDestroyed);
1730
1731 if (m_bDestroyed)
1732 {
1733 // Need to remove it from callqueue in case it was queued by OnDamageStateChanged
1734 GetGame().GetCallqueue().Remove(GoToDestroyedState);
1735 GoToDestroyedState(true);
1736 }
1737
1738 return true;
1739 }
1740
1741 //------------------------------------------------------------------------------------------------
1743 override void OnFilteredContact(IEntity owner, IEntity other, Contact contact)
1744 {
1745 if (IsProxy())
1746 return;
1747
1748 if (GetHealth() <= 0)
1749 return;
1750
1751 if (other && other.IsInherited(SCR_DebrisSmallEntity)) // Ignore impacts from debris
1752 return;
1753
1754 // Get the physics of the dynamic object (if owner is static, then we use the other object instead)
1755 Physics physics = contact.Physics1;
1756 int responseIndex = physics.GetResponseIndex();
1757 float ownerMass = physics.GetMass();
1758 float otherMass;
1759 if (!physics.IsDynamic())
1760 {
1761 physics = contact.Physics2;
1762 if (!physics)
1763 return; // This only happens with ragdolls, other objects do have physics here, as collision only happens between physical objects
1764
1765 otherMass = physics.GetMass();
1766 }
1767 else
1768 {
1769 Physics otherPhysics = other.GetPhysics();
1770 if (!otherPhysics)
1771 return; // This only happens with ragdolls, other objects do have physics here, as collision only happens between physical objects
1772
1773 otherMass = otherPhysics.GetMass();
1774 }
1775
1776 float momentum = SCR_DestructionUtility.CalculateMomentum(contact, ownerMass, otherMass);
1777
1778 vector outMat[3];
1779 vector relVel = contact.VelocityBefore2 - contact.VelocityBefore1;
1780 outMat[0] = contact.Position; // Hit position
1781 outMat[1] = relVel.Normalized(); // Hit direction
1782 outMat[2] = contact.Normal; // Hit normal
1783
1784 float damage = momentum * 0.05; // Todo replace with attribute
1785
1786 // Send damage to damage handling
1787 Instigator instigator = Instigator.CreateInstigator(other);
1788 SCR_DamageContext dmgContext = new SCR_DamageContext(EDamageType.COLLISION, damage, outMat, GetOwner(), null, instigator, null, -1, -1);
1789 HandleDamage(dmgContext);
1790
1791 return;
1792 }
1793
1794 //------------------------------------------------------------------------------------------------
1796 protected override void OnFrame(IEntity owner, float timeSlice)
1797 {
1798 LerpPosition(owner, timeSlice);
1799 LerpRotation(owner, timeSlice);
1800 }
1801
1802 //------------------------------------------------------------------------------------------------
1803 override void OnPostInit(IEntity owner)
1804 {
1805 super.OnPostInit(owner);
1806 SetEventMask(owner, EntityEvent.CONTACT);
1807 }
1808
1809#ifdef WORKBENCH
1810 //------------------------------------------------------------------------------------------------
1811 override array<ref WB_UIMenuItem> _WB_GetContextMenuItems(IEntity owner)
1812 {
1813 array<ref WB_UIMenuItem> items = { new WB_UIMenuItem("Toggle Interior Bounding Box Debug", 0) };
1814
1815 return items;
1816 }
1817
1818 //------------------------------------------------------------------------------------------------
1819 override void _WB_OnContextMenu(IEntity owner, int id)
1820 {
1821 switch (id)
1822 {
1823 case 0:
1824 {
1825 DiagMenu.SetValue(SCR_DebugMenuID.DEBUGUI_SHOW_INTERIOR_BOUNDING_BOX, !DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_SHOW_INTERIOR_BOUNDING_BOX));
1826 }
1827 }
1828 }
1829
1830 //------------------------------------------------------------------------------------------------
1831 protected override void _WB_AfterWorldUpdate(IEntity owner, float timeSlice)
1832 {
1833 super._WB_AfterWorldUpdate(owner, timeSlice);
1834
1835 if (!DiagMenu.GetBool(SCR_DebugMenuID.DEBUGUI_SHOW_INTERIOR_BOUNDING_BOX))
1836 return;
1837
1838 GenericEntity entity = GenericEntity.Cast(owner);
1839 if (!entity)
1840 return;
1841
1842 WorldEditorAPI api = entity._WB_GetEditorAPI();
1843 if (!api || !api.IsEntitySelected(api.EntityToSource(owner)))
1844 return;
1845
1846 // Draw Interior Bounding Box Debug
1847 vector ownerTransform[4];
1848 owner.GetWorldTransform(ownerTransform);
1849
1850 foreach (SCR_InteriorBoundingBox boundingBox : GetInteriorBoundingBoxes())
1851 {
1852 if (boundingBox)
1853 boundingBox.DrawDebug(ownerTransform);
1854 }
1855 }
1856
1857 //------------------------------------------------------------------------------------------------
1858 protected override event bool _WB_GetKeySpaceMatrixWorld(IEntity owner, BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent, out vector transformSpaceWorld[4])
1859 {
1860 if(src.GetClassName() == "SCR_InteriorBoundingBox" && key == "m_vScale")
1861 {
1862 //m_vScale key from SCR_InteriorBoundingBox object is being edited via bounding box tool so
1863 //we are going to provide a world transformation matrix/space for editing the bound box
1864
1865 BaseContainer ownerCont = null;
1866 if(ownerContainers.Count() > 0)
1867 ownerCont = ownerContainers[ownerContainers.Count() - 1];
1868
1869 if(ownerCont && ownerCont.GetClassName() == "SCR_DestructibleBuildingComponent")
1870 {
1871 //The SCR_InteriorBoundingBox object is owned by SCR_DestructibleBuildingComponent. That's correct.
1872
1873 //Find bound box index of the SCR_InteriorBoundingBox object (it's inside an object array)
1874 BaseContainerList bboxes = ownerCont.GetObjectArray("m_aInteriorQueryBoundingBoxes");
1875 int bboxIndex = -1;
1876
1877 if(bboxes)
1878 {
1879 for (int i = 0; i < bboxes.Count(); i++)
1880 {
1881 BaseContainer box = bboxes.Get(i);
1882
1883 if(box == src)
1884 {
1885 bboxIndex = i;
1886 break;
1887 }
1888 }
1889 }
1890
1891 if(bboxIndex >= 0)
1892 {
1893 //Use the bboxIndex to retreive appropriate SCR_InteriorBoundingBox object itself from prefab data
1894 SCR_InteriorBoundingBox intBox = null;
1895
1896 array<ref SCR_InteriorBoundingBox> boundingBoxes = GetInteriorBoundingBoxes();
1897 if(boundingBoxes)
1898 intBox = boundingBoxes[bboxIndex];
1899
1900 if(intBox)
1901 {
1902 //get local bbox transform given by PointInfo object
1903 vector boxLocalTransform[4];
1904 intBox.m_vCenter.GetTransform(boxLocalTransform);
1905
1906 //get entity transform
1907 vector entityWorldTransform[4];
1908 owner.GetWorldTransform(entityWorldTransform);
1909
1910 //calc result transform in world space
1911 vector boxTransform[4];
1912 Math3D.MatrixMultiply4(entityWorldTransform, boxLocalTransform, boxTransform);
1913
1914 transformSpaceWorld = boxTransform;
1915 return true;
1916 }
1917 }
1918 }
1919 }
1920
1921 return super._WB_GetKeySpaceMatrixWorld(owner, src, key, ownerContainers, parent, transformSpaceWorld);
1922 }
1923
1924#endif
1925
1926 //------------------------------------------------------------------------------------------------
1928 protected bool IsProxy()
1929 {
1930 RplComponent rplComponent = RplComponent.Cast(GetOwner().FindComponent(RplComponent));
1931 return rplComponent && rplComponent.IsProxy();
1932 }
1933
1934 //------------------------------------------------------------------------------------------------
1935 protected bool IsMaster()
1936 {
1937 RplComponent rplComponent = RplComponent.Cast(GetOwner().FindComponent(RplComponent));
1938 return rplComponent && rplComponent.IsMaster();
1939 }
1940}
SCR_DebugMenuID
This enum contains all IDs for DiagMenu entries added in script.
Definition DebugMenuID.c:4
ArmaReforgerScripted GetGame()
Definition game.c:1398
SCR_HybridPhysicsComponentClass m_fMass
Class for storing physics setup info for SCR_HybridPhysicsComponent.
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
vector GetOrigin()
void SCR_AudioSource(SCR_AudioSourceConfiguration audioSourceConfiguration, vector mat[4])
void GetSpawnTransform(IEntity owner, out vector outMat[4], bool localCoords=false)
Calculates the spawn tranformation matrix for the object.
vector m_vOffsetPosition
vector m_vOffsetRotation
override bool HandleDamage(BaseDamageContext damageContext, IEntity owner)
SCR_CharacterSoundComponentClass GetComponentData()
SCR_InteriorBoundingBox ROTATION_Z
SCR_InteriorBoundingBox ROTATION_Y
SCR_InteriorBoundingBox ROTATION_X
void SCR_DestructibleEntity(IEntitySource src, IEntity parent)
vector direction
float m_fRandomVelocityLinear
float m_fRandomVelocityAngular
Get all prefabs that have the spawner data
float GetHealth()
ResourceName m_Particle
ref SCR_AudioSourceConfiguration m_AudioSourceConfiguration
void SCR_PowerPole(IEntitySource src, IEntity parent)
void ParticleEffectEntity(IEntitySource src, IEntity parent)
SCR_AudioSourceConfiguration GetAudioSourceConfiguration()
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
void SCR_VehicleDamageManagerComponent(IEntityComponentSource src, IEntity ent, IEntity parent)
enum EVehicleType IEntity
void SetCurve(EAnimationCurve curve)
Definition Color.c:13
Definition Math.c:22
Definition Debug.c:13
proto external WorldEditorAPI _WB_GetEditorAPI()
This returns world editor API, which is safe to use from editor events bellow.
proto external Managed FindComponent(typename typeName)
proto external void SetAngles(vector angles)
Same as SetYawPitchRoll(), but sets rotation around X, Y and Z axis.
proto external vector GetLocalAngles()
Same as GetLocalYawPitchRoll(), but returns rotation vector around X, Y and Z axis.
proto external void SetOrigin(vector orig)
proto external vector GetOrigin()
proto external Physics GetPhysics()
proto external int AddChild(notnull IEntity child, TNodeId pivot, EAddChildFlags flags=EAddChildFlags.AUTO_TRANSFORM)
Add Entity to hierarchy. Pivot is pivot index, or -1 for center of parent.
proto external void GetBounds(out vector mins, out vector maxs)
proto external IEntity GetChildren()
proto external void GetWorldTransform(out vector mat[])
See IEntity::GetTransform.
proto external int Update()
proto external BaseWorld GetWorld()
proto external EntityFlags SetFlags(EntityFlags flags, bool recursively=false)
proto external bool SetWorldTransform(vector mat[4])
See IEntity::SetTransform. Returns false, if there is no change in transformation.
proto external void GetTransform(out vector mat[])
proto external IEntity GetParent()
proto external IEntity GetSibling()
Definition Math.c:13
PointInfo - allows to define position.
Definition PointInfo.c:9
Main replication API.
Definition Replication.c:14
Object holding reference to resource. In destructor release the resource.
Definition Resource.c:25
void GetNavmeshRebuildAreas(IEntity entity, out notnull array< ref Tuple2< vector, vector > > outAreas, out notnull array< bool > redoRoads)
void RequestNavmeshRebuildAreas(notnull array< ref Tuple2< vector, vector > > areas, notnull array< bool > redoRoads)
notnull SCR_BuildingDestructionData GetData(inout int index)
void ClampVector(inout vector currentOrigin, vector startOrigin, vector endOrigin)
override void OnFrame(IEntity owner, float timeSlice)
Handles per-frame operations, only enabled while the building is sinking.
void SpawnEffects(float percentDone, IEntity owner, bool immediateDestruction)
void StoreNavmeshData()
Stores navmesh data to regenerate navmesh later.
void SetAudioSource(SCR_AudioSource audioSource)
void FreeData()
Frees the data stored in building destruction manager.
ref array< ref SCR_InteriorBoundingBox > GetInteriorBoundingBoxes()
override void OnFilteredContact(IEntity owner, IEntity other, Contact contact)
Contact to deal damage.
float GetSpeed()
Returns prefab data stored speed.
void GoToDestroyedState(bool immediate, bool spawnEffects=true)
bool AddEntityCallback(notnull IEntity e)
Used by Query in DestroyInterior.
float GetSpeedGradualMultiplier()
Returns prefab data stored speed gradual multiplier.
void DeleteBuilding()
Handles the destruction of building itself after interior is handled.
override event bool OnRplLoad(ScriptBitReader reader)
Loads serialized state on client.
void RegenerateNavmesh()
Regenerates navmesh using previously stored data.
void RPC_GoToDestroyedState(int seed)
Used to do runtime synchronization of state.
void DestroyInteriorInit(bool immediate)
Handles destruction of interior by gathering objects using AABB (will probably use OBB) and deleting ...
void FinishLerp(IEntity owner, bool immediate, bool updateEntity, bool spawnEffects=true)
vector LerpAngles(vector start, vector current, vector target, float rotationSpeed, float timeSlice, notnull SCR_BuildingDestructionData data)
float GetDelay()
Returns prefab data stored delay.
void LerpPosition(IEntity owner, float timeSlice)
override event bool OnRplSave(ScriptBitWriter writer)
Serializes state over network.
override void OnDamageStateChanged(EDamageState newState, EDamageState previousDamageState, bool isJIP)
Called when damage state is changed.
SCR_BuildingDestructionData GetData()
Returns centrally stored data from building destruction manager.
bool TraceFilter(notnull IEntity e, vector start="0 0 0", vector dir="0 0 0")
Filters out unwanted entities.
bool IsInside(notnull IEntity entity)
Checks whether or not an entity is inside of the building, using a trace in each world axis.
void FinishDestruction()
Last method to be called after destruction happens, data is cleared.
bool QueryFilterCallback(notnull IEntity entity)
Used to filter out entities that are not meant to be handled in AddEntityCallback.
void LerpRotation(IEntity owner, float timeSlice)
bool IsProxy()
Returns true if local instance is proxy (not the authority).
override bool HijackDamageHandling(notnull BaseDamageContext damageContext)
bool PerformTrace(notnull TraceParam param, vector start, vector direction, notnull BaseWorld world, float lengthMultiplier=1)
SCR_AudioSourceConfiguration GetSlowDownAudioSourceConfiguration()
array< ref SCR_TimedEffect > GetEffects()
Returns pointer to prefab data stored array of effects, do not modify the array!
static float CalculateMomentum(notnull Contact contact, float ownerMass, float otherMass)
void SetEntityState(EEditableEntityState state, bool toSet)
bool Delete(bool changedByUser=false, bool updateNavmesh=false)
static RplComponent GetEntityRplComponent(notnull IEntity entity)
Class to temporarily store information about the last hit that dealt damage.
Definition SCR_HitInfo.c:3
void GetBounds(out vector mins, out vector maxs)
static IEntity GetLocalControlledEntity()
void SetParticleParams(ParticleEffectEntity emitter, inout notnull SCR_BuildingDestructionData data)
override void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
Plays particles.
ref SCR_ParticleSpawnable m_Particle
override void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
Spawns prefab.
ref SCR_AudioSourceConfiguration m_AudioSourceConfiguration
override void ExecuteEffect(IEntity owner, SCR_HitInfo hitInfo, inout notnull SCR_BuildingDestructionData data)
Plays effects.
enum EPhysicsLayerPresets Vehicle
Definition gameLib.c:24
void EntitySpawnParams()
Definition gameLib.c:130
IEntity GetOwner()
Owner entity of the fuel tank.
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
ShapeFlags
Definition ShapeFlags.c:13
@ NONE
When Shape is created and not initialized yet.
Definition ShapeType.c:15
EEditableEntityState
SCR_FieldOfViewSettings Attribute
EAddChildFlags
EntityEvent
Various entity events.
Definition EntityEvent.c:14
EntityFlags
Various entity flags.
Definition EntityFlags.c:14
EDamageType
Definition EDamageType.c:13
EDamageState
ECurveType
Definition ECurveType.c:13
BaseProjectileComponentClass GameComponentClass GetInstigator()
void RplRpc(RplChannel channel, RplRcver rcver, RplCondition condition=RplCondition.None, string customConditionName="")
Definition EnNetwork.c:95
RplRcver
Definition RplRcver.c:59
RplChannel
Communication channel. Reliable is guaranteed to be delivered. Unreliable not.
Definition RplChannel.c:14
T2 param2
Definition tuple.c:92
Tuple param1
proto native vector Vector(float x, float y, float z)
EmitterParam
EQueryEntitiesFlags
TraceFlags
Definition TraceFlags.c:13