Arma Reforger Explorer  1.1.0.42
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
SCR_PowerlineGeneratorEntity.c
Go to the documentation of this file.
1 [EntityEditorProps(category: "GameLib/Scripted/Generator", description: "Power Line Generator", dynamicBox: true, visible: false)]
3 {
4 }
5 
7 {
8  [Attribute(defvalue: "1", desc: "How far should the poles be from each other.", category: "Pole Setup")]
9  protected float m_fDistance;
10 
11  [Attribute(params: "et", category: "Pole Setup")]
12  protected ResourceName m_DefaultPole;
13 
14  [Attribute(params: "et", category: "Pole Setup")]
15  protected ResourceName m_StartPole;
16 
17  [Attribute(params: "et", category: "Pole Setup")]
18  protected ResourceName m_EndPole;
19 
20  [Attribute(params: "et", category: "Pole Setup")]
21  protected ResourceName m_DefaultJunctionPole;
22 
23  [Attribute(defvalue: "0", category: "Pole Setup")]
24  protected bool m_bRotate180DegreeYawStartPole;
25 
26  [Attribute(defvalue: "0", category: "Pole Setup")]
27  protected bool m_bRotate180DegreeYawEndPole;
28 
29  [Attribute(defvalue: "0", desc: "Expected empty space around the poles line by other generators", params: "0 100 1", category: "Pole Setup")]
30  protected float Clearance;
31 
32  [Attribute(defvalue: "0", desc: "Maximum random pitch angle (from Y-axis)", params: "0 180 1", category: "Randomisation")]
33  protected float m_fRandomPitchAngle;
34 
35  [Attribute(defvalue: "1", desc: "Make the random pitch possible on both sides (-180..+180°) or, if disabled, one side only (0..180°)", category: "Randomisation")]
36  protected bool m_bRandomPitchOnBothSides;
37 
38  [Attribute(defvalue: "0", desc: "Maximum random roll angle (from Y-axis)", params: "0 180 1", category: "Randomisation")]
39  protected float m_fRandomRollAngle;
40 
41  [Attribute(defvalue: "1", desc: "Make the random roll possible on both sides (-180..+180°) or, if disabled, one side only (0..180°)", category: "Randomisation")]
42  protected bool m_bRandomRollOnBothSides;
43 
44  [Attribute(defvalue: "0", desc: "Apply randomisation to default poles", category: "Randomisation")]
45  protected bool m_bApplyPitchAndRollDefault;
46 
47  [Attribute(defvalue: "0", desc: "Apply randomisation to start pole", category: "Randomisation")]
48  protected bool m_bApplyPitchAndRollStart;
49 
50  [Attribute(defvalue: "0", desc: "Apply randomisation to end pole", category: "Randomisation")]
51  protected bool m_bApplyPitchAndRollEnd;
52 
53  [Attribute(uiwidget: UIWidgets.ResourceNamePicker, params: "emat", category: "Power Lines")]
54  protected ResourceName m_PowerlineMaterial;
55 
56  [Attribute(defvalue: "0", category: "Debug")]
57  protected bool m_bDrawDebugShapes;
58 
59  protected ref array<IEntity> m_aMyJunctionPoles = {};
60  protected ref array<IEntity> m_aOtherJunctionPoles = {};
61 
62  protected ShapeEntity m_ParentShape;
63 
64  protected ref array<ref Shape> m_aDebugShapes = {};
65  protected IEntitySource m_PreviousPowerPoleSource;
66 
67  // QUERY DATA
68  protected static ref array<IEntitySource> s_aGenerators = {};
69  protected static IEntitySource s_CurrentQueryGenerator;
70 
71  protected bool m_bLastJunctionWasSameLine = true;
72 
73  protected IEntitySource m_StartPoleEntitySource;
74 
75 #ifdef WORKBENCH
76  protected static WorldEditorAPI s_WorldEditorAPI;
77 
78  // #ifdef debug?
79  // protected static ref array<ref Shape> s_aDebugShapes = {};
80 
81  protected static const float TOLERANCE_SQUARED = 0.01; // 0.1 * 0.1
82  protected static const string POINT_DATA_CLASS = "SCR_PowerlineGeneratorPointData"; // TODO: use .IsInherited(SCR_PowerlineGeneratorPointData)
83 
84  //------------------------------------------------------------------------------------------------
85  protected static void GenerateGeneratorJunctions(notnull IEntitySource generator)
86  {
87  if (!s_WorldEditorAPI || s_WorldEditorAPI.UndoOrRedoIsRestoring())
88  return;
89 
90  if (!s_WorldEditorAPI.IsDoingEditAction())
91  {
92  s_WorldEditorAPI.BeginEntityAction();
93  GenerateGeneratorJunctions(generator);
94  s_WorldEditorAPI.EndEntityAction();
95  return;
96  }
97 
98  if (s_aGenerators)
99  s_aGenerators.Clear();
100  else
101  s_aGenerators = {};
102 
103  s_CurrentQueryGenerator = generator;
104  s_aGenerators.Insert(generator);
105  QueryGenerators(generator);
106 
107  SCR_PowerlineGeneratorEntity queriedGenerator;
108  int generatorsCount = s_aGenerators.Count();
109  foreach (IEntitySource generatorSource : s_aGenerators)
110  {
111  queriedGenerator = SCR_PowerlineGeneratorEntity.Cast(s_WorldEditorAPI.SourceToEntity(generatorSource));
112  if (!queriedGenerator)
113  continue;
114 
115  queriedGenerator.DeletePoles();
116  queriedGenerator.GenerateJunctions();
117  }
118 
119  SCR_PowerlineGeneratorEntity otherQueriedGenerator;
120  foreach (IEntitySource generatorSource : s_aGenerators)
121  {
122  queriedGenerator = SCR_PowerlineGeneratorEntity.Cast(s_WorldEditorAPI.SourceToEntity(generatorSource));
123  if (!queriedGenerator)
124  continue;
125 
126  foreach (IEntitySource otherGeneratorSource : s_aGenerators)
127  {
128  if (generatorSource == otherGeneratorSource)
129  continue;
130 
131  otherQueriedGenerator = SCR_PowerlineGeneratorEntity.Cast(s_WorldEditorAPI.SourceToEntity(otherGeneratorSource));
132  if (!otherQueriedGenerator)
133  continue;
134 
135  queriedGenerator.FindCommonJunctionsPoints(otherQueriedGenerator.m_aMyJunctionPoles);
136  }
137 
138  queriedGenerator.GeneratePoles();
139  }
140  }
141 
142  //------------------------------------------------------------------------------------------------
143  protected bool IsJunctionPoint(vector point)
144  {
145  foreach (IEntity junctionPole : m_aMyJunctionPoles)
146  {
147  if (vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
148  return true;
149  }
150 
151  return false;
152  }
153 
154  //------------------------------------------------------------------------------------------------
155  protected bool HasCommonPoint(SCR_PowerlineGeneratorEntity other)
156  {
157  IEntitySource sourceOther;
158  sourceOther = s_WorldEditorAPI.EntityToSource(other);
159  if (!sourceOther)
160  return false;
161 
162  IEntitySource shapeSourceOther;
163  shapeSourceOther = sourceOther.GetParent();
164  if (!m_ParentShapeSource || !shapeSourceOther)
165  return false;
166 
167  array<vector> pointsThis = GetPoints(m_ParentShapeSource);
168  array<vector> pointsOther = GetPoints(shapeSourceOther);
169 
170  foreach (vector pointThis : pointsThis)
171  {
172  foreach (vector pointOther : pointsOther)
173  {
174  if (vector.DistanceSq(CoordToParent(pointThis), other.CoordToParent(pointOther)) < TOLERANCE_SQUARED)
175  return true;
176  }
177  }
178 
179  return false;
180  }
181 
182  //------------------------------------------------------------------------------------------------
183  protected void FindCommonJunctionsPoints(notnull array<IEntity> otherJunctionPoles)
184  {
185  array<vector> points = GetPoints(m_Source.GetParent());
186  foreach (IEntity junctionPole : otherJunctionPoles)
187  {
188  foreach (vector point : points)
189  {
190  if (vector.DistanceSq(CoordToParent(point), junctionPole.GetOrigin()) < TOLERANCE_SQUARED)
191  {
192  // Isn't in the array yet
193  if (!m_aOtherJunctionPoles.Contains(junctionPole))
194  m_aOtherJunctionPoles.Insert(junctionPole);
195 
196  // We skip the rest of the points
197  break;
198  }
199  }
200  }
201  }
202 
203  //------------------------------------------------------------------------------------------------
204  // Generate junctions, happening before generating poles
205  protected void GenerateJunctions()
206  {
207  m_aMyJunctionPoles.Clear();
208 
209  IEntity shapeEntity = s_WorldEditorAPI.SourceToEntity(m_ParentShapeSource);
210  if (!shapeEntity)
211  return;
212 
213  BaseContainerList points = m_ParentShapeSource.GetObjectArray("Points");
214  int count = points.Count();
215  if (count < 2)
216  return;
217 
218  vector parentPositionXZ = shapeEntity.GetOrigin();
219  float parentY = parentPositionXZ[1];
220  BaseContainer point;
221  vector lastPointPosition;
222  vector currentPointPosition;
223  float yaw;
224 
225  for (int i; i < count; i++)
226  {
227  point = points.Get(i);
228 
229  lastPointPosition = currentPointPosition;
230  point.Get("Position", currentPointPosition);
231 
232  if (i == 0)
233  {
234  points.Get(1).Get("Position", lastPointPosition); // variable reuse
235  yaw = (lastPointPosition - currentPointPosition).Normalized().ToYaw();
236  }
237  else
238  {
239  yaw = (currentPointPosition - lastPointPosition).Normalized().ToYaw();
240  }
241 
242  GenerateJunctionOnPoint(point, yaw);
243  }
244  }
245 
246  //------------------------------------------------------------------------------------------------
247  override bool _WB_OnKeyChanged(BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent)
248  {
249  super._WB_OnKeyChanged(src, key, ownerContainers, parent);
250 
251  if (key == "coords")
252  return false;
253 
254  s_WorldEditorAPI = _WB_GetEditorAPI();
255  if (!s_WorldEditorAPI)
256  return false;
257 
258  IEntitySource thisSrc = s_WorldEditorAPI.EntityToSource(this);
259  BaseContainerTools.WriteToInstance(this, thisSrc);
260 
261  m_ParentShape = ShapeEntity.Cast(parent);
262  if (m_ParentShape)
263  OnShapeInit(m_ParentShapeSource, m_ParentShape);
264 
265  return true;
266  }
267 
268  //------------------------------------------------------------------------------------------------
269  protected void GenerateJunctionOnPoint(notnull BaseContainer point, float yaw)
270  {
272 
273  BaseContainerList dataArr = point.GetObjectArray("Data");
274  BaseContainer data;
275  for (int j = 0, dataCount = dataArr.Count(); j < dataCount; ++j)
276  {
277  data = dataArr.Get(j);
278  if (data.GetClassName() == POINT_DATA_CLASS)
279  {
280  data.Get("m_JunctionData", junctionData);
281  break;
282  }
283  }
284 
285  if (!junctionData)
286  return;
287 
288  ResourceName junctionResourceName = junctionData.m_sJunctionPrefab;
289  if (junctionResourceName.IsEmpty())
290  junctionResourceName = m_DefaultJunctionPole;
291 
292  if (junctionResourceName.IsEmpty())
293  return;
294 
295  vector pointPosition;
296  point.Get("Position", pointPosition);
297 
298  IEntitySource poleSrc = s_WorldEditorAPI.CreateEntity(junctionResourceName, "", 0, m_Source, pointPosition, vector.Zero);
299  if (!poleSrc)
300  return;
301 
302  yaw += junctionData.m_fYawOffset;
303  if (yaw > 360)
304  yaw -= 360;
305 
306  s_WorldEditorAPI.SetVariableValue(poleSrc, null, "angleY", yaw.ToString());
307 
308  SCR_PowerPole powerPole = GetPowerPoleFromEntitySource(poleSrc);
309  if (!powerPole)
310  return;
311 
312  if (junctionData.m_bPowerSource)
313  {
314  IEntitySource powerPoleSrc = s_WorldEditorAPI.EntityToSource(powerPole);
315  if (powerPoleSrc)
316  {
317  s_WorldEditorAPI.SetVariableValue(powerPoleSrc, null, "PowerSource", junctionData.m_bPowerSource.ToString(true));
318 
319  //Refresh powerpole pointer
320  powerPole = GetPowerPoleFromEntitySource(poleSrc);
321  }
322  }
323 
324  m_aMyJunctionPoles.Insert(powerPole);
325  }
326 
327  //------------------------------------------------------------------------------------------------
328  protected void DeletePoles()
329  {
330  if (!s_WorldEditorAPI.IsDoingEditAction())
331  {
332  Print("DeletePoles was called while not in edit action", LogLevel.ERROR);
333  return;
334  }
335 
336  DeleteAllChildren();
337  m_aMyJunctionPoles.Clear();
338  }
339 
340  //------------------------------------------------------------------------------------------------
341  // happens after having generated junctions
342  protected void GeneratePoles()
343  {
344  if (!m_ParentShape)
345  {
346  Print("Parent Shape is null, cannot generate power poles", LogLevel.ERROR);
347  return;
348  }
349 
350  if (!m_bDrawDebugShapes && m_aDebugShapes)
351  m_aDebugShapes.Clear();
352 
353  s_WorldEditorAPI = _WB_GetEditorAPI();
354  if (!s_WorldEditorAPI)
355  return;
356 
357  if (!s_WorldEditorAPI.IsDoingEditAction())
358  {
359  Print("GeneratePoles was called while not in edit action", LogLevel.ERROR);
360  return;
361  }
362 
363  array<vector> anchorPoints = {};
364  array<vector> shapePoints = {};
365  m_ParentShape.GetPointsPositions(anchorPoints);
366  m_ParentShape.GenerateTesselatedShape(shapePoints);
367  int shapePointsCount = shapePoints.Count();
368  if (shapePointsCount < 2)
369  {
370  Print("Power Line Generator shape only has one point - no power line will be generated", LogLevel.WARNING);
371  return;
372  }
373 
374  BaseContainerList pointsSource = m_ParentShapeSource.GetObjectArray("Points");
375 
376  float pointDistance, poleOffset, currentDistance, yaw, nextPoleDistance;
377  vector currentPoint, prevPoint, prevAnchorPoint;
378  bool isAnchorPoint, spawnPerPoint, previousWasPerPoint;
379  BaseContainerList dataArr;
380  BaseContainer data;
381  vector direction, lastPolePosition, nextPolePosition;
382  vector parentPositionXZ = m_ParentShape.GetOrigin();
383  float parentY = parentPositionXZ[1];
384  parentPositionXZ[1] = 0;
385  vector currAnchorPoint = anchorPoints[0];
386  int currentAnchorIndex;
387  int anchorCount = anchorPoints.Count();
388 
389  // foreach = change GenerateEndPole at the end + prevPoint management
390  for (int i; i < shapePointsCount; i++)
391  {
392  prevPoint = currentPoint;
393  previousWasPerPoint = spawnPerPoint;
394  currentPoint = shapePoints[i];
395 
396  if (isAnchorPoint) // if the -previous- point was an anchor, increment
397  {
398  prevAnchorPoint = currAnchorPoint;
399  currentAnchorIndex++;
400  if (currentAnchorIndex < anchorCount)
401  currAnchorPoint = anchorPoints[currentAnchorIndex];
402  }
403 
404  isAnchorPoint = currentPoint == currAnchorPoint;
405 
406  poleOffset = 0;
407 
408  if (i > 0)
409  {
410  if (spawnPerPoint)
411  AttachJunctionOnPoint(prevAnchorPoint);
412  else
413  AttachJunctionOnPoint(prevPoint);
414 
415  // Get data of previous point
416  if (currentAnchorIndex > 0) // currentAnchorIndex check not really required
417  {
418  dataArr = pointsSource.Get(currentAnchorIndex - 1).GetObjectArray("Data");
419  for (int j = 0, dataCount = dataArr.Count(); j < dataCount; ++j)
420  {
421  data = dataArr.Get(j);
422  if (data.GetClassName() != POINT_DATA_CLASS)
423  continue;
424 
425  data.Get("m_fPoleOffset", poleOffset);
426  if (m_fDistance + poleOffset < 1)
427  {
428  Print("Pole offset is too small!", LogLevel.WARNING);
429  poleOffset = 1 - m_fDistance;
430  }
431 
432  data.Get("m_bGeneratePerPoint", spawnPerPoint);
433  break;
434  }
435  }
436  }
437 
438  direction = (currentPoint - prevPoint).Normalized();
439  yaw = direction.ToYaw();
440 
441  if (i == 1) //Generate start pole
442  GenerateStartPole(prevPoint, parentY, parentPositionXZ, yaw);
443 
444  if (spawnPerPoint)
445  {
446  if (!isAnchorPoint)
447  continue;
448 
449  if (i < 2) // neither 0 (no previous point) nor 1 (Start Pole created)
450  continue;
451 
452  if (!IsJunctionPoint(prevAnchorPoint))
453  GeneratePole(prevAnchorPoint, parentPositionXZ, parentY, yaw);
454  }
455  else // not spawnPerPoint
456  {
457  if (i == 0)
458  continue;
459 
460  if (previousWasPerPoint)
461  {
462  GeneratePole(prevAnchorPoint, parentPositionXZ, parentY, yaw);
463  currentDistance = 0;
464  }
465 
466  pointDistance = vector.Distance(currentPoint, prevPoint);
467  lastPolePosition = prevPoint;
468 
469  if (currentDistance + pointDistance > m_fDistance + poleOffset)
470  {
471  nextPoleDistance = (m_fDistance + poleOffset - currentDistance);
472  pointDistance -= nextPoleDistance;
473 
474  nextPolePosition = prevPoint + direction * nextPoleDistance;
475 
476  lastPolePosition = nextPolePosition;
477  GeneratePole(lastPolePosition, parentPositionXZ, parentY, yaw);
478  currentDistance = 0;
479  }
480  else // the point is too close, we add the distance and continue to the next point
481  {
482  currentDistance += pointDistance - poleOffset;
483  continue;
484  }
485 
486  if (m_fDistance == 0 && poleOffset == 0)
487  {
488  Print("m_fDistance in Powerline Generator and pole offset is 0. Not generating any poles.", LogLevel.WARNING);
489  }
490  else
491  {
492  while (pointDistance > m_fDistance + poleOffset)
493  {
494  nextPolePosition = lastPolePosition + direction * (poleOffset + m_fDistance);
495 
496  lastPolePosition = nextPolePosition;
497  GeneratePole(lastPolePosition, parentPositionXZ, parentY, yaw);
498  pointDistance -= m_fDistance + poleOffset;
499  }
500  }
501 
502  if (pointDistance > 0)
503  currentDistance += pointDistance;
504  }
505  }
506 
507  GenerateEndPole(currentPoint, parentY, parentPositionXZ, yaw);
508  }
509 
510  //------------------------------------------------------------------------------------------------
511  protected IEntity FindJunctionOnPoint(vector point, out bool sameLine)
512  {
513  foreach (IEntity junctionPole : m_aMyJunctionPoles)
514  {
515  if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
516  {
517  sameLine = true;
518  return junctionPole;
519  }
520  }
521 
522  foreach (IEntity junctionPole : m_aOtherJunctionPoles)
523  {
524  if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
525  {
526  sameLine = false;
527  return junctionPole;
528  }
529  }
530 
531  return null;
532  }
533 
534  //------------------------------------------------------------------------------------------------
535  protected void AttachJunctionOnPoint(vector point)
536  {
537  foreach (IEntity junctionPole : m_aMyJunctionPoles)
538  {
539  if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
540  AttachJunction(junctionPole, true);
541  }
542 
543  foreach (IEntity junctionPole : m_aOtherJunctionPoles)
544  {
545  if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
546  AttachJunction(junctionPole, false);
547  }
548  }
549 
550  //------------------------------------------------------------------------------------------------
551  protected void AttachJunction(notnull IEntity junction, bool sameLine)
552  {
553  IEntitySource junctionSource = s_WorldEditorAPI.EntityToSource(junction);
554  if (!junctionSource)
555  return;
556 
557  SCR_PowerPole junctionPole = GetPowerPoleFromEntitySource(junctionSource);
558  if (!junctionPole)
559  return;
560 
561  m_bLastJunctionWasSameLine = sameLine;
562 
564  CreatePowerLines(GetPowerPoleFromEntitySource(m_PreviousPowerPoleSource), junctionPole, sameLine);
565 
566  m_PreviousPowerPoleSource = junctionSource;
567  }
568 
569  //------------------------------------------------------------------------------------------------
570  protected void GenerateEndPole(vector currentPoint, float parentY, vector parentPositionXZ, float yaw)
571  {
572  if (m_EndPole.IsEmpty())
573  return;
574 
575  vector endPolePos = currentPoint;
576  bool sameLine;
577  IEntitySource poleSource = s_WorldEditorAPI.EntityToSource(FindJunctionOnPoint(currentPoint, sameLine));
578 
579  if (poleSource)
580  {
581  m_bLastJunctionWasSameLine = sameLine;
582  }
583  else
584  {
585  poleSource = GeneratePoleAt(endPolePos, parentPositionXZ, parentY, m_EndPole);
586  sameLine = true;
587 
588  if (SCR_JunctionPowerPole.Cast(GetPowerPoleFromEntitySource(poleSource)))
589  Print("End pole is of type SCR_JunctionPowerPole, make sure to do this using junction data on the last point instead.", LogLevel.WARNING);
590 
591  if (m_bRotate180DegreeYawEndPole)
592  {
593  yaw += 180;
594  if (yaw > 360)
595  yaw -= 360;
596  }
597 
598  s_WorldEditorAPI.SetVariableValue(poleSource, null, "angleY", yaw.ToString());
599  if (m_bApplyPitchAndRollEnd)
600  ApplyRandomPitchAndRoll(poleSource);
601  }
602 
603  SCR_PowerPole endPole = GetPowerPoleFromEntitySource(poleSource);
604  if (endPole)
605  CreatePowerLines(GetPowerPoleFromEntitySource(m_PreviousPowerPoleSource), endPole, sameLine, m_bRotate180DegreeYawEndPole);
606 
607  if (m_bDrawDebugShapes)
608  {
609  Shape shape = Shape.CreateSphere(ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, endPolePos + parentPositionXZ, 1);
610  m_aDebugShapes.Insert(shape);
611  }
612  }
613 
614  //------------------------------------------------------------------------------------------------
615  protected void GenerateStartPole(vector relativePos, float parentY, vector parentPositionXZ, float yaw)
616  {
617  if (!s_WorldEditorAPI)
618  {
619  Print("WorldEditorAPI not found in SCR_PowerlineGeneratorEntity.", LogLevel.WARNING);
620  return;
621  }
622 
623  if (m_StartPole.IsEmpty())
624  return;
625 
626  vector startPolePos = relativePos;
627  bool sameLine;
628  IEntity pole = FindJunctionOnPoint(relativePos, sameLine);
629  IEntitySource poleSrc;
630 
631  if (pole)
632  {
633  m_bLastJunctionWasSameLine = sameLine;
634  poleSrc = s_WorldEditorAPI.EntityToSource(pole);
635  }
636  else
637  {
638  poleSrc = GeneratePoleAt(startPolePos, parentPositionXZ, parentY, m_StartPole);
639  pole = s_WorldEditorAPI.SourceToEntity(poleSrc);
640  if (!pole)
641  {
642  Print("Pole entity not created in SCR_PowerlineGeneratorEntity.", LogLevel.WARNING);
643  return;
644  }
645 
646  if (SCR_JunctionPowerPole.Cast(pole))
647  {
648  Print("Start pole is of type SCR_JunctionPowerPole, make sure to do this using junction data on the first point instead.", LogLevel.WARNING);
650  }
651 
652  if (m_bRotate180DegreeYawStartPole)
653  {
654  yaw += 180;
655  if (yaw > 360)
656  yaw -= 360;
657  }
658 
659  s_WorldEditorAPI.SetVariableValue(poleSrc, null, "angleY", yaw.ToString());
660  if (m_bApplyPitchAndRollStart)
661  ApplyRandomPitchAndRoll(poleSrc);
662  }
663 
664  m_PreviousPowerPoleSource = poleSrc;
665  m_StartPoleEntitySource = poleSrc;
666 
667  if (m_bDrawDebugShapes)
668  {
669  Shape shape = Shape.CreateSphere(ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, startPolePos + parentPositionXZ, 1);
670  m_aDebugShapes.Insert(shape);
671  }
672  }
673 
674  //------------------------------------------------------------------------------------------------
675  protected IEntitySource GeneratePoleAt(vector localPos, vector parentPositionXZ, float parentY, ResourceName poleResourceName)
676  {
677  float y;
678  if (s_WorldEditorAPI.TryGetTerrainSurfaceY(localPos[0] + parentPositionXZ[0], localPos[2] + parentPositionXZ[2], y))
679  localPos[1] = y - parentY;
680 
681  return s_WorldEditorAPI.CreateEntity(poleResourceName, string.Empty, 0, m_Source, localPos, vector.Zero);
682  }
683 
684  //------------------------------------------------------------------------------------------------
685  protected void ApplyRandomPitchAndRoll(IEntitySource powerPole)
686  {
687  float pitch = 0, roll = 0;
688  if (m_bRandomPitchOnBothSides)
689  pitch = -m_fRandomPitchAngle + Math.RandomFloat(0, 2 * m_fRandomPitchAngle);
690  else
691  pitch = Math.RandomFloat(0, m_fRandomPitchAngle);
692 
693  if (m_bRandomRollOnBothSides)
694  roll = -m_fRandomRollAngle + Math.RandomFloat(0, 2 * m_fRandomRollAngle);
695  else
696  roll = Math.RandomFloat(0, m_fRandomRollAngle);
697 
698  s_WorldEditorAPI.SetVariableValue(powerPole, null, "angleX", pitch.ToString());
699  s_WorldEditorAPI.SetVariableValue(powerPole, null, "angleZ", roll.ToString());
700  }
701 
702  //------------------------------------------------------------------------------------------------
703  protected IEntitySource GeneratePole(vector lastPolePosition, vector parentPositionXZ, float parentY, float yaw, ResourceName customPoleResourceName = string.Empty)
704  {
705  float y;
706  IEntitySource poleSource;
707  if (s_WorldEditorAPI.TryGetTerrainSurfaceY(lastPolePosition[0] + parentPositionXZ[0], lastPolePosition[2] + parentPositionXZ[2], y))
708  lastPolePosition[1] = y - parentY;
709 
710  ResourceName poleResourceName;
711  if (customPoleResourceName.GetPath().IsEmpty())
712  poleResourceName = m_DefaultPole; // already checked for empty string
713  else
714  poleResourceName = customPoleResourceName;
715 
716  poleSource = s_WorldEditorAPI.CreateEntity(poleResourceName, string.Empty, 0, m_Source, lastPolePosition, vector.Zero);
717  if (!poleSource)
718  return null;
719 
720  s_WorldEditorAPI.SetVariableValue(poleSource, null, "angleY", yaw.ToString());
721  if (m_bApplyPitchAndRollDefault)
722  ApplyRandomPitchAndRoll(poleSource);
723 
724  if (m_PreviousPowerPoleSource && poleSource)
725  {
726  SCR_PowerPole thisPowerPole = GetPowerPoleFromEntitySource(poleSource);
727  if (thisPowerPole)
728  {
729  bool inverse = m_StartPoleEntitySource == m_PreviousPowerPoleSource && m_bRotate180DegreeYawStartPole;
730  CreatePowerLines(GetPowerPoleFromEntitySource(m_PreviousPowerPoleSource), thisPowerPole, inverse: inverse);
731  }
732  }
733 
734  m_PreviousPowerPoleSource = poleSource;
735 
736  if (m_bDrawDebugShapes)
737  {
738  Shape shape = Shape.CreateSphere(ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, lastPolePosition + parentPositionXZ, 1);
739  m_aDebugShapes.Insert(shape);
740  }
741 
742  return poleSource;
743  }
744 
745  //------------------------------------------------------------------------------------------------
746  override void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array<vector> mins, array<vector> maxes)
747  {
748  super.OnShapeChangedInternal(shapeEntitySrc, shapeEntity, mins, maxes);
749 
750  // TODO handle this case better if needed, use the bbox arrays
751  m_ParentShape = shapeEntity;
752  OnShapeInit(shapeEntitySrc, shapeEntity);
753  }
754 
755  //------------------------------------------------------------------------------------------------
762  protected IEntitySource CreatePowerLines(SCR_PowerPole existingPole, SCR_PowerPole addedPole, bool sameLine = true, bool inverse = false)
763  {
764  if (!existingPole || !addedPole || existingPole == addedPole)
765  return null;
766 
767  if (SCR_JunctionPowerPole.Cast(existingPole) || SCR_JunctionPowerPole.Cast(addedPole))
768  sameLine = m_bLastJunctionWasSameLine;
769 
770  int numCables = Math.Min(addedPole.GetSlotsCount(sameLine), existingPole.GetSlotsCount(sameLine));
771  if (numCables < 1)
772  {
773  Print("0 slots found on one of the power poles, please check the setup of your power poles.", LogLevel.WARNING);
774  return null;
775  }
776 
777  // Gather slot positions for both poles in world coordinates
778  array<vector> startPoints = {};
779  array<vector> endPoints = {};
780  vector startSlotPos;
781  SCR_JunctionPowerPole junctionPowerPole;
782  for (int i = 0; i < numCables; ++i)
783  {
784  startSlotPos = addedPole.CoordToParent(addedPole.GetSlot(i, sameLine));
785  startPoints.Insert(startSlotPos);
786  endPoints.Insert(existingPole.TryGetSlot(i, startSlotPos, sameLine));
787  }
788 
789  // Create the PowerlineEntity
790  IEntitySource powerlineSrc = s_WorldEditorAPI.CreateEntity("PowerlineEntity", string.Empty, 0, m_Source, CoordToLocal(startPoints[0]), vector.Zero);
791 
792  s_WorldEditorAPI.SetVariableValue(powerlineSrc, null, "Material", m_PowerlineMaterial);
793 
794  array<ref ContainerIdPathEntry> containerPath;
795  vector startPos, endPos;
796  // Create cables for the entity
797  foreach (int i, vector startPoint : startPoints)
798  {
799  s_WorldEditorAPI.CreateObjectArrayVariableMember(powerlineSrc, null, "Cables", "Cable", i);
800 
801  containerPath = { new ContainerIdPathEntry("Cables", i) };
802 
803  // Add cable between each slot, convert to local coordinate space
804  startPos = s_WorldEditorAPI.SourceToEntity(powerlineSrc).CoordToLocal(startPoint);
805  s_WorldEditorAPI.SetVariableValue(powerlineSrc, containerPath, "StartPoint", startPos.ToString(false));
806 
807  IEntity powerline = s_WorldEditorAPI.SourceToEntity(powerlineSrc);
808 
809  if (inverse)
810  endPos = powerline.CoordToLocal(endPoints[numCables - i - 1]);
811  else
812  endPos = powerline.CoordToLocal(endPoints[i]);
813 
814  s_WorldEditorAPI.SetVariableValue(powerlineSrc, containerPath, "EndPoint", endPos.ToString(false));
815  }
816 
817  return powerlineSrc;
818  }
819 
820  //------------------------------------------------------------------------------------------------
824  protected static SCR_PowerPole GetPowerPoleFromEntitySource(notnull IEntitySource powerPoleEntitySource)
825  {
826  IEntity powerPoleEntity = s_WorldEditorAPI.SourceToEntity(powerPoleEntitySource);
827  SCR_PowerPole result = SCR_PowerPole.Cast(powerPoleEntity);
828  if (result)
829  return result;
830 
831  for (int i = 0, childrenCount = powerPoleEntitySource.GetNumChildren(); i < childrenCount; i++)
832  {
833  result = SCR_PowerPole.Cast(s_WorldEditorAPI.SourceToEntity(powerPoleEntitySource.GetChild(i)));
834  if (result)
835  return result;
836  }
837 
838  return null;
839  }
840 
841  //------------------------------------------------------------------------------------------------
844  protected static void QueryGenerators(notnull IEntitySource generator, out array<IEntitySource> checkedGenerators = null)
845  {
846  if (!s_WorldEditorAPI || !s_aGenerators)
847  return;
848 
849  IEntitySource shapeSource = generator.GetParent();
850  if (!shapeSource)
851  return;
852 
853  IEntity shapeEntity = s_WorldEditorAPI.SourceToEntity(shapeSource);
854  if (!shapeEntity)
855  return;
856 
857  if (!checkedGenerators)
858  checkedGenerators = {};
859  else if (checkedGenerators.Contains(generator))
860  return;
861 
862  checkedGenerators.Insert(generator);
863 
864  BaseContainerList points = shapeSource.GetObjectArray("Points");
865 
866  // Get bbox
867  array<vector> vectorPoints = GetPoints(shapeSource);
868  SCR_AABB bbox = new SCR_AABB(vectorPoints);
869 
870  // Query entities in bbox
871  vector mat[4];
872  shapeEntity.GetTransform(mat);
873 
874  IEntity generatorEntity = s_WorldEditorAPI.SourceToEntity(generator);
875  BaseWorld world = generatorEntity.GetWorld();
876 
877  bbox.m_vMin[1] = -50;
878  bbox.m_vMax[1] = 50;
879 
880  // #ifdef debug?
881  //s_aDebugShapes.Insert(Shape.Create(ShapeType.BBOX, ARGB(255, Math.RandomFloat(0, 255), Math.RandomFloat(0, 255), Math.RandomFloat(0, 255)), ShapeFlags.NOZWRITE | ShapeFlags.WIREFRAME, generatorEntity.CoordToParent(bbox.m_vMin), generatorEntity.CoordToParent(bbox.m_vMax)));
882 
883  world.QueryEntitiesByAABB(generatorEntity.CoordToParent(bbox.m_vMin), generatorEntity.CoordToParent(bbox.m_vMax), QueryFilter);
884 
885  foreach (IEntitySource generator2 : s_aGenerators)
886  {
887  if (checkedGenerators.Contains(generator2))
888  continue;
889 
890  s_CurrentQueryGenerator = generator2;
891  QueryGenerators(generator2, checkedGenerators);
892  }
893  }
894 
895  //------------------------------------------------------------------------------------------------
897  protected static bool QueryFilter(IEntity entity)
898  {
899  ShapeEntity shape = ShapeEntity.Cast(entity);
900  if (!shape)
901  return true;
902 
903  IEntitySource shapeSource = s_WorldEditorAPI.EntityToSource(shape);
904  if (!shapeSource)
905  return true;
906 
907  IEntitySource otherGeneratorSource;
908  SCR_PowerlineGeneratorEntity otherGeneratorEntity;
909  SCR_PowerlineGeneratorEntity currentGeneratorEntity;
910  for (int i = shapeSource.GetNumChildren() - 1; i >= 0; --i)
911  {
912  otherGeneratorSource = shapeSource.GetChild(i);
913  otherGeneratorEntity = SCR_PowerlineGeneratorEntity.Cast(s_WorldEditorAPI.SourceToEntity(otherGeneratorSource));
914  if (!otherGeneratorEntity)
915  continue;
916 
917  //Has to be refreshed here
918  currentGeneratorEntity = SCR_PowerlineGeneratorEntity.Cast(s_WorldEditorAPI.SourceToEntity(s_CurrentQueryGenerator));
919  if (!currentGeneratorEntity)
920  continue;
921 
922  if (otherGeneratorSource && otherGeneratorSource != s_CurrentQueryGenerator && !s_aGenerators.Contains(otherGeneratorSource) && currentGeneratorEntity.HasCommonPoint(otherGeneratorEntity))
923  {
924  s_aGenerators.Insert(otherGeneratorSource);
925  break;
926  }
927  }
928 
929  return true;
930  }
931 
932  //------------------------------------------------------------------------------------------------
934  protected void AddPointData(BaseContainerList points)
935  {
936  if (!s_WorldEditorAPI || s_WorldEditorAPI.UndoOrRedoIsRestoring())
937  return;
938 
939  BaseContainerList dataArr;
940  for (int i = points.Count() - 1; i >= 0; i--)
941  {
942  dataArr = points.Get(i).GetObjectArray("Data");
943  int dataCount = dataArr.Count();
944  bool hasPointData = false;
945  for (int j = 0; j < dataCount; ++j)
946  {
947  if (dataArr.Get(j).GetClassName() == POINT_DATA_CLASS)
948  {
949  hasPointData = true;
950  break;
951  }
952  }
953 
954  if (!hasPointData)
955  s_WorldEditorAPI.CreateObjectArrayVariableMember(points[i], null, "Data", POINT_DATA_CLASS, dataCount);
956  }
957  }
958 
959  //------------------------------------------------------------------------------------------------
960  override void OnShapeInitInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity)
961  {
962  super.OnShapeInitInternal(shapeEntitySrc, shapeEntity);
963 
964  s_WorldEditorAPI = _WB_GetEditorAPI();
965 
966  BaseContainerList points = shapeEntitySrc.GetObjectArray("Points");
967  if (!points)
968  return;
969 
970  AddPointData(points);
971 
972  GenerateGeneratorJunctions(m_Source);
973  }
974 #endif
975 
976  //------------------------------------------------------------------------------------------------
977  // constructor
980  void SCR_PowerlineGeneratorEntity(IEntitySource src, IEntity parent)
981  {
982 #ifdef WORKBENCH
983  if (GetGame().InPlayMode())
984  return;
985 
986  m_ParentShape = ShapeEntity.Cast(parent);
987 
988  SetEventMask(EntityEvent.INIT);
989 #endif
990  }
991 }
992 
995 {
996  [Attribute(desc: "Junction prefab to be used", uiwidget: UIWidgets.ResourcePickerThumbnail, params: "et")]
997  ResourceName m_sJunctionPrefab;
998 
999  // not a slider for performance reason (type the value directly)
1000  [Attribute(defvalue: "0", desc: "Set the junction's yaw offset; can be used to setup the prefab properly", /* uiwidget: UIWidgets.Slider, */params: "0 360")]
1001  float m_fYawOffset;
1002 
1003  [Attribute(defvalue: "0", desc: "Define whether or not this junction is a power source")]
1004  bool m_bPowerSource;
1005 }
1006 
1007 class SCR_PowerlineGeneratorPointData : ShapePointDataScriptBase
1008 {
1009  [Attribute(desc: "Offset of the pole, in metres")]
1010  float m_fPoleOffset;
1011 
1012  [Attribute(defvalue: "0", desc: "Generate poles on anchor points until a point with this attribute unchecked is reached.")]
1014 
1015  [Attribute()]
1017 }
SCR_PowerlineGeneratorEntityClass
Definition: SCR_PowerlineGeneratorEntity.c:2
direction
vector direction
Definition: SCR_DestructibleTreeV2.c:31
SCR_GeneratorBaseEntity
SCR_GeneratorBaseEntityClass GeneratorBaseEntityClass SCR_GeneratorBaseEntity(IEntitySource src, IEntity parent)
Definition: SCR_GeneratorBaseEntity.c:335
m_fDistance
float m_fDistance
Definition: SCR_AIGroupTargetCluster.c:38
m_aOtherJunctionPoles
protected ref array< IEntity > m_aOtherJunctionPoles
Definition: SCR_PowerlineGeneratorEntity.c:60
EntityEditorProps
enum EQueryType EntityEditorProps(category:"GameScripted/Sound", description:"THIS IS THE SCRIPT DESCRIPTION.", color:"0 0 255 255")
Definition: SCR_AmbientSoundsComponent.c:12
SCR_AABB
Definition: SCR_AABB.c:3
QueryGenerators
array< SCR_ResourceGenerator > QueryGenerators(float range, vector origin)
Definition: SCR_ResourceGrid.c:690
SCR_PowerlineGeneratorEntity
void SCR_PowerlineGeneratorEntity(IEntitySource src, IEntity parent)
Definition: SCR_PowerlineGeneratorEntity.c:980
GetGame
ArmaReforgerScripted GetGame()
Definition: game.c:1424
m_bLastJunctionWasSameLine
protected bool m_bLastJunctionWasSameLine
Definition: SCR_PowerlineGeneratorEntity.c:71
desc
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
Definition: SCR_RespawnBriefingComponent.c:17
m_fRandomRollAngle
float m_fRandomRollAngle
Definition: ForestGeneratorObjects.c:57
Attribute
SCR_PowerlineGeneratorEntityClass SCR_GeneratorBaseEntityClass Attribute(defvalue:"1", desc:"How far should the poles be from each other.", category:"Pole Setup")
Definition: SCR_PowerlineGeneratorEntity.c:8
m_ParentShape
protected ShapeEntity m_ParentShape
Definition: SCR_PowerlineGeneratorEntity.c:62
m_PreviousPowerPoleSource
protected IEntitySource m_PreviousPowerPoleSource
Definition: SCR_PowerlineGeneratorEntity.c:65
m_bGeneratePerPoint
bool m_bGeneratePerPoint
Definition: SCR_PowerlineGeneratorEntity.c:1013
m_Source
protected IEntitySource m_Source
Definition: SCR_PowerPole.c:14
m_StartPoleEntitySource
protected IEntitySource m_StartPoleEntitySource
Definition: SCR_PowerlineGeneratorEntity.c:73
m_JunctionData
ref SCR_PowerlineGeneratorJunctionData m_JunctionData
Definition: SCR_PowerlineGeneratorEntity.c:1016
m_aDebugShapes
protected ref array< ref Shape > m_aDebugShapes
Definition: SCR_PowerlineGeneratorEntity.c:64
SCR_PowerlineGeneratorJunctionData
Definition: SCR_PowerlineGeneratorEntity.c:994
data
Get all prefabs that have the spawner data
Definition: SCR_EntityCatalogManagerComponent.c:305
params
Configs ServerBrowser KickDialogs params
Definition: SCR_NotificationSenderComponent.c:24
m_fRandomPitchAngle
float m_fRandomPitchAngle
Definition: ForestGeneratorObjects.c:54
SCR_GeneratorBaseEntityClass
Definition: SCR_GeneratorBaseEntity.c:1
SCR_JunctionPowerPole
void SCR_JunctionPowerPole(IEntitySource src, IEntity parent)
Definition: SCR_JunctionPowerPole.c:87
SCR_PowerPole
void SCR_PowerPole(IEntitySource src, IEntity parent)
Definition: SCR_PowerPole.c:87
BaseContainerProps
SCR_AIGoalReaction_Follow BaseContainerProps
Handles insects that are supposed to be spawned around selected prefabs defined in prefab names array...
Definition: SCR_AIGoalReaction.c:468
category
params category
Definition: SCR_VehicleDamageManagerComponent.c:180