Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_DestructibleTreeV2.c
Go to the documentation of this file.
1[EntityEditorProps(category: "GameScripted/TreeDestructionV2", description: "A tree entity that can be destroyed.", color: "0 0 255 255", visible: false, dynamicBox: true)]
2class SCR_DestructibleTreeV2Class : TreeClass
3{
4 [Attribute("200")]
5 float m_fDamageThreshold;
6
7 [Attribute("30000")]
8 float m_fHealth;
9
10 [Attribute("-1", desc: "Pick a prefab of the broken tree.")]
11 ref SCR_TreePartHierarchyV2 m_TreePartHierarchy;
12
13 //------------------------------------------------------------------------------------------------
14 // constructor
16 void SCR_DestructibleTreeV2Class(BaseContainer prefab)
17 {
18// Print("CreatePrefabData");
19// Print(prefab.GetClassName());
20// Print(prefab.GetName());
21// Print(m_fDamageThreshold);
22// Print(m_fHealth);
23 }
24}
25
26class SCR_TreeTraceInfo
27{
28 float impulse;
29 float distance;
33}
34
36{
37 int treePartIdx;
38 vector position;
39 float rotation[4];
40 bool isDynamic;
41 bool isParented;
42}
43
44class SCR_ActiveTreeData
45{
46 ref array<SCR_TreePartV2> m_aTreeParts = null;
47 ref array<ref SCR_TreeTraceInfo> m_aTreeTraceInfo = null;
48 ref array<ref SCR_DestroyedTreePartsData> m_aCachedTreePartsToDestroy = null;
49 ref array<int> m_aTreePartsToBreak = null;
50 float m_fDamage = 0;
52}
53
56{
57#ifdef ENABLE_DESTRUCTION
58 static const int INACTIVE_TREE_INDEX = -1;
59 static const int DESTROYED_TREE_INDEX = -2;
60
61 static bool s_bPrintTreeDamage = 0;
62 static SCR_DestructibleTreesSynchManager synchManager = null;
63
64 //THESE ARE RUNTIME
65 //private ref SCR_ActiveTreeData treeData = null;
66 private int m_iIndex = INACTIVE_TREE_INDEX;
67
68 //------------------------------------------------------------------------------------------------
70 bool GetIsDestroyed()
71 {
72 return m_iIndex == DESTROYED_TREE_INDEX;
73 }
74
75 //------------------------------------------------------------------------------------------------
77 void SetToDestroy(bool sentRPC = false)
78 {
79 SetEventMask(EntityEvent.FRAME);
80 SetFlags(EntityFlags.ACTIVE);
81
82 if (RplSession.Mode() == RplMode.Client)
83 return;
84
85 if (!sentRPC)
86 {
88 return;
89 EntityID id = GetID();
90 synchManager.SynchronizeSetToDestroy(id);
91 }
92 }
93
94 //------------------------------------------------------------------------------------------------
97 private void Destroy()
98 {
99 //Print("Destroy");
100 if (!GetIsDestroyed())
101 {
102 Physics physics = GetPhysics();
103 if (physics)
104 physics.Destroy();
105 SetObject(null, "");
106
107 //Store new the newly spawned prefab in child variable.
108 IEntity child = BuildHierarchy();
109
110 if (!child)
111 child = GetChildren();
112 }
113
114 if (m_iIndex < 0 || !synchManager)
115 return;
116
117 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
118
119 if (treeData && treeData.m_aTreePartsToBreak)
120 {
121 foreach (int i : treeData.m_aTreePartsToBreak)
122 {
123 SetToBreak(i);
124 RemoveTreePartFromParent(i)
125 }
126
127 treeData.m_aTreePartsToBreak.Clear();
128 treeData.m_aTreePartsToBreak = null;
129 }
130
131 m_iIndex = DESTROYED_TREE_INDEX;
132
133 // TODO: Hotfix calling update to ensure colliders are traceable in this frame, remove once engine handles this.
134 if (treeData && treeData.m_aTreeParts)
135 {
136 foreach (SCR_TreePartV2 treePart : treeData.m_aTreeParts)
137 {
138 treePart.Update();
139 }
140 }
141
142 Trace();
143
144 if (!treeData || !treeData.m_aCachedTreePartsToDestroy)
145 return;
146
147 int cachedTreePartsCount = treeData.m_aCachedTreePartsToDestroy.Count();
148 for (int i = cachedTreePartsCount-1; i >= 0; i--)
149 {
150 SCR_TreePartV2 treePart = FindTreePart(treeData.m_aCachedTreePartsToDestroy[i].treePartIdx);
151 //Print(treePart);
152 if (treePart)
153 {
154 if (treeData.m_aCachedTreePartsToDestroy[i].isDynamic)
155 treePart.SetPhysics(true);
156 else
157 treePart.SetPhysics(false);
158 //Print(m_aCachedTreePartsToDestroy[i].isParented);
159 treePart.CachePosition(treeData.m_aCachedTreePartsToDestroy[i].position);
160 treePart.CacheRotation(treeData.m_aCachedTreePartsToDestroy[i].rotation);
161// Print("Caching data rotation: ");
162// Print(m_aCachedTreePartsToDestroy[i].rotation);
163 if (!treeData.m_aCachedTreePartsToDestroy[i].isParented)
164 treePart.ClientSetToBreakFromParent();
165 }
166 treeData.m_aCachedTreePartsToDestroy[i] = null;
167 treeData.m_aCachedTreePartsToDestroy.Remove(i);
168 }
169 treeData.m_aCachedTreePartsToDestroy.Clear();
170 treeData.m_aCachedTreePartsToDestroy = null;
171 }
172
173 //------------------------------------------------------------------------------------------------
174 private IEntity BuildHierarchy()
175 {
177 if (!prefabData || !prefabData.m_TreePartHierarchy || prefabData.m_TreePartHierarchy.m_Prefab.GetPath() == "-1")
178 return null;
179
180 if (m_iIndex < 0 || !synchManager)
181 return null;
182
183 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
184
185 if (!treeData)
186 treeData = new SCR_ActiveTreeData();
187
188 if (!treeData.m_aTreeParts)
189 treeData.m_aTreeParts = {};
190
191 Resource resource = Resource.Load(prefabData.m_TreePartHierarchy.m_Prefab);
192 if (!resource.IsValid())
193 return null;
194
195 vector mat[4];
196 GetTransform(mat);
197
198 IEntity rootEnt = GetGame().SpawnEntityPrefab(resource);
199
200 if (RplSession.Mode() == RplMode.Client)
201 AddChild(rootEnt, -1, EAddChildFlags.AUTO_TRANSFORM);
202 else
203 AddChild(rootEnt, -1, EAddChildFlags.NONE);
204
205 rootEnt.SetTransform(mat);
206 SCR_TreePartV2 rootTreePart = SCR_TreePartV2.Cast(rootEnt);
207
208 if (rootTreePart)
209 {
210 rootTreePart.m_vLockedOrigin = GetOrigin();
211 treeData.m_aTreeParts.Insert(rootTreePart);
212 }
213
214 prefabData.m_TreePartHierarchy.SpawnAllChildren(rootEnt, treeData.m_aTreeParts);
215
216 array<IEntity> children = {};
217 SCR_Global.GetHierarchyEntityList(rootEnt, children);
218
219 foreach (SCR_TreePartV2 treePart : treeData.m_aTreeParts)
220 {
221 treePart.m_ParentTree = this;
222 }
223
224 prefabData.m_TreePartHierarchy = null;
225
226 return rootEnt;
227 }
228
229 //------------------------------------------------------------------------------------------------
232 void SetRotationOfTreePart(int treePartIdx, float quat[4])
233 {
234 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
235 if (!foundTreePart)
236 return;
237
238 foundTreePart.SetRotation(quat);
239 }
240
241 //------------------------------------------------------------------------------------------------
245 void CacheRotationOfTreePart(int treePartIdx, float quat[4])
246 {
247 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
248
249 if (!foundTreePart)
250 return;
251
252 foundTreePart.CacheRotation(quat);
253 }
254
255 //------------------------------------------------------------------------------------------------
259 void CacheSwitchTreePartPhysics(int treePartIdx, bool toDynamic)
260 {
261 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
262
263 if (!foundTreePart)
264 return;
265
266 foundTreePart.SetPhysics(toDynamic);
267 }
268
269 //------------------------------------------------------------------------------------------------
275 void ServerSetToBreak(int treePartIdx, vector positionVector, vector impulseVector, EDamageType damageType)
276 {
277 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
278 if (!foundTreePart)
279 return;
280
281 foundTreePart.SetToBreak(treePartIdx, positionVector, impulseVector, damageType);
282 }
283
284 //------------------------------------------------------------------------------------------------
286 void SetToBreak(int treePartIdx)
287 {
288 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
289
290 if (!foundTreePart)
291 {
292 if (m_iIndex < 0 || !synchManager)
293 return;
294
295 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
296
297 if (!treeData)
298 treeData = new SCR_ActiveTreeData();
299
300 if (!treeData.m_aTreePartsToBreak)
301 treeData.m_aTreePartsToBreak = {};
302
303 treeData.m_aTreePartsToBreak.Insert(treePartIdx);
304 return;
305 }
306
307 foundTreePart.SetToBreak();
308 }
309
310 //------------------------------------------------------------------------------------------------
313 void RemoveTreePartFromParent(int treePartIndex)
314 {
315 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIndex);
316 if (!foundTreePart)
317 return;
318
319 foundTreePart.RemoveFromParent();
320 }
321
322 //------------------------------------------------------------------------------------------------
329 void CacheDestroyedTreePart(int treePartIdx, vector position, float quat[4], bool isDynamic, bool isParented)
330 {
332 data.treePartIdx = treePartIdx;
333 data.position[0] = position[0];
334 data.position[1] = position[1];
335 data.position[2] = position[2];
336 Math3D.QuatCopy(quat, data.rotation);
337 data.isDynamic = isDynamic;
338 data.isParented = isParented;
339
340 if (m_iIndex < 0 || !synchManager)
341 return;
342
343 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
344
345 if (!treeData)
346 treeData = new SCR_ActiveTreeData();
347
348 if (!treeData.m_aCachedTreePartsToDestroy)
349 treeData.m_aCachedTreePartsToDestroy = {};
350
351 treeData.m_aCachedTreePartsToDestroy.Insert(data);
352 }
353
354 //------------------------------------------------------------------------------------------------
361 void UpdateTransformOfTreePart(int treePartIdx, vector pos, float quat[4], vector velocityLinear, vector velocityAngular)
362 {
363 SCR_TreePartV2 treePart = FindTreePart(treePartIdx);
364 if (!treePart)
365 return;
366
367 treePart.UpdateTransform(pos, quat, velocityLinear, velocityAngular);
368 }
369
370 //------------------------------------------------------------------------------------------------
373 void SetPositionOfTreePart(int treePartIdx, vector pos)
374 {
375 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
376 if (!foundTreePart)
377 return;
378
379 foundTreePart.SetPosition(pos);
380 }
381
382 //------------------------------------------------------------------------------------------------
386 void CachePositionOfTreePart(int treePartIdx, vector pos)
387 {
388 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
389 if (!foundTreePart)
390 return;
391
392 foundTreePart.CachePosition(pos);
393 }
394
395 //------------------------------------------------------------------------------------------------
399 void UpdateVelocityOfTreePart(int treePartIdx, vector velocity)
400 {
401 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
402 if (!foundTreePart)
403 return;
404
405 foundTreePart.SetVelocity(velocity);
406 }
407
408 //------------------------------------------------------------------------------------------------
412 void UpdateAngularVelocityOfTreePart(int treePartIdx, vector angularVelocity)
413 {
414 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
415 if (!foundTreePart)
416 return;
417
418 foundTreePart.SetAngularVelocity(angularVelocity);
419 }
420
421 //------------------------------------------------------------------------------------------------
424 void ClientSwitchTreePartToStatic(int treePartIdx)
425 {
426 SCR_TreePartV2 foundTreePart = FindTreePart(treePartIdx);
427 if (!foundTreePart)
428 return;
429
430 foundTreePart.SetPhysics(false);
431 }
432
433 //------------------------------------------------------------------------------------------------
440 void SetToTrace(vector position, vector direction, float impulse, float distance, EDamageType type, IEntity other)
441 {
442 SCR_TreeTraceInfo info = new SCR_TreeTraceInfo();
443 info.position = position;
444 info.direction = direction;
445 info.impulse = impulse;
446 info.distance = distance;
447 info.type = type;
448
449 if (m_iIndex < 0 || !synchManager)
450 return;
451
452 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
453
454 if (!treeData)
455 treeData = new SCR_ActiveTreeData();
456
457 if (!treeData.m_aTreeTraceInfo)
458 treeData.m_aTreeTraceInfo = {};
459
460 treeData.m_aTreeTraceInfo.Insert(info);
461 }
462
463 //------------------------------------------------------------------------------------------------
465 void Trace()
466 {
467 if (m_iIndex < 0 || !synchManager)
468 return;
469
470 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
471
472 if (!treeData || !treeData.m_aTreeTraceInfo)
473 return;
474
475 int count = treeData.m_aTreeTraceInfo.Count();
476 for (int i = count - 1; i >= 0; i--)
477 {
478 SCR_TreeTraceInfo info = treeData.m_aTreeTraceInfo[i];
479
480 if (info.type == EDamageType.EXPLOSIVE)
481 {
482 foreach (SCR_TreePartV2 treePart : treeData.m_aTreeParts)
483 {
484 if (treePart.m_bDebugDamage)
485 treePart.PrintDamageDebug(info.impulse, info.type);
486
487 float reducedImpulse;
488 if (treePart.WouldBreak(info.impulse, info.type, reducedImpulse))
489 treePart.SetToBreak(positionVector: info.position, impulseVector: reducedImpulse * info.direction, damageType: info.type);
490 }
491 }
492 else
493 {
494 TraceSphere param = new TraceSphere();
495
496 param.Radius = 0.1;
497 param.Start = info.position;
498 param.End = info.position + info.direction;
499 param.Flags = TraceFlags.WORLD | TraceFlags.ENTS;
500 param.LayerMask = EPhysicsLayerDefs.Camera; // is this a correct layer mask?
501
502 SCR_Global.g_TraceFilterEnt = this;
503
504 vector tracePos = GetWorld().TraceMove(param, SCR_Global.FilterCallback_IgnoreAllButEntityWithChildren) * (param.End - param.Start) + param.Start;
505
506 IEntity traceEnt = param.TraceEnt;
507 if (traceEnt)
508 {
509 SCR_TreePartV2 tracedTreePart = SCR_TreePartV2.Cast(traceEnt);
510 if (tracedTreePart)
511 {
512 if (tracedTreePart.m_bDebugDamage)
513 tracedTreePart.PrintDamageDebug(info.impulse, info.type);
514
515 float reducedImpulse;
516 if (tracedTreePart.WouldBreak(info.impulse, info.type, reducedImpulse))
517 tracedTreePart.SetToBreak(positionVector: info.position, impulseVector: reducedImpulse * info.direction, damageType: info.type);
518 }
519 }
520
521 treeData.m_aTreeTraceInfo[i] = null;
522 }
523 }
524
525 treeData.m_aTreeTraceInfo.Clear();
526 treeData.m_aTreeTraceInfo = null;
527 }
528
529 //------------------------------------------------------------------------------------------------
530 void OnTreePartHitGround()
531 {
532 if (m_iIndex < 0 || !synchManager)
533 return;
534
535 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
536
537 if (treeData.m_fTimeSinceLastSoundEvent < 0.5)
538 return;
539 treeData.m_fTimeSinceLastSoundEvent = 0;
540
541 if (synchManager)
542 {
543 BaseSoundComponent soundComponent = synchManager.GetSoundComponent();
544 if (soundComponent)
545 soundComponent.PlayStr(SCR_SoundEvent.SOUND_HIT_GROUND);
546 }
547 }
548
549 //------------------------------------------------------------------------------------------------
550 override void OnDamage(EDamageType type,
551 float damage,
552 HitZone pHitZone,
553 notnull Instigator instigator,
554 inout vector hitTransform[3],
555 float speed,
556 int colliderID,
557 int nodeID)
558 {
559 if (RplSession.Mode() == RplMode.Client)
560 return;
561
562 if (s_bPrintTreeDamage)
563 Print(damage);
564
566
567 if (damage < prefabData.m_fDamageThreshold)
568 return;
569
570 if (!synchManager)
571 return;
572
573 if (m_iIndex < 0)
574 m_iIndex = synchManager.AddActiveTree();
575
576 if (m_iIndex < 0)
577 return;
578
579 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
580
581 treeData.m_fDamage += damage;
582 if (treeData.m_fDamage < prefabData.m_fHealth)
583 return;
584
585 float distance = 0;
586 if (damageSource)
587 distance = (damageSource.GetOrigin() - GetOrigin()).Length() * 1.1;
588
589 //Calculate impulse vector from direction vector and damage
590 vector positionVector = outMat[0];
591 vector direction = outMat[1];
592
593 if (type == EDamageType.EXPLOSIVE)
594 {
595 positionVector = "0 0 0";
596 direction = (GetOrigin() - outMat[0]) * damage;
597 }
598
599 vector position = outMat[0] + (-outMat[1] * 0.5); // Position of hit + normal of the hit
600
601 direction.Normalize();
602
603 Vehicle vehicle = Vehicle.Cast(damageSource);
604 if (vehicle)
605 SetToTrace(position, direction, damage, distance, type, vehicle);
606 else
607 SetToTrace(position, direction, damage, distance, type, null);
608
609 SetToDestroy();
610 }
611
612 //------------------------------------------------------------------------------------------------
613 private SCR_TreePartV2 FindTreePart(int treePartIdx)
614 {
615 if (m_iIndex < 0 || !synchManager)
616 return null;
617
618 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
619
620 if (!treeData || !treeData.m_aTreeParts)
621 return null;
622
623 foreach (SCR_TreePartV2 treePart : treeData.m_aTreeParts)
624 {
625 if (treePart.GetTreePartIndex() == treePartIdx)
626 return treePart;
627 }
628
629 return null;
630 }
631
632 //------------------------------------------------------------------------------------------------
633 override void EOnFrame(IEntity owner, float timeSlice)
634 {
635 if (m_iIndex < 0 || !synchManager)
636 return;
637
638 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(m_iIndex);
639
640 if (!GetIsDestroyed())
641 {
642 Destroy();
643
644 treeData.m_fTimeSinceLastSoundEvent = 0;
645 }
646
647 treeData.m_fTimeSinceLastSoundEvent += timeSlice;
648
649 if (treeData.m_fTimeSinceLastSoundEvent > 10)
650 {
651 treeData.m_fTimeSinceLastSoundEvent = 0;
652 ClearEventMask(EntityEvent.FRAME);
653 ClearFlags(EntityFlags.ACTIVE);
654 }
655 }
656#endif
657}
GetTransformResult JsonApiStruct GetTransform(notnull const BlenderRestAPI connection, string object_name)
ArmaReforgerScripted GetGame()
Definition game.c:1398
override string GetID(string fileName, string varName, array< BaseContainer > objects, array< int > indexes)
RplMode
Mode of replication.
Definition RplMode.c:9
vector GetOrigin()
SCR_CharacterBloodHitZone OnDamage
Resilience - incapacitation or death, depending on game mode settings.
enum SCR_ECompassType EntityEditorProps(category:"GameScripted/Gadgets", description:"Compass", color:"0 0 255 255")
Prefab data class for compass component.
float distance
ref array< ref SCR_DestroyedTreePartsData > m_aCachedTreePartsToDestroy
ref array< int > m_aTreePartsToBreak
ref array< ref SCR_TreeTraceInfo > m_aTreeTraceInfo
EDamageType type
vector direction
float m_fTimeSinceLastSoundEvent
SCR_DestructibleTreeV2Class impulse
vector position
class SCR_DestroyedTreePartsData m_aTreeParts
override bool Destroy(int editorPlayerID)
Get all prefabs that have the spawner data
float m_fDamage
override void EOnFrame(IEntity owner, float timeSlice)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
void GetChildren(out array< SCR_ScenarioFrameworkLayerBase > children)
SCR_VehicleDamageManagerComponentClass GetPrefabData()
proto external bool SetTransform(vector mat[4])
Object holding reference to resource. In destructor release the resource.
Definition Resource.c:25
Encapsulates the functionality of a destructible tree entity in the world.
Definition Tree.c:11
enum EPhysicsLayerPresets Vehicle
Definition gameLib.c:24
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
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
TraceFlags
Definition TraceFlags.c:13