1 #define ENABLE_BASE_DESTRUCTION
4 [
Attribute(
"100", UIWidgets.Slider,
"Base health value of the object. Damage received above this value results in destruction (overrides HPMax in hit zone)",
"0.01 100000 0.01",
category:
"Destruction Setup")]
6 [
Attribute(
"50", UIWidgets.Slider,
"Relative contact force to damage multiplier",
"0.01 50000 0.01",
category:
"Destruction Setup")]
7 float m_fForceToDamageScale;
8 [
Attribute(
"0.05", UIWidgets.Slider,
"Contact momentum to damage multiplier",
"0.01 10 0.01",
category:
"Destruction Setup")]
9 float m_fMomentumToDamageScale;
10 [
Attribute(
"0.5", UIWidgets.Slider,
"Minimum relative contact force (impulse / mass) threshold below which contacts are ignored",
"0 50000 0.01",
category:
"Destruction Setup")]
11 float m_fRelativeContactForceThresholdMinimum;
12 [
Attribute(
"3000", UIWidgets.Slider,
"Maximum damage threshold above which the object is completely destroyed and no effects are played (eg: nuclear bomb damage)",
"0.01 50000 0.01",
category:
"Destruction Setup")]
13 float m_fDamageThresholdMaximum;
14 [
Attribute(
"0",
desc:
"Should children destructible objects also receive the damage dealt to this one?",
category:
"Destruction Setup")]
15 bool m_bPassDamageToChildren;
16 [
Attribute(
"0",
desc:
"Should the parent of this object also be destroyed when this object gets destroyed?",
category:
"Destruction Setup")]
17 bool m_bDestroyParentWhenDestroyed;
19 [
Attribute(
"", UIWidgets.Object,
"List of objects (particles, debris, etc) to spawn on destruction of the object",
category:
"Destruction FX")]
20 ref array<ref SCR_BaseSpawnable> m_DestroySpawnObjects;
24 class SCR_DestructionDamageManagerComponent : SCR_DamageManagerComponent
26 #ifdef ENABLE_BASE_DESTRUCTION
28 protected static bool s_bReadingInit =
false;
31 protected static bool s_bPrintMissingComponent;
32 protected static bool s_bPrintMissingPlayerController;
33 protected static bool s_bPrintInitializationFailed;
36 private static int s_iFirstFreeDestructionBaseData = -1;
37 private static ref array<ref SCR_DestructionBaseData> s_aDestructionBaseData = {};
43 static ScriptInvoker GetOnDestructibleDestroyedInvoker()
51 static bool GetReadingInit(
bool readingInit)
53 return s_bReadingInit;
58 static void SetReadingInit(
bool readingInit)
60 s_bReadingInit = readingInit;
75 if (s_iFirstFreeDestructionBaseData == -1)
81 int returnIndex = s_iFirstFreeDestructionBaseData;
83 s_iFirstFreeDestructionBaseData =
data.m_iNextFreeIndex;
84 data.m_iNextFreeIndex = -1;
92 s_aDestructionBaseData[
index].Reset();
93 s_aDestructionBaseData[
index].m_iNextFreeIndex = s_iFirstFreeDestructionBaseData;
94 s_iFirstFreeDestructionBaseData =
index;
120 return prefabData && prefabData.m_bDestroyParentWhenDestroyed;
152 [
RplRpc(RplChannel.Unreliable, RplRcver.Broadcast)]
157 IEntity child =
GetOwner().GetChildren();
160 SCR_DestructionDamageManagerComponent
destructible = SCR_DestructionDamageManagerComponent.Cast(child.FindComponent(SCR_DestructionDamageManagerComponent));
164 child = child.GetSibling();
187 IEntity parent =
GetOwner().GetParent();
190 SCR_DestructionDamageManagerComponent
destructible = SCR_DestructionDamageManagerComponent.Cast(parent.FindComponent(SCR_DestructionDamageManagerComponent));
201 RplComponent.DeleteRplEntity(
GetOwner(),
false);
210 HitZone hitZone = GetDefaultHitZone();
222 HitZone hitZone = GetDefaultHitZone();
226 hitZone.SetHealth(hitZone.GetMaxHealth() - damage);
242 hitInfo.m_TotalDestruction = totalDestruction;
243 hitInfo.m_LastHealth = lastHealth;
244 hitInfo.m_HitDamage = hitDamage;
245 hitInfo.m_DamageType = damageType;
246 hitInfo.m_HitPosition = hitPosition;
247 hitInfo.m_HitDirection = hitDirection;
248 hitInfo.m_HitNormal = hitNormal;
269 Physics ownerPhysics =
GetOwner().GetPhysics();
272 int numSpawnOnDestroy = destroySpawnObjects.Count();
273 for (
int i = 0; i < numSpawnOnDestroy; i++)
275 SCR_BaseSpawnable spawnObject = destroySpawnObjects[i];
279 spawnObject.Spawn(
GetOwner(), ownerPhysics,
null);
287 if (Replication.IsClient())
295 array<ref Tuple2<vector, vector>> areas = {};
296 array<bool> redoRoads = {};
297 aiWorld.GetNavmeshRebuildAreas(
GetOwner(), areas, redoRoads);
298 GetGame().GetCallqueue().CallLater(aiWorld.RequestNavmeshRebuildAreas, 1000,
false, areas, redoRoads);
355 Resource resource = Resource.Load(model);
356 VObject asset =
null;
357 if (resource.IsValid())
359 BaseResourceObject resourceObject = resource.GetResource();
361 asset = resourceObject.ToVObject();
367 Physics phys =
GetOwner().GetPhysics();
372 if (!phys.UpdateGeometries())
385 [
RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
386 private void RPC_QueueDestroy(
bool totalDestruction,
float lastHealth,
float hitDamage,
EDamageType damageType, vector hitPosition, vector hitDirection, vector hitNormal)
389 CreateDestructionHitInfo(totalDestruction, lastHealth, hitDamage, damageType, hitPosition, hitDirection, hitNormal);
398 IEntity parent =
GetOwner().GetParent();
399 RplComponent currentRplComponent;
402 currentRplComponent = RplComponent.Cast(parent.FindComponent(RplComponent));
403 parent = parent.GetParent();
406 return currentRplComponent;
412 IEntity child =
GetOwner().GetChildren();
415 SCR_DestructionDamageManagerComponent destructionComponent = SCR_DestructionDamageManagerComponent.Cast(child.FindComponent(SCR_DestructionDamageManagerComponent));
416 if (!destructionComponent)
418 child = child.GetSibling();
422 IEntity currentChild = child;
423 child = child.GetSibling();
426 childContext.hitEntity = currentChild;
428 destructionComponent.HandleDamage(childContext);
436 RplComponent rplComponent = RplComponent.Cast(
GetOwner().FindComponent(RplComponent));
437 return (rplComponent && rplComponent.IsProxy());
444 Physics physics =
GetOwner().GetPhysics();
445 int responseIndex = physics.GetResponseIndex();
446 if (responseIndex <= MIN_DESTRUCTION_RESPONSE_INDEX)
450 int minMaxDiff = MAX_DESTRUCTION_RESPONSE_INDEX - MIN_DESTRUCTION_RESPONSE_INDEX + 1;
452 responseIndex = Math.ClampInt(MIN_DESTRUCTION_RESPONSE_INDEX - 1 + Math.Ceil(minMaxDiff * healthPercentage), MIN_DESTRUCTION_RESPONSE_INDEX, responseIndex);
453 physics.SetResponseIndex(responseIndex);
457 override bool OnContact(IEntity owner, IEntity other, Contact contact)
475 Physics physics = contact.Physics1;
476 int responseIndex = physics.GetResponseIndex();
477 float ownerMass = physics.GetMass();
479 int ownerReponseIndex = physics.GetResponseIndex();
480 int otherResponseIndex;
481 if (!physics.IsDynamic())
483 physics = contact.Physics2;
487 otherMass = physics.GetMass();
488 otherResponseIndex = physics.GetResponseIndex();
492 Physics otherPhysics = other.GetPhysics();
496 otherMass = otherPhysics.GetMass();
497 otherResponseIndex = otherPhysics.GetResponseIndex();
503 float relativeForce = 0;
504 if (physics.IsDynamic())
506 relativeForce = contact.Impulse / physics.GetMass();
509 if (responseIndex >= MIN_DESTRUCTION_RESPONSE_INDEX && responseIndex <= MAX_DESTRUCTION_RESPONSE_INDEX)
510 relativeForce *= 0.05;
513 if (relativeForce < componentData.m_fRelativeContactForceThresholdMinimum)
517 vector relVel = contact.VelocityBefore2 - contact.VelocityBefore1;
518 outMat[0] = contact.Position;
519 outMat[1] = relVel.Normalized();
520 outMat[2] = contact.Normal;
523 if (otherResponseIndex - MIN_MOMENTUM_RESPONSE_INDEX >= ownerReponseIndex - MIN_DESTRUCTION_RESPONSE_INDEX)
527 HandleDamage(damageContext);
533 float damage = momentum * componentData.m_fMomentumToDamageScale;
538 HandleDamage(damageContext);
547 super.OnDamage(damageContext);
553 if (componentData.m_bPassDamageToChildren)
573 Physics physics =
GetOwner().GetPhysics();
576 if (physics.IsDynamic() && !physics.IsActive())
577 physics.ApplyImpulse(vector.Up * physics.GetMass() * 0.001);
581 physics.SetInteractionLayer(EPhysicsLayerDefs.VehicleCast);
584 float previousHealth;
588 CreateDestructionHitInfo(damageContext.damageValue >= componentData.m_fDamageThresholdMaximum, previousHealth, damageContext.damageValue, damageContext.damageType, damageContext.hitPosition, damageContext.hitDirection, damageContext.hitNormal);
593 if (!destructionHitInfo)
598 Rpc(
RPC_QueueDestroy, destructionHitInfo.m_TotalDestruction, destructionHitInfo.m_LastHealth, destructionHitInfo.m_HitDamage, destructionHitInfo.m_DamageType, destructionHitInfo.m_HitPosition, destructionHitInfo.m_HitDirection, destructionHitInfo.m_HitNormal);
620 return componentData.m_fDamageThresholdMaximum;
626 super.OnPostInit(owner);
627 SetEventMask(owner, EntityEvent.CONTACT);
629 if (!GetDefaultHitZone())
635 Print(
"Component data is null!", LogLevel.ERROR);
647 s_bPrintMissingComponent =
false;
648 s_bPrintMissingPlayerController =
false;
649 s_bPrintInitializationFailed =
false;
664 vector m_HitPosition;
665 vector m_HitDirection;
679 static SCR_DestructionHitInfo FromHitInfo(
SCR_HitInfo hitInfo,
bool totalDestruction)
681 SCR_DestructionHitInfo destHitInfo =
new SCR_DestructionHitInfo;
682 destHitInfo.m_TotalDestruction = totalDestruction;
683 destHitInfo.m_LastHealth = hitInfo.m_LastHealth;
684 destHitInfo.m_HitDamage = hitInfo.m_HitDamage;
685 destHitInfo.m_DamageType = hitInfo.m_DamageType;
686 destHitInfo.m_HitPosition = hitInfo.m_HitPosition;
687 destHitInfo.m_HitDirection = hitInfo.m_HitDirection;
688 destHitInfo.m_HitNormal = hitInfo.m_HitNormal;
698 SCR_DestructionHitInfo destructionHitInfo =
new SCR_DestructionHitInfo();
699 destructionHitInfo.m_TotalDestruction = destructionData.m_bTotalDestruction;
700 destructionHitInfo.m_HitDamage = destructionData.m_fHitDamage;
701 destructionHitInfo.m_DamageType = destructionData.m_eDamageType;
702 destructionHitInfo.m_HitPosition = destructionData.m_vHitPosition;
703 destructionHitInfo.m_HitDirection = destructionData.m_vHitDirection;
704 destructionHitInfo.m_HitNormal = destructionData.m_vHitNormal;
705 return destructionHitInfo;
711 override bool _WB_GetCustomTitle(BaseContainer source, out
string title)
713 title =
"Small Debris";
718 class SCR_Spawnable_PrefabTitle : BaseContainerCustomTitle
729 override bool _WB_GetCustomTitle(BaseContainer source, out
string title)
731 title =
"Particle Effect";
737 class SCR_BaseSpawnable
739 [
Attribute(
"0 0 0", UIWidgets.Coords,
desc:
"Positional offset (in local space to the destructible)")]
740 protected vector m_vOffsetPosition;
741 [
Attribute(
"0 0 0", UIWidgets.Coords,
desc:
"Yaw, pitch & roll offset (in local space to the destructible)")]
742 protected vector m_vOffsetRotation;
750 void SetVariables(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
752 if (source.GetResourceName().Contains(
"BrickWall_01/BrickWall_01_white_2m.et"))
756 api.SetVariableValue(source, path,
"m_vOffsetPosition",
string.Format(
"%1 %2 %3", m_vOffsetPosition[0], m_vOffsetPosition[1], m_vOffsetPosition[2]));
757 api.SetVariableValue(source, path,
"m_vOffsetRotation",
string.Format(
"%1 %2 %3", m_vOffsetRotation[0], m_vOffsetRotation[1], m_vOffsetRotation[2]));
763 bool CompareAttributes(SCR_BaseSpawnable other)
765 if (other.m_vOffsetPosition != m_vOffsetPosition)
768 if (other.m_vOffsetRotation != m_vOffsetRotation)
780 bool AlreadyExists(WorldEditorAPI api, IEntitySource source,
int index)
782 array<ref BaseDestructionPhase> phases = {};
783 source.Get(
"DamagePhases", phases);
785 if (phases && phases.IsIndexValid(
index))
788 for (
int i = phase.m_aPhaseDestroySpawnObjects.Count() - 1; i >= 0; i--)
790 if (phase.m_aPhaseDestroySpawnObjects[i].Type() == Type())
792 if (CompareAttributes(phase.m_aPhaseDestroySpawnObjects[i]))
808 bool CreateObject(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
810 if (!AlreadyExists(api, source,
index))
812 api.CreateObjectArrayVariableMember(source, path,
"m_aPhaseDestroySpawnObjects",
"SCR_BaseSpawnable",
index);
826 void CopyToSource(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index,
string currentObjectName)
828 if (!CreateObject(api, source, path,
index))
832 int last = path.Insert(
new ContainerIdPathEntry(currentObjectName,
index));
834 SetVariables(api, source, path,
index);
845 void GetSpawnTransform(IEntity owner, out vector outMat[4],
bool localCoords =
false)
849 Math3D.AnglesToMatrix(m_vOffsetRotation, outMat);
851 if (m_vOffsetPosition == vector.Zero)
852 outMat[3] = vector.Up * 0.001;
854 outMat[3] = m_vOffsetPosition;
858 vector localMat[4], parentMat[4];
859 owner.GetWorldTransform(parentMat);
860 Math3D.AnglesToMatrix(m_vOffsetRotation, localMat);
861 localMat[3] = m_vOffsetPosition;
863 Math3D.MatrixMultiply4(parentMat, localMat, outMat);
874 IEntity
Spawn(IEntity owner, Physics parentPhysics,
SCR_HitInfo hitInfo,
bool snapToTerrain =
false)
884 [
Attribute(ResourceName.Empty, UIWidgets.ResourcePickerThumbnail,
"Debris model prefabs to spawn (spawns ALL of them)",
"et xob")]
885 ref array<ResourceName> m_ModelPrefabs;
887 [
Attribute(
"10", UIWidgets.Slider,
"Mass of the debris",
"0.01 1000 0.01")]
890 [
Attribute(
"5", UIWidgets.Slider,
"Minimum lifetime value for the debris (in s)",
"0 3600 0.5")]
891 float m_fLifetimeMin;
893 [
Attribute(
"10", UIWidgets.Slider,
"Maximum lifetime value for the debris (in s)",
"0 3600 0.5")]
894 float m_fLifetimeMax;
896 [
Attribute(
"200", UIWidgets.Slider,
"Maximum distance from camera above which the debris is not spawned (in m)",
"0 3600 0.5")]
897 float m_fDistanceMax;
899 [
Attribute(
"0", UIWidgets.Slider,
"Higher priority overrides lower priority if at or over debris limit",
"0 100 1")]
902 [
Attribute(
"0.1", UIWidgets.Slider,
"Damage received to physics impulse (speed / mass) multiplier",
"0 10000 0.01")]
903 float m_fDamageToImpulse;
905 [
Attribute(
"2", UIWidgets.Slider,
"Damage to speed multiplier, used when objects get too much damage to impulse",
"0 10000 0.01")]
906 float m_fMaxDamageToSpeedMultiplier;
908 [
Attribute(
"0.5", UIWidgets.Slider,
"Random linear velocity multiplier (m/s)",
"0 200 0.1")]
909 float m_fRandomVelocityLinear;
911 [
Attribute(
"180", UIWidgets.Slider,
"Random angular velocity multiplier (deg/s)",
"0 3600 0.1")]
912 float m_fRandomVelocityAngular;
919 override bool CompareAttributes(SCR_BaseSpawnable other)
923 if (!super.CompareAttributes(other))
960 override void SetVariables(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
962 super.SetVariables(api, source, path,
index);
964 string prefabsArray =
"";
974 api.SetVariableValue(source, path,
"m_ModelPrefabs", prefabsArray);
976 api.SetVariableValue(source, path,
"m_fMass",
m_fMass.ToString());
977 api.SetVariableValue(source, path,
"m_fLifetimeMin",
m_fLifetimeMin.ToString());
978 api.SetVariableValue(source, path,
"m_fLifetimeMax",
m_fLifetimeMax.ToString());
979 api.SetVariableValue(source, path,
"m_fDistanceMax",
m_fDistanceMax.ToString());
980 api.SetVariableValue(source, path,
"m_fPriority",
m_fPriority.ToString());
981 api.SetVariableValue(source, path,
"m_fDamageToImpulse",
m_fDamageToImpulse.ToString());
989 override bool CreateObject(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
991 if (!AlreadyExists(api, source,
index))
993 api.CreateObjectArrayVariableMember(source, path,
"m_aPhaseDestroySpawnObjects",
"SCR_DebrisSpawnable",
index);
1002 override IEntity Spawn(IEntity owner, Physics parentPhysics,
SCR_HitInfo hitInfo,
bool snapToTerrain =
false)
1007 int numModelPrefabs = 0;
1011 for (
int i = 0; i < numModelPrefabs; i++)
1015 ResourceName modelPath;
1017 SCR_Global.GetModelAndRemapFromResource(prefabPath, modelPath, remap);
1019 if (modelPath == ResourceName.Empty)
1023 GetSpawnTransform(owner, spawnMat);
1025 SCR_DestructionDamageManagerComponent destructionComponent = SCR_DestructionDamageManagerComponent.Cast(owner.FindComponent(SCR_DestructionDamageManagerComponent));
1029 vector linearVelocity = hitInfo.m_HitDirection * Math.RandomFloat(0, 1);
1030 linearVelocity += Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) *
m_fRandomVelocityLinear;
1031 linearVelocity *= dmgSpeed;
1032 vector angularVelocity = Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) * Math.RandomFloat(0.25, 4) *
m_fRandomVelocityAngular;
1033 angularVelocity *= dmgSpeed;
1037 linearVelocity += parentPhysics.GetVelocity();
1038 angularVelocity += parentPhysics.GetAngularVelocity();
1040 #ifdef ENABLE_BASE_DESTRUCTION
1041 SCR_DebrisSmallEntity.SpawnDebris(owner.GetWorld(), spawnMat, modelPath,
m_fMass, Math.RandomFloat(
m_fLifetimeMin,
m_fLifetimeMax),
m_fDistanceMax,
m_fPriority, linearVelocity, angularVelocity, remap,
false,
m_eMaterialSoundType);
1050 class SCR_PrefabSpawnable : SCR_BaseSpawnable
1052 [
Attribute(ResourceName.Empty, UIWidgets.ResourcePickerThumbnail,
"Prefabs to spawn (spawns ALL of them)",
"et")]
1053 ref array<ResourceName> m_Prefabs;
1055 [
Attribute(
"0.1", UIWidgets.Slider,
"Damage received to physics impulse (speed / mass) multiplier",
"0 10000 0.01")]
1058 [
Attribute(
"0.25", UIWidgets.Slider,
"Random linear velocity multiplier (m/s)",
"0 200 0.1")]
1061 [
Attribute(
"45", UIWidgets.Slider,
"Random angular velocity multiplier (deg/s)",
"0 3600 0.1")]
1064 [
Attribute(
"0", UIWidgets.CheckBox,
"Whether the spawned prefabs should be set as children (sets auto-transform)")]
1065 bool m_bSpawnAsChildren;
1069 override bool CompareAttributes(SCR_BaseSpawnable other)
1071 SCR_PrefabSpawnable otherPrefab = SCR_PrefabSpawnable.Cast(other);
1073 if (!super.CompareAttributes(other))
1076 int count = m_Prefabs.Count();
1077 if (otherPrefab.m_Prefabs.Count() != count)
1080 for (
int i = count - 1; i >= 0; i--)
1082 if (otherPrefab.m_Prefabs[i] != m_Prefabs[i])
1095 if (otherPrefab.m_bSpawnAsChildren != m_bSpawnAsChildren)
1102 override void SetVariables(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
1104 super.SetVariables(api, source, path,
index);
1106 string prefabsArray =
"";
1108 for (
int i = 0, count = m_Prefabs.Count(); i < count; i++)
1110 prefabsArray += m_Prefabs[i];
1113 prefabsArray +=
",";
1116 api.SetVariableValue(source, path,
"m_Prefabs", prefabsArray);
1118 api.SetVariableValue(source, path,
"m_fDamageToImpulse",
m_fDamageToImpulse.ToString());
1121 api.SetVariableValue(source, path,
"m_bSpawnAsChildren", m_bSpawnAsChildren.ToString(
true));
1125 override bool CreateObject(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
1127 if (!AlreadyExists(api, source,
index))
1129 api.CreateObjectArrayVariableMember(source, path,
"m_aPhaseDestroySpawnObjects",
"SCR_PrefabSpawnable",
index);
1138 override IEntity
Spawn(IEntity owner, Physics parentPhysics,
SCR_HitInfo hitInfo,
bool snapToTerrain =
false)
1143 int numModelPrefabs = 0;
1145 numModelPrefabs = m_Prefabs.Count();
1147 for (
int i = 0; i < numModelPrefabs; i++)
1149 ResourceName prefabPath = m_Prefabs[i];
1152 if (
SCR_Global.GetResourceContainsComponent(prefabPath,
"RplComponent", isPrefab) && RplSession.Mode() == RplMode.Client)
1159 GetSpawnTransform(owner, spawnMat, m_bSpawnAsChildren);
1161 EntitySpawnParams prefabSpawnParams = EntitySpawnParams();
1162 prefabSpawnParams.Transform = spawnMat;
1163 IEntity spawnedPrefab =
GetGame().SpawnEntityPrefab(Resource.Load(prefabPath),
null, prefabSpawnParams);
1167 if (m_bSpawnAsChildren)
1169 owner.AddChild(spawnedPrefab, -1, EAddChildFlags.AUTO_TRANSFORM);
1173 Physics prefabPhysics = spawnedPrefab.GetPhysics();
1174 if (!prefabPhysics || !prefabPhysics.IsDynamic())
1179 vector linearVelocity = hitInfo.m_HitDirection * Math.RandomFloat(0, 1);
1180 linearVelocity += Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) *
m_fRandomVelocityLinear;
1181 linearVelocity *= dmgSpeed;
1182 vector angularVelocity = Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) * Math.RandomFloat(0.25, 4) *
m_fRandomVelocityAngular;
1183 angularVelocity *= dmgSpeed;
1187 linearVelocity += parentPhysics.GetVelocity();
1188 angularVelocity += parentPhysics.GetAngularVelocity();
1191 prefabPhysics.SetVelocity(linearVelocity);
1192 prefabPhysics.SetAngularVelocity(angularVelocity * Math.DEG2RAD);
1200 class SCR_ParticleSpawnable : SCR_BaseSpawnable
1202 [
Attribute(ResourceName.Empty, UIWidgets.ResourceNamePicker,
"Particle effect to spawn",
"ptc")]
1204 [
Attribute(
"1", UIWidgets.CheckBox,
"If true, the particle effect will play at the object's bounding box instead of at its origin")]
1207 [
Attribute(
"1",
desc:
"If true, the particle effect will play rotated in the hit direction.")]
1208 bool m_bDirectional;
1212 override bool CompareAttributes(SCR_BaseSpawnable other)
1214 SCR_ParticleSpawnable otherParticle = SCR_ParticleSpawnable.Cast(other);
1216 if (!super.CompareAttributes(other))
1222 if (otherParticle.m_bAtCenter != m_bAtCenter)
1225 if (otherParticle.m_bDirectional != m_bDirectional)
1231 override void SetVariables(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
1233 super.SetVariables(api, source, path,
index);
1236 api.SetVariableValue(source, path,
"m_Particle",
m_Particle);
1237 api.SetVariableValue(source, path,
"m_bAtCenter", m_bAtCenter.ToString(
true));
1238 api.SetVariableValue(source, path,
"m_bDirectional", m_bDirectional.ToString(
true));
1242 override bool CreateObject(WorldEditorAPI api, IEntitySource source, array<ref ContainerIdPathEntry> path,
int index)
1244 if (!AlreadyExists(api, source,
index))
1246 api.CreateObjectArrayVariableMember(source, path,
"m_aPhaseDestroySpawnObjects",
"SCR_ParticleSpawnable",
index);
1260 ParticleEffectEntity SpawnAsChild(IEntity owner,
SCR_HitInfo hitInfo,
bool snapToTerrain =
false)
1266 GetSpawnTransform(owner, spawnMat);
1271 owner.GetBounds(mins, maxs);
1272 vector center = (maxs - mins) * 0.5 + mins;
1273 spawnMat[3] = center.Multiply4(spawnMat);
1285 vector newRight, newUp, newForward;
1287 newUp = -hitInfo.m_HitDirection;
1288 newRight = newUp * spawnMat[2];
1289 newForward = newUp * newRight;
1291 spawnMat[0] = newRight;
1292 spawnMat[1] = newUp;
1293 spawnMat[2] = newForward;
1300 override IEntity
Spawn(IEntity owner, Physics parentPhysics,
SCR_HitInfo hitInfo,
bool snapToTerrain =
false)
1302 if (!hitInfo ||
m_Particle == ResourceName.Empty)
1306 GetSpawnTransform(owner, spawnMat);
1311 owner.GetBounds(mins, maxs);
1312 vector center = (maxs - mins) * 0.5 + mins;
1313 spawnMat[3] = center.Multiply4(spawnMat);
1325 vector newRight, newUp, newForward;
1327 newUp = -hitInfo.m_HitDirection;
1328 newRight = newUp * spawnMat[2];
1329 newForward = newUp * newRight;
1331 spawnMat[0] = newRight;
1332 spawnMat[1] = newUp;
1333 spawnMat[2] = newForward;