1 [
EntityEditorProps(
category:
"GameScripted/TreeDestructionV2", description:
"A tree entity that can be destroyed.", color:
"0 0 255 255", visible:
false, dynamicBox:
true)]
5 float m_fDamageThreshold;
26 class SCR_TreeTraceInfo
44 class SCR_ActiveTreeData
57 #ifdef ENABLE_DESTRUCTION
58 static const int INACTIVE_TREE_INDEX = -1;
59 static const int DESTROYED_TREE_INDEX = -2;
61 static bool s_bPrintTreeDamage = 0;
66 private int m_iIndex = INACTIVE_TREE_INDEX;
72 return m_iIndex == DESTROYED_TREE_INDEX;
77 void SetToDestroy(
bool sentRPC =
false)
79 SetEventMask(EntityEvent.FRAME);
80 SetFlags(EntityFlags.ACTIVE);
82 if (RplSession.Mode() == RplMode.Client)
90 synchManager.SynchronizeSetToDestroy(
id);
100 if (!GetIsDestroyed())
102 Physics physics = GetPhysics();
108 IEntity child = BuildHierarchy();
117 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
119 if (treeData && treeData.m_aTreePartsToBreak)
121 foreach (
int i : treeData.m_aTreePartsToBreak)
124 RemoveTreePartFromParent(i)
127 treeData.m_aTreePartsToBreak.Clear();
128 treeData.m_aTreePartsToBreak =
null;
134 if (treeData && treeData.m_aTreeParts)
144 if (!treeData || !treeData.m_aCachedTreePartsToDestroy)
147 int cachedTreePartsCount = treeData.m_aCachedTreePartsToDestroy.Count();
148 for (
int i = cachedTreePartsCount-1; i >= 0; i--)
150 SCR_TreePartV2 treePart = FindTreePart(treeData.m_aCachedTreePartsToDestroy[i].treePartIdx);
154 if (treeData.m_aCachedTreePartsToDestroy[i].isDynamic)
155 treePart.SetPhysics(
true);
157 treePart.SetPhysics(
false);
159 treePart.CachePosition(treeData.m_aCachedTreePartsToDestroy[i].position);
160 treePart.CacheRotation(treeData.m_aCachedTreePartsToDestroy[i].rotation);
163 if (!treeData.m_aCachedTreePartsToDestroy[i].isParented)
164 treePart.ClientSetToBreakFromParent();
166 treeData.m_aCachedTreePartsToDestroy[i] =
null;
167 treeData.m_aCachedTreePartsToDestroy.Remove(i);
169 treeData.m_aCachedTreePartsToDestroy.Clear();
170 treeData.m_aCachedTreePartsToDestroy =
null;
174 private IEntity BuildHierarchy()
177 if (!prefabData || !prefabData.m_TreePartHierarchy || prefabData.m_TreePartHierarchy.m_Prefab.GetPath() ==
"-1")
183 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
186 treeData =
new SCR_ActiveTreeData();
188 if (!treeData.m_aTreeParts)
189 treeData.m_aTreeParts = {};
191 Resource resource = Resource.Load(prefabData.m_TreePartHierarchy.m_Prefab);
192 if (!resource.IsValid())
198 IEntity rootEnt =
GetGame().SpawnEntityPrefab(resource);
200 if (RplSession.Mode() == RplMode.Client)
201 AddChild(rootEnt, -1, EAddChildFlags.AUTO_TRANSFORM);
203 AddChild(rootEnt, -1, EAddChildFlags.NONE);
205 rootEnt.SetTransform(mat);
210 rootTreePart.m_vLockedOrigin =
GetOrigin();
211 treeData.m_aTreeParts.Insert(rootTreePart);
214 prefabData.m_TreePartHierarchy.SpawnAllChildren(rootEnt, treeData.m_aTreeParts);
216 array<IEntity> children = {};
217 SCR_Global.GetHierarchyEntityList(rootEnt, children);
221 treePart.m_ParentTree =
this;
224 prefabData.m_TreePartHierarchy =
null;
232 void SetRotationOfTreePart(
int treePartIdx,
float quat[4])
238 foundTreePart.SetRotation(quat);
245 void CacheRotationOfTreePart(
int treePartIdx,
float quat[4])
252 foundTreePart.CacheRotation(quat);
259 void CacheSwitchTreePartPhysics(
int treePartIdx,
bool toDynamic)
266 foundTreePart.SetPhysics(toDynamic);
275 void ServerSetToBreak(
int treePartIdx, vector positionVector, vector impulseVector,
EDamageType damageType)
281 foundTreePart.SetToBreak(treePartIdx, positionVector, impulseVector, damageType);
286 void SetToBreak(
int treePartIdx)
295 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
298 treeData =
new SCR_ActiveTreeData();
300 if (!treeData.m_aTreePartsToBreak)
301 treeData.m_aTreePartsToBreak = {};
303 treeData.m_aTreePartsToBreak.Insert(treePartIdx);
307 foundTreePart.SetToBreak();
313 void RemoveTreePartFromParent(
int treePartIndex)
319 foundTreePart.RemoveFromParent();
329 void CacheDestroyedTreePart(
int treePartIdx, vector
position,
float quat[4],
bool isDynamic,
bool isParented)
332 data.treePartIdx = treePartIdx;
336 Math3D.QuatCopy(quat,
data.rotation);
337 data.isDynamic = isDynamic;
338 data.isParented = isParented;
343 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
346 treeData =
new SCR_ActiveTreeData();
348 if (!treeData.m_aCachedTreePartsToDestroy)
349 treeData.m_aCachedTreePartsToDestroy = {};
351 treeData.m_aCachedTreePartsToDestroy.Insert(
data);
361 void UpdateTransformOfTreePart(
int treePartIdx, vector pos,
float quat[4], vector velocityLinear, vector velocityAngular)
367 treePart.UpdateTransform(pos, quat, velocityLinear, velocityAngular);
373 void SetPositionOfTreePart(
int treePartIdx, vector pos)
379 foundTreePart.SetPosition(pos);
386 void CachePositionOfTreePart(
int treePartIdx, vector pos)
392 foundTreePart.CachePosition(pos);
399 void UpdateVelocityOfTreePart(
int treePartIdx, vector velocity)
405 foundTreePart.SetVelocity(velocity);
412 void UpdateAngularVelocityOfTreePart(
int treePartIdx, vector angularVelocity)
418 foundTreePart.SetAngularVelocity(angularVelocity);
424 void ClientSwitchTreePartToStatic(
int treePartIdx)
430 foundTreePart.SetPhysics(
false);
442 SCR_TreeTraceInfo info =
new SCR_TreeTraceInfo();
452 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
455 treeData =
new SCR_ActiveTreeData();
457 if (!treeData.m_aTreeTraceInfo)
458 treeData.m_aTreeTraceInfo = {};
460 treeData.m_aTreeTraceInfo.Insert(info);
470 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
472 if (!treeData || !treeData.m_aTreeTraceInfo)
475 int count = treeData.m_aTreeTraceInfo.Count();
476 for (
int i = count - 1; i >= 0; i--)
478 SCR_TreeTraceInfo info = treeData.m_aTreeTraceInfo[i];
484 if (treePart.m_bDebugDamage)
485 treePart.PrintDamageDebug(info.impulse, info.type);
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);
494 TraceSphere param =
new TraceSphere();
497 param.Start = info.position;
498 param.End = info.position + info.direction;
499 param.Flags = TraceFlags.WORLD | TraceFlags.ENTS;
500 param.LayerMask = EPhysicsLayerDefs.Camera;
504 vector tracePos = GetWorld().TraceMove(param,
SCR_Global.FilterCallback_IgnoreAllButEntityWithChildren) * (param.End - param.Start) + param.Start;
506 IEntity traceEnt = param.TraceEnt;
512 if (tracedTreePart.m_bDebugDamage)
513 tracedTreePart.PrintDamageDebug(info.impulse, info.type);
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);
521 treeData.m_aTreeTraceInfo[i] =
null;
525 treeData.m_aTreeTraceInfo.Clear();
526 treeData.m_aTreeTraceInfo =
null;
530 void OnTreePartHitGround()
535 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
537 if (treeData.m_fTimeSinceLastSoundEvent < 0.5)
539 treeData.m_fTimeSinceLastSoundEvent = 0;
543 BaseSoundComponent soundComponent = synchManager.GetSoundComponent();
554 inout vector hitTransform[3],
559 if (RplSession.Mode() == RplMode.Client)
562 if (s_bPrintTreeDamage)
567 if (damage < prefabData.m_fDamageThreshold)
574 m_iIndex = synchManager.AddActiveTree();
579 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
581 treeData.m_fDamage += damage;
582 if (treeData.m_fDamage < prefabData.m_fHealth)
590 vector positionVector = outMat[0];
595 positionVector =
"0 0 0";
599 vector
position = outMat[0] + (-outMat[1] * 0.5);
603 Vehicle vehicle = Vehicle.Cast(damageSource);
618 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
620 if (!treeData || !treeData.m_aTreeParts)
625 if (treePart.GetTreePartIndex() == treePartIdx)
633 override void EOnFrame(IEntity owner,
float timeSlice)
638 SCR_ActiveTreeData treeData = synchManager.GetActiveTreeData(
m_iIndex);
640 if (!GetIsDestroyed())
644 treeData.m_fTimeSinceLastSoundEvent = 0;
647 treeData.m_fTimeSinceLastSoundEvent += timeSlice;
649 if (treeData.m_fTimeSinceLastSoundEvent > 10)
651 treeData.m_fTimeSinceLastSoundEvent = 0;
652 ClearEventMask(EntityEvent.FRAME);
653 ClearFlags(EntityFlags.ACTIVE);