Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_DestructionIndicesAssignTool.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[WorkbenchToolAttribute(
3 name: "Destruction indices assign tool",
4 description: "Automatically assigns response indices to destructible objects\n\n" +
5 "- button 1 = Fix phases\n" +
6 "- button 2 = Print all destructible resource names\n" +
7 "- button 3 = Revert to parent\n" +
8 "- button 4 = Update Prefabs\n" +
9 "- button 5 = Remove phases\n" +
10 "- button 6 = Filter Prefabs\n" +
11 "- button 7 = Show Mass\n" +
12 "- button 8 = Convert Prefabs\n" +
13 "- button 9 = Assign Indices\n" +
14 "- button 10 = Set building destruction effects - TODO",
15 awesomeFontCode: 0xF7E4)]
16class SCR_DestructionIndicesAssignTool : WorldEditorTool
17{
18 [Attribute(category: "General")]
19 protected ref array<ref SCR_MassResponseIndexPair> m_aPairs;
20
21 [Attribute(category: "General")]
22 protected ref array<string> m_aProperties;
23
24 protected const ref array<string> EXTENSIONS = { "et" };
25 protected const ref array<string> DESTRUCTIBLE_COMPONENT_CLASSES = { "SCR_DestructionDamageManagerComponent", "SCR_DestructionMultiPhaseComponent" };
26 protected const string DESTRUCTION_COMPONENT_CLASS = "SCR_DestructionMultiPhaseComponent";
27 protected const string PHYSICS_COMPONENT = "RigidBody";
28
29 //------------------------------------------------------------------------------------------------
30 [ButtonAttribute("1 FP")]
31 protected void FixPhasesButton()
32 {
33 m_API.BeginEntityAction();
34 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS);
35
36 Resource resource;
37 BaseResourceObject baseResourceObject;
38 BaseContainer baseContainer, parent;
39 IEntitySource parentEntitySource, entitySource;
40 IEntityComponentSource destructionComponent;
41 BaseContainerList parentPhasesList, phasesList;
42 BaseContainerList parentOldPhasesList, oldPhasesList;
43 int count = resourceNames.Count();
44 while (count > 0)
45 {
46 for (int i = count - 1; i >= 0; i--)
47 {
48 // Only in prefabs folder
49 if (!resourceNames[i].Contains("Prefabs/"))// || !resourceNames[i].Contains("BrickWall_01/BrickWall_01_white_2m.et"))
50 {
51 count--;
52 resourceNames.Remove(i);
53 continue;
54 }
55
56 // Validation
57 resource = Resource.Load(resourceNames[i]);
58 if (!resource)
59 {
60 count--;
61 resourceNames.Remove(i);
62 continue;
63 }
64
65 // Validation
66 baseResourceObject = resource.GetResource();
67 if (!baseResourceObject)
68 {
69 count--;
70 resourceNames.Remove(i);
71 continue;
72 }
73
74 // Validation
75 baseContainer = baseResourceObject.ToBaseContainer();
76 if (!baseContainer)
77 {
78 count--;
79 resourceNames.Remove(i);
80 continue;
81 }
82
83 // Class must be SCR_DestructibleEntity
84 if (baseContainer.GetClassName() != "SCR_DestructibleEntity")
85 {
86 count--;
87 resourceNames.Remove(i);
88 continue;
89 }
90
91 entitySource = baseContainer.ToEntitySource();
92 destructionComponent = FindComponent(entitySource, "SCR_DestructionMultiPhaseComponent");
93
94 parent = entitySource.GetAncestor();
95
96 if (!parent)
97 {
98 count--;
99 resourceNames.Remove(i);
100 continue;
101 }
102
103 if (parent.GetClassName() == "SCR_DestructibleEntity" && resourceNames.Find(parent.GetResourceName()) != -1)
104 continue; // has parent that wasn't handled yet, keep in array & count, so we determine this prefab later
105
106 if (resourceNames[i] == "{A00A15B03B48A2AF}Prefabs/Structures/Walls/Brick/BrickWall_01/BrickWall_01_8m.et")
107 Print("Test");
108
109 parentEntitySource = parent.ToEntitySource();
110 phasesList = baseContainer.GetObjectArray("DamagePhases");
111 parentPhasesList = parent.GetObjectArray("DamagePhases");
112
113 if (resourceNames[i] == "{A00A15B03B48A2AF}Prefabs/Structures/Walls/Brick/BrickWall_01/BrickWall_01_8m.et")
114 {
115 Print(parentPhasesList.Count());
116 Print(parent.GetResourceName());
117 }
118
119 if (parentPhasesList && parentPhasesList.Count() != 0)
120 {
121 // Parent has some phases, we have to redo the current objects phases
122 //if (entitySource.IsVariableSetDirectly("DamagePhases"))
123 // m_API.ClearVariableValue(entitySource, null, "DamagePhases");
124
125 ConvertPhases(entitySource, destructionComponent);
126
127 //oldPhasesList = destructionComponent.GetObjectArray("m_aDamagePhases");
128 //parentOldPhasesList = FindComponent(parentEntitySource, "SCR_DestructionMultiPhaseComponent").GetObjectArray("m_aDamagePhases");
129
130 // first compare phases within old list to see which one is inherited and just changed, then adjust the new ones
131 }
132
133 // This prefab was handled, remove it from the list
134 count--;
135 resourceNames.Remove(i);
136 continue;
137 }
138 }
139
140 m_API.EndEntityAction();
141 }
142
143 //------------------------------------------------------------------------------------------------
144 protected void ConvertPhases(IEntitySource source, IEntityComponentSource componentSource)
145 {
146 array<ref SCR_DamagePhaseData> parentDamagePhases;
147 BaseContainer parentContainer = source.GetAncestor();
148 if (parentContainer)
149 {
150 parentDamagePhases = {};
151 FindComponent(parentContainer.ToEntitySource(), "SCR_DestructionMultiPhaseComponent").Get("m_aDamagePhases", parentDamagePhases);
152 }
153
154 array<ref SCR_DamagePhaseData> damagePhases = {};
155 componentSource.Get("m_aDamagePhases", damagePhases);
156 int damagePhasesCount = damagePhases.Count();
157
158 // Get total health of all damage phases
159 float totalHealth = GetPhasesTotalHealth(componentSource, damagePhases);
160 float healthNormalized = 1;
161 float currentPhaseHealth;
162 componentSource.Get("m_fBaseHealth", currentPhaseHealth);
163
164 healthNormalized -= currentPhaseHealth / totalHealth; // This will define next phase health normalized
165
166 array<ref ContainerIdPathEntry> path = {};
167 bool phaseExists = false;
168 for (int j = 0; j < damagePhasesCount; j++)
169 {
170 if (j == damagePhasesCount - 1) // Last phase
171 break;
172
173 phaseExists = false;
174 if (parentDamagePhases && parentDamagePhases.IsIndexValid(j))
175 {
176 for (int k = parentDamagePhases.Count() - 1; k >= 0; k--)
177 {
178 if (parentDamagePhases[k].m_fPhaseHealth == damagePhases[j].m_fPhaseHealth)
179 {
180 // Same phase most likely
181 phaseExists = true;
182 }
183 }
184 }
185
186 // Regular phase - not first, not last
187 // Create damage phase only if we shouldn't override the inherited
188 if (!phaseExists)
189 m_API.CreateObjectArrayVariableMember(source, path, "DamagePhases", "SCR_BaseDestructionPhase", j);
190
191 // Change path to the current damage phase
192 path.Insert(new ContainerIdPathEntry("DamagePhases", j));
193
194 // Set variables of the damage phase
195 if (parentDamagePhases && parentDamagePhases.IsIndexValid(j))
196 {
197 if (parentDamagePhases[j].m_PhaseModel != damagePhases[j].m_PhaseModel)
198 m_API.SetVariableValue(source, path, "m_sPhaseModel", damagePhases[j].m_PhaseModel);
199 }
200
201 m_API.SetVariableValue(source, path, "Threshold", healthNormalized.ToString());
202 healthNormalized -= damagePhases[j].m_fPhaseHealth / totalHealth; // This will define next phase health normalized
203
204 //m_API.ClearVariableValue(source, path, "m_aPhaseDestroySpawnObjects");
205
206 // Use effects of the next phase as exit particles
207 if (j + 1 < damagePhasesCount)
208 {
209 foreach (int k, SCR_BaseSpawnable spawnObject : damagePhases[j + 1].m_PhaseDestroySpawnObjects)
210 {
211 spawnObject.CopyToSource(m_API, source, path, k, "m_aPhaseDestroySpawnObjects");
212 }
213 }
214 else
215 {
216 array<ref SCR_BaseSpawnable> destroySpawnObjects = {};
217 componentSource.Get("m_DestroySpawnObjects", destroySpawnObjects);
218
219 foreach (int k, SCR_BaseSpawnable destroySpawnObject : destroySpawnObjects)
220 {
221 destroySpawnObject.CopyToSource(m_API, source, path, k, "m_aPhaseDestroySpawnObjects");
222 }
223 }
224
225 path.Clear();
226 }
227 }
228
229 //------------------------------------------------------------------------------------------------
230 [ButtonAttribute("2 PADRN")]
231 protected void PrintAllDestructibleResourceNamesButton()
232 {
233 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS, rootPath: "$ArmaReforger:Prefabs");
234
235 Resource resource;
236 BaseResourceObject baseResourceObject;
237 BaseContainer baseContainer;
238 IEntitySource entitySource;
239 IEntityComponentSource destructionComponent;
240 foreach (ResourceName resourceName : resourceNames)
241 {
242 // Validation
243 resource = Resource.Load(resourceName);
244 if (!resource.IsValid())
245 continue;
246
247 // Validation
248 baseResourceObject = resource.GetResource();
249 if (!baseResourceObject)
250 continue;
251
252 // Validation
253 baseContainer = baseResourceObject.ToBaseContainer();
254 if (!baseContainer)
255 continue;
256
257 // Check if old destruction is enabled
258 if (!HasComponent(baseResourceObject, "SCR_DestructionMultiPhaseComponent", true))
259 {
260 // Old destruction is disabled, check for new one
261 entitySource = baseContainer.ToEntitySource();
262 if (!entitySource || entitySource.GetClassName() != "SCR_DestructibleEntity")
263 continue; // Not even new destruction is enabled, so destruction is completely disabled on this object
264 }
265
266 // Has enabled destruction
267 Print(resourceName, LogLevel.NORMAL);
268 }
269 }
270
271 //------------------------------------------------------------------------------------------------
272 protected float CalculateDensity(notnull IEntity entity)
273 {
274 Physics physics = entity.GetPhysics();
275 if (!physics)
276 return 0;
277
278 array<SurfaceProperties> surfaces = {};
279 GameMaterial gameMaterial;
280 BallisticInfo ballisticInfo;
281
282 float totalDensity;
283 int j, densitiesCount;
284
285 for (int i = physics.GetNumGeoms() - 1; i >= 0; i--)
286 {
287 surfaces.Clear();
288 physics.GetGeomSurfaces(i, surfaces);
289
290 for (j = surfaces.Count() - 1; i >= 0; i--)
291 {
292 gameMaterial = surfaces[j];
293 if (!gameMaterial)
294 continue;
295
296 ballisticInfo = gameMaterial.GetBallisticInfo();
297 if (!ballisticInfo)
298 continue;
299
300 totalDensity += ballisticInfo.GetDensity();
301 densitiesCount++;
302 }
303 }
304
305 if (densitiesCount != 0)
306 return totalDensity / densitiesCount;
307 else
308 return 0;
309 }
310
311 //------------------------------------------------------------------------------------------------
312 [ButtonAttribute("3 RTP")]
313 protected void RevertToParentButton()
314 {
315 m_API.BeginEntityAction();
316 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS, rootPath: "Prefabs");
317 //array<ResourceName> resourceNames = {};
318 //resourceNames.Insert("{AAAF45FD8D585128}Prefabs/Structures/Walls/Brick/BrickWall_02/BrickWall_02_8m.et");
319 //resourceNames.Insert("{F0F75F23D500A3F2}Prefabs/Props/Agriculture/Beehive_01/Dst/Beehive_01_dst_01_blue.et");
320
321 Resource resource;
322 BaseResourceObject baseResourceObject;
323 BaseContainer baseContainer;
324 IEntitySource entitySource;
325 IEntityComponentSource destructionComponent;
326 BaseContainer parent;
327 string parentValue, childValue, defaultValue;
328 for (int i = resourceNames.Count() - 1; i >= 0; i--)
329 {
330 // Only in prefabs folder
331 if (!resourceNames[i].Contains("Prefabs/"))
332 {
333 resourceNames.Remove(i);
334 continue;
335 }
336
337 // Validation
338 resource = Resource.Load(resourceNames[i]);
339 if (!resource)
340 {
341 resourceNames.Remove(i);
342 continue;
343 }
344
345 // Validation
346 baseResourceObject = resource.GetResource();
347 if (!baseResourceObject)
348 {
349 resourceNames.Remove(i);
350 continue;
351 }
352
353 // Validation
354 baseContainer = baseResourceObject.ToBaseContainer();
355 if (!baseContainer)
356 {
357 resourceNames.Remove(i);
358 continue;
359 }
360
361 // Only entity type SCR_DestructibleEntity
362 entitySource = baseContainer.ToEntitySource();
363 if (!entitySource || entitySource.GetClassName() != "SCR_DestructibleEntity")
364 {
365 resourceNames.Remove(i);
366 continue;
367 }
368
369 // Only with the "old" destruction component
370 if (!HasComponent(baseResourceObject, "SCR_DestructionMultiPhaseComponent", false))
371 {
372 resourceNames.Remove(i);
373 continue;
374 }
375
376 parent = entitySource.GetAncestor();
377
378 foreach (int j, string property : m_aProperties)
379 {
380 if (!entitySource.IsVariableSetDirectly(property)) // Variable is not set directly, we don't need to clear it
381 continue;
382
383 entitySource.Get(property, childValue);
384 entitySource.GetDefaultAsString(property, defaultValue);
385
386 if (resourceNames[i] == "{B31031F1F682586E}Prefabs/Props/Furniture/BenchWooden_02_Base.et")
387 {
388 if (property == "MaxHealth")
389 {
390 Print(defaultValue);
391 Print(childValue);
392 }
393 }
394
395 if (childValue == defaultValue) // default value is either the real default value or the inherited value from parent prefab
396 {
397 m_API.ClearVariableValue(entitySource, null, property); // Thus we can clear the variable when set directly
398 /*if (!parent || parent.GetClassName() != "SCR_DestructibleEntity") // Has no parent or inherits the value from same type parent
399 {
400 m_API.ClearVariableValue(entitySource, null, property); // Thus we can clear the variable when set directly
401 continue;
402 }*/
403 }
404 if (parent && parent.GetClassName() == "SCR_DestructibleEntity") // value is not default, check if it's inherited
405 {
406 parent.Get(property, parentValue);
407 if (childValue == parentValue)
408 m_API.ClearVariableValue(entitySource, null, property);
409 }
410 }
411
412 if (!parent)
413 continue;
414
415 // If there is parent prefab, clear these two variables, because we want the inherited version
416 if (entitySource.IsVariableSetDirectly("FirstDestructionPhase") && parent.IsVariableSetDirectly("FirstDestructionPhase"))
417 m_API.ClearVariableValue(entitySource, null, "FirstDestructionPhase");
418 if (entitySource.IsVariableSetDirectly("LastDestructionPhase") && parent.IsVariableSetDirectly("LastDestructionPhase"))
419 m_API.ClearVariableValue(entitySource, null, "LastDestructionPhase");
420 }
421
422 m_API.EndEntityAction();
423 }
424
425 //------------------------------------------------------------------------------------------------
426 // Adds all models to the phases destroy objects
427 [ButtonAttribute("4 UP")]
428 protected void UpdatePrefabsButton()
429 {
430 m_API.BeginEntityAction();
431 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS, rootPath: "Prefabs");
432
433 Resource resource;
434 BaseResourceObject baseResourceObject;
435 BaseContainer baseContainer;
436 IEntitySource entitySource;
437 IEntityComponentSource destructionComponent;
438 for (int i = resourceNames.Count() - 1; i >= 0; i--)
439 {
440 // Only in prefabs folder
441 if (!resourceNames[i].Contains("Prefabs/"))
442 {
443 resourceNames.Remove(i);
444 continue;
445 }
446
447 // Validation
448 resource = Resource.Load(resourceNames[i]);
449 if (!resource)
450 {
451 resourceNames.Remove(i);
452 continue;
453 }
454
455 // Validation
456 baseResourceObject = resource.GetResource();
457 if (!baseResourceObject)
458 {
459 resourceNames.Remove(i);
460 continue;
461 }
462
463 // Validation
464 baseContainer = baseResourceObject.ToBaseContainer();
465 if (!baseContainer)
466 {
467 resourceNames.Remove(i);
468 continue;
469 }
470
471 // Only entity type SCR_DestructibleEntity
472 entitySource = baseContainer.ToEntitySource();
473 if (!entitySource || entitySource.GetClassName() != "SCR_DestructibleEntity")
474 {
475 resourceNames.Remove(i);
476 continue;
477 }
478
479 // Only with the "old" destruction component
480 if (!HasComponent(baseResourceObject, "SCR_DestructionMultiPhaseComponent", false))
481 {
482 resourceNames.Remove(i);
483 continue;
484 }
485
486 destructionComponent = FindComponent(entitySource, "SCR_DestructionMultiPhaseComponent");
487
488 // Setup first phase
489 array<ref ContainerIdPathEntry> path = {};
490 array<ref SCR_DamagePhaseData> damagePhases = {};
491 destructionComponent.Get("m_aDamagePhases", damagePhases);
492 int damagePhasesCount = damagePhases.Count();
493
494 BaseContainer phaseObject;
495 path.Insert(new ContainerIdPathEntry("FirstDestructionPhase"));
496 if (damagePhasesCount > 0)
497 {
498 // Clear the array first
499 phaseObject = entitySource.GetObject("FirstDestructionPhase");
500 ClearArrayOfPhaseEffects(phaseObject, path, entitySource);
501
502 foreach (int j, SCR_BaseSpawnable spawnObject : damagePhases[0].m_PhaseDestroySpawnObjects)
503 {
504 spawnObject.CopyToSource(m_API, entitySource, path, j, "m_aPhaseDestroySpawnObjects");
505 }
506 }
507 // End of first phase setup
508
509 path.Clear();
510 if (damagePhasesCount == 0) // making sure last phase is always set
511 {
512 array<ref SCR_BaseSpawnable> destroySpawnObjects = {};
513 destructionComponent.Get("m_DestroySpawnObjects", destroySpawnObjects);
514 path.Insert(new ContainerIdPathEntry("LastDestructionPhase"));
515
516 // Clear the array first
517 phaseObject = entitySource.GetObject("LastDestructionPhase");
518 ClearArrayOfPhaseEffects(phaseObject, path, entitySource);
519
520 foreach (int j, SCR_BaseSpawnable spawnObject : destroySpawnObjects)
521 {
522 spawnObject.CopyToSource(m_API, entitySource, path, j, "m_aPhaseDestroySpawnObjects");
523 }
524
525 path.Clear();
526 }
527
528 BaseContainerList phasesList;
529 phasesList = entitySource.GetObjectArray("DamagePhases");
530 for (int j = 0; j < damagePhasesCount; j++)
531 {
532 if (j == damagePhasesCount - 1) // Last phase
533 continue;
534
535 // Regular phase - not first, not last
536 path.Insert(new ContainerIdPathEntry("DamagePhases", j));
537
538 // Set variables of the damage phase
539 // Use effects of the next phase as exit particles
540 if (j + 1 < damagePhasesCount)
541 {
542 // Clear the array first
543 phaseObject = phasesList.Get(j);
544 ClearArrayOfPhaseEffects(phaseObject, path, entitySource);
545
546 foreach (int k, SCR_BaseSpawnable spawnObject : damagePhases[j + 1].m_PhaseDestroySpawnObjects)
547 {
548 spawnObject.CopyToSource(m_API, entitySource, path, k, "m_aPhaseDestroySpawnObjects");
549 }
550 }
551 else
552 {
553 array<ref SCR_BaseSpawnable> destroySpawnObjects = {};
554 destructionComponent.Get("m_DestroySpawnObjects", destroySpawnObjects);
555
556 // Clear the array first
557 phaseObject = phasesList.Get(j);
558 ClearArrayOfPhaseEffects(phaseObject, path, entitySource);
559
560 foreach (int k, SCR_BaseSpawnable spawnObject : destroySpawnObjects)
561 {
562 spawnObject.CopyToSource(m_API, entitySource, path, k, "m_aPhaseDestroySpawnObjects");
563 }
564 }
565
566 path.Clear();
567 }
568
569 }
570
571 m_API.EndEntityAction();
572 }
573
574 //------------------------------------------------------------------------------------------------
575 // Removes duplicate phases (where model is the same)
576 // Recalculates phase thresholds if there are some phases with the same threshold (inheritance issues after conversion)
577 [ButtonAttribute("5 RP")]
578 protected void RemovePhasesButton()
579 {
580 m_API.BeginEntityAction();
581 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS, rootPath: "Prefabs");
582
583 Resource resource;
584 BaseResourceObject baseResourceObject;
585 BaseContainer baseContainer;
586 IEntitySource entitySource;
587 IEntityComponentSource destructionComponent;
588 for (int i = resourceNames.Count() - 1; i >= 0; i--)
589 {
590 // Only in prefabs folder
591 if (!resourceNames[i].Contains("Prefabs/"))
592 {
593 resourceNames.Remove(i);
594 continue;
595 }
596
597 // Validation
598 resource = Resource.Load(resourceNames[i]);
599 if (!resource)
600 {
601 resourceNames.Remove(i);
602 continue;
603 }
604
605 // Validation
606 baseResourceObject = resource.GetResource();
607 if (!baseResourceObject)
608 {
609 resourceNames.Remove(i);
610 continue;
611 }
612
613 // Validation
614 baseContainer = baseResourceObject.ToBaseContainer();
615 if (!baseContainer)
616 {
617 resourceNames.Remove(i);
618 continue;
619 }
620
621 // Only entity type SCR_DestructibleEntity
622 entitySource = baseContainer.ToEntitySource();
623 if (!entitySource || entitySource.GetClassName() != "SCR_DestructibleEntity")
624 {
625 resourceNames.Remove(i);
626 continue;
627 }
628
629 // Only with the "old" destruction component
630 if (!HasComponent(baseResourceObject, "SCR_DestructionMultiPhaseComponent", false))
631 {
632 resourceNames.Remove(i);
633 continue;
634 }
635
636 array<ref ContainerIdPathEntry> path = {};
637 BaseContainer phaseObject;
638 BaseContainerList phasesList;
639 phasesList = entitySource.GetObjectArray("DamagePhases");
640 int phasesCount = phasesList.Count();
641 float threshold;
642 array<float> thresholds = {};
643 ResourceName phaseModel;
644 array<ResourceName> phaseModels = {};
645 bool recalculate = false;
646
647 for (int j = 0; j < phasesCount; j++)
648 {
649 phaseObject = phasesList.Get(j);
650 phaseObject.Get("Threshold", threshold);
651
652 phaseObject.Get("m_sPhaseModel", phaseModel);
653
654 if (phaseModels.Contains(phaseModel))
655 {
656 m_API.RemoveObjectArrayVariableMember(entitySource, path, "DamagePhases", j);
657 phasesList = entitySource.GetObjectArray("DamagePhases");
658 phasesCount = phasesList.Count();
659 j--;
660 continue; // duplicate model == duplicate phase, just delete it
661 }
662
663 phaseModels.Insert(phaseModel);
664
665 if (thresholds.Contains(threshold))
666 recalculate = true;
667 else
668 thresholds.Insert(threshold);
669 }
670
671 if (!recalculate)
672 continue;
673
674 destructionComponent = FindComponent(entitySource, "SCR_DestructionMultiPhaseComponent");
675 array<ref SCR_DamagePhaseData> damagePhases = {};
676 destructionComponent.Get("m_aDamagePhases", damagePhases);
677 float totalHealth = GetPhasesTotalHealth(destructionComponent, damagePhases);
678 float healthNormalized = 1;
679// float currentPhaseHealth;
680
681 for (int j = 0; j < phasesCount; j++)
682 {
683 path.Insert(new ContainerIdPathEntry("DamagePhases", j));
684 healthNormalized -= damagePhases[j].m_fPhaseHealth / totalHealth;
685 m_API.SetVariableValue(entitySource, path, "Threshold", healthNormalized.ToString());
686 path.Clear();
687 }
688 }
689
690 m_API.EndEntityAction();
691 }
692
693 //------------------------------------------------------------------------------------------------
694 void ClearArrayOfPhaseEffects(BaseContainer phaseObject, array<ref ContainerIdPathEntry> path, IEntitySource entitySource)
695 {
696 if (!phaseObject)
697 return;
698
699 BaseContainerList effectsList = phaseObject.GetObjectArray("m_aPhaseDestroySpawnObjects");
700 if (!effectsList)
701 return;
702
703 for (int i = effectsList.Count() - 1; i >= 0; --i)
704 {
705 m_API.RemoveObjectArrayVariableMember(entitySource, path, "m_aPhaseDestroySpawnObjects", 0);
706 }
707 }
708
709 //------------------------------------------------------------------------------------------------
710 [ButtonAttribute("6 FP")]
711 protected void FilterPrefabsButton()
712 {
713 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS, rootPath: "Prefabs");
714
715 array<string> componentClassNames = {};
716 componentClassNames.Insert("SCR_DestructionMultiPhaseComponent");
717
718 array<ResourceName> outResourceNames = {};
719 FilterByComponents(resourceNames, outResourceNames, componentClassNames, false, true);
720
721 resourceNames = {};
722 componentClassNames = {};
723 componentClassNames.Insert("ActionsManagerComponent");
724 FilterByComponents(outResourceNames, resourceNames, componentClassNames, true);
725
726 Print("" + resourceNames.Count() + " resources", LogLevel.NORMAL);
727 foreach (ResourceName resourceName : resourceNames)
728 {
729 if (resourceName.Contains("Glass") || resourceName.Contains("Lamp"))
730 continue;
731
732 int startIndex = resourceName.IndexOf("{") + 1;
733 int endIndex = resourceName.IndexOf("}");
734 int length = endIndex - startIndex;
735 Print(resourceName.Substring(startIndex, length));
736 }
737 }
738
739 //------------------------------------------------------------------------------------------------
740 [ButtonAttribute("7 SM")]
741 protected void ShowMassButton()
742 {
743 IEntitySource entitySource = m_API.GetSelectedEntity();
744 IEntity entity = m_API.SourceToEntity(entitySource);
745 float volume = MeshObjectVolumeCalculator.GetVolumeFromColliders(entity, EPhysicsLayerDefs.FireGeometry);
746 float density = CalculateDensity(entity);
747 Print(density * 1000 * volume);
748 }
749
750 //------------------------------------------------------------------------------------------------
751 [ButtonAttribute("8 CP")]
752 protected void ConvertPrefabsButton()
753 {
754 m_API.BeginEntityAction();
755 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS, rootPath: "Prefabs");
756
757 array<string> componentClassNames = {};
758 componentClassNames.Insert("SCR_DestructionMultiPhaseComponent");
759 //componentClassNames.Insert("");
760 array<ResourceName> outResourceNames = {};
761 //FilterByComponents(resourceNames, outResourceNames, componentClassNames);
762 FilterByClass(resourceNames, outResourceNames, "SCR_DestructibleEntity");
763
764 Resource resource;
765 BaseResourceObject baseResource;
766 IEntitySource source;
767 IEntityComponentSource componentSource;
768 BaseContainer defaultHitZone;
769 array<ref ContainerIdPathEntry> path = {};
770 bool deleteAfterFinalPhase = false;
771 SCR_EMaterialSoundTypeBreak materialSoundType;
772 float momentumToDamage;
773 foreach (ResourceName resourceName : outResourceNames)
774 {
775 resource = Resource.Load(resourceName);
776 if (!resource) // test
777 continue;
778
779 baseResource = resource.GetResource();
780 source = baseResource.ToEntitySource();
781
782 for (int j = source.GetComponentCount() - 1; j >= 0; j--)
783 {
784 componentSource = source.GetComponent(j);
785 if (componentSource.GetClassName() != DESTRUCTION_COMPONENT_CLASS)
786 continue;
787
788 break;
789 }
790
791 if (!componentSource)
792 continue;
793
794 if (source.GetClassName() != "SCR_DestructibleEntity")
795 {
796 Print("The following prefab is not SCR_DestructibleEntity type!");
797 Print("Cannot convert!");
798 Print(source.GetClassName());
799 Print(source.GetResourceName());
800 continue;
801 }
802
803 array<ref SCR_DamagePhaseData> damagePhases = {};
804 componentSource.Get("m_aDamagePhases", damagePhases);
805
806 array<ref SCR_BaseDestructionPhase> newPhases = {};
807 SCR_BaseDestructionPhase newPhase;
808 for (int j = damagePhases.Count() - 1; j >= 0; j--)
809 {
810 newPhase = new SCR_BaseDestructionPhase();
811 newPhase.m_sPhaseModel = damagePhases[j].m_PhaseModel;
812 newPhase.m_aPhaseDestroySpawnObjects = {};
813 CopySpawnObjectsArray(newPhase.m_aPhaseDestroySpawnObjects, damagePhases[j].m_PhaseDestroySpawnObjects);
814
815 //calculate HP and set the percentage
816 newPhases.Insert(newPhase);
817 }
818
819 // Reset path
820 path = {};
821
822 // Copy damage multipliers from hitzones
823 defaultHitZone = GetHitZone(source);
824 if (defaultHitZone)
825 {
826 float multiplier;
827 defaultHitZone.Get("Collision multiplier", multiplier);
828 m_API.SetVariableValue(source, path, "Collision multiplier", multiplier.ToString());
829
830 defaultHitZone.Get("Melee multiplier", multiplier);
831 m_API.SetVariableValue(source, path, "Melee multiplier", multiplier.ToString());
832
833 defaultHitZone.Get("Kinetic multiplier", multiplier);
834 m_API.SetVariableValue(source, path, "Kinetic multiplier", multiplier.ToString());
835
836 defaultHitZone.Get("Fragmentation multiplier", multiplier);
837 m_API.SetVariableValue(source, path, "Fragmentation multiplier", multiplier.ToString());
838
839 defaultHitZone.Get("Explosive multiplier", multiplier);
840 m_API.SetVariableValue(source, path, "Explosive multiplier", multiplier.ToString());
841
842 defaultHitZone.Get("Incendiary multiplier", multiplier);
843 m_API.SetVariableValue(source, path, "Incendiary multiplier", multiplier.ToString());
844
845 defaultHitZone.Get("BaseDamageMultiplier", multiplier);
846 m_API.SetVariableValue(source, path, "BaseDamageMultiplier", multiplier.ToString());
847
848 defaultHitZone.Get("DamageReduction", multiplier);
849 m_API.SetVariableValue(source, path, "DamageReduction", multiplier.ToString());
850
851 defaultHitZone.Get("DamageThreshold", multiplier);
852 m_API.SetVariableValue(source, path, "DamageThreshold", multiplier.ToString());
853 }
854 // End of copying multipliers
855
856 // Copy other relevant attributes
857 componentSource.Get("m_bDeleteAfterFinalPhase", deleteAfterFinalPhase);
858 m_API.SetVariableValue(source, path, "DestroyAtNoHealth", deleteAfterFinalPhase.ToString(true));
859
860 componentSource.Get("m_eMaterialSoundType", materialSoundType);
861 m_API.SetVariableValue(source, path, "m_eMaterialSoundType", materialSoundType.ToString());
862
863 componentSource.Get("m_fMomentumToDamageScale", momentumToDamage);
864 m_API.SetVariableValue(source, path, "m_fMomentumToDamageScale", momentumToDamage.ToString());
865 // End
866
867 // Setup first phase
868 int damagePhasesCount = damagePhases.Count();
869 IEntityComponentSource meshObject = FindComponent(source, "MeshObject");
870 ResourceName firstPhaseResourceName;
871 if (meshObject)
872 meshObject.Get("Object", firstPhaseResourceName);
873
874 m_API.CreateObjectVariableMember(source, path, "FirstDestructionPhase", "SCR_BaseDestructionPhase");
875
876 path.Insert(new ContainerIdPathEntry("FirstDestructionPhase"));
877 m_API.SetVariableValue(source, path, "m_sPhaseModel", firstPhaseResourceName);
878
879 if (damagePhasesCount > 0)
880 {
881 foreach (int k, SCR_BaseSpawnable spawnObject : damagePhases[0].m_PhaseDestroySpawnObjects)
882 {
883 spawnObject.CopyToSource(m_API, source, path, k, "m_aPhaseDestroySpawnObjects");
884 }
885 }
886 // End of first phase setup
887
888 // Get total health of all damage phases
889 float totalHealth = GetPhasesTotalHealth(componentSource, damagePhases);
890 float healthNormalized = 1;
891 float currentPhaseHealth;
892 componentSource.Get("m_fBaseHealth", currentPhaseHealth);
893
894 healthNormalized -= currentPhaseHealth / totalHealth; // This will define next phase health normalized
895
896 path = {};
897 m_API.SetVariableValue(source, path, "MaxHealth", totalHealth.ToString());
898 if (damagePhasesCount == 0) // making sure last phase is always set
899 {
900 m_API.CreateObjectVariableMember(source, path, "LastDestructionPhase", "SCR_BaseDestructionPhase");
901
902 array<ref SCR_BaseSpawnable> destroySpawnObjects = {};
903 componentSource.Get("m_DestroySpawnObjects", destroySpawnObjects);
904 path.Insert(new ContainerIdPathEntry("LastDestructionPhase"));
905
906 foreach (int j, SCR_BaseSpawnable spawnObject : destroySpawnObjects)
907 {
908 spawnObject.CopyToSource(m_API, source, path, j, "m_aPhaseDestroySpawnObjects");
909 }
910
911 m_API.SetVariableValue(source, path, "Threshold", "0");
912
913 path.Clear();
914 }
915
916 for (int j = 0; j < damagePhasesCount; j++)
917 {
918 if (j == damagePhasesCount - 1) // Last phase
919 {
920 // Change path to the current damage phase
921 m_API.CreateObjectVariableMember(source, path, "LastDestructionPhase", "SCR_BaseDestructionPhase");
922
923 path.Insert(new ContainerIdPathEntry("LastDestructionPhase"));
924 healthNormalized = 0;
925 }
926 else //Regular phase - not first, not last
927 {
928 // Create damage phase
929 m_API.CreateObjectArrayVariableMember(source, path, "DamagePhases", "SCR_BaseDestructionPhase", j);
930
931 // Change path to the current damage phase
932 path.Insert(new ContainerIdPathEntry("DamagePhases", j));
933 }
934
935 // Set variables of the damage phase
936 m_API.SetVariableValue(source, path, "m_sPhaseModel", damagePhases[j].m_PhaseModel);
937
938 m_API.SetVariableValue(source, path, "Threshold", healthNormalized.ToString());
939 healthNormalized -= damagePhases[j].m_fPhaseHealth / totalHealth; // This will define next phase health normalized
940
941 // Use effects of the next phase as exit particles
942 if (j + 1 < damagePhasesCount)
943 {
944 foreach (int k, SCR_BaseSpawnable spawnObject : damagePhases[j + 1].m_PhaseDestroySpawnObjects)
945 {
946 spawnObject.CopyToSource(m_API, source, path, k, "m_aPhaseDestroySpawnObjects");
947 }
948 }
949 else
950 {
951 array<ref SCR_BaseSpawnable> destroySpawnObjects = {};
952 componentSource.Get("m_DestroySpawnObjects", destroySpawnObjects);
953
954 foreach (int k, SCR_BaseSpawnable spawnObject : destroySpawnObjects)
955 {
956 spawnObject.CopyToSource(m_API, source, path, k, "m_aPhaseDestroySpawnObjects");
957 }
958 }
959
960 path.Clear();
961 }
962
963 // Deletes the now no longer used destruction component
964 path.Insert(new ContainerIdPathEntry("SCR_DestructionMultiPhaseComponent"));
965 m_API.SetVariableValue(source, path, "Enabled", "0");
966 path.Clear();
967
968 path.Insert(new ContainerIdPathEntry("RplComponent"));
969 m_API.SetVariableValue(source, path, "Enabled", "0");
970 path.Clear();
971 }
972
973 m_API.EndEntityAction();
974 }
975
976 //------------------------------------------------------------------------------------------------
977 BaseContainer GetHitZone(IEntitySource source)
978 {
979 IEntityComponentSource componentSource;
980 for (int i = source.GetComponentCount() - 1; i >= 0; i--)
981 {
982 componentSource = source.GetComponent(i);
983 if (componentSource.GetClassName() != DESTRUCTION_COMPONENT_CLASS)
984 continue;
985
986 break;
987 }
988
989 BaseContainerList hitZonesList = componentSource.GetObjectArray("Additional hit zones");
990 if (!hitZonesList || hitZonesList.Count() == 0)
991 return null;
992
993 return hitZonesList[0];
994 }
995
996 //------------------------------------------------------------------------------------------------
997 float GetPhasesTotalHealth(IEntityComponentSource componentSource, array<ref SCR_DamagePhaseData> phases)
998 {
999 float health;
1000 componentSource.Get("m_fBaseHealth", health);
1001
1002 foreach (SCR_DamagePhaseData phase : phases)
1003 {
1004 health += phase.m_fPhaseHealth;
1005 }
1006
1007 return health;
1008 }
1009
1010 //------------------------------------------------------------------------------------------------
1011 IEntityComponentSource FindComponent(IEntitySource source, string componentType)
1012 {
1013 for (int i = source.GetComponentCount() - 1; i >= 0; i--)
1014 {
1015 if (source.GetComponent(i).GetClassName() == componentType)
1016 return source.GetComponent(i);
1017 }
1018
1019 return null;
1020 }
1021
1022 //------------------------------------------------------------------------------------------------
1023 void CopySpawnObjectsArray(out array<ref SCR_BaseSpawnable> to, array<ref SCR_BaseSpawnable> from)
1024 {
1025 foreach (SCR_BaseSpawnable object : from)
1026 {
1027 to.Insert(object);
1028 }
1029 }
1030
1031 //------------------------------------------------------------------------------------------------
1032 protected bool HasComponent(BaseResourceObject baseResource, string componentClassName, bool checkEnabled)
1033 {
1034 IEntitySource source = baseResource.ToEntitySource();
1035 string currentComponentClassName;
1036 for (int i = source.GetComponentCount() - 1; i >= 0; i--)
1037 {
1038 currentComponentClassName = source.GetComponent(i).GetClassName();
1039 if (currentComponentClassName == componentClassName)
1040 {
1041 if (!checkEnabled)
1042 return true;
1043 else
1044 {
1045 bool enabled;
1046 source.GetComponent(i).Get("Enabled", enabled);
1047
1048 return enabled;
1049 }
1050 }
1051 }
1052
1053 return false;
1054 }
1055
1056 //------------------------------------------------------------------------------------------------
1057 protected void FilterByClass(notnull array<ResourceName> resourceNames, notnull out array<ResourceName> outResourceNames, string className)
1058 {
1059 Resource resource;
1060 BaseResourceObject baseResource;
1061 IEntitySource source;
1062 foreach (ResourceName resourceName : resourceNames)
1063 {
1064 resource = Resource.Load(resourceName);
1065 if (!resource)
1066 continue;
1067
1068 baseResource = resource.GetResource();
1069 if (!baseResource)
1070 continue;
1071
1072 source = baseResource.ToEntitySource();
1073 if (source && source.GetClassName() == className)
1074 outResourceNames.Insert(resourceName);
1075 }
1076 }
1077
1078 //------------------------------------------------------------------------------------------------
1079 protected void FilterByComponents(notnull array<ResourceName> resourceNames, notnull out array<ResourceName> outResourceNames, notnull array<string> componentClassNames, bool removeWithComponent = false, bool checkEnabled = false)
1080 {
1081 Resource resource;
1082 BaseResourceObject baseResource;
1083 IEntitySource entitySource;
1084 IEntityComponentSource component;
1085
1086 for (int i = resourceNames.Count() - 1; i >= 0; i--)
1087 {
1088 resource = Resource.Load(resourceNames[i]);
1089 baseResource = resource.GetResource();
1090 if (!baseResource)
1091 {
1092 resourceNames.Remove(i);
1093 continue;
1094 }
1095
1096 entitySource = baseResource.ToEntitySource();
1097
1098 if (!entitySource || entitySource.GetNumChildren() > 0 || entitySource.GetParent())
1099 {
1100 resourceNames.Remove(i);
1101 continue;
1102 }
1103
1104 for (int j = componentClassNames.Count() - 1; j >= 0; j--)
1105 {
1106 if (HasComponent(baseResource, componentClassNames[j], checkEnabled))
1107 {
1108 if (removeWithComponent)
1109 continue;
1110 else
1111 outResourceNames.Insert(resourceNames[i]);
1112 }
1113 else
1114 {
1115 if (removeWithComponent)
1116 outResourceNames.Insert(resourceNames[i]);
1117 else
1118 continue;
1119 }
1120 }
1121 }
1122 }
1123
1124 //------------------------------------------------------------------------------------------------
1125 [ButtonAttribute("9 AI")]
1126 protected void AssignIndicesButton()
1127 {
1128 Debug.BeginTimeMeasure();
1129
1130 array<ResourceName> resourceNames = SCR_WorkbenchHelper.SearchWorkbenchResources(EXTENSIONS);
1131
1132 Resource resource;
1133 BaseResourceObject baseResource;
1134 IEntitySource source;
1135 IEntitySource runtimeSource;
1136 string componentClassName;
1137 IEntity entity;
1138 int componentCount;
1139 int j, k, l;
1140 bool hasDestruction, hasPhysics;
1141 int componentClassesCount = DESTRUCTIBLE_COMPONENT_CLASSES.Count();
1142 float density;
1143 float volume;
1144 float mass;
1145 int pairsCount = m_aPairs.Count();
1146 string indexName;
1147
1148 IEntityComponentSource physicsSource;
1149
1150 array<ref ContainerIdPathEntry> entryPath = {ContainerIdPathEntry(PHYSICS_COMPONENT)};
1151
1152 m_API.BeginEntityAction();
1153
1154 for (int i = resourceNames.Count() - 1; i >= 0; i--)
1155 {
1156 hasDestruction = false;
1157 hasPhysics = false;
1158 resource = Resource.Load(resourceNames[i]);
1159 baseResource = resource.GetResource();
1160 if (!baseResource)
1161 {
1162 resourceNames.Remove(i);
1163 continue;
1164 }
1165
1166 source = baseResource.ToEntitySource();
1167 componentCount = source.GetComponentCount();
1168 for (j = componentCount - 1; j >= 0; j--)
1169 {
1170 componentClassName = source.GetComponent(j).GetClassName();
1171 if (!hasPhysics && componentClassName == PHYSICS_COMPONENT)
1172 hasPhysics = true;
1173
1174 for (k = componentClassesCount - 1; k >= 0; k--)
1175 {
1176 if (componentClassName == DESTRUCTIBLE_COMPONENT_CLASSES[k])
1177 {
1178 physicsSource = source.GetComponent(j);
1179 hasDestruction = true;
1180 break;
1181 }
1182 }
1183
1184 if (hasPhysics && hasDestruction)
1185 break;
1186 }
1187
1188 if (!hasDestruction || !hasPhysics)
1189 continue;
1190
1191 runtimeSource = m_API.CreateEntity(resourceNames[i], "", 0, null, vector.Zero, vector.Zero);
1192 if (!runtimeSource)
1193 continue;
1194
1195 entity = m_API.SourceToEntity(runtimeSource);
1196
1197 volume = MeshObjectVolumeCalculator.GetVolumeFromColliders(entity, EPhysicsLayerDefs.FireGeometry);
1198
1199 density = CalculateDensity(entity);
1200
1201 mass = density * 1000 * volume; // 1000 g/cm3 -> kg/m3
1202
1203 for (l = 0; l < pairsCount; l++)
1204 {
1205 if (mass < m_aPairs[l].m_fMass)
1206 {
1207 indexName = m_aPairs[l].m_sIndexName;
1208 break;
1209 }
1210 else if (l == pairsCount - 1) // last pair, mass is bigger than last entry -> set max response index
1211 {
1212 indexName = SCR_DamageManagerComponent.MAX_DESTRUCTION_RESPONSE_INDEX_NAME;
1213 }
1214 }
1215
1216 //Edit prefab here
1217 m_API.SetVariableValue(source, entryPath, "ResponseIndex", indexName);
1218
1219 m_API.DeleteEntity(runtimeSource);
1220 }
1221
1222 m_API.EndEntityAction();
1223
1224 Debug.EndTimeMeasure("Assigning done");
1225 }
1226
1227 //------------------------------------------------------------------------------------------------
1228 [ButtonAttribute("10 SBDE")]
1229 protected void SetBuildingDestructionEffectButton()
1230 {
1231 // TODO
1232 }
1233}
1234
1236class SCR_MassResponseIndexPair
1237{
1238 [Attribute(desc: "Objects with smaller weight will have this index. [kg]")]
1239 float m_fMass;
1240
1241 [Attribute(desc: "Refer to physics settings of the project.")]
1242 int m_iIndex;
1243
1244 [Attribute(desc: "Refer to physics settings of the project.")]
1245 string m_sIndexName;
1246}
1247#endif // WORKBENCH
string path
void ContainerIdPathEntry(string propertyName, int index=-1)
Definition worldEditor.c:30
SCR_HybridPhysicsComponentClass m_fMass
Class for storing physics setup info for SCR_HybridPhysicsComponent.
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
ResourceName resourceName
Definition SCR_AIGroup.c:66
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum EVehicleType IEntity
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
SCR_FieldOfViewSettings Attribute
proto bool Contains(T value)
void Debug()
Definition Types.c:327