Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_DestructionFractalComponent.c
Go to the documentation of this file.
2{
3 FORCE_NONE = -1,
6}
8[ComponentEditorProps(category: "GameScripted/Destruction", description: "Fractal destruction component. For objects that should shatter/splinter etc")]
9class SCR_DestructionFractalComponentClass: SCR_DestructionDamageManagerComponentClass
11 [Attribute(ResourceName.Empty, UIWidgets.ResourceNamePicker, "Particle effect to play when a fragment is destroyed", "ptc", category: "Destruction Fractal")]
12 ResourceName m_ParticleDestroyFragment;
14 [Attribute("", UIWidgets.Object, "Debris settings to use when a fragment is destroyed and debris is spawned", category: "Destruction Fractal")]
15 ref SCR_FragmentDebris m_DebrisDestroyFragment;
17 [Attribute("10", UIWidgets.Slider, "Health value of each fragment", "0.01 100000 0.01", category: "Destruction Fractal")]
18 float m_fFragmentHealth;
20 [Attribute("1", UIWidgets.Slider, "Whether to destroy the fragment at the impact point when damage leads to fracturing of the object", category: "Destruction Fractal")]
21 bool m_bDestroyFragmentOnFracture;
23 [Attribute("0", UIWidgets.CheckBox, "If true, the object will be deleted after the last fragment has been destroyed", category: "Destruction Fractal")]
24 bool m_bDeleteAfterFinalFragment;
25
26 [Attribute("1", UIWidgets.Slider, "Whether structural integrity is enabled (fragments that have no anchor fragments to hold on to fall as well when a nearby fragment is destroyed)", category: "Destruction Fractal")]
27 bool m_bEnableStructuralIntegrity;
28
29 [Attribute("", UIWidgets.Object, "List of fractal setup variations (chosen using position as seed)", category: "Destruction Fractal")]
30 ref array<ref SCR_FractalVariation> m_FractalVariants;
31}
32
34class SCR_DestructionFractalComponent: SCR_DestructionDamageManagerComponent
35{
36 #ifdef ENABLE_DESTRUCTION
37 protected SCR_FractalVariation m_UsedFractalData;
38 protected static ref array<SCR_FragmentEntity> s_FragmentList = {};
39 protected static ref array<SCR_FragmentEntity> s_FragmentToCheckList = {};
40 protected static ref array<SCR_FragmentEntity> s_FragmentCheckedList = {};
41
42 protected static ref s_RandomGenerator = new RandomGenerator();
43
44 #ifdef WORKBENCH
45 [Attribute("0", UIWidgets.CheckBox, "Check to generate fragment hierarchies for each fractal variant", category: "EDITOR: Destruction Fractal")]
46 protected bool GenerateFragmentHierarchies;
47
48 [Attribute("0", UIWidgets.CheckBox, "Check to toggle display of visualizers in the World Editor", category: "EDITOR: Destruction Fractal")]
49 protected bool ToggleVisualizers;
50
51 static bool s_WBDisplayVisualizers = false;
52 static GenericEntity s_WBVisualizeEntity = null;
53 static ref array<SCR_PreviewEntity> s_WBFragmentList = {};
54
55 //------------------------------------------------------------------------------------------------
56 protected static void ClearVisualizers()
57 {
58 s_WBVisualizeEntity = null;
59 SCR_PreviewEntity fragVis;
60 foreach (SCR_PreviewEntity preview : s_WBFragmentList)
61 {
62 fragVis = s_WBFragmentList[i];
63 delete fragVis;
64 }
65 s_WBFragmentList.Clear();
66 }
67
68 //------------------------------------------------------------------------------------------------
69 override bool _WB_OnKeyChanged(IEntity owner, BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent)
70 {
71 GenericEntity genOwner = GenericEntity.Cast(owner);
72 if (!genOwner)
73 return true;
74
75 WorldEditorAPI api = genOwner._WB_GetEditorAPI();
76 if (!api || api.UndoOrRedoIsRestoring())
77 return false;
78
79 switch (key)
80 {
81 case "GenerateFragmentHierarchies":
82 {
83 src.ClearVariable("GenerateFragmentHierarchies");
84
85 Print("SCR_DestructionFractalComponent: Generating fragment hierarchies...", LogLevel.NORMAL);
86
87 BaseContainerList srcFractalVariantList = src.GetObjectArray("m_FractalVariants");
88 int numVariants = m_FractalVariants.Count();
89 for (int v = 0; v < numVariants; v++)
90 {
91 BaseContainer srcFractalVariant = srcFractalVariantList.Get(v);
92 SCR_FractalVariation fractalVariant = m_FractalVariants[v];
93 fractalVariant.m_Hierarchy = new SCR_FragmentHierarchy;
94 fractalVariant.m_Hierarchy.GenerateHierarchy(fractalVariant);
95 srcFractalVariant.Set("m_Hierarchy", fractalVariant.m_Hierarchy);
96 }
97
98 BaseContainerTools.WriteToInstance(this, src);
99 genOwner._WB_GetEditorAPI().UpdateSelectionGui();
100 break;
101 }
102
103 case "ToggleVisualizers":
104 {
105 src.ClearVariable("ToggleVisualizers");
106 BaseContainerTools.WriteToInstance(this, src);
107
108 s_WBDisplayVisualizers = !s_WBDisplayVisualizers;
109 if (!s_WBDisplayVisualizers)
110 ClearVisualizers();
111
112 genOwner._WB_GetEditorAPI().UpdateSelectionGui();
113
114 break;
115 }
116
117 // The code below stores the newly set model to the MeshObject component on the entity
118// case "m_ModelNormal":
119// {
120// // Model has changed in a variation, check if it's the one we are using
121// int currentIndex = m_FractalVariants.Find(m_UsedFractalData);
122// if (currentIndex == -1)
123// break;
124//
125// // Get the fractal destruction component and mesh object component from parent entity (if present)
126// BaseContainer fractalComp = null;
127// BaseContainer meshComp = null;
128// BaseContainer ownerEnt = null;
129// int contCount = ownerContainers.Count();
130// if (contCount > 0)
131// fractalComp = ownerContainers.Get(contCount - 1);
132// if (contCount > 1)
133// ownerEnt = ownerContainers.Get(contCount - 2);
134// if (ownerEnt)
135// {
136// BaseContainerList ownerEntCompList = ownerEnt.GetObjectArray("components");
137// if (ownerEntCompList)
138// {
139// for (int i = 0, num = ownerEntCompList.Count(); i < num; i++)
140// {
141// BaseContainer cont = ownerEntCompList.Get(i);
142// if (cont.GetClassName() == "MeshObject")
143// {
144// meshComp = cont;
145// break;
146// }
147// }
148// }
149// }
150//
151// if (!meshComp || !fractalComp)
152// break;
153//
154// // Get the index of the fractal variant in the variants container
155// BaseContainerList fractalVariantsList = fractalComp.GetObjectArray("m_FractalVariants");
156// if (!fractalVariantsList)
157// break;
158//
159// int currentSrcIndex = -1;
160// for (int i = 0, num = fractalVariantsList.Count(); i < num; i++)
161// {
162// BaseContainer variant = fractalVariantsList.Get(i);
163// if (variant != src)
164// continue;
165// currentSrcIndex = i;
166// break;
167// }
168//
169// if (currentSrcIndex != currentIndex)
170// break;
171//
172// // Get the new model
173// ResourceName newModel = ResourceName.Empty;
174// if (!src.Get(key, newModel))
175// break;
176//
177// // Set the new model and store it to the instance
178// meshComp.Set("Object", newModel);
179// BaseContainerTools.WriteToInstance(this, meshComp);
180// UpdateModel(SCR_EFractalDestructionForceModel.FORCE_UNFRACTURED);
181//
182// break;
183// }
184
185 default:
186 {
187 // Get our fractal component source
188 BaseContainer srcFractalComp = null;
189 if (src.GetClassName() == "SCR_DestructionFractalComponent")
190 {
191 srcFractalComp = src;
192 }
193 else
194 {
195 for (int c = 0, srcCount = ownerContainers.Count(); c < srcCount; c++)
196 {
197 BaseContainer compSrc = ownerContainers.Get(c);
198 if (compSrc.GetClassName() != "SCR_DestructionFractalComponent")
199 continue;
200
201 srcFractalComp = compSrc;
202 break;
203 }
204 }
205
206 // Now go through each fractal variant and validate hierarchies
207 if (srcFractalComp)
208 {
209 BaseContainerList srcFVariantList = srcFractalComp.GetObjectArray("m_FractalVariants");
210 int numVariants = m_FractalVariants.Count();
211 bool needResave = false;
212 for (int v = 0; v < numVariants; v++)
213 {
214 BaseContainer srcFVariant = srcFVariantList.Get(v);
215 if (!srcFVariant)
216 continue;
217
218 BaseContainer srcHier = srcFVariant.GetObject("m_Hierarchy");
219 if (!srcHier)
220 continue;
221
222 SCR_FractalVariation fracVariant = m_FractalVariants[v];
223 if (fracVariant.m_Hierarchy && fracVariant.m_Hierarchy.ValidateHierarchy(fracVariant, srcHier, v))
224 needResave = true;
225 }
226
227 if (needResave)
228 {
229 BaseContainerTools.WriteToInstance(this, srcFractalComp);
230 genOwner._WB_GetEditorAPI().UpdateSelectionGui();
231 }
232 }
233
234 ClearVisualizers();
235 break;
236 }
237 }
238
239 return true;
240 }
241
242 //------------------------------------------------------------------------------------------------
246 SCR_PreviewEntity CreateFragmentVisualizer(int index)
247 {
248 bool isAnchor = false;
250 Resource resource = Resource.Load(m_UsedFractalData.GetFragmentModel(index, isAnchor));
251 VObject asset = resource.GetResource().ToVObject();
252 string materials[256];
253 int numMats = asset.GetMaterials(materials);
254 string remap = "";
255 for (int m = 0; m < numMats; m++)
256 {
257 remap += "$remap '" + materials[m] + "' '{639855E4E1F52285}Assets/Editor/PlacingPreview/Preview_Scriptable.emat';";
258 }
259
260 const int intensity = 200;
261 const int intensityOther = 16;
262 int mod = index % 6;
263 if (mod == 0)
264 fragVis.m_Color = ARGB(255, intensityOther, intensityOther, intensity);
265 else
266 if (mod == 1)
267 fragVis.m_Color = ARGB(255, intensityOther, intensity, intensityOther);
268 else
269 if (mod == 2)
270 fragVis.m_Color = ARGB(255, intensity, intensityOther, intensityOther);
271 else
272 if (mod == 3)
273 fragVis.m_Color = ARGB(255, intensity, intensityOther, intensity);
274 else
275 if (mod == 4)
276 fragVis.m_Color = ARGB(255, intensityOther, intensity, intensity);
277 else
278 if (mod == 5)
279 fragVis.m_Color = ARGB(255, intensity, intensity, intensityOther);
280
281 fragVis.SetObject(asset, remap);
282 fragVis.SetFlags(EntityFlags.ACTIVE);
283 m_Owner.AddChild(fragVis, -1);
284 fragVis.Update();
285
286 return fragVis;
287 }
288
289 //------------------------------------------------------------------------------------------------
290 override int _WB_GetAfterWorldUpdateSpecs(IEntity owner, IEntitySource src)
291 {
292 return EEntityFrameUpdateSpecs.CALL_WHEN_ENTITY_VISIBLE;
293 }
294
295 //------------------------------------------------------------------------------------------------
296 override void _WB_AfterWorldUpdate(IEntity owner, float timeSlice)
297 {
298 GenericEntity gEntity = GenericEntity.Cast(owner);
299 if (!s_WBDisplayVisualizers)
300 {
301 if (s_WBVisualizeEntity)
302 ClearVisualizers();
303
304 return;
305 }
306
307 // Are we selected as the main entity?
308 if (!gEntity._WB_GetEditorAPI().IsEntitySelectedAsMain(gEntity))
309 {
310 if (s_WBVisualizeEntity == gEntity)
311 ClearVisualizers();
312
313 return;
314 }
315
316 vector textMat[4], camMat[4];
317 gEntity.GetWorld().GetCurrentCamera(camMat);
318 gEntity.GetWorld().GetCurrentCamera(textMat);
319 vector camDir = camMat[2];
320 vector camPos = camMat[3];
321 vector entPos = gEntity.GetOrigin();
322 vector entCenter = SCR_Global.GetEntityCenterWorld(gEntity);
323 vector entMins, entMaxs;
324 gEntity.GetWorldBounds(entMins, entMaxs);
325 vector entTop = Vector((entMaxs[0] + entMins[0]) * 0.5, entMaxs[1], (entMaxs[2] + entMins[2]) * 0.5);
326 float distScale = Math.Clamp(vector.Distance(camPos, entCenter) * 0.15, 0.1, 3);
327
328 // A different entity was selected before, so clear visualizers and make our own
329 if (s_WBVisualizeEntity != gEntity)
330 {
331 ClearVisualizers();
332 s_WBVisualizeEntity = gEntity;
333
334 if (!m_UsedFractalData)
335 InitDestruction();
336
337 if (!m_UsedFractalData)
338 return;
339
340 int numFrags = m_UsedFractalData.CountFragments();
341 for (int i = 0; i < numFrags; i++)
342 {
343 s_WBFragmentList.Insert(CreateFragmentVisualizer(i));
344 }
345 }
346
347 // No fractal data, so display text indicating so
348 if (!m_UsedFractalData)
349 {
350 textMat[3] = entTop + vector.Up * 0.25;
351 CreateSimpleText("NO FRACTAL VARIANT", textMat, 0.17 * distScale, ARGB(255, 255, 0, 0), ShapeFlags.ONCE | ShapeFlags.TRANSP | ShapeFlags.NOZBUFFER, null, 0.7, true, ARGB(128, 0, 0, 0));
352
353 return;
354 }
355
357 int highlighted = -1;
358 int numFrags = m_UsedFractalData.CountFragments();
359 for (int i = 0; i < numFrags; i++)
360 {
361 SCR_PreviewEntity fragVis = s_WBFragmentList[i];
362 bool isAnchor = m_UsedFractalData.GetFragmentIndexIsAnchor(i);
363 vector fragCenter = SCR_Global.GetEntityCenterWorld(fragVis);
364 vector fragToCamDir = (fragCenter - camPos).Normalized();
365 textMat[3] = fragCenter;
366
367 int textColor = ARGB(255, 255, 255, 255);
368 int bgColor = ARGB(128, 0, 0, 0);
369 float textSize = 0.12;
370 if (highlighted == -1 && vector.Dot(fragToCamDir, highlightDir) > 0.9997)
371 {
372 highlighted = i;
373 textColor = ARGB(255, 0, 255, 0);
374 bgColor = ARGB(200, 0, 32, 0);
375 textSize = 0.2;
376 fragVis.ClearFlags(EntityFlags.VISIBLE, false);
377
378 if (m_UsedFractalData.m_Hierarchy)
379 {
380 SCR_FragmentLinkage link = m_UsedFractalData.m_Hierarchy.GetFragmentLinkage(i);
381 if (link)
382 {
383 vector from = fragCenter + vector.Up * distScale * -0.2;
384 int numLinked = link.m_aOtherIndexes.Count();
385 for (int n = 0; n < numLinked; n++)
386 {
387 int otherIndex = link.m_aOtherIndexes[n];
388 if (otherIndex < 0 || otherIndex >= numFrags || otherIndex == i)
389 continue;
390
391 SCR_PreviewEntity otherFragVis = s_WBFragmentList[otherIndex];
392 bool isOtherAnchor = m_UsedFractalData.GetFragmentIndexIsAnchor(otherIndex);
393 vector fragOtherCenter = SCR_Global.GetEntityCenterWorld(otherFragVis);
394
395 int arrowsColor = ARGB(255, 255, 0, 0);
396 if (isOtherAnchor || isAnchor)
397 arrowsColor = ARGB(255, 0, 128, 255);
398
399 int numArrows = Math.Ceil(vector.Distance(fragCenter, fragOtherCenter) * 10 / distScale);
400 if (numArrows == 0)
401 numArrows = 1;
402
403 Shape.Create(ShapeType.LINE, arrowsColor, ShapeFlags.ONCE | ShapeFlags.TRANSP | ShapeFlags.NOZBUFFER, from, fragOtherCenter);
404 }
405
406 CreateCircle(from, camDir, 0.02 * distScale, ARGB(255, 128, 128, 128), 12, ShapeFlags.ONCE | ShapeFlags.TRANSP | ShapeFlags.NOZBUFFER);
407 }
408 }
409 }
410 else
411 {
412 fragVis.SetFlags(EntityFlags.VISIBLE, false);
413 }
414
415 textMat[3] = textMat[3] + camMat[1] * textSize * distScale * -0.5;
416 CreateSimpleText(i.ToString(), textMat, textSize * distScale, textColor, ShapeFlags.ONCE | ShapeFlags.TRANSP | ShapeFlags.NOZBUFFER, null, 1, true, bgColor);
417 if (isAnchor)
418 {
419 textMat[3] = textMat[3] - vector.Up * distScale * textSize * 1.6;
420 CreateSimpleText("ANCHOR", textMat, textSize * distScale, ARGB(255, 255, 64, 64), ShapeFlags.ONCE | ShapeFlags.TRANSP | ShapeFlags.NOZBUFFER, null, 1, true, bgColor);
421 }
422 }
423 }
424 #endif // WORKBENCH
425
426 //------------------------------------------------------------------------------------------------
428 bool GetFractured()
429 {
430 return GetDestroyed();
431 }
432
433 //------------------------------------------------------------------------------------------------
434 // \return how many fragments are left
435 int CountFragments()
436 {
437 int numFragments = 0;
438
439 IEntity child = m_Owner.GetChildren();
440 while (child)
441 {
442 SCR_FragmentEntity fragment = SCR_FragmentEntity.Cast(child);
443 child = child.GetSibling();
444 if (fragment)
445 numFragments++;
446 }
447
448 return numFragments;
449 }
450
451 //------------------------------------------------------------------------------------------------
452 // Fills the input list with fragments and returns the maximum amount
455 int FillCompleteOrderedFragmentList(notnull array<SCR_FragmentEntity> fragmentList)
456 {
457 fragmentList.Clear();
458 int maxFragments = m_UsedFractalData.CountFragments();
459 for (int i = 0; i < maxFragments; i++)
460 {
461 fragmentList.Insert(null);
462 }
463
464 IEntity child = m_Owner.GetChildren();
465 while (child)
466 {
467 SCR_FragmentEntity fragment = SCR_FragmentEntity.Cast(child);
468 child = child.GetSibling();
469 if (fragment)
470 fragmentList[fragment.GetIndex()] = fragment;
471 }
472
473 return fragmentList.Count();
474 }
475
476 //------------------------------------------------------------------------------------------------
477 // Fills the input list with fragments and returns the amount
480 int FillFragmentList(notnull array<SCR_FragmentEntity> fragmentList)
481 {
482 fragmentList.Clear();
483
484 IEntity child = m_Owner.GetChildren();
485 while (child)
486 {
487 SCR_FragmentEntity fragment = SCR_FragmentEntity.Cast(child);
488 child = child.GetSibling();
489 if (fragment)
490 fragmentList.Insert(fragment);
491 }
492
493 return fragmentList.Count();
494 }
495
496 //------------------------------------------------------------------------------------------------
497 // Fills the input list with fragments' indexes and returns the amount
500 int FillFragmentIndexList(array<int> fragmentIndexList)
501 {
502 fragmentIndexList.Clear();
503
504 IEntity child = m_Owner.GetChildren();
505 while (child)
506 {
507 SCR_FragmentEntity fragment = SCR_FragmentEntity.Cast(child);
508 child = child.GetSibling();
509 if (fragment)
510 fragmentIndexList.Insert(fragment.GetIndex());
511 }
512
513 return fragmentIndexList.Count();
514 }
515
516 //------------------------------------------------------------------------------------------------
518 SCR_FractalVariation GetCurrentFractalVariant()
519 {
520 return m_UsedFractalData;
521 }
522
523 //------------------------------------------------------------------------------------------------
525 SCR_FractalVariation GetRandomFractalVariant()
526 {
527 int numVariants = m_FractalVariants.Count();
528 if (numVariants == 0)
529 return null;
530
531 int seed = 0;
532 vector pos;
533 IEntity parent = SCR_Global.GetMainParent(m_Owner);
534 if (parent) // We are parented, so use main parent position plus child count (as we will always be first child) for the seed
535 {
536 pos = parent.GetOrigin();
537 seed = SCR_Global.CountChildren(m_Owner.GetParent()) * 11111;
538 }
539 else // Just use our position
540 {
541 pos = m_Owner.GetOrigin();
542 }
543
544 int x = Math.Floor(pos[0] * 1000);
545 int z = Math.Floor(pos[2] * 1000);
546 x = x % 10000;
547 z = z % 10000;
548 seed += x + z;
549 s_RandomGenerator.Randomize(seed); // Set randomizer to seed based on position
550
551 int randomVariant = s_RandomGenerator.RandomInt(0, numVariants);
552 SCR_FractalVariation variant = m_FractalVariants.Get(randomVariant);
553
554 return variant;
555 }
556
557 //------------------------------------------------------------------------------------------------
559 void DeleteFragments()
560 {
561 if (!m_Owner)
562 return;
563
564 IEntity child = m_Owner.GetChildren();
565 while (child)
566 {
567 SCR_FragmentEntity fragment = SCR_FragmentEntity.Cast(child);
568 child = child.GetSibling();
569 if (fragment)
570 delete fragment;
571 }
572 }
573
574 //------------------------------------------------------------------------------------------------
578 SCR_FragmentEntity FindFragment(int index)
579 {
580 IEntity child = m_Owner.GetChildren();
581 while (child)
582 {
583 SCR_FragmentEntity fragment = SCR_FragmentEntity.Cast(child);
584 child = child.GetSibling();
585 if (fragment.GetIndex() == index)
586 return fragment;
587 }
588
589 return null;
590 }
591
592 //------------------------------------------------------------------------------------------------
596 protected SCR_FragmentEntity CreateFragment(int index)
597 {
598 bool isAnchor = false;
599 ResourceName assetPath = m_UsedFractalData.GetFragmentModel(index, isAnchor);
600 if (assetPath == ResourceName.Empty)
601 return null;
602
604 fragment.Initialize(this, index, m_fFragmentHealth, assetPath);
605
606 return fragment;
607 }
608
609 //------------------------------------------------------------------------------------------------
612 protected void CreateFragments(bool addToTraceIgnoreList = false)
613 {
614 if (!m_UsedFractalData)
615 return;
616
617 if (addToTraceIgnoreList)
618 SCR_Global.g_TraceFilterList.Clear();
619
620 int num = m_UsedFractalData.CountFragments();
621 for (int i = 0; i < num; i++)
622 {
623 SCR_FragmentEntity fragment = CreateFragment(i);
624 if (addToTraceIgnoreList && fragment)
625 SCR_Global.g_TraceFilterList.Insert(fragment);
626 }
627 }
628
629 //------------------------------------------------------------------------------------------------
636 protected void UpdateStructuralIntegrity(SCR_FragmentEntity fromFragment, EDamageType damageType, float damage, vector hitPosition, vector hitDirection)
637 {
638 // First get ordered list of existing fragments
639 int numFragments = FillCompleteOrderedFragmentList(s_FragmentList);
640
641 // Exclude the 'from' fragment from the list
642 s_FragmentList[fromFragment.GetIndex()] = null;
643
644 // If we don't have a hierarchy, ignore
645 if (!m_UsedFractalData.m_Hierarchy)
646 return;
647
648 // Get the linkage node for the 'from' fragment
649 SCR_FragmentLinkage fromLinkage = m_UsedFractalData.m_Hierarchy.GetFragmentLinkage(fromFragment.GetIndex());
650 if (!fromLinkage)
651 return;
652
653 // Add all directly linked fragments to the 'to check' list
654 s_FragmentToCheckList.Clear();
655 for (int i = fromLinkage.m_aOtherIndexes.Count() - 1; i >= 0; i--)
656 {
657 SCR_FragmentEntity neighborFragment = s_FragmentList[fromLinkage.m_aOtherIndexes[i]];
658 if (!neighborFragment)
659 continue;
660
661 s_FragmentToCheckList.Insert(neighborFragment);
662 }
663
664 // Now go through each fragment in the 'to check' list and find whether it is connected to an anchor
665 while (!s_FragmentToCheckList.IsEmpty())
666 {
667 SCR_FragmentEntity checkFragment = s_FragmentToCheckList[0];
668 s_FragmentToCheckList.RemoveItem(checkFragment);
669
670 // Check if anchored, if not remove from lists, add any connected fragments to the check list and queue its deletion
671 s_FragmentCheckedList.Clear();
672 if (!CheckFragmentAnchored(checkFragment, numFragments))
673 {
674 s_FragmentList[checkFragment.GetIndex()] = null;
675
676 SCR_FragmentLinkage linkage = m_UsedFractalData.m_Hierarchy.GetFragmentLinkage(checkFragment.GetIndex());
677 if (linkage)
678 {
679 int numOther = linkage.m_aOtherIndexes.Count();
680 for (int i = 0; i < numOther; i++)
681 {
682 int otherIndex = linkage.m_aOtherIndexes[i];
683 if (otherIndex < 0 || otherIndex >= numFragments)
684 continue;
685
686 SCR_FragmentEntity neighborFragment = s_FragmentList[otherIndex];
687 if (!neighborFragment)
688 continue;
689
690 if (s_FragmentToCheckList.Find(neighborFragment) == -1)
691 s_FragmentToCheckList.Insert(neighborFragment);
692 }
693 }
694
695 checkFragment.QueueDestroy(damageType, damage, hitPosition, hitDirection, false);
696 }
697 }
698 }
699
700 //------------------------------------------------------------------------------------------------
704 private bool CheckFragmentAnchored(SCR_FragmentEntity fragment, int numFragments)
705 {
706 if (s_FragmentCheckedList.Find(fragment) >= 0)
707 return false;
708
709 s_FragmentCheckedList.Insert(fragment);
710
711 SCR_FragmentLinkage linkage = m_UsedFractalData.m_Hierarchy.GetFragmentLinkage(fragment.GetIndex());
712 if (!linkage)
713 return false;
714
715 if (linkage.m_bIsAnchor)
716 return true;
717
718 int numOther = linkage.m_aOtherIndexes.Count();
719 for (int i = 0; i < numOther; i++)
720 {
721 int otherIndex = linkage.m_aOtherIndexes[i];
722 if (otherIndex < 0 || otherIndex >= numFragments)
723 continue;
724
725 SCR_FragmentEntity neighborFragment = s_FragmentList[otherIndex];
726 if (!neighborFragment)
727 continue;
728
729 if (CheckFragmentAnchored(neighborFragment, numFragments))
730 return true;
731 }
732
733 return false;
734 }
735
736 //------------------------------------------------------------------------------------------------
743 void OnFragmentDestroyed(SCR_FragmentEntity fragment, EDamageType damageType, float damage, vector hitPosition, vector hitDirection)
744 {
745 if (m_bDeleteAfterFinalFragment)
746 EnableOnFrame(true); // Enable frame, will check if all fragments gone
747
749 if (!manager)
750 return;
751
752 //TODO Send hit data with our own rpc
753 //manager.SendHitData(this, fragment.GetIndex(), damageType, damage, hitPosition, hitDirection);
754
755 if (m_bEnableStructuralIntegrity)
756 UpdateStructuralIntegrity(fragment, damageType, damage, hitPosition, hitDirection);
757 }
758
759 //------------------------------------------------------------------------------------------------
766 override void NetReceiveHitData(int hitIndex, EDamageType damageType, float damage, vector hitPosition, vector hitDirection)
767 {
768 SCR_FragmentEntity fragment = FindFragment(hitIndex);
769 if (!fragment)
770 return;
771
772 if (m_bEnableStructuralIntegrity)
773 UpdateStructuralIntegrity(fragment, damageType, damage, hitPosition, hitDirection);
774
775 fragment.QueueDestroy(damageType, damage, hitPosition, hitDirection);
776 }
777
778 //------------------------------------------------------------------------------------------------
779 override void OnFrame(IEntity owner, float timeSlice)
780 {
781 // If we are meant to delete after the final fragment, do so
782 if (m_bDeleteAfterFinalFragment && !m_DestructionHitInfo)
783 {
784 if (CountFragments() <= 0)
785 DeleteSelf();
786 else
787 EnableOnFrame(false);
788 }
789 else
790 {
791 super.OnFrame(owner, timeSlice);
792 }
793 }
794
795 //------------------------------------------------------------------------------------------------
798 protected void UpdateModel(SCR_EFractalDestructionForceModel forceState = SCR_EFractalDestructionForceModel.FORCE_NONE)
799 {
800 if (!m_UsedFractalData)
801 return;
802
803 bool fractured = GetFractured();
804 if (forceState == SCR_EFractalDestructionForceModel.FORCE_UNFRACTURED)
805 fractured = false;
806 else if (forceState == SCR_EFractalDestructionForceModel.FORCE_FRACTURED)
807 fractured = true;
808
809 ResourceName assetPath;
810 if (fractured)
811 assetPath = m_UsedFractalData.m_ModelDestroyed;
812 else
813 assetPath = m_UsedFractalData.m_ModelNormal;
814
815 // Update physics and visual model
816 Physics phys = m_Owner.GetPhysics();
817 if (phys)
818 phys.Destroy();
819
820 if (assetPath == ResourceName.Empty)
821 {
822 m_Owner.SetObject(null, string.Empty);
823 m_Owner.ClearFlags(EntityFlags.VISIBLE, false);
824 m_Owner.Update();
825 }
826 else
827 {
828 Resource resource = Resource.Load(assetPath);
829 if (resource.IsValid())
830 {
831 BaseResourceObject baseResource = resource.GetResource();
832 if (baseResource)
833 {
834 VObject asset = baseResource.ToVObject();
835 if (asset)
836 {
837 m_Owner.SetObject(asset, string.Empty);
838 m_Owner.SetFlags(EntityFlags.VISIBLE, false);
839 m_Owner.Update();
840
841 Physics.CreateStatic(m_Owner, -1);
842 }
843 else
844 Print("FRACTAL DESTRUCTION::UpdateModel: Could not load visual object '" + assetPath + "'!!", LogLevel.WARNING);
845 }
846 else
847 Print("FRACTAL DESTRUCTION::UpdateModel: Could not load base resource for model '" + assetPath + "'!!", LogLevel.WARNING);
848 }
849 else
850 Print("FRACTAL DESTRUCTION::UpdateModel: Could not load model '" + assetPath + "'!!", LogLevel.WARNING);
851 }
852
853 //UpdatePhysicsInteractionLayers();
854 }
855
856 //------------------------------------------------------------------------------------------------
858 override void HandleDestruction()
859 {
860 UpdateModel();
861
862 // Only create fragments if not in total destruction
863 if (!m_DestructionHitInfo.m_TotalDestruction)
864 {
865 CreateFragments(true);
866
867 if (m_bDestroyFragmentOnFracture)
868 {
869 // Now try to trace the fragment at the position we hit
870 TraceParam param = new TraceParam();
871 param.Exclude = m_Owner;
872 param.Start = m_DestructionHitInfo.m_HitPosition + m_DestructionHitInfo.m_HitDirection * -0.25;
873 param.End = m_DestructionHitInfo.m_HitPosition + m_DestructionHitInfo.m_HitDirection * 0.25;
874 param.Flags = TraceFlags.WORLD | TraceFlags.ENTS;
875 param.LayerMask = -1;
876 if (m_Owner.GetWorld().TraceMove(param, SCR_Global.FilterCallback_IgnoreNotInList) < 1)
877 {
878 IEntity child = m_Owner.GetChildren();
879 while (child)
880 {
881 SCR_FragmentEntity fragment = SCR_FragmentEntity.Cast(child);
882 child = child.GetSibling();
883 if (fragment && param.TraceEnt == fragment)
884 {
885 //delete fragment;
886 fragment.QueueDestroy(m_DestructionHitInfo.m_DamageType, m_DestructionHitInfo.m_HitDamage, m_DestructionHitInfo.m_HitPosition, m_DestructionHitInfo.m_HitDirection);
887 UpdateStructuralIntegrity(fragment, m_DestructionHitInfo.m_DamageType, m_DestructionHitInfo.m_HitDamage, m_DestructionHitInfo.m_HitPosition, m_DestructionHitInfo.m_HitDirection);
888 break;
889 }
890 }
891 }
892 }
893 }
894
895 delete m_DestructionHitInfo;
896 }
897
898 //------------------------------------------------------------------------------------------------
901 override void NetReadInit(ScriptBitReader reader)
902 {
903 int fractalVariantIndex;
904 reader.Read(fractalVariantIndex, 32); // Read which fractal variant is used
905 if (fractalVariantIndex == -1)
906 return;
907
908 DeleteFragments();
909
910 SetHitZoneDamage(GetMaxHealth());
911
912 m_UsedFractalData = m_FractalVariants[fractalVariantIndex];
913 UpdateModel(SCR_EFractalDestructionForceModel.FORCE_FRACTURED);
914
915 int numBitMasks;
916 reader.Read(numBitMasks, 32); // Read num bitmasks
917 if (numBitMasks == 0)
918 return;
919
920 // Create bitmask array with values of fragments
921 SCR_BitMaskArray fragmentsBitMaskArray = new SCR_BitMaskArray(numBitMasks);
922 for (int i = 0; i < numBitMasks; i++)
923 {
924 int bitMask;
925 reader.Read(bitMask, 32); // Read fragment bitmask
926 fragmentsBitMaskArray.SetBitMask(i, bitMask);
927 }
928
929 // Now create the fragments stored in the bitmask
930 int numFragmentsMax = m_UsedFractalData.CountFragments();
931 for (int i = 0; i < numFragmentsMax; i++)
932 {
933 if (fragmentsBitMaskArray.GetBit(i))
934 CreateFragment(i);
935 }
936 }
937
938 //------------------------------------------------------------------------------------------------
940 override void NetWriteInit(ScriptBitWriter writer)
941 {
942 int fractalVariantIndex = -1;
943 if (m_UsedFractalData)
944 {
945 fractalVariantIndex = m_FractalVariants.Find(m_UsedFractalData);
946 writer.Write(fractalVariantIndex, 32); // Write which fractal variant is used
947 }
948 else
949 {
950 writer.Write(-1, 32); // Write null fractal variant
951 return;
952 }
953
954 array<int> fragmentIndexList = {};
955 int numFragments = FillFragmentIndexList(fragmentIndexList);
956
957 if (numFragments == 0) // All were destroyed
958 {
959 writer.Write(0, 32); // Write that there are no bitmasks
960 return;
961 }
962
963 // Now write existing fragments into a bitmask array that we can send (for compression)
964 SCR_BitMaskArray fragmentsBitMaskArray = new SCR_BitMaskArray(m_UsedFractalData.CountFragments());
965 int numBitMasks = fragmentsBitMaskArray.GetNumBitMasks();
966 writer.Write(numBitMasks, 32); // Write num bitmasks
967
968 // Write fragment bits into bitmask array
969 for (int i = 0; i < numFragments; i++)
970 {
971 fragmentsBitMaskArray.SetBit(fragmentIndexList[i], true);
972 }
973
974 // Write bitmask arrays
975 for (int i = 0; i < numBitMasks; i++)
976 {
977 int bitMask = fragmentsBitMaskArray.GetBitMask(i);
978 writer.Write(bitMask, 32); // Write fragment bitmask
979 }
980 }
981
982 //------------------------------------------------------------------------------------------------
984 override void InitDestruction()
985 {
986 m_UsedFractalData = GetRandomFractalVariant();
987 UpdateModel();
988 }
989
990 //------------------------------------------------------------------------------------------------
993 {
994 #ifdef WORKBENCH
995 ClearVisualizers();
996 #endif
997
998 if (GetFractured())
999 DeleteFragments();
1000 }
1001 #endif // ENABLE_DESTRUCTION
1002}
1003
1004class SCR_FractalVariationTitle : BaseContainerCustomTitle
1005{
1006 //------------------------------------------------------------------------------------------------
1007 override bool _WB_GetCustomTitle(BaseContainer source, out string title)
1008 {
1009 array<ResourceName> fragMdls = {};
1010 array<ResourceName> fragAnchorMdls = {};
1011 source.Get("m_aModelFragments", fragMdls);
1012 source.Get("m_aModelAnchorFragments", fragAnchorMdls);
1013 int num = 0;
1014 if (fragMdls)
1015 num = fragMdls.Count();
1016
1017 if (fragAnchorMdls)
1018 num += fragAnchorMdls.Count();
1019
1020 title = "Variation | FRAGS: " + num.ToString();
1021 return true;
1022 }
1023}
1024
1025[BaseContainerProps(), SCR_FractalVariationTitle()]
1027{
1028 [Attribute("", UIWidgets.ResourcePickerThumbnail, "Model to use when the object is undamaged", "xob")]
1030
1031 [Attribute("", UIWidgets.ResourcePickerThumbnail, "Model to use when the object is damaged/destroyed", "xob")]
1033
1034 [Attribute("", UIWidgets.ResourceAssignArray, "List of fragment models (excluding anchor fragments)", "xob")]
1035 protected ref array<ResourceName> m_aModelFragments;
1036
1037 [Attribute("", UIWidgets.ResourceAssignArray, "List of anchor fragment models (these are fragments that are considered firmly attached and hold other fragments in place, if structural integrity is enabled)", "xob")]
1038 protected ref array<ResourceName> m_aModelAnchorFragments;
1039
1040 [Attribute("", UIWidgets.ResourceAssignArray, "List of fragment debris models (excluding anchor fragments), if empty uses m_aModelFragments", "xob")]
1041 protected ref array<ResourceName> m_aDebrisModelFragments;
1042
1043 [Attribute("", UIWidgets.ResourceAssignArray, "List of anchor fragment debris models (these are fragments that are considered firmly attached and hold other fragments in place, if structural integrity is enabled), if empty uses m_aModelAnchorFragments", "xob")]
1044 protected ref array<ResourceName> m_aDebrisModelAnchorFragments;
1045
1046 [Attribute("", UIWidgets.Object, "Hierarchy between fragments")]
1047 protected ref SCR_FragmentHierarchy m_Hierarchy;
1048
1049 //------------------------------------------------------------------------------------------------
1052 {
1053 return m_aModelFragments.Count() + m_aModelAnchorFragments.Count();
1054 }
1055
1056 //------------------------------------------------------------------------------------------------
1059 {
1060 int numFrags = m_aModelFragments.Count();
1061 int numAnchorFrags = m_aModelAnchorFragments.Count();
1062 if (index >= (numFrags + numAnchorFrags))
1063 return false;
1064
1065 return index >= numFrags;
1066 }
1067
1068 //------------------------------------------------------------------------------------------------
1072 ResourceName GetFragmentModel(int index, out bool isAnchor)
1073 {
1074 isAnchor = false;
1075 if (index < 0)
1076 return ResourceName.Empty;
1077
1078 int numFrags = m_aModelFragments.Count();
1079 int numAnchorFrags = m_aModelAnchorFragments.Count();
1080 if (index >= (numFrags + numAnchorFrags))
1081 return ResourceName.Empty;
1082
1083 if (index < numFrags)
1084 return m_aModelFragments[index];
1085 else
1086 {
1087 isAnchor = true;
1088 return m_aModelAnchorFragments[index - numFrags];
1089 }
1090 }
1091
1092 //------------------------------------------------------------------------------------------------
1097 {
1098 isAnchor = false;
1099 if (index < 0)
1100 return ResourceName.Empty;
1101
1102 int numFrags = m_aModelFragments.Count();
1103 int numAnchorFrags = m_aModelAnchorFragments.Count();
1104 if (index >= (numFrags + numAnchorFrags))
1105 return ResourceName.Empty;
1106
1107 if (index < numFrags)
1108 {
1109 if (!m_aDebrisModelFragments || m_aDebrisModelFragments.Count() <= index) // No debris model defined, so use normal model
1110 return m_aModelFragments[index];
1111 else
1113 }
1114 else
1115 {
1116 isAnchor = true;
1117 index -= numFrags;
1118 if (! m_aDebrisModelAnchorFragments || m_aDebrisModelAnchorFragments.Count() <= index) // No anchor debris model defined, so use normal anchor model
1120 else
1122 }
1123 }
1124}
1125
1126class SCR_Spawnable_FragmentDebrisTitle : BaseContainerCustomTitle
1127{
1128 //------------------------------------------------------------------------------------------------
1129 override bool _WB_GetCustomTitle(BaseContainer source, out string title)
1130 {
1131 title = "Fragment Debris";
1132 return true;
1133 }
1135
1138 //------------------------------------------------------------------------------------------------
1139 override bool _WB_GetCustomTitle(BaseContainer source, out string title)
1141 title = "Fragment Hierarchy";
1142 return true;
1144}
1145
1146class SCR_FragmentLinkageTitle : BaseContainerCustomTitle
1147{
1148 //------------------------------------------------------------------------------------------------
1149 override bool _WB_GetCustomTitle(BaseContainer source, out string title)
1150 {
1151 int index = -1;
1152 bool isAnchor = false;
1153 source.Get("m_iIndex", index);
1154 source.Get("m_bIsAnchor", isAnchor);
1155 if (index == -1)
1156 title = "INVALID INDEX";
1157 else if (isAnchor)
1158 title = "Anchor | Index: " + index.ToString();
1159 else
1160 title = "------ | Index: " + index.ToString();
1161
1162 return true;
1163 }
1164}
1165
1166[BaseContainerProps(), SCR_Spawnable_FragmentDebrisTitle()]
1168{
1169 [Attribute("10", UIWidgets.Slider, "Mass of the debris", "0.01 1000 0.01")]
1170 protected float m_fMass;
1171
1172 [Attribute("5", UIWidgets.Slider, "Minimum lifetime value for the debris (in s)", "0 3600 0.5")]
1173 protected float m_fLifetimeMin;
1175 [Attribute("10", UIWidgets.Slider, "Maximum lifetime value for the debris (in s)", "0 3600 0.5")]
1176 protected float m_fLifetimeMax;
1177
1178 [Attribute("200", UIWidgets.Slider, "Maximum distance from camera above which the debris is not spawned (in m)", "0 3600 0.5")]
1179 protected float m_fDistanceMax;
1180
1181 [Attribute("0", UIWidgets.Slider, "Higher priority overrides lower priority if at or over debris limit", "0 100 1")]
1182 protected int m_fPriority;
1183
1184 [Attribute("0.1", UIWidgets.Slider, "Damage received to physics impulse (speed / mass) multiplier", "0 10000 0.01")]
1185 protected float m_fDamageToImpulse;
1186
1187 [Attribute("0.5", UIWidgets.Slider, "Random linear velocity multiplier (m/s)", "0 200 0.1")]
1189
1190 [Attribute("180", UIWidgets.Slider, "Random angular velocity multiplier (deg/s)", "0 3600 0.1")]
1192
1193 //------------------------------------------------------------------------------------------------
1198 //! \param[in] hitDirection
1199 void Spawn(SCR_FragmentEntity fragment, Physics parentPhysics, float damage, vector hitDirection)
1200 {
1201 #ifndef ENABLE_DESTRUCTION
1202 return;
1203 #else
1204 SCR_FractalVariation fractalVariation = fragment.GetDestructibleParent().GetCurrentFractalVariant();
1205 if (!fractalVariation)
1206 return;
1207
1208 int fragmentIndex = fragment.GetIndex();
1209
1210 bool isAnchor;
1211 ResourceName modelPath = fractalVariation.GetFragmentDebrisModel(fragmentIndex, isAnchor);
1212 if (modelPath == ResourceName.Empty)
1213 return;
1214
1215 vector spawnMat[4];
1216 fragment.GetTransform(spawnMat);
1217
1218 float dmgSpeed = damage * m_fDamageToImpulse / m_fMass;
1219
1220 vector linearVelocity = hitDirection * Math.RandomFloat(0, 1);
1221 linearVelocity += Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) * m_fRandomVelocityLinear;
1222 linearVelocity *= dmgSpeed;
1223 vector angularVelocity = Vector(Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1), Math.RandomFloat(-1, 1)) * Math.RandomFloat(0.25, 4) * m_fRandomVelocityAngular;
1224 angularVelocity *= dmgSpeed;
1225
1226 if (parentPhysics)
1227 {
1228 linearVelocity += parentPhysics.GetVelocity();
1229 angularVelocity += parentPhysics.GetAngularVelocity();
1230 }
1231
1232 SCR_DebrisSmallEntity debris = SCR_DebrisSmallEntity.SpawnDebris(fragment.GetWorld(), spawnMat, modelPath, m_fMass, Math.RandomFloat(m_fLifetimeMin, m_fLifetimeMax), m_fDistanceMax, m_fPriority, linearVelocity, angularVelocity);
1233 #endif // ENABLE_DESTRUCTION
1234 }
1235}
1236
1238class SCR_FragmentHierarchy
1239{
1240 [Attribute(desc: "Hierarchical list of fragments containing which fragments they are connected to")]
1241 protected ref array<ref SCR_FragmentLinkage> m_aLinks;
1242
1243 #ifdef ENABLE_DESTRUCTION
1244 //------------------------------------------------------------------------------------------------
1247 SCR_FragmentLinkage GetFragmentLinkage(int index)
1248 {
1249 int numLinks = m_aLinks.Count();
1250 foreach (SCR_FragmentLinkage link : m_aLinks)
1251 {
1252 if (link.m_iIndex == index)
1253 return link;
1254 }
1255
1256 return null;
1257 }
1258
1259 #ifdef WORKBENCH
1260 //------------------------------------------------------------------------------------------------
1266 bool ValidateHierarchy(SCR_FractalVariation fractalVariant, BaseContainer srcHierarchy, int variantIndex)
1267 {
1268 if (!fractalVariant)
1269 return false;
1270
1271 if (!m_aLinks)
1272 return false;
1273
1274 bool result = false;
1275
1276 BaseContainerList srcLinks = srcHierarchy.GetObjectArray("m_aLinks");
1277 array<int> foundIndexes = {};
1278 int numFrags = fractalVariant.CountFragments();
1279 int numLinks = m_aLinks.Count();
1280 for (int l = 0; l < numLinks; l++)
1281 {
1282 BaseContainer srcLink = srcLinks.Get(l);
1283 int srcLinkIndex = -1;
1284 if (!srcLink.Get("m_iIndex", srcLinkIndex))
1285 continue;
1286
1287 bool srcLinkIsAnchor = false;
1288 if (!srcLink.Get("m_bIsAnchor", srcLinkIsAnchor))
1289 continue;
1290
1291 // Found a duplicate, so change index
1292 if (foundIndexes.Find(srcLinkIndex) != -1 || srcLinkIndex < -1 || srcLinkIndex >= numFrags)
1293 {
1294 Print("SCR_DestructionFractalComponent: Bad index (" + srcLinkIndex.ToString() + ") detected in linkage object index " + l.ToString() + " in variant index " + variantIndex.ToString() + ", setting to -1", LogLevel.WARNING);
1295 srcLinkIndex = -1;
1296 srcLink.Set("m_iIndex", srcLinkIndex);
1297 BaseContainerTools.WriteToInstance(this, srcLink);
1298 result = true;
1299 continue;
1300 }
1301
1302 foundIndexes.Insert(srcLinkIndex);
1303
1304 // Mismatch in stored anchor value to true anchor value, so update
1305 if (srcLinkIsAnchor != fractalVariant.GetFragmentIndexIsAnchor(srcLinkIndex))
1306 {
1307 Print("SCR_DestructionFractalComponent: Updating anchor setting in linkage object index " + l + " in variant index " + variantIndex, LogLevel.NORMAL);
1308 srcLinkIsAnchor = !srcLinkIsAnchor;
1309 srcLink.Set("m_bIsAnchor", srcLinkIsAnchor);
1310 BaseContainerTools.WriteToInstance(this, srcLink);
1311 result = true;
1312 }
1313
1314 // Check other indexes in linkage
1315 array<int> srcOtherLinks;
1316 srcLink.Get("m_aOtherIndexes", srcOtherLinks);
1317 bool badOtherIndex = false;
1318 int numOtherLinks = srcOtherLinks.Count();
1319 for (int o = 0; o < numOtherLinks; o++)
1320 {
1321 int srcLinkOtherIndex = srcOtherLinks[o];
1322 if (srcLinkOtherIndex < -1 || srcLinkOtherIndex >= numFrags)
1323 {
1324 Print("SCR_DestructionFractalComponent: Bad other index (" + srcLinkOtherIndex.ToString() + ") detected in linkage object index " + l.ToString() + " in variant index " + variantIndex.ToString() + ", setting to -1", LogLevel.WARNING);
1325 srcOtherLinks[o] = -1;
1326 badOtherIndex = true;
1327 }
1328 }
1329
1330 if (badOtherIndex)
1331 {
1332 srcLink.Set("m_aOtherIndexes", srcOtherLinks);
1333 BaseContainerTools.WriteToInstance(this, srcLink);
1334 result = true;
1335 }
1336 }
1337
1338 if (result)
1339 BaseContainerTools.WriteToInstance(this, srcHierarchy);
1340
1341 return result;
1342 }
1343
1344 //------------------------------------------------------------------------------------------------
1347 void GenerateHierarchy(SCR_FractalVariation fractalVariant)
1348 {
1349 if (m_aLinks)
1350 m_aLinks.Clear();
1351 else
1352 m_aLinks = {};
1353
1354 if (!fractalVariant)
1355 return;
1356
1357 // First count fragment model counts
1358 int numFrags = fractalVariant.m_aModelFragments.Count();
1359 int numAnchorFrags = fractalVariant.m_aModelAnchorFragments.Count();
1360 int numTotal = numFrags + numAnchorFrags;
1361
1362 // Create a generic entity for getting bounds sizes and then load each model and get its bounds
1364 array<vector> fragment_mins = {};
1365 array<vector> fragment_maxs = {};
1366 for (int f = 0; f < numFrags; f++)
1367 {
1368 Resource resource = Resource.Load(fractalVariant.m_aModelFragments[f]);
1369 VObject asset = resource.GetResource().ToVObject();
1370 fragmentDummy.SetObject(asset, "");
1371
1372 vector mins, maxs;
1373 fragmentDummy.GetBounds(mins, maxs);
1374 fragment_mins.Insert(mins);
1375 fragment_maxs.Insert(maxs);
1376 }
1377
1378 for (int f = 0; f < numAnchorFrags; f++)
1379 {
1380 Resource resource = Resource.Load(fractalVariant.m_aModelAnchorFragments[f]);
1381 VObject asset = resource.GetResource().ToVObject();
1382 fragmentDummy.SetObject(asset, "");
1383
1384 vector mins, maxs;
1385 fragmentDummy.GetBounds(mins, maxs);
1386 fragment_mins.Insert(mins);
1387 fragment_maxs.Insert(maxs);
1388 }
1389
1390 // Now do bounding box overlaps and find out which fragments overlap
1391 for (int f = 0; f < numTotal; f++)
1392 {
1393 SCR_FragmentLinkage fragLinkage = null;
1394
1395 vector fragMins = fragment_mins[f];
1396 vector fragMaxs = fragment_maxs[f];
1397 for (int f2 = 0; f2 < numTotal; f2++)
1398 {
1399 if (f == f2)
1400 continue;
1401
1402 vector fragOtherMins = fragment_mins[f2];
1403 vector fragOtherMaxs = fragment_maxs[f2];
1404
1405 if (IntersectionBoxBox(fragMins, fragMaxs, fragOtherMins, fragOtherMaxs))
1406 continue;
1407
1408 // Frag linkage object not created yet, so create
1409 if (!fragLinkage)
1410 {
1411 fragLinkage = new SCR_FragmentLinkage();
1412 fragLinkage.m_bIsAnchor = f >= numFrags;
1413 fragLinkage.m_iIndex = f;
1414 fragLinkage.m_aOtherIndexes = {};
1415 m_aLinks.Insert(fragLinkage);
1416 }
1417
1418 fragLinkage.m_aOtherIndexes.Insert(f2);
1419 }
1420 }
1421
1422 delete fragmentDummy;
1423 }
1424
1425 //------------------------------------------------------------------------------------------------
1432 bool IntersectionBoxBox(vector mins1, vector maxs1, vector mins2, vector maxs2)
1433 {
1434 return (mins1[0] > maxs2[0] || mins1[1] > maxs2[1] || mins1[2] > maxs2[2] || maxs1[0] < mins2[0] || maxs1[1] < mins2[1] || maxs1[2] < mins2[2]);
1435 }
1436 #endif // WORKBENCH
1437 #endif // ENABLE_DESTRUCTION
1438}
1439
1440[BaseContainerProps(), SCR_FragmentLinkageTitle()]
1442{
1443 [Attribute("0", UIWidgets.None, "Whether the fragment is an anchor")]
1444 bool m_bIsAnchor;
1445
1446 [Attribute("-1", UIWidgets.EditBox, "Index of the fragment")]
1447 int m_iIndex;
1448
1449 [Attribute("", UIWidgets.EditBox, "List of indexes of the surrounding fragments")]
1450 ref array<int> m_aOtherIndexes;
1451}
void CreateSimpleText(string text, vector mat[4], float size, int color, ShapeFlags flags, array< ref Shape > output=null, float scaleWidth=1, bool doBackground=false, int backgroundColor=0x80000000)
Shape CreateCircle(vector pos, vector aroundDir, float radius, int color, int subdivisions, ShapeFlags flags)
Definition DebugShapes.c:91
ArmaReforgerScripted GetGame()
Definition game.c:1398
IEntity SpawnEntity(ResourceName entityResourceName, notnull IEntity slotOwner)
override bool _WB_OnKeyChanged(IEntity owner, BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent)
Any property value has been changed. You can use editor API here and do some additional edit actions ...
float GetMaxHealth()
class SCR_FragmentDebris SCR_FragmentHierarchyTitle()] class SCR_FragmentHierarchy
class SCR_FragmentDebris BaseContainerProps()
enum SCR_EFractalDestructionForceModel ComponentEditorProps(category:"GameScripted/Destruction", description:"Fractal destruction component. For objects that should shatter/splinter etc")
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
ref array< MapLink > m_aLinks
proto external WorldEditorAPI _WB_GetEditorAPI()
This returns world editor API, which is safe to use from editor events bellow.
proto external vector GetOrigin()
proto external void GetBounds(out vector mins, out vector maxs)
proto external BaseWorld GetWorld()
proto external void GetWorldBounds(out vector mins, out vector maxs)
proto external IEntity GetSibling()
Definition Math.c:13
Object holding reference to resource. In destructor release the resource.
Definition Resource.c:25
Fractal destruction component. For objects that should shatter/splinter etc.
ref array< ResourceName > m_aModelAnchorFragments
ref array< ResourceName > m_aDebrisModelAnchorFragments
ref array< ResourceName > m_aDebrisModelFragments
ref SCR_FragmentHierarchy m_Hierarchy
ref array< ResourceName > m_aModelFragments
ResourceName GetFragmentModel(int index, out bool isAnchor)
ResourceName GetFragmentDebrisModel(int index, out bool isAnchor)
void Spawn(SCR_FragmentEntity fragment, Physics parentPhysics, float damage, vector hitDirection)
An entity used to represent fragments of destructible objects such as glass shards or wood splinters.
static vector ProjWorldEditorMouseScreenToWorld(GenericEntity referenceEntity)
Definition Functions.c:1586
static SCR_MPDestructionManager GetInstance()
Returns the instance of the destruction manager.
An entity used for previews.
Instance of created debug visualizer.
Definition Shape.c:14
Visual object.
Definition VObject.c:14
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
ShapeType
Definition ShapeType.c:13
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
ShapeFlags
Definition ShapeFlags.c:13
SCR_FieldOfViewSettings Attribute
EntityFlags
Various entity flags.
Definition EntityFlags.c:14
EDamageType
Definition EDamageType.c:13
class SCR_BaseManualCameraComponent _WB_GetCustomTitle(BaseContainer source, out string title)
proto native vector Vector(float x, float y, float z)
TraceFlags
Definition TraceFlags.c:13
proto int ARGB(int a, int r, int g, int b)