Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_TreePartV2.c
Go to the documentation of this file.
1 [EntityEditorProps(category: "GameScripted/TreeDestructionV2", description: "A part of tree that reacts to damage.", color: "0 0 255 255", visible: false, dynamicBox: true)]
2 class SCR_TreePartV2Class: TreeClass
3 {
4 };
5 
6 //------------------------------------------------------------------------------------------------
7 //Encapsulates the functionality of a tree part entity in the world.
8 class SCR_TreePartV2 : Tree
9 {
10  //********************//
11  //ATTRIBUTES - PHYSICS//
12  //********************//
13  [Attribute("1", UIWidgets.EditBox, "Enter the mass of this tree part.", category: "Physics settings")]
14  private float m_fMass;
15  [Attribute("9", UIWidgets.EditBox, "Enter the max linear acceleration of this tree part for each local axis.", category: "Physics settings")]
16  private float m_fMaxAccelerationLinear;
17  [Attribute("5 5 5", UIWidgets.EditBox, "Enter the max angular acceleration of this tree part.", category: "Physics settings")]
18  private vector m_vMaxAccelerationAngular;
19  [Attribute("0.05", UIWidgets.EditBox, "Enter the max speed of rotation around local Y axis of this tree part.", category: "Physics settings")]
20  private float m_fMaxYRotationVelocity;
21  [Attribute("1", UIWidgets.CheckBox, "Allow linear movement of this object?", category: "Physics settings")]
22  private bool m_bAllowLinearMovement;
23  [Attribute("1", UIWidgets.CheckBox, "Allow rotation around global Y axis of this object?", category: "Physics settings")]
24  private bool m_bAllowGlobalYRotation;
25  [Attribute("200", UIWidgets.EditBox, "Enter the max joint load.", category: "Physics settings")]
26  private float m_fMaxJointLoad;
27 
28  //*******************//
29  //ATTRIBUTES - DAMAGE//
30  //*******************//
31  [Attribute("1", UIWidgets.EditBox, "Enter the minimum impact that should be able to move with this object. Impact = reduced impulse.", category: "Damage settings")]
32  private float m_fMinImpact;
33 
34 
35  [Attribute("1", UIWidgets.EditBox, "Enter explosion resistance of this tree part. Every explosive impulse on this tree part will be divided by this number.", category: "Damage settings")]
36  private float m_fExplosionResistance;
37  [Attribute("1", UIWidgets.EditBox, "Enter kinetic resistance of this tree part. Every kinetic (projectile) impulse on this tree part will be divided by this number.", category: "Damage settings")]
38  private float m_fKineticResistance;
39  [Attribute("1", UIWidgets.EditBox, "Enter collision resistance of this tree part. Every collision (vehicle) impulse on this tree part will be divided by this number.", category: "Damage settings")]
40  private float m_fCollisionResistance;
41  [Attribute("1", UIWidgets.EditBox, "Enter melee resistance of this tree part. Every melee impulse on this tree part will be divided by this number.", category: "Damage settings")]
42  private float m_fMeleeResistance;
43 
44  [Attribute("0", UIWidgets.CheckBox, "Allows you to debug values that are damage related.", category: "Damage settings")]
45  bool m_bDebugDamage;
46 
47  //************************//
48  //ATTRIBUTES - RECOGNITION//
49  //************************//
50  [Attribute("0", UIWidgets.EditBox, "Enter the index of this tree part.", category: "Recognition settings")]
51  private int m_iTreePartIndex;
52  [Attribute("-1", UIWidgets.ResourceNamePicker, "Pick the root dirt prefab.", category: "Root dirt settings")]
53  private ResourceName m_RootDirt;
54  [Attribute("0 0 0", UIWidgets.Auto, "The offset of the root dirt object.", category: "Root dirt settings")]
55  private vector m_vRootDirtOffset;
56 
57 #ifdef ENABLE_DESTRUCTION
58 
59  static SCR_DestructibleTreesSynchManager synchManager = null;
60 
61  //*********************//
62  //GLOBAL SLEEP SETTINGS//
63  //*********************//
64  static const float GO_TO_STATIC_THRESHOLD = 0.2;
65  static const float GO_TO_STATIC_ANGULAR_THRESHOLD = 0.2;
66  static const int GO_TO_STATIC_TIME_THRESHOLD = 2;
67  static const int GO_TO_STATIC_TIME_THRESHOLD_MAX = 20;
68 
69  //*******************************//
70  //GLOBAL SYNCHRONIZATION SETTINGS//
71  //*******************************//
72  static const int TARGET_RPC_COUNT = 4;
73  static const float NET_TELEPORT_DISTANCE = 10;
74  static const float NET_TELEPORT_ANGLE = 5;
75 
76  //****************************//
77  //MEMBER COMPONENTS & ENTITIES//
78  //****************************//
79  private Physics m_Physics = null;
80 
81  //TODO Minimize the stored data amount
82  //************************//
83  //RUNTIME MEMBER VARIABLES//
84  //************************//
85  private vector m_vCenterOfMass;
86  private float m_fLastSpeedLinear = 0;
87  private vector m_vLastSpeedAngular = "0 0 0";
88  private bool m_bSpawnedRootBall = false;
89  private IEntity m_RootsEntity = null;
90  private Physics m_RootsPhysics = null;
91  private vector m_vBBOXMin;
92  private vector m_vBBOXMax;
93  private vector m_vSynchVelocity;
94  private vector m_vLerpStartVector;
95  private vector m_vLerpTargetPosition;
96  private float m_fLerpStartQuat[4];
97  private float m_fLerpTargetQuat[4];
98  private float m_fLerpAmountPos = -1;
99  private float m_fLerpAmountRot = -1;
100  private ref array<IEntity> m_aQuerriedEnts = new array<IEntity>();
101  private float m_fSynchTime = 0;
102  vector m_vLockedOrigin;
103  SCR_DestructibleTreeV2 m_ParentTree = null;
104 
105  private vector m_vNetPosition;
106  private vector m_vNetVelocityLinear;
107  private vector m_vNetVelocityAngular;
108  private float m_fTimeSinceLastTick;
109  private float m_fNetRotation[4];
110  private bool m_bExtrapolate = false;
111 
112  //************//
113  //DATA CACHING//
114  //************//
115  private bool m_bApplyCachedRotation = false;
116  private float m_fCachedRotation[4];
117  private bool m_bApplyCachedPosition = false;
118  private vector m_vCachedPosition;
119  private bool m_bBreak = false;
120  private vector m_vImpulseVector = "0 0 0";
121  private vector m_vPositionVector = "0 0 0";
122  private EDamageType m_eDamageType;
123 
124  //*****//
125  //SLEEP//
126  //*****//
127  private float m_fThresholdTime = 0;
128  private float m_fThresholdMaxTime = 0;
129 
130  //*****************//
131  //PHYSICS SWITCHING//
132  //*****************//
133  private bool m_bWakeUp = false;
134  private bool m_bSwitchToDynamic;
135  private bool m_bSwitchToStatic;
136  private ref SCR_HybridPhysicsInfo m_HybridPhysicsInfo = null;
137  private int m_iContactsCount = 0;
138  private bool m_bBreakFromParent = false;
139 
140  //******//
141  //JOINTS//
142  //******//
143  private bool m_bCreatedJoints = false;
144  private ref array<PhysicsJoint> m_aChildrenJoints = new array<PhysicsJoint>();
145  private PhysicsJoint m_ParentJoint;
146 
147  //***************//
148  //SYNCHRONIZATION//
149  //***************//
150  private bool m_bSynchronizeHasParent = true;
151  private static float m_fTargetSynchTime = -1;
152 
153  //*****//
154  //SOUND//
155  //*****//
156  private bool m_bSoundHitGroundPlayed = false;
157 
158  //------------------------------------------------------------------------------------------------
160  private void ClearPhysicsInfo()
161  {
162  if (!m_HybridPhysicsInfo)
163  return;
164 
165  delete m_HybridPhysicsInfo;
166  m_HybridPhysicsInfo = null;
167  }
168 
169  //------------------------------------------------------------------------------------------------
171  private void StorePhysicsInfo(IEntity owner)
172  {
173  if (!m_Physics || !owner)
174  return;
175 
176  ClearPhysicsInfo();
177 
178  m_HybridPhysicsInfo = new SCR_HybridPhysicsInfo;
179  m_HybridPhysicsInfo.m_fMass = m_Physics.GetMass();
180 
181  int numGeoms = m_Physics.GetNumGeoms();
182  for (int i = 0; i < numGeoms; i++)
183  {
184  m_HybridPhysicsInfo.m_aLayerMasks.Insert(m_Physics.GetGeomInteractionLayer(i));
185  }
186 
187  m_Physics.Destroy();
188  m_Physics = Physics.CreateStatic(owner, -1);
189 
190  int numStoredGeoms = m_HybridPhysicsInfo.m_aLayerMasks.Count();
191  numGeoms = m_Physics.GetNumGeoms();
192  for (int i = 0; i < numStoredGeoms; i++)
193  {
194  if (i >= numGeoms)
195  break;
196 
197  m_Physics.SetGeomInteractionLayer(i, m_HybridPhysicsInfo.m_aLayerMasks.Get(i));
198  }
199  }
200 
201  //------------------------------------------------------------------------------------------------
203  private void ApplyPhysicsInfo(IEntity owner)
204  {
205  if (!m_Physics)
206  {
207  ClearPhysicsInfo();
208  return;
209  }
210  if (!owner || !m_HybridPhysicsInfo)
211  return;
212 
213  m_Physics.Destroy();
214  m_Physics = Physics.CreateDynamic(owner, m_HybridPhysicsInfo.m_fMass, -1);
215 
216  int numStoredGeoms = m_HybridPhysicsInfo.m_aLayerMasks.Count();
217  int numGeoms = m_Physics.GetNumGeoms();
218  for (int i = 0; i < numStoredGeoms; i++)
219  {
220  if (i >= numGeoms)
221  break;
222 
223  m_Physics.SetGeomInteractionLayer(i, m_HybridPhysicsInfo.m_aLayerMasks.Get(i));
224  }
225 
226  ClearPhysicsInfo();
227  }
228 
229  //------------------------------------------------------------------------------------------------
230  void SetToBreak(int treePartIdx = -1, vector positionVector = "0 0 0", vector impulseVector = "0 0 0", EDamageType damageType = EDamageType.MELEE)
231  {
232  m_bBreak = true;
233 
234  if (RplSession.Mode() == RplMode.Client || !synchManager)
235  return;
236 
237  if (treePartIdx == -1)
238  treePartIdx = m_iTreePartIndex;
239 
240  if (!m_ParentTree)
241  return;
242 
243  synchManager.SynchronizeSetToBreak(treePartIdx, positionVector, impulseVector, damageType, m_ParentTree.GetID());
244 
245  m_ParentTree.SetFlags(EntityFlags.ACTIVE);
246 
247  SetEventMask(EntityEvent.FRAME);
248  m_vImpulseVector = impulseVector * 50;
249  m_vPositionVector = positionVector;
250  ResetThresholdTime();
251  m_eDamageType = damageType;
252  }
253 
254  //------------------------------------------------------------------------------------------------
255  //Break this tree part from it's parent and apply impulse.
256  private void Break()
257  {
258  WakeUpHierarchy();
259 
260  if (!m_Physics || !m_Physics.IsDynamic())
261  return;
262 
263  if (!m_bSpawnedRootBall && m_iTreePartIndex == 0)
264  SpawnRootDirt();
265 
266  RemoveFromParent();
267 
268  if (m_vPositionVector == "0 0 0")
269  m_vPositionVector = (m_vCenterOfMass + GetOrigin());
270 
271  //Apply the impulse to this tree part.
272  if (m_eDamageType != EDamageType.COLLISION)
273  {
274  m_Physics.ApplyImpulseAt(m_vPositionVector, m_vImpulseVector);
275  LimitAcceleration();
276  }
277 
278  if (!m_bAllowLinearMovement)
279  {
280  SetOrigin(m_vLockedOrigin);
281  m_Physics.SetVelocity("0 0 0");
282  }
283 
284  WakeUp(true);
285  WakeUpOthers();
286 
287  // SOUND
288  if (!m_bSoundHitGroundPlayed) // ensure that SOUND_BREAK does not play after SOUND_HIT_GROUND
289  {
290  if (synchManager)
291  {
292  BaseSoundComponent soundComponent = synchManager.GetSoundComponent();
293  if (soundComponent)
294  {
295  vector mat[4];
296  Math3D.MatrixIdentity3(mat);
297  mat[3] = m_vPositionVector;
298  soundComponent.SetTransformation(mat);
299  soundComponent.PlayStr(SCR_SoundEvent.SOUND_BREAK);
300  }
301  }
302  }
303 
304  m_bBreak = false;
305  }
306 
307  //------------------------------------------------------------------------------------------------
308  //TODO: Update this
309  private void SpawnRootDirt()
310  {
311  if (m_RootDirt.GetPath() == "-1")
312  return;
313 
314  Resource resource = Resource.Load(m_RootDirt);
315 
316  if (!resource.IsValid())
317  return;
318 
319  vector worldPos = GetOrigin();
320 
321  //ALIGN TO NORMAL
322  vector mat[4];
323  TraceParam traceParam = new TraceParam();
324  traceParam.Start = worldPos + vector.Up;
325  traceParam.End = worldPos - vector.Up;
326  traceParam.Flags = TraceFlags.WORLD;
327  GetWorld().TraceMove(traceParam, null);
328  GetTransform(mat);
329 
330  if (traceParam.TraceNorm != vector.Zero)
331  {
332  vector newUp = traceParam.TraceNorm;
333  newUp.Normalize();
334 
335  //Shape shape;
336  //shape = Shape.Create(ShapeType.LINE, ARGB(255, 0, 255, 0), ShapeFlags.NOZBUFFER, worldPos, worldPos + newUp);
337  //m_aDebugShapes.Insert(shape);
338  vector newRight = newUp * mat[2];
339  newRight.Normalize();
340  //shape = Shape.Create(ShapeType.LINE, ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, worldPos, worldPos + newRight);
341  //m_aDebugShapes.Insert(shape);
342  vector newForward = newRight * newUp;
343  newForward.Normalize();
344  //shape = Shape.Create(ShapeType.LINE, ARGB(255, 0, 0, 255), ShapeFlags.NOZBUFFER, worldPos, worldPos + newForward);
345  //m_aDebugShapes.Insert(shape);
346 
347  mat[0] = newRight;
348  mat[1] = newUp;
349  mat[2] = newForward;
350  }
351  mat[3] = m_vRootDirtOffset;
352 
353  //Spawn parameters setup
354  EntitySpawnParams param = new EntitySpawnParams();
355  param.TransformMode = ETransformMode.LOCAL;
356  param.Transform = mat;
357  param.Parent = this;
358 
359  ArmaReforgerScripted game = GetGame();
360 
361  if (!game)
362  return;
363 
364  m_RootsEntity = game.SpawnEntityPrefab(resource, GetWorld(), param);
365 
366  if (!m_RootsEntity)
367  return;
368 
369  m_bSpawnedRootBall = true;
370  }
371 
372  //------------------------------------------------------------------------------------------------
373  //Remove this tree part from it's parent, move it to correct world position and break it's parent joint.
374  void RemoveFromParent()
375  {
376  IEntity parent = GetParent();
377  if (!parent)
378  return;
379 
380  vector parentMat[4];
381 
382  parent.RemoveChild(this);
383  parent.GetTransform(parentMat);
384 
385  SetTransform(parentMat);
386  SCR_TreePartV2 parentTreePart = SCR_TreePartV2.Cast(parent);
387  BreakJoint(m_ParentJoint, this, parentTreePart);
388  m_bSynchronizeHasParent = false;
389 
390  if (RplSession.Mode() == RplMode.Client)
391  return;
392 
393  if (m_ParentTree && synchManager)
394  synchManager.SynchronizeRemoveFromParent(m_iTreePartIndex, m_ParentTree.GetID());
395  }
396 
397  //------------------------------------------------------------------------------------------------
398  //Returns parent joint of this tree part.
399  PhysicsJoint GetParentJoint()
400  {
401  return m_ParentJoint;
402  }
403 
404  //------------------------------------------------------------------------------------------------
405  //Removes the given joint and sets the pointer to null.
406  void RemoveJoint(PhysicsJoint physicsJoint)
407  {
408  if (!physicsJoint)
409  return;
410 
411  if (physicsJoint == m_ParentJoint)
412  {
413  m_ParentJoint = null;
414  return;
415  }
416 
417  int count = m_aChildrenJoints.Count();
418  for (int i = 0; i < count; i++)
419  {
420  if (physicsJoint == m_aChildrenJoints.Get(i))
421  {
422  m_aChildrenJoints.Remove(i);
423  return;
424  }
425  }
426  }
427 
428  //------------------------------------------------------------------------------------------------
429  //Removes all the joins from this tree part.
430  void DestroyAllJoints()
431  {
432  array<IEntity> children = new array<IEntity>();
433  GetAllChildren(this, children);
434  int count = children.Count();
435 
436  for (int i = 0; i < count; i++)
437  {
438  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(children[i]);
439  if (treePart)
440  {
441  PhysicsJoint physicsJoint = treePart.GetParentJoint();
442  if (physicsJoint)
443  BreakJoint(physicsJoint, this, treePart);
444  treePart.DestroyAllJoints();
445  }
446  }
447  }
448 
449  //------------------------------------------------------------------------------------------------
450  //Switches physics of this tree part to static.
451  private void SwitchPhysicsToStatic(bool switchChildrenPhysics)
452  {
453  if (m_Physics)
454  {
455  //If dynamic physics is present, destroy it.
456  if (m_Physics.IsDynamic())
457  {
458  StorePhysicsInfo(this);
459  DestroyPhysics();
460  }
461  else
462  {
463  m_bSwitchToStatic = false;
464  return; //Only if static physics is already present.
465  }
466  }
467 
468  IEntity parent = GetParent();
469  if (parent)
470  {
471  vector mat[4];
472  parent.GetTransform(mat);
473  SetTransform(mat);
474  }
475 
476  m_bSwitchToStatic = false;
477  m_bCreatedJoints = false;
478 
479  //Reset the sleep timer.
480  ResetThresholdTime();
481 
482  //Generate new physics component.
483  m_Physics = Physics.CreateStatic(this, -1);
484  if (m_RootsEntity && !m_RootsPhysics)
485  {
486  m_RootsPhysics = Physics.CreateStatic(m_RootsEntity, -1);
487  }
488 
489  //Switches all children physics to static.
490  if (switchChildrenPhysics)
491  {
492  DestroyAllJoints();
493  m_bCreatedJoints = false;
494  array<IEntity> children = new array<IEntity>();
495  SCR_Global.GetHierarchyEntityList(this, children);
496  if (children)
497  {
498  int count = children.Count();
499  for (int i = 0; i < count; i++)
500  {
501  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(children[i]);
502  if (treePart)
503  treePart.SwitchPhysicsToStatic(false);
504  }
505  }
506  }
507 
508  ClearEventMask(EntityEvent.SIMULATE | EntityEvent.POSTSIMULATE);
509 
510  if (RplSession.Mode() == RplMode.Client)
511  return;
512 
513  float q[4];
514  vector mat[4];
515 
516  GetTransform(mat);
517  Math3D.MatrixToQuat(mat, q);
518 
519  if (m_ParentTree && synchManager)
520  {
521  synchManager.SynchronizeStaticRotation(m_iTreePartIndex, q, m_ParentTree.GetID());
522  synchManager.SynchronizeStaticPosition(m_iTreePartIndex, mat[3], m_ParentTree.GetID());
523  }
524  }
525 
526  //------------------------------------------------------------------------------------------------
527  //Switches physics to dynamic.
528  private void SwitchPhysicsToDynamic()
529  {
530  if (m_Physics && m_Physics.IsDynamic())
531  return;
532 
533  //Go through all children of this tree part and switch them to dynamic physics.
534  array<IEntity> children = new array<IEntity>();
535  SCR_Global.GetHierarchyEntityList(this, children);
536  if (children)
537  {
538  int count = children.Count();
539  for (int i = 0; i < count; i++)
540  {
541  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(children[i]);
542  if (treePart)
543  treePart.SwitchPhysicsToDynamic();
544  }
545  }
546 
547  vector mat[4];
548  GetTransform(mat);
549 
550  m_bSwitchToDynamic = false;
551  ResetThresholdTime();
552 
553  if (m_RootsPhysics)
554  {
555  m_RootsPhysics.Destroy();
556  m_RootsPhysics = null;
557  }
558 
559  if (m_Physics)
560  {
561  //Static physics is present, destroy it.
562  if (!m_Physics.IsDynamic())
563  DestroyPhysics();
564  else
565  {
566  m_bSwitchToDynamic = false;
567  return; //Dynamic physics is present, don't create it again.
568  }
569  }
570 
571  m_Physics = Physics.CreateDynamic(this, m_fMass, -1);
572 
573  if (!m_Physics)
574  return;
575 
576  ApplyPhysicsInfo(this);
577  m_vCenterOfMass = m_Physics.GetCenterOfMass();
578 
579  /*if (RplSession.Mode() == RplMode.Client)
580  m_Physics.SetDamping(1, 1);
581  else
582  CreateJoints();*/
583  CreateJoints();
584 
585  SetEventMask(EntityEvent.SIMULATE | EntityEvent.POSTSIMULATE);
586  }
587 
588  //------------------------------------------------------------------------------------------------
589  //Destroys the current physics component.
590  private void DestroyPhysics()
591  {
592  if (m_Physics)
593  {
594  m_Physics.Destroy();
595  m_Physics = null;
596  }
597  }
598 
599  //------------------------------------------------------------------------------------------------
600  //Limits the linear acceleration of this tree part.
601  private void LimitAcceleration()
602  {
603  if (!m_Physics)
604  return;
605 
606  vector velocity = m_Physics.GetVelocity();
607  float currentSpeed = velocity.Length();
608 
609  //Stop if is not moving
610  if (currentSpeed != 0)
611  {
612  //Clamp current speed
613  //float targetSpeed = Math.Clamp(currentSpeed, m_fLastSpeedLinear - m_fMaxAccelerationLinear, m_fLastSpeedLinear + m_fMaxAccelerationLinear);
614  float targetSpeed = Math.Clamp(currentSpeed, 0, m_fLastSpeedLinear + m_fMaxAccelerationLinear);
615  float multiplier = targetSpeed / currentSpeed;
616 
617  //Apply the limitation
618  velocity *= multiplier;
619  m_Physics.SetVelocity(velocity);
620  m_fLastSpeedLinear = velocity.Length();
621  }
622 
623  vector velocityAngular = m_Physics.GetAngularVelocity();
624  vector mat[4];
625  GetTransform(mat);
626  vector localVelocityAngular = velocityAngular.InvMultiply3(mat);
627  for (int i = 0; i < 3; i++)
628  {
629  float targetVelocityAngular = Math.Clamp(localVelocityAngular[i], 0, m_vLastSpeedAngular[i] + m_vMaxAccelerationAngular[i]);
630  localVelocityAngular[i] = targetVelocityAngular;
631  }
632 
633  velocityAngular = localVelocityAngular.Multiply3(mat);
634  m_Physics.SetAngularVelocity(velocityAngular);
635  }
636 
637  //------------------------------------------------------------------------------------------------
638  //Limit rotation of this tree part around the Y axis.
639  private void LimitYRotation()
640  {
641  if (!m_Physics)
642  return;
643  if (!m_Physics.IsDynamic())
644  return;
645 
646  vector angularVelocity = m_Physics.GetAngularVelocity();
647  //Is the rotation velocity in the world higher than the max velocity along any of the axis?
648  if (Math.AbsFloat(angularVelocity[0]) > m_fMaxYRotationVelocity ||
649  Math.AbsFloat(angularVelocity[1]) > m_fMaxYRotationVelocity ||
650  Math.AbsFloat(angularVelocity[2]) > m_fMaxYRotationVelocity)
651  {
652  vector mat[4];
653  GetTransform(mat);
654  float scale = GetScale();
655  float matMultiplier = 1;
656  //if (scale != 0); //TODO Is this potentially causing a bug?
657  matMultiplier /= GetScale();
658 
659  mat[0] = mat[0] * matMultiplier;
660  mat[1] = mat[1] * matMultiplier;
661  mat[2] = mat[2] * matMultiplier;
662 
663  //Move the rotation velocity to local space.
664  vector localAngularVelocity = angularVelocity.InvMultiply3(mat);
665  float velocity = localAngularVelocity[1];
666 
667  //Clamp the roration velocity.
668  velocity = Math.Clamp(velocity, -m_fMaxYRotationVelocity, m_fMaxYRotationVelocity);
669  localAngularVelocity[1] = velocity;
670 
671  //Move the rotation velocity back to world space and apply it.
672  angularVelocity = localAngularVelocity.Multiply3(mat);
673  }
674 
675  //Block the rotation around global Y axis.
676  if (!m_bAllowGlobalYRotation)
677  angularVelocity[1] = 0;
678 
679  m_Physics.SetAngularVelocity(angularVelocity);
680  }
681 
682  //------------------------------------------------------------------------------------------------
683  //Breaks the given joint, set's all references to this joint to null and destroys it.
684  private void BreakJoint(PhysicsJoint joint, SCR_TreePartV2 ent1, SCR_TreePartV2 ent2)
685  {
686  if (!joint || !ent1 || !ent2)
687  return;
688 
689  ent1.RemoveJoint(joint);
690  ent2.RemoveJoint(joint);
691  joint.Destroy();
692  }
693 
694  //------------------------------------------------------------------------------------------------
695  //Creates all joints for this tree part and all its children.
696  private void CreateJoints()
697  {
698  if (!m_Physics || m_bCreatedJoints)
699  return;
700 
701  //Get the array of children, go through them and create their joints.
702  array<IEntity> children = new array<IEntity>();
703  GetAllChildren(this, children);
704  int count = children.Count();
705 
706  for (int i = 0; i < count; i++)
707  {
708  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(children[i]);
709  if (treePart && !treePart.m_ParentJoint)
710  {
711  PhysicsJoint physicsJoint = treePart.CreateLowerJoint(this);
712  if (physicsJoint)
713  m_aChildrenJoints.Insert(physicsJoint);
714  treePart.CreateJoints();
715  }
716  }
717  m_bCreatedJoints = true;
718  }
719 
720  //------------------------------------------------------------------------------------------------
721  //Get all children of the given parent IEntity and store them in array of IEntities allChildren.
722  //This only gets children that are directly below this parent in hierarchy, doesn't go any deeper.
723  private void GetAllChildren(IEntity parent, notnull inout array<IEntity> allChildren)
724  {
725  if (!parent)
726  return;
727 
728  IEntity child = parent.GetChildren();
729 
730  while (child)
731  {
732  allChildren.Insert(child);
733  child = child.GetSibling();
734  }
735  }
736 
737  //------------------------------------------------------------------------------------------------
738  //immediately stops the tree part.
739  private void StopSelf()
740  {
741  if (!m_Physics)
742  return;
743 
744  if (!m_Physics.IsDynamic())
745  return;
746 
747  if (!m_bAllowLinearMovement)
748  {
749  SetOrigin(m_vLockedOrigin);
750  m_Physics.SetVelocity(vector.Zero);
751  }
752  }
753 
754  //------------------------------------------------------------------------------------------------
755  //Checks whether the tree part is faster or slower than the threshold requires.
756  //When it is slower, incerement the threshold time.
757  private void CheckThreshold(float timeSlice)
758  {
759  if (GetParent())
760  return;
761 
762  m_fThresholdMaxTime += timeSlice;
763  if (m_fThresholdMaxTime > GO_TO_STATIC_TIME_THRESHOLD_MAX)
764  {
765  if (m_ParentTree && synchManager)
766  synchManager.SynchronizeSwitchTreePartToStatic(m_iTreePartIndex, m_ParentTree.GetID());
767  m_bSwitchToStatic = true;
768  }
769 
770  if (m_iContactsCount <= 0) //Reset the threshold time if there happend no contacts last simulation step.
771  {
772  m_bSoundHitGroundPlayed = false;
773  m_fThresholdTime = 0;
774  return;
775  }
776 
777  if (!m_Physics || m_bSynchronizeHasParent)
778  return;
779  if (!m_Physics.IsDynamic())
780  return;
781 
782  float velocity = m_Physics.GetVelocity().Length();
783  float angularVelocity = m_Physics.GetAngularVelocity().Length();
784  if (velocity < GO_TO_STATIC_THRESHOLD && angularVelocity < GO_TO_STATIC_ANGULAR_THRESHOLD)
785  {
786  m_fThresholdTime += timeSlice;
787  if (m_fThresholdTime > GO_TO_STATIC_TIME_THRESHOLD)
788  {
789  if (m_ParentTree && synchManager)
790  synchManager.SynchronizeSwitchTreePartToStatic(m_iTreePartIndex, m_ParentTree.GetID());
791  m_bSwitchToStatic = true;
792  }
793  }
794  }
795 
796  //------------------------------------------------------------------------------------------------
797  //Creates and returns the parent joint for the tree part.
798  PhysicsJoint CreateLowerJoint(SCR_TreePartV2 parent)
799  {
800  if (!parent || !m_Physics || m_ParentJoint)
801  return null;
802 
803  m_ParentJoint = PhysicsJoint.CreateFixed(parent, this, m_vCenterOfMass * GetScale(), parent.m_vCenterOfMass * parent.GetScale(), true, m_fMaxJointLoad);
804 
805  return m_ParentJoint;
806  }
807 
808  //------------------------------------------------------------------------------------------------
809  void WakeUpParent()
810  {
811  IEntity parent = GetParent();
812  if (!parent)
813  WakeUp(false);
814 
815  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(parent);
816 
817  if (treePart)
818  treePart.WakeUpParent();
819  }
820 
821  //------------------------------------------------------------------------------------------------
822  private bool AddEnt(IEntity ent)
823  {
824  m_aQuerriedEnts.Insert(ent);
825  return true;
826  }
827 
828  //------------------------------------------------------------------------------------------------
829  private bool FilterEnt(IEntity ent)
830  {
831  if (IsInHierarchy(ent) || ent == this)
832  return false;
833  return true;
834  }
835 
836  //------------------------------------------------------------------------------------------------
837  private bool IsInHierarchy(IEntity ent)
838  {
839  if (!ent)
840  return false;
841 
842  array<IEntity> children = new array<IEntity>();
843  SCR_Global.GetHierarchyEntityList(this, children);
844  int count = children.Count();
845  for (int i = 0; i < count; i++)
846  {
847  if (ent == children[i])
848  return true;
849  }
850 
851  return false;
852  }
853 
854  //------------------------------------------------------------------------------------------------
855  void WakeUpOthers()
856  {
857  m_aQuerriedEnts.Clear();
858  vector mat[4];
859 
860  GetTransform(mat);
861  GetWorld().QueryEntitiesByOBB(m_vBBOXMin, m_vBBOXMax, mat, AddEnt, FilterEnt);
862 
863  int count = m_aQuerriedEnts.Count();
864  for (int i = 0; i < count; i++)
865  {
866  if (m_aQuerriedEnts[i])
867  {
868  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(m_aQuerriedEnts[i]);//TODO make this for all destructible objects
869  if (treePart)
870  treePart.WakeUp(true);
871  }
872  else break;
873  }
874  }
875 
876  //------------------------------------------------------------------------------------------------
877  void WakeUp(bool wakeUpParent)
878  {
879  if (!m_Physics)
880  return;
881 
882  if (wakeUpParent)
883  {
884  WakeUpParent();
885  return;
886  }
887 
888  m_bWakeUp = true;
889  }
890 
891  //------------------------------------------------------------------------------------------------
892  void WakeUpHierarchy()
893  {
894  SwitchPhysicsToDynamic();
895 
896  //Generate all joints for this tree part and it's children.
897  CreateJoints();
898 
899  m_bWakeUp = false;
900  }
901 
902  //------------------------------------------------------------------------------------------------
903  int GetTreePartIndex()
904  {
905  return m_iTreePartIndex;
906  }
907 
908  //------------------------------------------------------------------------------------------------
909  void UpdateTransform(vector position, float quat[4], vector velocityLinear, vector velocityAngular)
910  {
911  m_bExtrapolate = true;
912 
913  m_vNetPosition = position;
914  m_vNetVelocityLinear = velocityLinear;
915  m_vNetVelocityAngular = velocityAngular;
916  Math3D.QuatCopy(quat, m_fNetRotation);
917 
918  if (m_Physics)
919  {
920  if (m_Physics.IsDynamic())
921  {
922  m_Physics.SetVelocity(velocityLinear);
923  m_Physics.SetAngularVelocity(velocityAngular * Math.DEG2RAD);
924  }
925  else
926  {
927  vector mat[4];
928  Math3D.QuatToMatrix(quat, mat);
929 
930 // mat *= GetScale();
931  mat[3] = position;
932 
933  SetTransform(mat);
934  }
935  }
936 
937  m_fTimeSinceLastTick = 0;
938  }
939 
940  //------------------------------------------------------------------------------------------------
941  void CacheRotation(float quat[4])
942  {
943 // Print("Caching rotation");
944 // Print(quat);
945  m_bApplyCachedRotation = true;
946  Math3D.QuatCopy(quat, m_fCachedRotation);
947  }
948 
949  //------------------------------------------------------------------------------------------------
950  void CachePosition(vector pos)
951  {
952 // Print("Caching " + pos);
953  m_bApplyCachedPosition = true;
954  m_vCachedPosition[0] = pos[0];
955  m_vCachedPosition[1] = pos[1];
956  m_vCachedPosition[2] = pos[2];
957  }
958 
959  //------------------------------------------------------------------------------------------------
960  void SetRotation(float quat[4])
961  {
962  vector mat[4];
963  Math3D.QuatToMatrix(quat, mat);
964 // mat *= GetScale();
965  mat[3] = GetOrigin();
966 
967  SetTransform(mat);
968  }
969 
970  //------------------------------------------------------------------------------------------------
971  void SetPosition(vector pos)
972  {
973 // Print(pos);
974  SetOrigin(pos);
975  }
976 
977  //------------------------------------------------------------------------------------------------
978  void SetVelocity(vector velocity)
979  {
980 // Print(velocity);
981  if (velocity.Length() < 0.02)
982  return;
983 // Print(velocity);
984  if (m_Physics)
985  m_Physics.SetVelocity(velocity);
986  }
987 
988  //------------------------------------------------------------------------------------------------
989  void SetAngularVelocity(vector angularVelocity)
990  {
991 // Print(angularVelocity);
992  if (angularVelocity.Length() < 0.02)
993  return;
994 // Print(angularVelocity);
995  if (m_Physics)
996  m_Physics.SetAngularVelocity(angularVelocity);
997  }
998 
999  //------------------------------------------------------------------------------------------------
1000  void SetPhysics(bool dynamic)
1001  {
1002  if (dynamic)
1003  m_bSwitchToDynamic = true;
1004  else
1005  m_bSwitchToStatic = true;
1006  }
1007 
1008  //------------------------------------------------------------------------------------------------
1009  void ClientSetToBreakFromParent()
1010  {
1011  m_bBreakFromParent = true;
1012 
1013  if (m_Physics && !m_Physics.IsDynamic())
1014  {
1015  IEntity parent = GetParent();
1016  if (parent)
1017  {
1018  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(parent);
1019 
1020  parent.RemoveChild(this);
1021  }
1022 
1023 // Print(m_vCachedPosition);
1024 // Print(m_bApplyCachedPosition);
1025  if (m_vCachedPosition && m_bApplyCachedPosition)
1026  {
1027 // Print(m_vCachedPosition);
1028  SetPosition(m_vCachedPosition);
1029  m_bApplyCachedPosition = false;
1030  }
1031 
1032 // Print(m_fCachedRotation);
1033 // Print(m_bApplyCachedRotation);
1034  if (m_fCachedRotation && m_bApplyCachedRotation)
1035  {
1036 // Print(m_fCachedRotation);
1037  SetRotation(m_fCachedRotation);
1038  m_bApplyCachedRotation = false;
1039  }
1040 
1041  m_bBreakFromParent = false;
1042  }
1043  }
1044 
1045  //------------------------------------------------------------------------------------------------
1046  float GetQuatDiff(float quat1[4], float quat2[4])
1047  {
1048  float diff0 = Math.AbsFloat(quat1[0] - quat2[0]);
1049  float diff1 = Math.AbsFloat(quat1[1] - quat2[1]);
1050  float diff2 = Math.AbsFloat(quat1[2] - quat2[2]);
1051  float diff3 = Math.AbsFloat(quat1[3] - quat2[3]);
1052  float diffSum = diff0 + diff1 + diff2 + diff3;
1053 
1054  return diffSum;
1055  }
1056 
1057  //------------------------------------------------------------------------------------------------
1058  void PrintDamageDebug(float impulse, EDamageType damageType)
1059  {
1060  float reducedDamage = -1;
1061  switch (damageType)
1062  {
1063  case EDamageType.MELEE:
1064  if (m_bDebugDamage)
1065  Print("Melee");
1066  if (m_fMeleeResistance != 0)
1067  reducedDamage = impulse / m_fMeleeResistance;
1068  break;
1069  case EDamageType.KINETIC:
1070  if (m_bDebugDamage)
1071  Print("Kinetic");
1072  if (m_fKineticResistance != 0)
1073  reducedDamage = impulse / m_fKineticResistance;
1074  break;
1075  case EDamageType.INCENDIARY:
1076  if (m_bDebugDamage)
1077  Print("Incendiary");
1078  break;
1079  case EDamageType.EXPLOSIVE:
1080  if (m_bDebugDamage)
1081  Print("Explosion");
1082  if (m_fExplosionResistance != 0)
1083  reducedDamage = impulse / m_fExplosionResistance;
1084  break;
1085  case EDamageType.FIRE:
1086  if (m_bDebugDamage)
1087  Print("Fire");
1088  Print("Damage after reduction: " + "NOT IMPLEMENTED YET");
1089  break;
1090  case EDamageType.COLLISION:
1091  if (m_bDebugDamage)
1092  Print("Collision");
1093  if (m_fCollisionResistance != 0)
1094  reducedDamage = impulse / m_fCollisionResistance;
1095  break;
1096  }
1097 
1098  if (m_bDebugDamage)
1099  Print("Damage before reduction: " + impulse);
1100  if (reducedDamage != -1)
1101  Print("Damage after reduction: " + reducedDamage);
1102  else Print("Damage after reduction: NOT IMPLEMENTED YET");
1103  }
1104 
1105  //------------------------------------------------------------------------------------------------
1106  void ResetThresholdTime()
1107  {
1108  m_fThresholdTime = 0;
1109  m_fThresholdMaxTime = 0;
1110  }
1111 
1112  //------------------------------------------------------------------------------------------------
1113  IEntity FindSuperParent()
1114  {
1115  IEntity current = this;
1116  while (current.GetParent())
1117  {
1118  current = current.GetParent();
1119  }
1120 
1121  return current;
1122  }
1123 
1124  //------------------------------------------------------------------------------------------------
1125  bool WouldBreak(float impulse, EDamageType damageType)
1126  {
1127  switch (damageType)
1128  {
1129  case EDamageType.MELEE:
1130  if (m_fMeleeResistance != 0)
1131  impulse /= m_fMeleeResistance;
1132  break;
1133  case EDamageType.KINETIC:
1134  if (m_fKineticResistance != 0)
1135  impulse /= m_fKineticResistance;
1136  break;
1137  case EDamageType.INCENDIARY:
1138  break;
1139  case EDamageType.EXPLOSIVE:
1140  if (m_fExplosionResistance != 0)
1141  impulse /= m_fExplosionResistance;
1142  break;
1143  case EDamageType.FIRE:
1144  break;
1145  case EDamageType.COLLISION:
1146  if (m_fCollisionResistance != 0)
1147  impulse /= m_fCollisionResistance;
1148  break;
1149  }
1150 
1151  if (impulse > (m_fMinImpact * GetScale()))
1152  return true;
1153  else
1154  return false;
1155  }
1156 
1157  //------------------------------------------------------------------------------------------------
1158  bool WouldBreak(float impulse, EDamageType damageType, out float reducedImpulse)
1159  {
1160  reducedImpulse = impulse;
1161 
1162  switch (damageType)
1163  {
1164  case EDamageType.MELEE:
1165  if (m_fMeleeResistance != 0)
1166  reducedImpulse /= m_fMeleeResistance;
1167  break;
1168  case EDamageType.KINETIC:
1169  if (m_fKineticResistance != 0)
1170  reducedImpulse /= m_fKineticResistance;
1171  break;
1172  case EDamageType.INCENDIARY:
1173  break;
1174  case EDamageType.EXPLOSIVE:
1175  if (m_fExplosionResistance != 0)
1176  reducedImpulse /= m_fExplosionResistance;
1177  break;
1178  case EDamageType.FIRE:
1179  break;
1180  case EDamageType.COLLISION:
1181  if (m_fCollisionResistance != 0)
1182  reducedImpulse /= m_fCollisionResistance;
1183  break;
1184  }
1185 
1186  if (reducedImpulse > (m_fMinImpact * GetScale()))
1187  return true;
1188  else
1189  return false;
1190  }
1191 
1192  //------------------------------------------------------------------------------------------------
1193  override void OnDamage(float damage,
1194  EDamageType type,
1195  IEntity pHitEntity,
1196  inout vector outMat[3],
1197  IEntity damageSource,
1198  notnull Instigator instigator,
1199  int colliderID,
1200  float speed)
1201  {
1202  if (RplSession.Mode() == RplMode.Client)
1203  return;
1204 
1205  //Calculate direction vector based on normal of the hit
1206  vector directionVector = -outMat[2];
1207  directionVector.Normalize();
1208 
1209  if (m_bDebugDamage)
1210  PrintDamageDebug(damage, type);
1211  //If the damage / impulse was big enough to move the tree part
1212  if (damage > m_fMinImpact)
1213  {
1214  //Calculate impulse vector from direction vector and damage
1215  vector positionVector = outMat[0];
1216 
1217  if (type == EDamageType.EXPLOSIVE)
1218  {
1219  positionVector = "0 0 0";
1220  directionVector = (GetOrigin() - outMat[0]);
1221  }
1222 
1223  directionVector.Normalize();
1224  vector impulseVector = directionVector * (damage / 10); //TODO REMOVE THIS CONSTANT VALUE
1225 
1226  if (WouldBreak(damage, type))
1227  SetToBreak(positionVector: positionVector, impulseVector: impulseVector, damageType: type);
1228  }
1229  }
1230 
1231  //------------------------------------------------------------------------------------------------
1232  //Handles methods that change stuff in physics world that cannot be done in simulate or contact methods.
1233  override void EOnFrame(IEntity owner, float timeSlice)
1234  {
1235  if (m_bBreak) //Break this tree part if necessary.
1236  Break();
1237  if (m_bWakeUp && !owner.GetParent())
1238  WakeUpHierarchy();
1239  if (m_bSwitchToDynamic) //Switch physics to dynamic if necessary.
1240  SwitchPhysicsToDynamic();
1241  if (m_bSwitchToStatic) //Switch physics to static if necessary.
1242  {
1243  SwitchPhysicsToStatic(true);
1244  }
1245  }
1246 
1247  //------------------------------------------------------------------------------------------------
1248  // TODO move extrapolation into generic / global methods
1249  // GenericEntity entity is the entity you want to be affected by extrapolation.
1250  // Physics physics is the physics that the extrapolation should calculate with.
1251  // vector netPosition is the last received position.
1252  // vector netVelocity is the last received velocity.
1253  // float netTeleportDistance is the max distance between position and netPosition, anything over this causes the entity to teleport.
1254  // float netRotation[4] is the last received rotation.
1255  // vector netVelocityAngular is the last received angular velocity.
1256  // float netTeleportAng is the max angle between current rotation and replicated rotation, anything over this causes the entity to teleport.
1257  // float timeSinceLastTick is the time since last synchronization of extrapolation relevant data was received, it should already be incremented by timeSlice by you!
1258  // float timeSlice is the time since last frame / simulation step.
1259  static void Extrapolate(GenericEntity entity, Physics physics, vector netPosition, vector netVelocityLinear, float netTeleportDistance, float netRotation[4], vector netVelocityAngular, float netTeleportAng, float timeSinceLastTick, float timeSlice, float tickTime)
1260  {
1261  float scale = entity.GetScale();
1262  vector currentMatrix[4];
1263  entity.GetWorldTransform(currentMatrix);
1264 
1265  // Lerp to positions/rotations received
1266  vector position = currentMatrix[3];
1267  float rotation[4];
1268  Math3D.MatrixToQuat(currentMatrix, rotation);
1269 
1270  // Static object, ensure exact rotation/position
1271  if (!physics || !physics.IsDynamic())
1272  {
1273  if (rotation != netRotation)
1274  Math3D.QuatToMatrix(netRotation, currentMatrix);
1275 
1276 // currentMatrix *= entity.GetScale();
1277 
1278  currentMatrix[3] = netPosition;
1279 
1280  entity.SetWorldTransform(currentMatrix);
1281  entity.SetScale(scale);
1282  return;
1283  }
1284 
1285  // Dynamic object, so calculate projected position/rotation based on last tick
1286  vector projectedPos = netPosition + netVelocityLinear * timeSinceLastTick;
1287 
1288  netVelocityAngular = netVelocityAngular * timeSinceLastTick;
1289  vector netVelocityAngularFlipped = GetFixedAxisVector(netVelocityAngular);
1290  float projectedRotation[4];
1291  float netVelocityAngularQuat[4];
1292  netVelocityAngularFlipped.QuatFromAngles(netVelocityAngularQuat);
1293  Math3D.QuatMultiply(projectedRotation, netRotation, netVelocityAngularQuat);
1294 
1295  // Calculate the position and rotation error
1296  float posError = vector.Distance(projectedPos, position);
1297  float rotError = Math3D.QuatAngle(projectedRotation, rotation);
1298 
1299  // If too far off position, teleport
1300  //if (posError > netTeleportDistance && posError > netVelocityLinear.Length() * tickTime * 2)
1301  if (posError > netTeleportDistance)
1302  {
1303  entity.SetOrigin(netPosition);
1304  posError = 0;
1305  }
1306 
1307  // If too far off rotation, teleport
1308  //if (rotError > netTeleportAng && rotError > netVelocityAngular.Length() * tickTime * 2)
1309  if (rotError > netTeleportAng)
1310  {
1311  Math3D.QuatToMatrix(netRotation, currentMatrix);
1312 // currentMatrix *= entity.GetScale();
1313  currentMatrix[3] = entity.GetOrigin();
1314  entity.SetWorldTransform(currentMatrix);
1315  rotError = 0.0;
1316  }
1317 
1318  float timeStep = Math.Clamp(timeSlice * 2, 0, 1);
1319  float timeStepTick = Math.Clamp(timeSlice / tickTime, 0, 1);
1320 
1321  // Adjust to account for errors in position/rotation
1322  if (posError > 0.01)
1323  {
1324  entity.SetOrigin(MoveTowards(position, projectedPos, posError * timeStep));
1325  physics.SetVelocity(physics.GetVelocity() + (projectedPos - position) * timeStepTick);
1326  }
1327 
1328  if (rotError > 0.01)
1329  {
1330  float outRot[4];
1331  Math3D.QuatRotateTowards(outRot, rotation, projectedRotation, (rotError * timeStep) * Math.RAD2DEG);
1332  Math3D.QuatToMatrix(outRot, currentMatrix);
1333  //currentMatrix *= entity.GetScale();
1334  currentMatrix[3] = entity.GetOrigin();
1335 // Print(currentMatrix);
1336  entity.SetWorldTransform(currentMatrix);
1337 
1338  float rotDiff[4];
1339  float rotInv[4];
1340  Math3D.QuatInverse(rotInv, rotation);
1341  Math3D.QuatMultiply(rotDiff, projectedRotation, rotInv);
1342  vector angularVelocity = Math3D.QuatToAngles(rotDiff);
1343  angularVelocity = FixEulerVector180(angularVelocity) * Math.DEG2RAD * timeStepTick;
1344  angularVelocity += physics.GetAngularVelocity() * Math.DEG2RAD;
1345  physics.SetAngularVelocity(angularVelocity);
1346  }
1347 
1348  entity.SetScale(scale);
1349  }
1350 
1351  //------------------------------------------------------------------------------------------------
1352  // Flips X and Y axis of the vector.
1353  static vector GetFixedAxisVector(vector toFlip)
1354  {
1355  vector flipped;
1356  flipped[0] = toFlip[1];
1357  flipped[1] = toFlip[0];
1358  flipped[2] = toFlip[2];
1359  return flipped;
1360  }
1361 
1362  //------------------------------------------------------------------------------------------------
1363  static void RotateTowards(out float result[4], float from[4], float to[4], float maxDegreesDelta)
1364  {
1365  float num = Math3D.QuatAngle(from, to);
1366  if (float.AlmostEqual(num, 0.0))
1367  {
1368  Math3D.QuatCopy(to, result);
1369  return;
1370  }
1371  float t = Math.Min(1, maxDegreesDelta / num);
1372  Math3D.QuatLerp(result, from, to, t);
1373  }
1374 
1375  //------------------------------------------------------------------------------------------------
1376  // Moves a point start in a straight line towards a target point.
1377  static vector MoveTowards(vector start, vector target, float maxDistanceDelta)
1378  {
1379  vector diff = target - start;
1380  float magnitude = diff.Length();
1381  if (magnitude <= maxDistanceDelta || float.AlmostEqual(magnitude, 0.0))
1382  return target;
1383  return start + diff / magnitude * maxDistanceDelta;
1384  }
1385 
1386  //----------------------------------------------------------------------------------------------------
1387  // Ensures the angles are in range <-180; 180>
1388  static vector FixEulerVector180(vector angles)
1389  {
1390  // Goes through each member of the vector and fixes it if necessary
1391  for (int a = 0; a < 3; a++)
1392  {
1393  while (angles[a] < -180)
1394  angles[a] = angles[a] + 360;
1395  while (angles[a] > 180)
1396  angles[a] = angles[a] - 360;
1397  }
1398 
1399  return angles;
1400  }
1401 
1402  //------------------------------------------------------------------------------------------------
1403  override void EOnPhysicsActive(IEntity owner, bool activeState)
1404  {
1405  if (!activeState)
1406  {
1407  SetEventMask(EntityEvent.FRAME);
1408  m_bSwitchToStatic = true;
1409  }
1410  }
1411 
1412  //------------------------------------------------------------------------------------------------
1413  override void EOnInit(IEntity owner)
1414  {
1415  if (GetParent())
1416  m_bSynchronizeHasParent = true;
1417 
1418  GetBounds(m_vBBOXMin, m_vBBOXMax);
1419 
1420  SwitchPhysicsToStatic(false);
1421  SetEventMask(EntityEvent.FRAME | EntityEvent.PHYSICSACTIVE);
1422  }
1423 
1424  //------------------------------------------------------------------------------------------------
1425  //Only called when dynamic.
1426  override void EOnSimulate(IEntity owner, float timeSlice)
1427  {
1428  if (!m_Physics)
1429  return;
1430 
1431  //TODO: Move this to method & try to optimize & make sure mat scale is fine
1432  if (m_bBreakFromParent)
1433  {
1434  IEntity parent = GetParent();
1435  if (parent)
1436  {
1437  SCR_TreePartV2 treePart = SCR_TreePartV2.Cast(parent);
1438  if (treePart && m_ParentJoint)
1439  BreakJoint(m_ParentJoint, this, treePart);
1440 
1441  parent.RemoveChild(this);
1442  }
1443 
1444  if (m_vCachedPosition && m_bApplyCachedPosition)
1445  {
1446  SetPosition(m_vCachedPosition);
1447  m_bApplyCachedPosition = false;
1448  }
1449 
1450  if (m_fCachedRotation && m_bApplyCachedRotation)
1451  {
1452  SetRotation(m_fCachedRotation);
1453  m_bApplyCachedRotation = false;
1454  }
1455 
1456  m_bBreakFromParent = false;
1457  }
1458 
1459  StopSelf();
1460  LimitYRotation();
1461 
1462  if (m_Physics.IsDynamic() && RplSession.Mode() == RplMode.Client)
1463  {
1464 /* m_Physics.SetVelocity(vector.Zero);
1465  m_Physics.SetAngularVelocity(vector.Zero);*/
1466  return;
1467  }
1468 
1469  CheckThreshold(timeSlice);
1470 
1471  m_iContactsCount = 0; //Reset contanct count for next simulation step.
1472  }
1473 
1474  //------------------------------------------------------------------------------------------------
1475  override void EOnPostSimulate(IEntity owner, float timeSlice)
1476  {
1477  if (RplSession.Mode() == RplMode.Client)
1478  {
1479  if (m_bExtrapolate)
1480  {
1481  m_fTimeSinceLastTick += timeSlice;
1482  Extrapolate(this, m_Physics, m_vNetPosition, m_vNetVelocityLinear, NET_TELEPORT_DISTANCE, m_fNetRotation, m_vNetVelocityAngular, NET_TELEPORT_ANGLE, m_fTimeSinceLastTick, timeSlice, m_fTargetSynchTime);
1483  }
1484  return;
1485  }
1486 
1487  /*if (!this.GetParent() && synchManager)
1488  {
1489  float q[4];
1490  vector mat[4];
1491 
1492  GetWorldTransform(mat);
1493  Math3D.MatrixToQuat(mat, q);
1494 
1495  if (m_ParentTree)
1496  {
1497  vector parentOrigin = m_ParentTree.GetOrigin();
1498 
1499  if (m_Physics)
1500  {
1501  synchManager.SynchronizeTreePartTransform(m_iTreePartIndex, q, GetOrigin(), m_Physics.GetVelocity(), m_Physics.GetAngularVelocity(), m_ParentTree.GetID());
1502  }
1503  }
1504  }*/
1505 
1506  m_fSynchTime += timeSlice;
1507  if (m_fSynchTime > m_fTargetSynchTime)
1508  {
1509  if (!this.GetParent() && synchManager)
1510  {
1511  float q[4];
1512  vector mat[4];
1513 
1514  GetWorldTransform(mat);
1515  Math3D.MatrixToQuat(mat, q);
1516 
1517  if (m_ParentTree)
1518  {
1519  vector parentOrigin = m_ParentTree.GetOrigin();
1520 
1521  if (m_Physics)
1522  {
1523  synchManager.SynchronizeTreePartTransform(m_iTreePartIndex, q, GetOrigin(), m_Physics.GetVelocity(), m_Physics.GetAngularVelocity(), m_ParentTree.GetID());
1524  }
1525  }
1526  }
1527  m_fSynchTime = 0;
1528  }
1529  }
1530 
1531  //------------------------------------------------------------------------------------------------
1532  override void EOnContact(IEntity owner, IEntity other, Contact contact)
1533  {
1534  if (RplSession.Mode() == RplMode.Client)
1535  return;
1536 
1537  // SOUND
1538  if (m_bSoundHitGroundPlayed) // ensure that SOUND_HIT_GROUND only plays once per tree part
1539  return;
1540 
1541  if (GenericTerrainEntity.Cast(other)) // check if contacted entity is ground
1542  {
1543  float vNormBefore = contact.GetRelativeNormalVelocityBefore();
1544  float vNormAfter = contact.GetRelativeNormalVelocityAfter();
1545  float dV = vNormAfter - vNormBefore;
1546  float impulse = contact.Impulse;
1547 
1548  if (dV > 2.5 && impulse > 100) // check if impact above a certain threshold
1549  {
1550  if (m_ParentTree)
1551  {
1552  m_ParentTree.OnTreePartHitGround();
1553  }
1554  m_bSoundHitGroundPlayed = true;
1555  }
1556  }
1557 
1558  if (Vehicle.Cast(other))
1559  {
1560  auto superParent = SCR_TreePartV2.Cast(FindSuperParent());
1561  if (superParent)
1562  superParent.ResetThresholdTime();
1563  }
1564 
1565  m_iContactsCount++;
1566  }
1567 
1568  //------------------------------------------------------------------------------------------------
1569  void SCR_TreePartV2(IEntitySource src, IEntity parent)
1570  {
1571  if (TARGET_RPC_COUNT > 0 && m_fTargetSynchTime == -1)
1572  {
1573  m_fTargetSynchTime = 1 / TARGET_RPC_COUNT;
1574  }
1575  SetEventMask(EntityEvent.INIT);
1576  }
1577 
1578  //------------------------------------------------------------------------------------------------
1579  void ~SCR_TreePartV2()
1580  {
1581  m_Physics = null;
1582  m_ParentJoint = null;
1583  m_HybridPhysicsInfo = null;
1584  m_aChildrenJoints = null;
1585  m_aQuerriedEnts = null;
1586  }
1587 #endif
1588 };
EntityEditorProps
enum EQueryType EntityEditorProps(category:"GameScripted/Sound", description:"THIS IS THE SCRIPT DESCRIPTION.", color:"0 0 255 255")
Definition: SCR_AmbientSoundsComponent.c:12
OnDamage
override protected void OnDamage(notnull BaseDamageContext damageContext)
Definition: SCR_ArmorDamageManagerComponent.c:11
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
EOnFrame
override void EOnFrame(IEntity owner, float timeSlice)
Definition: SCR_PlayerProfileManagerComponent.c:199
SCR_SoundEvent
Definition: SCR_SoundEvent.c:1
EOnContact
override void EOnContact(IEntity owner, IEntity other, Contact contact)
Definition: SCR_ParticleContactComponent.c:89
EOnSimulate
override void EOnSimulate(IEntity owner, float timeSlice)
Definition: InteractableBoxComponent.c:173
SCR_TreePartV2Class
Definition: SCR_TreePartV2.c:2
m_Physics
private Physics m_Physics
Definition: InteractableBoxComponent.c:13
GenericEntity
SCR_GenericBoxEntityClass GenericEntity
Instigator
Definition: Instigator.c:6
GetOrigin
vector GetOrigin()
Definition: SCR_AIUtilityComponent.c:279
impulse
SCR_DestructibleTreeV2Class impulse
SCR_TreePartV2
Definition: SCR_TreePartV2.c:8
Attribute
typedef Attribute
Post-process effect of scripted camera.
EOnPhysicsActive
void EOnPhysicsActive(IEntity owner, bool activeState)
Definition: SCR_VehicleDamageManagerComponent.c:1385
SCR_DestructibleTreeV2
Encapsulates the functionality of a destructible tree entity in the world.
Definition: SCR_DestructibleTreeV2.c:55
rotation
RespawnSystemComponentClass GameComponentClass vector vector rotation
Definition: RespawnSystemComponent.c:23
SCR_DestructibleTreesSynchManager
Definition: SCR_DestructibleTreesSynchManager.c:6
m_fMass
SCR_HybridPhysicsComponentClass m_fMass
Class for storing physics setup info for SCR_HybridPhysicsComponent.
EOnInit
override void EOnInit(IEntity owner)
Definition: SCR_AIConfigComponent.c:79
SetTransform
override void SetTransform(vector transform[4], bool changedByUser=false)
Definition: SCR_EditableCharacterComponent.c:455
SCR_Global
Definition: Functions.c:6
type
EDamageType type
Definition: SCR_DestructibleTreeV2.c:32
EDamageType
EDamageType
Definition: EDamageType.c:12
position
vector position
Definition: SCR_DestructibleTreeV2.c:30
category
params category
Definition: SCR_VehicleDamageManagerComponent.c:180