Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
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)]
5
6//------------------------------------------------------------------------------------------------
7//Encapsulates the functionality of a tree part entity in the world.
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
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,
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};
GetTransformResult JsonApiStruct GetTransform(notnull const BlenderRestAPI connection, string object_name)
vector scale
ref DSGameConfig game
Definition DSConfig.c:81
ArmaReforgerScripted GetGame()
Definition game.c:1398
ref array< string > angles
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.
EDamageType type
SCR_DestructibleTreeV2Class impulse
vector position
override bool SetTransform(vector transform[4], bool changedByUser=false)
Faction GetParent()
override void EOnContact(IEntity owner, IEntity other, Contact contact)
override void EOnFrame(IEntity owner, float timeSlice)
void EOnPhysicsActive(IEntity owner, bool activeState)
proto external float GetScale()
proto external void SetOrigin(vector orig)
proto external vector GetOrigin()
proto external IEntity GetChildren()
proto external void GetWorldTransform(out vector mat[])
See IEntity::GetTransform.
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 void RemoveChild(notnull IEntity child, bool keepTransform=false)
Remove Entity from hierarchy.
proto external IEntity GetSibling()
proto external void SetScale(float scale)
Definition Math.c:13
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
void EntitySpawnParams()
Definition gameLib.c:130
override void EOnInit(IEntity owner)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
SCR_FieldOfViewSettings Attribute
EntityEvent
Various entity events.
Definition EntityEvent.c:14
EntityFlags
Various entity flags.
Definition EntityFlags.c:14
RespawnSystemComponentClass GameComponentClass vector vector rotation
EDamageType
Definition EDamageType.c:13
TraceFlags
Definition TraceFlags.c:13