1 [
EntityEditorProps(
category:
"GameLib/Scripted/Generator", description:
"Power Line Generator", dynamicBox:
true, visible:
false)]
12 protected ResourceName m_DefaultPole;
15 protected ResourceName m_StartPole;
18 protected ResourceName m_EndPole;
21 protected ResourceName m_DefaultJunctionPole;
24 protected bool m_bRotate180DegreeYawStartPole;
27 protected bool m_bRotate180DegreeYawEndPole;
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;
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;
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;
45 protected bool m_bApplyPitchAndRollDefault;
48 protected bool m_bApplyPitchAndRollStart;
51 protected bool m_bApplyPitchAndRollEnd;
54 protected ResourceName m_PowerlineMaterial;
57 protected bool m_bDrawDebugShapes;
59 protected ref array<IEntity> m_aMyJunctionPoles = {};
68 protected static ref array<IEntitySource> s_aGenerators = {};
69 protected static IEntitySource s_CurrentQueryGenerator;
76 protected static WorldEditorAPI s_WorldEditorAPI;
81 protected static const float TOLERANCE_SQUARED = 0.01;
82 protected static const string POINT_DATA_CLASS =
"SCR_PowerlineGeneratorPointData";
85 protected static void GenerateGeneratorJunctions(notnull IEntitySource generator)
87 if (!s_WorldEditorAPI || s_WorldEditorAPI.UndoOrRedoIsRestoring())
90 if (!s_WorldEditorAPI.IsDoingEditAction())
92 s_WorldEditorAPI.BeginEntityAction();
93 GenerateGeneratorJunctions(generator);
94 s_WorldEditorAPI.EndEntityAction();
99 s_aGenerators.Clear();
103 s_CurrentQueryGenerator = generator;
104 s_aGenerators.Insert(generator);
108 int generatorsCount = s_aGenerators.Count();
109 foreach (IEntitySource generatorSource : s_aGenerators)
112 if (!queriedGenerator)
115 queriedGenerator.DeletePoles();
116 queriedGenerator.GenerateJunctions();
120 foreach (IEntitySource generatorSource : s_aGenerators)
123 if (!queriedGenerator)
126 foreach (IEntitySource otherGeneratorSource : s_aGenerators)
128 if (generatorSource == otherGeneratorSource)
132 if (!otherQueriedGenerator)
135 queriedGenerator.FindCommonJunctionsPoints(otherQueriedGenerator.m_aMyJunctionPoles);
138 queriedGenerator.GeneratePoles();
143 protected bool IsJunctionPoint(vector point)
145 foreach (IEntity junctionPole : m_aMyJunctionPoles)
147 if (vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
157 IEntitySource sourceOther;
158 sourceOther = s_WorldEditorAPI.EntityToSource(other);
162 IEntitySource shapeSourceOther;
163 shapeSourceOther = sourceOther.GetParent();
164 if (!m_ParentShapeSource || !shapeSourceOther)
167 array<vector> pointsThis = GetPoints(m_ParentShapeSource);
168 array<vector> pointsOther = GetPoints(shapeSourceOther);
170 foreach (vector pointThis : pointsThis)
172 foreach (vector pointOther : pointsOther)
174 if (vector.DistanceSq(CoordToParent(pointThis), other.CoordToParent(pointOther)) < TOLERANCE_SQUARED)
183 protected void FindCommonJunctionsPoints(notnull array<IEntity> otherJunctionPoles)
185 array<vector> points = GetPoints(
m_Source.GetParent());
186 foreach (IEntity junctionPole : otherJunctionPoles)
188 foreach (vector point : points)
190 if (vector.DistanceSq(CoordToParent(point), junctionPole.GetOrigin()) < TOLERANCE_SQUARED)
205 protected void GenerateJunctions()
207 m_aMyJunctionPoles.Clear();
209 IEntity shapeEntity = s_WorldEditorAPI.SourceToEntity(m_ParentShapeSource);
213 BaseContainerList points = m_ParentShapeSource.GetObjectArray(
"Points");
214 int count = points.Count();
218 vector parentPositionXZ = shapeEntity.GetOrigin();
219 float parentY = parentPositionXZ[1];
221 vector lastPointPosition;
222 vector currentPointPosition;
225 for (
int i; i < count; i++)
227 point = points.Get(i);
229 lastPointPosition = currentPointPosition;
230 point.Get(
"Position", currentPointPosition);
234 points.Get(1).Get(
"Position", lastPointPosition);
235 yaw = (lastPointPosition - currentPointPosition).Normalized().ToYaw();
239 yaw = (currentPointPosition - lastPointPosition).Normalized().ToYaw();
242 GenerateJunctionOnPoint(point, yaw);
247 override bool _WB_OnKeyChanged(BaseContainer src,
string key, BaseContainerList ownerContainers, IEntity parent)
249 super._WB_OnKeyChanged(src, key, ownerContainers, parent);
254 s_WorldEditorAPI = _WB_GetEditorAPI();
255 if (!s_WorldEditorAPI)
258 IEntitySource thisSrc = s_WorldEditorAPI.EntityToSource(
this);
259 BaseContainerTools.WriteToInstance(
this, thisSrc);
269 protected void GenerateJunctionOnPoint(notnull BaseContainer point,
float yaw)
273 BaseContainerList dataArr = point.GetObjectArray(
"Data");
275 for (
int j = 0, dataCount = dataArr.Count(); j < dataCount; ++j)
277 data = dataArr.Get(j);
278 if (
data.GetClassName() == POINT_DATA_CLASS)
280 data.Get(
"m_JunctionData", junctionData);
288 ResourceName junctionResourceName = junctionData.m_sJunctionPrefab;
289 if (junctionResourceName.IsEmpty())
290 junctionResourceName = m_DefaultJunctionPole;
292 if (junctionResourceName.IsEmpty())
295 vector pointPosition;
296 point.Get(
"Position", pointPosition);
298 IEntitySource poleSrc = s_WorldEditorAPI.CreateEntity(junctionResourceName,
"", 0,
m_Source, pointPosition, vector.Zero);
302 yaw += junctionData.m_fYawOffset;
306 s_WorldEditorAPI.SetVariableValue(poleSrc,
null,
"angleY", yaw.ToString());
308 SCR_PowerPole powerPole = GetPowerPoleFromEntitySource(poleSrc);
312 if (junctionData.m_bPowerSource)
314 IEntitySource powerPoleSrc = s_WorldEditorAPI.EntityToSource(powerPole);
317 s_WorldEditorAPI.SetVariableValue(powerPoleSrc,
null,
"PowerSource", junctionData.m_bPowerSource.ToString(
true));
320 powerPole = GetPowerPoleFromEntitySource(poleSrc);
324 m_aMyJunctionPoles.Insert(powerPole);
328 protected void DeletePoles()
330 if (!s_WorldEditorAPI.IsDoingEditAction())
332 Print(
"DeletePoles was called while not in edit action", LogLevel.ERROR);
337 m_aMyJunctionPoles.Clear();
342 protected void GeneratePoles()
346 Print(
"Parent Shape is null, cannot generate power poles", LogLevel.ERROR);
353 s_WorldEditorAPI = _WB_GetEditorAPI();
354 if (!s_WorldEditorAPI)
357 if (!s_WorldEditorAPI.IsDoingEditAction())
359 Print(
"GeneratePoles was called while not in edit action", LogLevel.ERROR);
363 array<vector> anchorPoints = {};
364 array<vector> shapePoints = {};
367 int shapePointsCount = shapePoints.Count();
368 if (shapePointsCount < 2)
370 Print(
"Power Line Generator shape only has one point - no power line will be generated", LogLevel.WARNING);
374 BaseContainerList pointsSource = m_ParentShapeSource.GetObjectArray(
"Points");
376 float pointDistance, poleOffset, currentDistance, yaw, nextPoleDistance;
377 vector currentPoint, prevPoint, prevAnchorPoint;
378 bool isAnchorPoint, spawnPerPoint, previousWasPerPoint;
379 BaseContainerList dataArr;
381 vector
direction, lastPolePosition, nextPolePosition;
383 float parentY = parentPositionXZ[1];
384 parentPositionXZ[1] = 0;
385 vector currAnchorPoint = anchorPoints[0];
386 int currentAnchorIndex;
387 int anchorCount = anchorPoints.Count();
390 for (
int i; i < shapePointsCount; i++)
392 prevPoint = currentPoint;
393 previousWasPerPoint = spawnPerPoint;
394 currentPoint = shapePoints[i];
398 prevAnchorPoint = currAnchorPoint;
399 currentAnchorIndex++;
400 if (currentAnchorIndex < anchorCount)
401 currAnchorPoint = anchorPoints[currentAnchorIndex];
404 isAnchorPoint = currentPoint == currAnchorPoint;
411 AttachJunctionOnPoint(prevAnchorPoint);
413 AttachJunctionOnPoint(prevPoint);
416 if (currentAnchorIndex > 0)
418 dataArr = pointsSource.Get(currentAnchorIndex - 1).GetObjectArray(
"Data");
419 for (
int j = 0, dataCount = dataArr.Count(); j < dataCount; ++j)
421 data = dataArr.Get(j);
422 if (
data.GetClassName() != POINT_DATA_CLASS)
425 data.Get(
"m_fPoleOffset", poleOffset);
428 Print(
"Pole offset is too small!", LogLevel.WARNING);
432 data.Get(
"m_bGeneratePerPoint", spawnPerPoint);
438 direction = (currentPoint - prevPoint).Normalized();
442 GenerateStartPole(prevPoint, parentY, parentPositionXZ, yaw);
452 if (!IsJunctionPoint(prevAnchorPoint))
453 GeneratePole(prevAnchorPoint, parentPositionXZ, parentY, yaw);
460 if (previousWasPerPoint)
462 GeneratePole(prevAnchorPoint, parentPositionXZ, parentY, yaw);
466 pointDistance = vector.Distance(currentPoint, prevPoint);
467 lastPolePosition = prevPoint;
469 if (currentDistance + pointDistance >
m_fDistance + poleOffset)
471 nextPoleDistance = (
m_fDistance + poleOffset - currentDistance);
472 pointDistance -= nextPoleDistance;
474 nextPolePosition = prevPoint +
direction * nextPoleDistance;
476 lastPolePosition = nextPolePosition;
477 GeneratePole(lastPolePosition, parentPositionXZ, parentY, yaw);
482 currentDistance += pointDistance - poleOffset;
488 Print(
"m_fDistance in Powerline Generator and pole offset is 0. Not generating any poles.", LogLevel.WARNING);
496 lastPolePosition = nextPolePosition;
497 GeneratePole(lastPolePosition, parentPositionXZ, parentY, yaw);
502 if (pointDistance > 0)
503 currentDistance += pointDistance;
507 GenerateEndPole(currentPoint, parentY, parentPositionXZ, yaw);
511 protected IEntity FindJunctionOnPoint(vector point, out
bool sameLine)
513 foreach (IEntity junctionPole : m_aMyJunctionPoles)
515 if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
524 if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
535 protected void AttachJunctionOnPoint(vector point)
537 foreach (IEntity junctionPole : m_aMyJunctionPoles)
539 if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
540 AttachJunction(junctionPole,
true);
545 if (junctionPole && vector.DistanceSq(point, CoordToLocal(junctionPole.GetOrigin())) < TOLERANCE_SQUARED)
546 AttachJunction(junctionPole,
false);
551 protected void AttachJunction(notnull IEntity junction,
bool sameLine)
553 IEntitySource junctionSource = s_WorldEditorAPI.EntityToSource(junction);
557 SCR_PowerPole junctionPole = GetPowerPoleFromEntitySource(junctionSource);
570 protected void GenerateEndPole(vector currentPoint,
float parentY, vector parentPositionXZ,
float yaw)
572 if (m_EndPole.IsEmpty())
575 vector endPolePos = currentPoint;
577 IEntitySource poleSource = s_WorldEditorAPI.EntityToSource(FindJunctionOnPoint(currentPoint, sameLine));
585 poleSource = GeneratePoleAt(endPolePos, parentPositionXZ, parentY, m_EndPole);
589 Print(
"End pole is of type SCR_JunctionPowerPole, make sure to do this using junction data on the last point instead.", LogLevel.WARNING);
591 if (m_bRotate180DegreeYawEndPole)
598 s_WorldEditorAPI.SetVariableValue(poleSource,
null,
"angleY", yaw.ToString());
599 if (m_bApplyPitchAndRollEnd)
600 ApplyRandomPitchAndRoll(poleSource);
603 SCR_PowerPole endPole = GetPowerPoleFromEntitySource(poleSource);
605 CreatePowerLines(GetPowerPoleFromEntitySource(
m_PreviousPowerPoleSource), endPole, sameLine, m_bRotate180DegreeYawEndPole);
607 if (m_bDrawDebugShapes)
609 Shape shape = Shape.CreateSphere(ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, endPolePos + parentPositionXZ, 1);
615 protected void GenerateStartPole(vector relativePos,
float parentY, vector parentPositionXZ,
float yaw)
617 if (!s_WorldEditorAPI)
619 Print(
"WorldEditorAPI not found in SCR_PowerlineGeneratorEntity.", LogLevel.WARNING);
623 if (m_StartPole.IsEmpty())
626 vector startPolePos = relativePos;
628 IEntity pole = FindJunctionOnPoint(relativePos, sameLine);
629 IEntitySource poleSrc;
634 poleSrc = s_WorldEditorAPI.EntityToSource(pole);
638 poleSrc = GeneratePoleAt(startPolePos, parentPositionXZ, parentY, m_StartPole);
639 pole = s_WorldEditorAPI.SourceToEntity(poleSrc);
642 Print(
"Pole entity not created in SCR_PowerlineGeneratorEntity.", LogLevel.WARNING);
648 Print(
"Start pole is of type SCR_JunctionPowerPole, make sure to do this using junction data on the first point instead.", LogLevel.WARNING);
652 if (m_bRotate180DegreeYawStartPole)
659 s_WorldEditorAPI.SetVariableValue(poleSrc,
null,
"angleY", yaw.ToString());
660 if (m_bApplyPitchAndRollStart)
661 ApplyRandomPitchAndRoll(poleSrc);
667 if (m_bDrawDebugShapes)
669 Shape shape = Shape.CreateSphere(ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, startPolePos + parentPositionXZ, 1);
675 protected IEntitySource GeneratePoleAt(vector localPos, vector parentPositionXZ,
float parentY, ResourceName poleResourceName)
678 if (s_WorldEditorAPI.TryGetTerrainSurfaceY(localPos[0] + parentPositionXZ[0], localPos[2] + parentPositionXZ[2], y))
679 localPos[1] = y - parentY;
681 return s_WorldEditorAPI.CreateEntity(poleResourceName,
string.Empty, 0,
m_Source, localPos, vector.Zero);
685 protected void ApplyRandomPitchAndRoll(IEntitySource powerPole)
687 float pitch = 0, roll = 0;
688 if (m_bRandomPitchOnBothSides)
693 if (m_bRandomRollOnBothSides)
698 s_WorldEditorAPI.SetVariableValue(powerPole,
null,
"angleX", pitch.ToString());
699 s_WorldEditorAPI.SetVariableValue(powerPole,
null,
"angleZ", roll.ToString());
703 protected IEntitySource GeneratePole(vector lastPolePosition, vector parentPositionXZ,
float parentY,
float yaw, ResourceName customPoleResourceName =
string.Empty)
706 IEntitySource poleSource;
707 if (s_WorldEditorAPI.TryGetTerrainSurfaceY(lastPolePosition[0] + parentPositionXZ[0], lastPolePosition[2] + parentPositionXZ[2], y))
708 lastPolePosition[1] = y - parentY;
710 ResourceName poleResourceName;
711 if (customPoleResourceName.GetPath().IsEmpty())
712 poleResourceName = m_DefaultPole;
714 poleResourceName = customPoleResourceName;
716 poleSource = s_WorldEditorAPI.CreateEntity(poleResourceName,
string.Empty, 0,
m_Source, lastPolePosition, vector.Zero);
720 s_WorldEditorAPI.SetVariableValue(poleSource,
null,
"angleY", yaw.ToString());
721 if (m_bApplyPitchAndRollDefault)
722 ApplyRandomPitchAndRoll(poleSource);
726 SCR_PowerPole thisPowerPole = GetPowerPoleFromEntitySource(poleSource);
736 if (m_bDrawDebugShapes)
738 Shape shape = Shape.CreateSphere(ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, lastPolePosition + parentPositionXZ, 1);
746 override void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array<vector> mins, array<vector> maxes)
748 super.OnShapeChangedInternal(shapeEntitySrc, shapeEntity, mins, maxes);
752 OnShapeInit(shapeEntitySrc, shapeEntity);
762 protected IEntitySource CreatePowerLines(
SCR_PowerPole existingPole,
SCR_PowerPole addedPole,
bool sameLine =
true,
bool inverse =
false)
764 if (!existingPole || !addedPole || existingPole == addedPole)
770 int numCables = Math.Min(addedPole.GetSlotsCount(sameLine), existingPole.GetSlotsCount(sameLine));
773 Print(
"0 slots found on one of the power poles, please check the setup of your power poles.", LogLevel.WARNING);
778 array<vector> startPoints = {};
779 array<vector> endPoints = {};
782 for (
int i = 0; i < numCables; ++i)
784 startSlotPos = addedPole.CoordToParent(addedPole.GetSlot(i, sameLine));
785 startPoints.Insert(startSlotPos);
786 endPoints.Insert(existingPole.TryGetSlot(i, startSlotPos, sameLine));
790 IEntitySource powerlineSrc = s_WorldEditorAPI.CreateEntity(
"PowerlineEntity",
string.Empty, 0,
m_Source, CoordToLocal(startPoints[0]), vector.Zero);
792 s_WorldEditorAPI.SetVariableValue(powerlineSrc,
null,
"Material", m_PowerlineMaterial);
794 array<ref ContainerIdPathEntry> containerPath;
795 vector startPos, endPos;
797 foreach (
int i, vector startPoint : startPoints)
799 s_WorldEditorAPI.CreateObjectArrayVariableMember(powerlineSrc,
null,
"Cables",
"Cable", i);
801 containerPath = {
new ContainerIdPathEntry(
"Cables", i) };
804 startPos = s_WorldEditorAPI.SourceToEntity(powerlineSrc).CoordToLocal(startPoint);
805 s_WorldEditorAPI.SetVariableValue(powerlineSrc, containerPath,
"StartPoint", startPos.ToString(
false));
807 IEntity powerline = s_WorldEditorAPI.SourceToEntity(powerlineSrc);
810 endPos = powerline.CoordToLocal(endPoints[numCables - i - 1]);
812 endPos = powerline.CoordToLocal(endPoints[i]);
814 s_WorldEditorAPI.SetVariableValue(powerlineSrc, containerPath,
"EndPoint", endPos.ToString(
false));
824 protected static SCR_PowerPole GetPowerPoleFromEntitySource(notnull IEntitySource powerPoleEntitySource)
826 IEntity powerPoleEntity = s_WorldEditorAPI.SourceToEntity(powerPoleEntitySource);
831 for (
int i = 0, childrenCount = powerPoleEntitySource.GetNumChildren(); i < childrenCount; i++)
833 result =
SCR_PowerPole.Cast(s_WorldEditorAPI.SourceToEntity(powerPoleEntitySource.GetChild(i)));
844 protected static void QueryGenerators(notnull IEntitySource generator, out array<IEntitySource> checkedGenerators =
null)
846 if (!s_WorldEditorAPI || !s_aGenerators)
849 IEntitySource shapeSource = generator.GetParent();
853 IEntity shapeEntity = s_WorldEditorAPI.SourceToEntity(shapeSource);
857 if (!checkedGenerators)
858 checkedGenerators = {};
859 else if (checkedGenerators.Contains(generator))
862 checkedGenerators.Insert(generator);
864 BaseContainerList points = shapeSource.GetObjectArray(
"Points");
867 array<vector> vectorPoints = GetPoints(shapeSource);
872 shapeEntity.GetTransform(mat);
874 IEntity generatorEntity = s_WorldEditorAPI.SourceToEntity(generator);
875 BaseWorld world = generatorEntity.GetWorld();
877 bbox.m_vMin[1] = -50;
883 world.QueryEntitiesByAABB(generatorEntity.CoordToParent(bbox.m_vMin), generatorEntity.CoordToParent(bbox.m_vMax), QueryFilter);
885 foreach (IEntitySource generator2 : s_aGenerators)
887 if (checkedGenerators.Contains(generator2))
890 s_CurrentQueryGenerator = generator2;
897 protected static bool QueryFilter(IEntity entity)
899 ShapeEntity shape = ShapeEntity.Cast(entity);
903 IEntitySource shapeSource = s_WorldEditorAPI.EntityToSource(shape);
907 IEntitySource otherGeneratorSource;
910 for (
int i = shapeSource.GetNumChildren() - 1; i >= 0; --i)
912 otherGeneratorSource = shapeSource.GetChild(i);
914 if (!otherGeneratorEntity)
919 if (!currentGeneratorEntity)
922 if (otherGeneratorSource && otherGeneratorSource != s_CurrentQueryGenerator && !s_aGenerators.Contains(otherGeneratorSource) && currentGeneratorEntity.HasCommonPoint(otherGeneratorEntity))
924 s_aGenerators.Insert(otherGeneratorSource);
934 protected void AddPointData(BaseContainerList points)
936 if (!s_WorldEditorAPI || s_WorldEditorAPI.UndoOrRedoIsRestoring())
939 BaseContainerList dataArr;
940 for (
int i = points.Count() - 1; i >= 0; i--)
942 dataArr = points.Get(i).GetObjectArray(
"Data");
943 int dataCount = dataArr.Count();
944 bool hasPointData =
false;
945 for (
int j = 0; j < dataCount; ++j)
947 if (dataArr.Get(j).GetClassName() == POINT_DATA_CLASS)
955 s_WorldEditorAPI.CreateObjectArrayVariableMember(points[i],
null,
"Data", POINT_DATA_CLASS, dataCount);
960 override void OnShapeInitInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity)
962 super.OnShapeInitInternal(shapeEntitySrc, shapeEntity);
964 s_WorldEditorAPI = _WB_GetEditorAPI();
966 BaseContainerList points = shapeEntitySrc.GetObjectArray(
"Points");
970 AddPointData(points);
972 GenerateGeneratorJunctions(
m_Source);
988 SetEventMask(EntityEvent.INIT);
996 [
Attribute(
desc:
"Junction prefab to be used", uiwidget: UIWidgets.ResourcePickerThumbnail,
params:
"et")]
997 ResourceName m_sJunctionPrefab;
1000 [
Attribute(defvalue:
"0",
desc:
"Set the junction's yaw offset; can be used to setup the prefab properly",
params:
"0 360")]
1003 [
Attribute(defvalue:
"0",
desc:
"Define whether or not this junction is a power source")]
1004 bool m_bPowerSource;
1007 class SCR_PowerlineGeneratorPointData : ShapePointDataScriptBase
1010 float m_fPoleOffset;
1012 [
Attribute(defvalue:
"0",
desc:
"Generate poles on anchor points until a point with this attribute unchecked is reached.")]