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