5 [
Attribute(
"0.05", UIWidgets.Slider,
"Contact momentum to damage multiplier",
"0.01 10 0.01",
category:
"Destruction Setup")]
6 float m_fMomentumToDamageScale;
8 [
Attribute(
"", UIWidgets.Object,
"List of objects (particles, debris, etc) to spawn on destruction of the object",
category:
"Destruction FX")]
9 ref array<ref SCR_BaseSpawnable> m_aDestroySpawnObjects;
11 [
Attribute(
"0", uiwidget: UIWidgets.ComboBox,
"Type of material for destruction sound",
"", ParamEnumArray.FromEnum(SCR_EMaterialSoundTypeBreak))]
12 SCR_EMaterialSoundTypeBreak m_eMaterialSoundType;
18 const int MAX_PHASES_BIT_SIZE = 4;
21 vector m_vHitPosition;
22 vector m_vHitDirection;
24 bool m_bTotalDestruction;
28 void SaveNormalizedVector(ScriptBitWriter w, vector norm)
36 void LoadNormalizedVector(ScriptBitReader r, out vector norm)
50 bool Save(ScriptBitWriter w)
53 w.WriteFloat(m_fHitDamage);
55 w.WriteInt(m_eDamageType);
58 w.WriteBool(m_bTotalDestruction);
59 w.WriteInt(m_iPreviousPhase);
65 bool Load(ScriptBitReader r)
68 r.ReadFloat(m_fHitDamage);
70 r.ReadInt(m_eDamageType);
72 r.ReadBool(m_bTotalDestruction);
73 r.ReadInt(m_iPreviousPhase);
83 protected static const int MIN_MOMENTUM_RESPONSE_INDEX = 1;
84 protected static const int MAX_MOMENTUM_RESPONSE_INDEX = 5;
85 protected static const int MIN_DESTRUCTION_RESPONSE_INDEX = 6;
86 protected static const string DAMAGE_PHASE_SIGNAL_NAME =
"DamagePhase";
87 static const int MAX_DESTRUCTION_RESPONSE_INDEX = 10;
88 static const string MAX_DESTRUCTION_RESPONSE_INDEX_NAME =
"HugeDestructible";
89 static const int TOTAL_DESTRUCTION_MAX_HEALTH_MULTIPLIER = 10;
96 return prefabData.GetDamageMultiplier(
type);
106 return prefabData.GetDamageReduction();
116 return prefabData.GetDamageThreshold();
128 return prefabData.GetMaxHealth();
134 if (Replication.IsClient())
142 array<ref Tuple2<vector, vector>> areas = {};
143 array<bool> redoAreas = {};
144 aiWorld.GetNavmeshRebuildAreas(
this, areas, redoAreas);
145 GetGame().GetCallqueue().CallLater(aiWorld.RequestNavmeshRebuildAreas, 1000,
false, areas, redoAreas);
149 override void OnDamage(
int previousState,
int newState,
EDamageType type,
float damageTaken,
float currentHealth, inout vector hitTransform[3], ScriptBitWriter frameData)
158 if (previousState == newState)
164 destructionData.m_vHitPosition = hitTransform[0];
165 destructionData.m_vHitDirection = hitTransform[1].Normalized();
166 destructionData.m_vHitNormal = hitTransform[2].Normalized();
167 destructionData.m_fHitDamage = damageTaken;
168 destructionData.m_eDamageType =
type;
169 destructionData.m_bTotalDestruction = damageTaken > maxHealth * TOTAL_DESTRUCTION_MAX_HEALTH_MULTIPLIER;
170 destructionData.m_iPreviousPhase = previousState;
171 destructionData.Save(frameData);
178 Resource resource = Resource.Load(model);
182 BaseResourceObject resourceObject = resource.GetResource();
184 asset = resourceObject.ToVObject();
191 SetObject(asset,
"");
194 Physics phys = GetPhysics();
199 if (!phys.UpdateGeometries())
207 Physics physics = GetPhysics();
209 array<ref SCR_BaseSpawnable> destroySpawnObjects = phase.m_aPhaseDestroySpawnObjects;
210 int numSpawnOnDestroy = destroySpawnObjects.Count();
211 for (
int i = 0; i < numSpawnOnDestroy; i++)
213 SCR_BaseSpawnable spawnObject = destroySpawnObjects[i];
214 SCR_DestructionHitInfo hitInfo = SCR_DestructionHitInfo.FromDestructionData(destructionData);
215 spawnObject.Spawn(
this, physics, hitInfo);
240 bool destroyAtNoHealth;
241 prefabData.GetPrefab().Get(
"DestroyAtNoHealth", destroyAtNoHealth);
246 if (damagePhaseIndex == prefabData.GetNumDestructionPhases() - 1 && destroyAtNoHealth)
268 GetBounds(mins, maxs);
269 return (maxs[0] - mins[0]) * (maxs[1] - mins[1]);
276 if (!soundManagerEntity)
280 if (!prefabData || prefabData.m_eMaterialSoundType == 0)
284 if (!destructionManager)
287 SCR_AudioSourceConfiguration audioSourceConfiguration = destructionManager.GetAudioSourceConfiguration();
288 if (!audioSourceConfiguration)
292 audioSourceConfiguration.m_sSoundEventName =
SCR_SoundEvent.SOUND_MPD_ +
typename.EnumToString(SCR_EMaterialSoundTypeBreak, prefabData.m_eMaterialSoundType);
294 SCR_AudioSource audioSource = soundManagerEntity.CreateAudioSource(
this, audioSourceConfiguration);
299 audioSource.SetSignalValue(
SCR_AudioSource.PHASES_TO_DESTROYED_PHASE_SIGNAL_NAME, prefabData.GetNumDestructionPhases() - damagePhaseIndex - 1);
309 GetWorldBounds(mins, maxs);
310 mat[3] = vector.Lerp(mins, maxs, 0.5);
313 soundManagerEntity.PlayAudioSource(audioSource, mat);
321 SignalsManagerComponent signalsManagerComponent = SignalsManagerComponent.Cast(FindComponent(SignalsManagerComponent));
322 if (!signalsManagerComponent)
326 signalsManagerComponent.SetSignalValue(signalsManagerComponent.AddOrFindSignal(DAMAGE_PHASE_SIGNAL_NAME), damagePhaseIndex);
330 override void OnStateChanged(
int destructibleState, ScriptBitReader frameData,
bool JIP)
334 destructionData.Load(frameData);
336 GoToDamagePhase(destructibleState, destructionData.m_iPreviousPhase, destructionData, JIP);
348 float DotMultiplier = vector.Dot(contact.VelocityAfter1.Normalized(), contact.VelocityBefore1.Normalized());
350 float MomentumAfter = ownerMass * contact.VelocityAfter1.Length() * DotMultiplier;
351 float momentumA = Math.AbsFloat(MomentumBefore - MomentumAfter);
353 DotMultiplier = vector.Dot(contact.VelocityAfter2.Normalized(), contact.VelocityBefore2.Normalized());
355 MomentumAfter = otherMass * contact.VelocityAfter2.Length() * DotMultiplier;
356 float momentumB = Math.AbsFloat(MomentumBefore - MomentumAfter);
357 return momentumA + momentumB;
364 Physics physics = GetPhysics();
368 int responseIndex = physics.GetResponseIndex();
369 if (responseIndex <= MIN_DESTRUCTION_RESPONSE_INDEX)
372 float healthPercentage = currentHealth /
GetMaxHealth();
373 int minMaxDiff = MAX_DESTRUCTION_RESPONSE_INDEX - MIN_DESTRUCTION_RESPONSE_INDEX + 1;
375 responseIndex = Math.ClampInt(MIN_DESTRUCTION_RESPONSE_INDEX - 1 + Math.Ceil(minMaxDiff * healthPercentage), MIN_DESTRUCTION_RESPONSE_INDEX, responseIndex);
376 physics.SetResponseIndex(responseIndex);
390 override void EOnContact(IEntity owner, IEntity other, Contact contact)
400 Physics physics = contact.Physics1;
402 int responseIndex = physics.GetResponseIndex();
403 float ownerMass = physics.GetMass();
405 int ownerReponseIndex = physics.GetResponseIndex();
406 int otherResponseIndex;
407 if (!physics.IsDynamic())
409 physics = contact.Physics2;
413 otherMass = physics.GetMass();
414 otherResponseIndex = physics.GetResponseIndex();
418 Physics otherPhysics = other.GetPhysics();
422 otherMass = otherPhysics.GetMass();
423 otherResponseIndex = otherPhysics.GetResponseIndex();
429 float relativeForce = 0;
430 if (physics.IsDynamic())
432 relativeForce = contact.Impulse / physics.GetMass();
435 if (responseIndex >= MIN_DESTRUCTION_RESPONSE_INDEX && responseIndex <= MAX_DESTRUCTION_RESPONSE_INDEX)
436 relativeForce *= 0.05;
443 vector relVel = contact.VelocityBefore2 - contact.VelocityBefore1;
444 outMat[0] = contact.Position;
445 outMat[1] = relVel.Normalized();
446 outMat[2] = contact.Normal;
449 if (otherResponseIndex - MIN_MOMENTUM_RESPONSE_INDEX >= ownerReponseIndex - MIN_DESTRUCTION_RESPONSE_INDEX)
451 HandleDamage(
EDamageType.TRUE, prefabData.GetMaxHealth(), outMat);
457 HandleDamage(
EDamageType.COLLISION, momentum * prefabData.m_fMomentumToDamageScale, outMat);
466 SetEventMask(EntityEvent.CONTACT);