1 #define ENABLE_BASE_DESTRUCTION
22 enum SCR_EMaterialSoundTypeBreak
42 [
ComponentEditorProps(
category:
"GameScripted/Destruction", description:
"Multi-Phase destruction component, for objects that go through several damage phases")]
45 [
Attribute(
"1", UIWidgets.CheckBox,
"If true, the object will be deleted after being damaged beyond the final damage phase",
category:
"Destruction Multi-Phase")]
46 bool m_bDeleteAfterFinalPhase;
48 [
Attribute(
"", UIWidgets.Object,
"List of the individual damage phases (sequential) above initial damage phase",
category:
"Destruction Multi-Phase")]
49 ref array<ref SCR_DamagePhaseData> m_aDamagePhases;
51 [
Attribute(
"0", uiwidget: UIWidgets.ComboBox,
"Type of material for destruction sound",
"", ParamEnumArray.FromEnum(SCR_EMaterialSoundTypeBreak))]
55 float m_fMeshChangeDelay;
59 class SCR_DestructionMultiPhaseComponent : SCR_DestructionDamageManagerComponent
61 #ifdef ENABLE_BASE_DESTRUCTION
62 private static int s_iFirstFreeDestructionMultiPhaseData = -1;
63 private static ref array<ref SCR_DestructionMultiPhaseData> s_aDestructionMultiPhaseData = {};
65 private int m_iDestructionMultiPhaseDataIndex = -1;
67 protected int m_iOriginalHealth;
69 static const string DAMAGE_PHASE_SIGNAL_NAME =
"DamagePhase";
74 if (m_iDestructionMultiPhaseDataIndex == -1)
75 m_iDestructionMultiPhaseDataIndex = AllocateDestructionMultiPhaseData();
77 return s_aDestructionMultiPhaseData[m_iDestructionMultiPhaseDataIndex];
81 private int AllocateDestructionMultiPhaseData()
83 if (s_iFirstFreeDestructionMultiPhaseData == -1)
87 int returnIndex = s_iFirstFreeDestructionMultiPhaseData;
89 s_iFirstFreeDestructionMultiPhaseData =
data.m_iNextFreeIndex;
90 data.m_iNextFreeIndex = -1;
96 private void FreeDestructionMultiPhaseData(
int index)
98 s_aDestructionMultiPhaseData[
index].Reset();
99 s_aDestructionMultiPhaseData[
index].m_iNextFreeIndex = s_iFirstFreeDestructionMultiPhaseData;
100 s_iFirstFreeDestructionMultiPhaseData =
index;
105 SCR_DamagePhaseData GetDamagePhaseData(
int damagePhase)
107 if (damagePhase <= 0 || damagePhase >= GetNumDamagePhases())
110 return SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner())).m_aDamagePhases.Get(damagePhase - 1);
122 bool GetIsInInitialDamagePhase()
124 return GetDamagePhase() == 0;
131 if (m_iDestructionMultiPhaseDataIndex == -1)
134 return GetDestructionMultiPhaseData().GetDamagePhase();
139 int GetNumDamagePhases()
141 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner()));
143 if (componentData.m_aDamagePhases)
144 return componentData.m_aDamagePhases.Count() + 1;
155 int CalculateDamagePhase(
int damagePhase,
float health,
float damage)
163 SCR_DamagePhaseData damagePhaseData = GetDamagePhaseData(damagePhase);
164 if (!damagePhaseData)
167 damage -= damagePhaseData.m_fPhaseHealth;
170 if (SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner())).m_bDeleteAfterFinalPhase)
171 return Math.ClampInt(damagePhase, 0, GetNumDamagePhases());
173 return Math.ClampInt(damagePhase, 0, GetNumDamagePhases() - 1);
183 return (maxs[0] - mins[0]) * (maxs[1] - mins[1]);
191 SignalsManagerComponent signalsManagerComponent = SignalsManagerComponent.Cast(
GetOwner().FindComponent(SignalsManagerComponent));
192 if (!signalsManagerComponent)
195 signalsManagerComponent.SetSignalValue(signalsManagerComponent.AddOrFindSignal(DAMAGE_PHASE_SIGNAL_NAME), GetDamagePhase());
199 protected void SetOriginalResourceName(ResourceName originalResourceName)
201 GetDestructionMultiPhaseData().SetOriginalResourceName(originalResourceName);
205 protected ResourceName GetOriginalResourceName()
207 if (m_iDestructionMultiPhaseDataIndex == -1)
210 return GetDestructionMultiPhaseData().GetOriginalResourceName();
214 protected int GetTargetDamagePhase()
216 if (m_iDestructionMultiPhaseDataIndex == -1)
219 return GetDestructionMultiPhaseData().GetTargetDamagePhase();
223 protected void SetTargetDamagePhase(
int targetDamagePhase)
225 GetDestructionMultiPhaseData().SetTargetDamagePhase(targetDamagePhase);
229 protected void SetDamagePhase(
int damagePhase)
231 GetDestructionMultiPhaseData().SetDamagePhase(damagePhase);
242 if (damagePhase >= GetNumDamagePhases())
247 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner()));
248 if (componentData && componentData.m_bDeleteAfterFinalPhase)
256 int currentDamagePhase = GetDamagePhase();
257 if (currentDamagePhase == damagePhase)
262 if (currentDamagePhase == 0)
264 VObject vObject =
GetOwner().GetVObject();
266 SetOriginalResourceName(vObject.GetResourceName());
269 SetDamagePhase(damagePhase);
270 SetTargetDamagePhase(damagePhase);
272 ApplyDamagePhaseData(GetDamagePhaseData(GetDamagePhase()), delayMeshChange);
279 void ApplyDamagePhaseData(SCR_DamagePhaseData damagePhaseData,
bool delayMeshChange)
282 float health, hitZoneHealth;
284 if (!damagePhaseData)
286 model = GetOriginalResourceName();
287 health = m_iOriginalHealth;
292 model = damagePhaseData.m_PhaseModel;
293 health = damagePhaseData.m_fPhaseHealth;
294 hitZoneHealth = damagePhaseData.m_fPhaseHealth;
300 SCR_DestructionMultiPhaseComponentClass prefabData = SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner()));
302 delay = prefabData.m_fMeshChangeDelay;
306 GetGame().GetCallqueue().CallLater(
SetModel, delay * 1000,
false, model);
310 SetModel(damagePhaseData.m_PhaseModel);
313 SetModel(damagePhaseData.m_PhaseModel);
315 if (!damagePhaseData)
330 if (!soundManagerEntity)
335 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(owner));
336 if (!componentData || componentData.m_eMaterialSoundType == 0)
340 if (!destructionManager)
343 SCR_AudioSourceConfiguration audioSourceConfiguration = destructionManager.GetAudioSourceConfiguration();
344 if (!audioSourceConfiguration)
348 audioSourceConfiguration.m_sSoundEventName =
SCR_SoundEvent.SOUND_MPD_ +
typename.EnumToString(SCR_EMaterialSoundTypeBreak, componentData.m_eMaterialSoundType);
350 SCR_AudioSource audioSource = soundManagerEntity.CreateAudioSource(owner, audioSourceConfiguration);
355 audioSource.SetSignalValue(
SCR_AudioSource.PHASES_TO_DESTROYED_PHASE_SIGNAL_NAME, GetNumDamagePhases() - GetDamagePhase() - 1);
360 owner.GetTransform(mat);
365 owner.GetWorldBounds(mins, maxs);
368 mat[3] = vector.Lerp(mins, maxs, 0.5);
371 soundManagerEntity.PlayAudioSource(audioSource, mat);
378 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner()));
382 if (componentData.m_bDeleteAfterFinalPhase)
383 return GetDamagePhase() < GetNumDamagePhases();
385 return GetDamagePhase() < GetNumDamagePhases() - 1;
393 Physics ownerPhysics =
GetOwner().GetPhysics();
398 if (GetIsInInitialDamagePhase())
401 int numSpawnOnDestroy = destroySpawnObjects.Count();
402 for (
int i = 0; i < numSpawnOnDestroy; i++)
404 SCR_BaseSpawnable spawnObject = destroySpawnObjects[i];
405 spawnObject.Spawn(
GetOwner(), ownerPhysics, spawnHitInfo);
410 SCR_DamagePhaseData damagePhaseData = GetDamagePhaseData(GetDamagePhase());
413 int numSpawnOnDestroy = damagePhaseData.m_PhaseDestroySpawnObjects.Count();
414 for (
int i = 0; i < numSpawnOnDestroy; i++)
416 SCR_BaseSpawnable spawnObject = damagePhaseData.m_PhaseDestroySpawnObjects[i];
417 spawnObject.Spawn(
GetOwner(), ownerPhysics, spawnHitInfo);
431 if (hitInfo.m_TotalDestruction)
435 SetTargetDamagePhase(CalculateDamagePhase(GetDamagePhase(), hitInfo.m_LastHealth, hitInfo.m_HitDamage));
456 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner()));
460 if (hitInfo.m_TotalDestruction)
462 if (componentData.m_bDeleteAfterFinalPhase)
479 SCR_DestructionMultiPhaseComponentClass prefabData = SCR_DestructionMultiPhaseComponentClass.Cast(GetComponentData(
GetOwner()));
480 if (!prefabData || !prefabData.m_bDeleteAfterFinalPhase)
483 Physics physics =
GetOwner().GetPhysics();
487 if (GetDamagePhase() + 1 >= GetNumDamagePhases())
489 super.UpdateResponseIndex();
497 [
RplRpc(RplChannel.Reliable, RplRcver.Broadcast)]
498 protected void RPC_ReplicateMultiPhaseDestructionState(
int phase)
506 int damagePhase = GetDamagePhase();
509 if (forceUpdate || damagePhase != 0)
510 Rpc(RPC_ReplicateMultiPhaseDestructionState, damagePhase);
514 protected override event bool OnRplSave(ScriptBitWriter writer)
516 if (!super.OnRplSave(writer))
519 bool writeDamagePhase = GetDamagePhase() != 0;
520 writer.Write(writeDamagePhase, 1);
522 if (writeDamagePhase)
523 writer.Write(GetDamagePhase(), 8);
529 protected override bool OnRplLoad(ScriptBitReader reader)
531 if (!super.OnRplLoad(reader))
534 bool readDamagePhase;
535 reader.Read(readDamagePhase, 1);
537 if (!readDamagePhase)
541 reader.Read(damagePhase, 8);
543 if (damagePhase != GetDamagePhase())
552 super.OnPostInit(owner);
559 class SCR_DamagePhaseTitle : BaseContainerCustomTitle
565 source.Get(
"m_fPhaseHealth", health);
566 title =
"Phase | HP: " + health.ToString();
573 class SCR_DamagePhaseData
575 [
Attribute(
"100", UIWidgets.Slider,
"Base health value for the damage phase. Upon switching to this phase, this value replaces the base health of the object",
"0.01 100000 0.01")]
576 float m_fPhaseHealth;
578 [
Attribute(ResourceName.Empty, UIWidgets.ResourcePickerThumbnail,
"Model to use for the damage phase",
"xob")]
579 ResourceName m_PhaseModel;
581 [
Attribute(
"", UIWidgets.Object,
"List of objects (particles, debris, etc) to spawn on destruction of the phase")]
582 ref array<ref SCR_BaseSpawnable> m_PhaseDestroySpawnObjects;