1[
ComponentEditorProps(
category:
"GameScripted/Destruction", description:
"Multi-Phase destruction component, for objects that go through several damage phases")]
4 [
Attribute(
"1",
UIWidgets.CheckBox,
"If true, the object will be deleted after being damaged beyond the final damage phase",
category:
"Destruction Multi-Phase")]
5 bool m_bDeleteAfterFinalPhase;
6 [
Attribute(
"",
UIWidgets.Object,
"List of the individual damage phases (sequential) above initial damage phase",
category:
"Destruction Multi-Phase")]
7 ref array<ref SCR_DamagePhaseData> m_aDamagePhases;
9 [
Attribute(
"0", uiwidget:
UIWidgets.ComboBox,
"Type of material for destruction sound",
"", ParamEnumArray.FromEnum(SCR_EMaterialSoundTypeBreak))]
10 SCR_EMaterialSoundTypeBreak m_eMaterialSoundType;
13 float m_fMeshChangeDelay;
20class SCR_DestructionMultiPhaseComponent : SCR_DestructionDamageManagerComponent
22#ifdef ENABLE_BASE_DESTRUCTION
23 private static int s_iFirstFreeDestructionMultiPhaseData = -1;
24 private static ref array<ref SCR_DestructionMultiPhaseData> s_aDestructionMultiPhaseData = {};
26 private int m_iDestructionMultiPhaseDataIndex = -1;
28 protected int m_iOriginalHealth;
29 protected float m_fNextPhaseHealth;
31 static const string DAMAGE_PHASE_SIGNAL_NAME =
"DamagePhase";
34 protected notnull SCR_DestructionMultiPhaseData GetDestructionMultiPhaseData()
36 if (m_iDestructionMultiPhaseDataIndex == -1)
37 m_iDestructionMultiPhaseDataIndex = AllocateDestructionMultiPhaseData();
39 return s_aDestructionMultiPhaseData[m_iDestructionMultiPhaseDataIndex];
43 private int AllocateDestructionMultiPhaseData()
45 if (s_iFirstFreeDestructionMultiPhaseData == -1)
46 return s_aDestructionMultiPhaseData.Insert(
new SCR_DestructionMultiPhaseData());
49 int returnIndex = s_iFirstFreeDestructionMultiPhaseData;
50 SCR_DestructionMultiPhaseData
data = s_aDestructionMultiPhaseData[returnIndex];
51 s_iFirstFreeDestructionMultiPhaseData =
data.m_iNextFreeIndex;
52 data.m_iNextFreeIndex = -1;
58 private void FreeDestructionMultiPhaseData(
int index)
60 s_aDestructionMultiPhaseData[
index].Reset();
61 s_aDestructionMultiPhaseData[
index].m_iNextFreeIndex = s_iFirstFreeDestructionMultiPhaseData;
62 s_iFirstFreeDestructionMultiPhaseData =
index;
67 SCR_DamagePhaseData GetDamagePhaseData(
int damagePhase)
69 if (damagePhase <= 0 || damagePhase >= GetNumDamagePhases())
72 return SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner())).m_aDamagePhases.Get(damagePhase - 1);
77 override bool GetDisablePhysicsOnDestroy()
84 bool GetIsInInitialDamagePhase()
86 return GetDamagePhase() == 0;
93 if (m_iDestructionMultiPhaseDataIndex == -1)
96 return GetDestructionMultiPhaseData().GetDamagePhase();
101 int GetNumDamagePhases()
103 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner()));
105 if (componentData.m_aDamagePhases)
106 return componentData.m_aDamagePhases.Count() + 1;
113 int CalculateDamagePhase(
int damagePhase,
float health,
float damage)
121 SCR_DamagePhaseData damagePhaseData = GetDamagePhaseData(damagePhase);
122 if (!damagePhaseData)
125 damage -= damagePhaseData.m_fPhaseHealth;
129 return Math.ClampInt(damagePhase, 0, GetNumDamagePhases());
131 return Math.ClampInt(damagePhase, 0, GetNumDamagePhases() - 1);
135 protected void SetOriginalResourceName(ResourceName originalResourceName)
137 GetDestructionMultiPhaseData().SetOriginalResourceName(originalResourceName);
141 protected ResourceName GetOriginalResourceName()
143 if (m_iDestructionMultiPhaseDataIndex == -1)
144 return ResourceName.Empty;
146 return GetDestructionMultiPhaseData().GetOriginalResourceName();
150 protected int GetTargetDamagePhase()
152 if (m_iDestructionMultiPhaseDataIndex == -1)
155 return GetDestructionMultiPhaseData().GetTargetDamagePhase();
159 protected void SetTargetDamagePhase(
int targetDamagePhase)
161 GetDestructionMultiPhaseData().SetTargetDamagePhase(targetDamagePhase);
165 protected void SetDamagePhase(
int damagePhase)
167 GetDestructionMultiPhaseData().SetDamagePhase(damagePhase);
169 SCR_DestructionUtility.SetDamagePhaseSignal(
GetOwner(), damagePhase);
176 if (damagePhase >= GetNumDamagePhases())
181 DeleteDestructibleDelayed();
185 int currentDamagePhase = GetDamagePhase();
186 if (currentDamagePhase == damagePhase)
189 SCR_DestructionUtility.RegenerateNavmeshDelayed(
GetOwner());
191 if (currentDamagePhase == 0)
195 SetOriginalResourceName(vObject.GetResourceName());
198 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner()));
202 float combinedPhaseHealth = componentData.m_fBaseHealth;
203 foreach (
int i, SCR_DamagePhaseData phase : componentData.m_aDamagePhases)
205 if (i + 1 <= damagePhase)
206 combinedPhaseHealth += phase.m_fPhaseHealth;
209 m_fNextPhaseHealth = m_iOriginalHealth - combinedPhaseHealth;
211 SetDamagePhase(damagePhase);
212 SetTargetDamagePhase(damagePhase + 1);
214 ScriptCallQueue callQueue =
GetGame().GetCallqueue();
219 float remainingTime = callQueue.GetRemainingTime(ChangeModel);
220 if (remainingTime > -1)
221 callQueue.Remove(ChangeModel);
225 delay = componentData.m_fMeshChangeDelay;
227 SCR_DamagePhaseData
data = GetDamagePhaseData(damagePhase);
231 callQueue.CallLater(ChangeModel, delay,
param1:
data.m_PhaseModel,
param2:
data.m_bUseMaterialsFromParent);
237 protected void ChangeModel(ResourceName modelName,
bool useMaterialFromParent =
false)
243 ResourceName modelPath;
245 SCR_Global.GetModelAndRemapFromResource(modelName, modelPath, remap);
246 if(useMaterialFromParent)
248 ResourceName modelPathSource;
250 ResourceName prefabPathSource = SCR_ResourceNameUtils.GetPrefabName(owner);
251 SCR_Global.GetModelAndRemapFromResource(prefabPathSource, modelPathSource, remapParent);
257 remap = SCR_DestructionUtility.SanitizeRemapString(remap, remapParent);
265 SCR_DestructionUtility.SetModel(owner, modelPath, remap);
270 override bool GetCanBeDamaged()
272 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner()));
276 if (componentData.m_bDeleteAfterFinalPhase)
277 return GetDamagePhase() < GetNumDamagePhases();
279 return GetDamagePhase() < GetNumDamagePhases() - 1;
284 override void OnDamage(notnull BaseDamageContext damageContext)
292 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner()));
297 SCR_DestructionHitInfo hitInfo = CreateDestructionHitInfo(damageContext.damageValue >= componentData.m_fDamageThresholdMaximum, previousHealth, damageContext.damageValue, damageContext.damageType, damageContext.hitPosition, damageContext.hitDirection, damageContext.hitNormal);
299 if (componentData.m_bPassDamageToChildren)
302 if (currentHealth <= 0)
304 int lastPhase = componentData.m_aDamagePhases.Count();
306 if (hitInfo.m_TotalDestruction)
308 if (!componentData.m_bDeleteAfterFinalPhase && componentData.m_aDamagePhases && !componentData.m_aDamagePhases.IsEmpty() && !(componentData.m_bDestroyChildrenWhenDestroyed &&
GetOwner().
GetParent()))
314 if (componentData.m_bDestroyChildrenWhenDestroyed)
315 DeleteDestructibleChildrenDelayed(
false);
320 DeleteDestructibleDelayed();
327 if (componentData.m_aDamagePhases && !componentData.m_aDamagePhases.IsEmpty())
329 SCR_DamagePhaseData lastPhaseData = GetDamagePhaseData(lastPhase);
331 if (componentData.m_eMaterialSoundType > 0)
332 SCR_DestructionUtility.PlaySound(
GetOwner(), componentData.m_eMaterialSoundType, GetNumDamagePhases(), GetDamagePhase());
334 if (lastPhaseData.m_PhaseDestroySpawnObjects && !lastPhaseData.m_PhaseDestroySpawnObjects.IsEmpty())
335 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), lastPhaseData.m_PhaseDestroySpawnObjects, hitInfo);
337 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), componentData.m_DestroySpawnObjects, hitInfo);
339 if (componentData.m_bDeleteAfterFinalPhase)
342 DeleteDestructibleDelayed();
349 if (componentData.m_bDestroyChildrenWhenDestroyed)
350 DeleteDestructibleChildrenDelayed();
355 if (componentData.m_eMaterialSoundType > 0)
356 SCR_DestructionUtility.PlaySound(
GetOwner(), componentData.m_eMaterialSoundType, GetNumDamagePhases(), GetDamagePhase());
358 if ( !componentData.m_bDestroyParentWhenDestroyed || !
GetOwner().
GetParent().GetPhysics() )
359 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), componentData.m_DestroySpawnObjects, hitInfo);
362 DeleteDestructibleDelayed();
369 int targetDamagePhase = GetTargetDamagePhase();
370 if (targetDamagePhase <= 0)
373 if (currentHealth <= m_fNextPhaseHealth)
375 if (componentData.m_eMaterialSoundType > 0)
376 SCR_DestructionUtility.PlaySound(
GetOwner(), componentData.m_eMaterialSoundType, GetNumDamagePhases(), GetDamagePhase());
378 SCR_DamagePhaseData targetDamagePhaseData = GetDamagePhaseData(targetDamagePhase);
379 if (targetDamagePhaseData.m_PhaseDestroySpawnObjects && !targetDamagePhaseData.m_PhaseDestroySpawnObjects.IsEmpty())
380 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), targetDamagePhaseData.m_PhaseDestroySpawnObjects, hitInfo);
382 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), componentData.m_DestroySpawnObjects, hitInfo);
387 if (componentData.m_bDestroyChildrenWhenDestroyed)
388 DeleteDestructibleChildrenDelayed();
390 if (!componentData.m_bDeleteAfterFinalPhase && targetDamagePhase == componentData.m_aDamagePhases.Count())
402 void UpdateResponseIndex()
404 SCR_DestructionMultiPhaseComponentClass prefabData = SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner()));
405 if (!prefabData || !prefabData.m_bDeleteAfterFinalPhase)
412 if (GetDamagePhase() + 1 < GetNumDamagePhases())
421 protected void RPC_ReplicateMultiPhaseDestructionState(
int phase, vector hitPosDirNorm[3],
float damage,
EDamageType damageType,
bool silent)
423 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner()));
427 SCR_DestructionHitInfo hitInfo = GetDestructionHitInfo(
true);
428 hitInfo.m_HitPosition = hitPosDirNorm[0];
429 hitInfo.m_HitDirection = hitPosDirNorm[1];
430 hitInfo.m_HitNormal = hitPosDirNorm[2];
431 hitInfo.m_HitDamage = damage;
432 hitInfo.m_DamageType = damageType;
434 SCR_DamagePhaseData phaseData = GetDamagePhaseData(phase);
439 if (phaseData.m_PhaseDestroySpawnObjects && !phaseData.m_PhaseDestroySpawnObjects.IsEmpty())
440 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), phaseData.m_PhaseDestroySpawnObjects, hitInfo);
442 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), componentData.m_DestroySpawnObjects, hitInfo);
444 if (componentData.m_eMaterialSoundType > 0)
445 SCR_DestructionUtility.PlaySound(
GetOwner(), componentData.m_eMaterialSoundType, GetNumDamagePhases(), GetDamagePhase());
454 SCR_DestructionUtility.SpawnDestroyObjects(
GetOwner(), componentData.m_DestroySpawnObjects, hitInfo);
455 if (componentData.m_eMaterialSoundType > 0)
456 SCR_DestructionUtility.PlaySound(
GetOwner(), componentData.m_eMaterialSoundType, GetNumDamagePhases(), GetDamagePhase());
464 SCR_DestructionHitInfo hitInfo = GetDestructionHitInfo();
467 vector hitPosDirNorm[3];
468 hitPosDirNorm[0] = hitInfo.m_HitPosition;
469 hitPosDirNorm[1] = hitInfo.m_HitDirection;
470 hitPosDirNorm[2] = hitInfo.m_HitNormal;
472 Rpc(RPC_ReplicateMultiPhaseDestructionState, damagePhase, hitPosDirNorm, hitInfo.m_HitDamage, hitInfo.m_DamageType, silent);
479 if( GetDamagePhase() != 0)
486 protected override event bool OnRplSave(ScriptBitWriter writer)
488 super.OnRplSave(writer);
490 bool writeDamagePhase = GetDamagePhase() != 0;
491 writer.Write(writeDamagePhase, 1);
493 if (writeDamagePhase)
494 writer.Write(GetDamagePhase(), 8);
500 protected override event bool OnRplLoad(ScriptBitReader reader)
502 super.OnRplLoad(reader);
504 bool readDamagePhase;
505 reader.Read(readDamagePhase, 1);
507 if (!readDamagePhase)
511 reader.Read(damagePhase, 8);
513 if (damagePhase != GetDamagePhase())
522 super.OnPostInit(owner);
529 if (!GetDefaultHitZone())
532 SCR_DestructionMultiPhaseComponentClass componentData = SCR_DestructionMultiPhaseComponentClass.Cast(
GetComponentData(
GetOwner()));
533 if (!componentData.m_aDamagePhases || componentData.m_aDamagePhases.IsEmpty())
536 m_iOriginalHealth = componentData.m_fBaseHealth;
537 foreach (SCR_DamagePhaseData damagePhase : componentData.m_aDamagePhases)
539 m_iOriginalHealth += damagePhase.m_fPhaseHealth;
542 SetHitZoneHealth(m_iOriginalHealth);
543 SetTargetDamagePhase(1);
544 m_fNextPhaseHealth = m_iOriginalHealth - componentData.m_fBaseHealth;
ArmaReforgerScripted GetGame()
enum EAIGroupCombatMode ComponentEditorProps(category:"GameScripted/AI", description:"Component for utility AI system for groups")
override bool OnRplSave(ScriptBitWriter writer)
override bool OnRplLoad(ScriptBitReader reader)
SCR_CharacterBloodHitZone OnDamage
Resilience - incapacitation or death, depending on game mode settings.
SCR_CharacterSoundComponentClass GetComponentData()
void GoToDamagePhase(int damagePhaseIndex, int previousDamagePhaseIndex, SCR_DestructionData destructionData, bool streamed)
Only call from OnStateChanged, otherwise you have HUGE desync.
SCR_DestructionDamageManagerComponentClass s_OnDestructibleDestroyed
Base destruction component, destruction types extend from this.
notnull SCR_DestructionBaseData GetDestructionBaseData()
void PassDamageToChildren(notnull BaseDamageContext damageContext)
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
destructible ReplicateDestructibleState()
override bool IsDestroyed()
Get all prefabs that have the spawner data
override bool HasDataToReplicate()
enum EVehicleType IEntity
proto external Physics GetPhysics()
proto external bool IsDeleted()
proto external VObject GetVObject()
Returns visual object set to this Entity. No reference is added.
float GetPreviousHealth()
IEntity GetOwner()
Owner entity of the fuel tank.
SCR_FieldOfViewSettings Attribute
void RplRpc(RplChannel channel, RplRcver rcver, RplCondition condition=RplCondition.None, string customConditionName="")
RplChannel
Communication channel. Reliable is guaranteed to be delivered. Unreliable not.