Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_DestructibleEntity.c
Go to the documentation of this file.
1[EntityEditorProps(category: "GameScripted/Destructibles", visible: false, dynamicBox: true)]
3{
4 [Attribute("0.05", UIWidgets.Slider, "Contact momentum to damage multiplier", "0.01 10 0.01", category: "Destruction Setup")]
5 float m_fMomentumToDamageScale;
6
7 [Attribute("", UIWidgets.Object, "List of objects (particles, debris, etc) to spawn on destruction of the object", category: "Destruction FX")]
8 ref array<ref SCR_BaseSpawnable> m_aDestroySpawnObjects;
9
10 [Attribute("0", uiwidget: UIWidgets.ComboBox, "Type of material for destruction sound", "", ParamEnumArray.FromEnum(SCR_EMaterialSoundTypeBreak))]
11 SCR_EMaterialSoundTypeBreak m_eMaterialSoundType;
12
13 [Attribute("0", uiwidget: UIWidgets.CheckBox, "Total destruction: If a destructible takes too much damage from a single source while this is enabled, instead of advancing to the next phase, it will be deleted from the world without any effects. Total destruction overrides \"Destroy at no health\"", "")]
14 bool m_bTotalDestructionEnabled;
15
16 [Attribute(defvalue: "0", uiwidget: UIWidgets.Slider, desc: "Amount of damage to take from a single source to trigger total destruction", params: "0 64000 1")]
17 float m_fTotalDestructionThreshold;
18}
19
21{
22 protected static const int MIN_MOMENTUM_RESPONSE_INDEX = 1;
23 protected static const int MIN_DESTRUCTION_RESPONSE_INDEX = 6;
24 protected static const float MIN_IMPULSE_THRESHOLD = 5;
25 static const int TOTAL_DESTRUCTION_MAX_HEALTH_MULTIPLIER = 10;
26
27 //------------------------------------------------------------------------------------------------
29 {
31 if (prefabData)
32 return prefabData.GetDamageMultiplier(type);
33
34 return 0;
35 }
36
37 //------------------------------------------------------------------------------------------------
39 {
41 if (prefabData)
42 return prefabData.GetDamageReduction();
43
44 return 0;
45 }
46
47 //------------------------------------------------------------------------------------------------
49 {
51 if (prefabData)
52 return prefabData.GetDamageThreshold();
53
54 return 0;
55 }
56
57 //------------------------------------------------------------------------------------------------
59 {
61 if (!prefabData)
62 return 0;
63
64 return prefabData.GetMaxHealth();
65 }
66
67 //------------------------------------------------------------------------------------------------
68 override void OnDamage(int previousState, int newState, float currentHealth, notnull BaseDamageContext damageContext, ScriptBitWriter frameData)
69 {
70 if (!frameData)
71 return;
72
73 // Handling double damage in one frame:
74 // FrameData exists from previous damage - the object was damaged twice in a frame, the second damage didn't actually change the phase,
75 // therefore not writing it into the destructionData.
76 if (previousState == newState)
77 return;
78
79 float maxHealth = GetMaxHealth();
80 SCR_DestructionData destructionData = new SCR_DestructionData();
81 destructionData.m_vHitPosition = damageContext.hitPosition;
82 destructionData.m_vHitDirection = damageContext.hitDirection.Normalized();
83 destructionData.m_vHitNormal = damageContext.hitNormal.Normalized();
84 destructionData.m_fHitDamage = damageContext.damageValue;
85 destructionData.m_eDamageType = damageContext.damageType;
86 destructionData.m_iPreviousPhase = previousState;
87
88 //if damage was too large, trigger total destruction (if enabled)
90 const bool totalDestruction = prefabData.m_bTotalDestructionEnabled && (damageContext.damageValue >= prefabData.m_fTotalDestructionThreshold);
91 destructionData.m_bTotalDestruction = totalDestruction;
92
93 //Total destruction plays no sound, spawns no particles, leaves no debris.
94 if(destructionData.m_bTotalDestruction)
95 {
96 //Even if they don't have rpl component, DeleteRplEntity will delete the entity for this client
97 RplComponent.DeleteRplEntity(this, false);
98 return;
99 }
100
101 destructionData.Save(frameData);
102 }
103
104 //------------------------------------------------------------------------------------------------
106 protected void GoToDamagePhase(int damagePhaseIndex, int previousDamagePhaseIndex, SCR_DestructionData destructionData, bool streamed)
107 {
109 if (!prefabData)
110 return;
111
112 SCR_BaseDestructionPhase phase = SCR_BaseDestructionPhase.Cast(prefabData.GetDestructionPhase(damagePhaseIndex));
113 if (!phase)
114 return; // Should never happen, unless some memory issue happens
115
116 SCR_DestructionUtility.SetDamagePhaseSignal(this, damagePhaseIndex);
117
118 //test
120
121 bool destroyAtNoHealth;
122 prefabData.GetPrefab().Get("DestroyAtNoHealth", destroyAtNoHealth);
123
124 bool lastPhase = prefabData.GetNumDestructionPhases() - 1 == damagePhaseIndex;
125
126 Physics phys = GetPhysics();
127 if (phys && phase.m_ePhysicalResponseIndex != SCR_EDestructionResponseIndex.DONT_CHANGE)
128 {
129 float maxHealth = GetMaxHealth();
130 if (phase.m_ePhysicalResponseIndex == SCR_EDestructionResponseIndex.AUTOMATIC)
131 SCR_DestructionUtility.UpdateResponseIndex(phys, maxHealth * phase.GetThreshold(), maxHealth);
132 else
133 phys.SetResponseIndex(phase.m_ePhysicalResponseIndex);
134 }
135
136 if (streamed)
137 {
138 if (destroyAtNoHealth && lastPhase)
139 return;//if it is going to be deleted then there is no point of changing the model
140
141 ResourceName modelPath;
142 string remap;
143 ResourceName parentModel = SCR_ResourceNameUtils.GetPrefabName(this);
144 if (phase.GetPhaseModelAndRemap(modelPath, remap, parentModel))
145 {
146 SCR_DestructionUtility.SetModel(this, modelPath, remap);
147 return;// When streamed, we don't care about sounds or effects, we just change the model and quit
148 }
149
150 return;
151 }
152
154
155 if (prefabData.m_eMaterialSoundType > 0)
156 SCR_DestructionUtility.PlaySound(this, prefabData.m_eMaterialSoundType, prefabData.GetNumDestructionPhases(), damagePhaseIndex);
157
158 if (lastPhase && destroyAtNoHealth) // is last phase and gets deleted
159 {
160 SCR_DestructionUtility.SpawnDestroyObjects(this, phase.m_aPhaseDestroySpawnObjects, SCR_DestructionHitInfo.FromDestructionData(destructionData));
161 return;
162 }
163
164 ResourceName modelPath;
165 string remap;
166
167 ResourceName parentModel = SCR_ResourceNameUtils.GetPrefabName(this);
168 if (phase.GetPhaseModelAndRemap(modelPath, remap, parentModel))
169 {
170 SCR_DestructionUtility.SetModel(this, modelPath, remap);
171 }
172
173 if (lastPhase)
174 ClearEventMask(EntityEvent.CONTACT);
175 else
176 SetEventMask(EntityEvent.CONTACT);
177
178 SCR_BaseDestructionPhase previousPhase = SCR_BaseDestructionPhase.Cast(prefabData.GetDestructionPhase(previousDamagePhaseIndex));
179 if (!previousPhase)
180 return; // Should never happen, unless some memory issue happens
181
182 SCR_DestructionUtility.SpawnDestroyObjects(this, previousPhase.m_aPhaseDestroySpawnObjects, SCR_DestructionHitInfo.FromDestructionData(destructionData));
183 }
184
185 //------------------------------------------------------------------------------------------------
187 {
188 ChimeraWorld world = ChimeraWorld.CastFrom(GetWorld());
189
190 if(!world)
191 return;
192
193 DestructionManager manager = world.GetDestructionManager();
194
195 if(!manager)
196 return;
197
199
200 vector minsOffset = "-5 -5 -5";
201 vector maxsOffset = "5 5 5";
202
203 minsOffset = minsOffset*1.5;
204 maxsOffset = maxsOffset*1.5;
205 entry.SetCategory(EDestructionHeatmapCategory.FOREST);
206 entry.SetWeight(10);
207 entry.SetBoundingBox(GetOrigin() + minsOffset, GetOrigin() + maxsOffset);
208 manager.QueueDestructionHeatmapEntry(entry);
209 }
210
211 //------------------------------------------------------------------------------------------------
212 override void OnStateChanged(int destructibleState, ScriptBitReader frameData, bool JIP)
213 {
214 SCR_DestructionData destructionData = new SCR_DestructionData();
215 if (frameData)
216 destructionData.Load(frameData);
217
218 //Total destruction plays no sound, spawns no particles, leaves no debris.
219 if(destructionData.m_bTotalDestruction)
220 {
221 return;
222 }
223
224 GoToDamagePhase(destructibleState, destructionData.m_iPreviousPhase, destructionData, JIP);
225 }
226
227 //------------------------------------------------------------------------------------------------
232
233 //------------------------------------------------------------------------------------------------
234 protected bool FilterContact(IEntity owner, IEntity other, Contact contact)
235 {
236 if (contact.Impulse < MIN_IMPULSE_THRESHOLD)
237 return false;
238
239 if (!other)
240 return false;
241
242 if (!owner.GetPhysics() || !other.GetPhysics())
243 return false;
244
246 if (!prefabData)
247 return false;
248
249 if (other.IsInherited(SCR_BaseDebrisSmallEntity)) // Ignore impacts from debris
250 return false;
251
252 if (ChimeraCharacter.Cast(other)) // Ignore impacts from characters
253 return false;
254
255 return true;
256 }
257
258 //------------------------------------------------------------------------------------------------
259 protected void OnFilteredContact(IEntity owner, IEntity other, Contact contact)
260 {
261 Physics ownerPhysics = contact.Physics1;
262 Physics otherPhysics = contact.Physics2;
263
264 float ownerMass = ownerPhysics.GetMass();
265 float otherMass = otherPhysics.GetMass();
266 int ownerReponseIndex = ownerPhysics.GetResponseIndex();
267 int otherResponseIndex = otherPhysics.GetResponseIndex();
268
270 float damage = prefabData.GetMaxHealth();
271 EDamageType damageType = EDamageType.TRUE;
272 // If vehicle response index is the same or higher -> automatically destroy
273 // Otherwise -> apply damage
274 if (ownerReponseIndex < MIN_DESTRUCTION_RESPONSE_INDEX || otherResponseIndex - MIN_MOMENTUM_RESPONSE_INDEX < ownerReponseIndex - MIN_DESTRUCTION_RESPONSE_INDEX)
275 {
276 float momentum = SCR_DestructionUtility.CalculateMomentum(contact, ownerMass, otherMass);
277 damage = momentum * prefabData.m_fMomentumToDamageScale;
278 if (damage < 1)
279 return;
280
281 damageType = EDamageType.COLLISION;
282 }
283
284 vector outMat[3];
285 vector relVel = contact.VelocityBefore2 - contact.VelocityBefore1;
286 outMat[0] = contact.Position; // Hit position
287 outMat[1] = relVel.Normalized(); // Hit direction
288 outMat[2] = contact.Normal; // Hit normal
289
290 HandleDamage(damageType, damage, outMat);
291 }
292
293 //------------------------------------------------------------------------------------------------
295 override void EOnContact(IEntity owner, IEntity other, Contact contact)
296 {
297 // Call FilterContact to check if contact should be registered
298 // This is done for consistency's sake since that's how damage manager component does it.
299 // Damage manager does not have access to EOnContact however.
300 if (!FilterContact(owner, other, contact))
301 return;
302
303 OnFilteredContact(owner, other, contact);
304 }
305
306 //------------------------------------------------------------------------------------------------
308 {
309 SetEventMask(EntityEvent.CONTACT);
310 }
311};
void SCR_BaseDebrisSmallEntity(IEntitySource src, IEntity parent)
vector GetOrigin()
override bool FilterContact(IEntity owner, IEntity other, Contact contact)
Armor doesn't take collisiondamage.
override bool HandleDamage(BaseDamageContext damageContext, IEntity owner)
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.
void HeatmapPrototype()
float GetDamageThreshold()
float GetDamageReduction()
float GetDamageMultiplier(EDamageType type)
SCR_DestructibleEntityClass MIN_MOMENTUM_RESPONSE_INDEX
void SCR_DestructibleEntity(IEntitySource src, IEntity parent)
float GetMaxHealth()
void GoToDamagePhase(int damagePhaseIndex, int previousDamagePhaseIndex, SCR_DestructionData destructionData, bool streamed)
Only call from OnStateChanged, otherwise you have HUGE desync.
EDamageType type
override void EOnContact(IEntity owner, IEntity other, Contact contact)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
override void OnFilteredContact(IEntity owner, IEntity other, Contact contact)
enum SCR_EPhysicsResponseIndex MIN_IMPULSE_THRESHOLD
SCR_VehicleDamageManagerComponentClass GetPrefabData()
void OnStateChanged(int destructibleState, ScriptBitReader frameData, bool JIP)
proto external Physics GetPhysics()
bool GetPhaseModelAndRemap(out ResourceName modelPath, out string remap, ResourceName parentPrefabPath=ResourceName.Empty)
static void SetDamagePhaseSignal(notnull IEntity owner, int damagePhaseIndex=0)
static void SpawnDestroyObjects(notnull IEntity owner, notnull array< ref SCR_BaseSpawnable > spawnList, notnull SCR_DestructionHitInfo destructionHitInfo)
Spawns objects that are meant to be created when the object is destroyed (particles,...
static void PlaySound(notnull IEntity owner, SCR_EMaterialSoundTypeBreak materialSoundType, int numDamagePhases, int damagePhaseIndex)
static float CalculateMomentum(notnull Contact contact, float ownerMass, float otherMass)
static void UpdateResponseIndex(notnull Physics physics, float health, float maxHealth)
static void SetModel(notnull IEntity owner, ResourceName modelName)
static void RegenerateNavmeshDelayed(notnull IEntity owner)
SCR_FieldOfViewSettings Attribute
EntityEvent
Various entity events.
Definition EntityEvent.c:14
EDamageType
Definition EDamageType.c:13