Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_PowerlineGeneratorEntity.c
Go to the documentation of this file.
1[EntityEditorProps(category: "GameScripted/Generators", description: "Power Line Generator", dynamicBox: true, visible: false)]
5
13
14// TODO: move most of the properties to PrefabData (https://community.bistudio.com/wiki/Arma_Reforger:Prefab_Data)
15class SCR_PowerlineGeneratorEntity : SCR_LineTerrainShaperGeneratorEntity
16{
17 /*
18 Pole Setup
19 */
20
21 [Attribute(defvalue: "30", desc: "How far should the poles be from each other", params: "1 " + MAX_CABLE_LENGTH, category: "Pole Setup")]
22 protected float m_fDistance;
23
24 [Attribute(params: "et", category: "Pole Setup")]
26
27 [Attribute(params: "et", category: "Pole Setup")]
29
30 [Attribute(params: "et", category: "Pole Setup")]
32
33 [Attribute(params: "et", category: "Pole Setup", desc: "The junction pole used when no Prefab is defined in shape's point data")]
35
36 [Attribute(defvalue: "0", category: "Pole Setup")]
38
39 [Attribute(defvalue: "0", category: "Pole Setup")]
41
42 [Attribute(defvalue: "0", desc: "Expected empty space around the poles line by other generators", params: "0 100 1", category: "Pole Setup")]
43 protected float Clearance;
44
45 /*
46 Randomisation
47 */
48
49 [Attribute(defvalue: "0", desc: "Maximum random yaw angle (×2, -value to +value) - only applied to default poles", params: "0 180 1", category: "Randomisation")]
50 protected float m_fRandomYawAngle;
51
52 [Attribute(defvalue: "0", desc: "Maximum random pitch angle (×2, -value to +value)", params: "0 180 1", category: "Randomisation")]
53 protected float m_fRandomPitchAngle;
54
55 [Attribute(defvalue: "0", desc: "Maximum random roll angle (×2, -value to +value)", params: "0 180 1", category: "Randomisation")]
56 protected float m_fRandomRollAngle;
57
58 [Attribute(defvalue: "0", desc: "Apply pitch and roll randomisation to default poles", category: "Randomisation")]
60
61 [Attribute(defvalue: "0", desc: "Apply randomisation to start pole", category: "Randomisation")]
63
64 [Attribute(defvalue: "0", desc: "Apply randomisation to end pole", category: "Randomisation")]
66
67 /*
68 Cables
69 */
70
71 [Attribute(desc: "Cable types-emat correspondence list", category: "Cables")]
72 protected ref array<ref SCR_PoleCable> m_aCables;
73
74 [Attribute(defvalue: DEFAULT_CABLE_PREFAB, desc: "Cable Prefab", params: "et class=" + POWER_LINE_CLASS, category: "Cables")]
76
77 [Attribute(defvalue: "{836210B285A81785}Assets/Structures/Infrastructure/Power/Powerlines/data/default_powerline_wire.emat", desc: "Default cable material if none above match", params: "emat", category: "Cables")]
79
80 /*
81 Debug
82 */
83
84 [Attribute(defvalue: "0", category: "Debug")]
85 protected bool m_bDrawDebugShapes;
86
87 protected static const string POWER_LINE_CLASS = "PowerlineEntity";
88 protected static const ResourceName DEFAULT_CABLE_PREFAB = "{613763D114E7CD1C}Prefabs/WEGenerators/Powerline/Powerline.et";
89 protected static const int MAX_CABLE_LENGTH = 500; // error when cable > 500
90
91#ifdef WORKBENCH
92
93 protected static ref SCR_DebugShapeManager s_DebugShapeManager; // static because SetSeed modifies the EntitySource, re-creating the entity
94
95 protected static const float JUNCTION_DISTANCEXZ = 0.1;
96 protected static const float JUNCTION_DISTANCEXZ_SQ = JUNCTION_DISTANCEXZ * JUNCTION_DISTANCEXZ;
97 protected static const vector JUNCTION_SEARCH_RANGE = { JUNCTION_DISTANCEXZ, SCR_PowerlineGeneratorJunctionData.MAX_Y_OFFSET, JUNCTION_DISTANCEXZ };
98
99 protected static const float MIN_CABLE_LENGTH_SQ = 0.01; // 0.1 * 0.1
100 protected static const float MAX_CABLE_LENGTH_SQ = MAX_CABLE_LENGTH * MAX_CABLE_LENGTH;
101 protected static const float CABLE_DEFAULT_DETECTION_BBOX_SIDE = 5; // in metres
102
103 protected static const float POLE_SAFEZONE_DISTANCE = 1.0; // any pole closer than that will be dropped
104
105 // DEBUG variables (color, pos, size)
106
107 protected static const int DEBUG_POLE_COLOUR_POS = Color.BLUE & 0x00FFFFFF | 0x88000000;
108 protected static const int DEBUG_POLE_COLOUR_ERROR = Color.RED & 0x00FFFFFF | 0x88000000;
109 protected static const float DEBUG_POLE_SIZE = 1;
110 protected static const vector DEBUG_POLE_POS = "0 5 0";
111
112 protected static const int DEBUG_SLOT_COLOUR_POS = Color.DARK_GREEN & 0x00FFFFFF | 0x88000000;
113 // protected static const int DEBUG_SLOT_COLOUR_ERROR = Color.RED & 0x00FFFFFF | 0x88000000;
114 protected static const int DEBUG_SLOT_COLOUR_UNUSED = Color.ORANGE & 0x00FFFFFF | 0x88000000;
115 protected static const float DEBUG_SLOT_SIZE = 0.1;
116
117 protected static const int DEBUG_CABLE_LENGTH_COLOUR_ERROR_SPHERE = Color.RED & 0x00FFFFFF | 0xAA000000;
118 protected static const int DEBUG_CABLE_LENGTH_COLOUR_ERROR_LINE = Color.RED;
119 protected static const vector DEBUG_CABLE_LENGTH_POS = "0 3 0";
120 protected static const float DEBUG_CABLE_LENGTH_POS_MAX_DIST = DEBUG_CABLE_LENGTH_POS.Length();
121 protected static const float DEBUG_CABLE_LENGTH_SIZE = 0.5;
122
123 protected static const int DEBUG_CABLE_COLOUR_POS = Color.YELLOW & 0x00FFFFFF | 0x88000000;
124
125 //------------------------------------------------------------------------------------------------
127 protected void Generate()
128 {
129 if (!m_ParentShapeSource)
130 return;
131
132 if (!m_bEnableGeneration)
133 {
134 Print("Power line generation is disabled for this shape (beware of junction issues with other lines) - tick it back on before saving", LogLevel.NORMAL);
135 return;
136 }
137
138 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
139 if (!worldEditorAPI || worldEditorAPI.UndoOrRedoIsRestoring() || !worldEditorAPI.AreGeneratorEventsEnabled())
140 return;
141
142 bool manageEntityAction = !worldEditorAPI.IsDoingEditAction();
143 if (manageEntityAction)
144 worldEditorAPI.BeginEntityAction();
145
146 SetSeed();
147
148 int childrenCount = m_Source.GetNumChildren();
149 if (childrenCount > 0)
150 {
151 Debug.BeginTimeMeasure();
152 DeleteAllChildren();
153 Debug.EndTimeMeasure("Delete " + childrenCount + " children");
154 }
155
157 {
158 if (s_DebugShapeManager)
159 s_DebugShapeManager.Clear();
160 else
161 s_DebugShapeManager = new SCR_DebugShapeManager();
162 }
163 else
164 {
165 s_DebugShapeManager = null;
166 }
167
168 // get on anchor generators
169 array<vector> worldAnchorPoints = GetWorldAnchorPoints(m_ParentShapeSource);
170 int worldAnchorPointsCount = worldAnchorPoints.Count();
171 if (worldAnchorPointsCount < 2)
172 return;
173
174 map<IEntitySource, IEntitySource> existingJunctions = new map<IEntitySource, IEntitySource>();
175
176 // TODO: support all poles?
177 IEntitySource existingObjectSource, existingPoleSource;
178 if (GetObjectAndPoleSourceAt(worldAnchorPoints[0], existingObjectSource, existingPoleSource))
179 existingJunctions.Insert(existingObjectSource, existingPoleSource);
180
181 if (GetObjectAndPoleSourceAt(worldAnchorPoints[worldAnchorPointsCount - 1], existingObjectSource, existingPoleSource))
182 existingJunctions.Insert(existingObjectSource, existingPoleSource);
183
185 map<IEntitySource, ref map<IEntitySource, vector>> junctionGeneratorAndPoleAndPosMap = new map<IEntitySource, ref map<IEntitySource, vector>>();
186 foreach (IEntitySource objectSource, IEntitySource poleSource : existingJunctions)
187 {
188 IEntitySource otherGenerator = objectSource.GetParent();
189 if (!otherGenerator)
190 continue;
191
192 map<IEntitySource, vector> otherJunctionPositions;
193 if (!junctionGeneratorAndPoleAndPosMap.Find(otherGenerator, otherJunctionPositions))
194 {
195 otherJunctionPositions = new map<IEntitySource, vector>();
196 junctionGeneratorAndPoleAndPosMap.Insert(otherGenerator, otherJunctionPositions);
197 }
198
199 otherJunctionPositions.Insert(poleSource, worldEditorAPI.SourceToEntity(objectSource).GetOrigin());
200 }
201
202 Debug.BeginTimeMeasure();
203 map<IEntitySource, IEntitySource> createdJunctions = GenerateJunctions();
204 Debug.EndTimeMeasure(createdJunctions.Count().ToString() + " junctions generation");
205
206 map<IEntitySource, vector> junctionPositions = new map<IEntitySource, vector>();
207 foreach (IEntitySource objectSource, IEntitySource poleSource : createdJunctions)
208 {
209 junctionPositions.Insert(poleSource, worldEditorAPI.SourceToEntity(objectSource).GetOrigin());
210 }
211
212 junctionGeneratorAndPoleAndPosMap.Insert(m_Source, junctionPositions);
213
214 Debug.BeginTimeMeasure();
215 GeneratePoles(junctionGeneratorAndPoleAndPosMap);
216 Debug.EndTimeMeasure("Poles generation");
217
218 ReconnectOtherGenerators(m_Source, createdJunctions);
219
220 if (manageEntityAction)
221 worldEditorAPI.EndEntityAction();
222 }
223
224 //------------------------------------------------------------------------------------------------
225 protected static bool GetObjectAndPoleSourceAt(vector worldPos, out IEntitySource parentSource, out IEntitySource poleSource)
226 {
227 WorldEditorAPI worldEditorAPI = GetWorldEditorAPI();
228 set<IEntity> entities = new set<IEntity>();
229 worldEditorAPI.GetWorld().QueryEntitiesByAABB(worldPos - JUNCTION_SEARCH_RANGE, worldPos + JUNCTION_SEARCH_RANGE, entities.Insert);
230
231 foreach (IEntity entity : entities)
232 {
233 if (vector.DistanceSqXZ(worldPos, entity.GetOrigin()) > JUNCTION_DISTANCEXZ_SQ)
234 continue;
235
236 if (SCR_PowerPole.Cast(entity))
237 {
238 parentSource = worldEditorAPI.EntityToSource(entity);
239 poleSource = parentSource;
240 return true;
241 }
242
243 IEntity child = entity.GetChildren();
244 while (child)
245 {
246 if (SCR_PowerPole.Cast(child))
247 {
248 parentSource = worldEditorAPI.EntityToSource(entity);
249 poleSource = worldEditorAPI.EntityToSource(child);
250 return true;
251 }
252
253 child = child.GetSibling();
254 }
255 }
256
257 return false;
258 }
259
260 //------------------------------------------------------------------------------------------------
263 protected void ReconnectOtherGenerators(notnull IEntitySource generator, notnull map<IEntitySource, IEntitySource> junctions)
264 {
265 WorldEditorAPI worldEditorAPI = GetWorldEditorAPI();
266 if (!worldEditorAPI)
267 return;
268
269 BaseWorld world = worldEditorAPI.GetWorld();
270 foreach (IEntitySource parentSource, IEntitySource poleSource : junctions)
271 {
272 vector parentPos = worldEditorAPI.SourceToEntity(parentSource).GetOrigin();
273
274 set<IEntity> entitiesSet = new set<IEntity>();
275 world.QueryEntitiesByAABB(parentPos - JUNCTION_SEARCH_RANGE, parentPos + JUNCTION_SEARCH_RANGE, entitiesSet.Insert);
276
277 set<IEntitySource> otherGeneratorSources = new set<IEntitySource>();
278 foreach (IEntity entity : entitiesSet)
279 {
280 if (!SCR_PowerlineGeneratorEntity.Cast(entity))
281 continue;
282
283 IEntitySource otherGeneratorSource = worldEditorAPI.EntityToSource(entity);
284 if (!otherGeneratorSource)
285 continue;
286
287 if (otherGeneratorSource == generator)
288 continue;
289
290 IEntitySource shapeSource = otherGeneratorSource.GetParent();
291 if (!shapeSource)
292 continue;
293
294 ShapeEntity parentShape = ShapeEntity.Cast(worldEditorAPI.SourceToEntity(shapeSource));
295 if (!parentShape)
296 continue;
297
298 array<vector> shapePoints = GetWorldAnchorPoints(shapeSource);
299 if (!shapePoints)
300 continue;
301
302 int shapesPointCount = shapePoints.Count();
303 if (shapesPointCount < 2)
304 continue;
305
306 if (vector.DistanceSqXZ(shapePoints[0], parentPos) > JUNCTION_DISTANCEXZ_SQ
307 && vector.DistanceSqXZ(shapePoints[shapesPointCount - 1], parentPos) > JUNCTION_DISTANCEXZ_SQ)
308 continue;
309
310 otherGeneratorSources.Insert(otherGeneratorSource);
311 }
312
313 foreach (IEntitySource otherGeneratorSource : otherGeneratorSources)
314 {
315 if (!ReconnectGeneratorToPole(otherGeneratorSource, poleSource))
316 Print("Cannot reconnect generator to pole " + Debug.GetEntityLinkString(worldEditorAPI.SourceToEntity(poleSource)), LogLevel.WARNING);
317 }
318 }
319 }
320
321 //------------------------------------------------------------------------------------------------
328 protected static bool ReconnectGeneratorToPole(notnull IEntitySource generatorSource, notnull IEntitySource otherLinePoleSource)
329 {
330 BaseContainer parentShape = generatorSource.GetParent();
331 if (!parentShape || !parentShape.GetClassName().ToType() || !parentShape.GetClassName().ToType().IsInherited(ShapeEntity))
332 return false;
333
334 int childrenCount = generatorSource.GetNumChildren();
335 if (childrenCount < 1)
336 return false;
337
338 WorldEditorAPI worldEditorAPI = GetWorldEditorAPI();
339 if (!worldEditorAPI)
340 return false;
341
342 SCR_PowerPole otherLinePole = SCR_PowerPole.Cast(worldEditorAPI.SourceToEntity(otherLinePoleSource));
343 if (!otherLinePole)
344 return false;
345
346 // can be either of the extremities OR a central point (why...)
347 // second case is NOT covered - use it properly or don't use it
348 array<vector> worldAnchorPoints = GetWorldAnchorPoints(parentShape);
349 int worldAnchorPointsCount = worldAnchorPoints.Count();
350 if (worldAnchorPointsCount < 2)
351 return false;
352
353 vector searchPos;
354 if (vector.DistanceSqXZ(worldAnchorPoints[0], otherLinePole.GetOrigin()) <= JUNCTION_DISTANCEXZ_SQ) // get the start pole
355 {
356 searchPos = worldAnchorPoints[0];
357 }
358 else // get the last pole before the junction
359 {
360 SCR_PowerlineGeneratorEntity generator = SCR_PowerlineGeneratorEntity.Cast(worldEditorAPI.SourceToEntity(generatorSource));
361 if (!generator)
362 return false;
363
364 array<vector> relPolePos = {};
365 map<int, ref SCR_PowerlineGeneratorPointData> pointDataMap = new map<int, ref SCR_PowerlineGeneratorPointData>();
366 if (!generator.GetPolesRelPosition(relPolePos, pointDataMap) || relPolePos.Count() < 2)
367 return false;
368
369 searchPos = generator.CoordToParent(relPolePos[relPolePos.Count() - 2]);
370 }
371
372 IEntitySource entitySource, localPoleSource;
373 if (!GetObjectAndPoleSourceAt(searchPos, entitySource, localPoleSource))
374 return false;
375
376 array<IEntitySource> cablesToDelete = GetCablesBetweenPoleSources(localPoleSource, otherLinePoleSource);
377 worldEditorAPI.DeleteEntities(cablesToDelete);
378
379 return CreatePowerLines(generatorSource, localPoleSource, otherLinePoleSource) != null;
380 }
381
382 //------------------------------------------------------------------------------------------------
386 protected static array<IEntitySource> GetCablesBetweenPoleSources(notnull IEntitySource generatorPoleSource, notnull IEntitySource otherPoleSource)
387 {
388 array<IEntitySource> result = {};
389 set<IEntity> entitiesAroundGeneratorPole = new set<IEntity>(); // using set to use directly in QueryEntitiesByAABB
390 set<IEntity> entitiesAroundOtherPole = new set<IEntity>();
391
392 vector mins, maxes;
393 WorldEditorAPI worldEditorAPI = GetWorldEditorAPI();
394 BaseWorld world = worldEditorAPI.GetWorld();
395
396 worldEditorAPI.SourceToEntity(generatorPoleSource).GetWorldBounds(mins, maxes);
397 if (mins == maxes)
398 {
399 worldEditorAPI.SourceToEntity(generatorPoleSource).GetRootParent().GetWorldBounds(mins, maxes);
400 if (mins == maxes)
401 {
402 mins = worldEditorAPI.SourceToEntity(generatorPoleSource).GetOrigin() - vector.One * CABLE_DEFAULT_DETECTION_BBOX_SIDE;
403 maxes = worldEditorAPI.SourceToEntity(generatorPoleSource).GetOrigin() + vector.One * CABLE_DEFAULT_DETECTION_BBOX_SIDE;
404 }
405 }
406
407 if (!world.QueryEntitiesByAABB(mins, maxes, entitiesAroundGeneratorPole.Insert))
408 return null;
409
410 worldEditorAPI.SourceToEntity(otherPoleSource).GetWorldBounds(mins, maxes);
411 if (mins == maxes)
412 {
413 worldEditorAPI.SourceToEntity(otherPoleSource).GetRootParent().GetWorldBounds(mins, maxes);
414 if (mins == maxes)
415 {
416 mins = worldEditorAPI.SourceToEntity(otherPoleSource).GetOrigin() - vector.One * CABLE_DEFAULT_DETECTION_BBOX_SIDE;
417 maxes = worldEditorAPI.SourceToEntity(otherPoleSource).GetOrigin() + vector.One * CABLE_DEFAULT_DETECTION_BBOX_SIDE;
418 }
419 }
420
421 if (!world.QueryEntitiesByAABB(mins, maxes, entitiesAroundOtherPole.Insert))
422 return null;
423
424 IEntitySource generatorSource = generatorPoleSource.GetParent();
425 foreach (IEntity entityAroundGeneratorPole : entitiesAroundGeneratorPole)
426 {
427 if (!entitiesAroundOtherPole.Contains(entityAroundGeneratorPole))
428 continue; // not in common
429
430 if (!entityAroundGeneratorPole.Type() || !entityAroundGeneratorPole.Type().IsInherited(PowerlineEntity))
431 continue; // not a cable
432
433 IEntitySource entitySource = worldEditorAPI.EntityToSource(entityAroundGeneratorPole);
434 if (!entitySource || entitySource.GetParent() != generatorSource)
435 continue; // not a generator cable
436
437 result.Insert(entitySource);
438 }
439
440 return result;
441 }
442
443 //------------------------------------------------------------------------------------------------
446 protected map<IEntitySource, IEntitySource> GenerateJunctions()
447 {
448 array<vector> anchorPoints = m_ShapeNextPointHelper.GetAnchorPoints();
449 int anchorPointsCountMinus1 = anchorPoints.Count() - 1;
450
451 if (anchorPointsCountMinus1 < 1)
452 return new map<IEntitySource, IEntitySource>();
453
454 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
455
456 map<IEntitySource, IEntitySource> result = new map<IEntitySource, IEntitySource>();
457
458 SCR_PowerlineGeneratorPointData pointData;
459 SCR_PowerlineGeneratorJunctionData junctionData;
460 IEntitySource objectSource;
461 IEntitySource poleSource;
462 map<int, ref ShapePointDataScriptBase> pointDataMap = GetFirstPointDataMap(SCR_PowerlineGeneratorPointData);
463 foreach (int i, vector anchorPoint : anchorPoints)
464 {
465 pointData = SCR_PowerlineGeneratorPointData.Cast(pointDataMap.Get(i));
466 if (!pointData)
467 continue;
468
469 junctionData = pointData.m_JunctionData;
470 if (!junctionData)
471 continue;
472
473 ResourceName junctionResourceName = junctionData.m_sJunctionPrefab;
474 if (junctionResourceName.IsEmpty())
475 {
476 if (m_DefaultJunctionPole.IsEmpty())
477 continue;
478
479 junctionResourceName = m_DefaultJunctionPole;
480 }
481
482 vector vectorDir;
483 if (i == 0)
484 {
485 vectorDir = vector.Direction(anchorPoint, anchorPoints[1]);
487 vectorDir = -vectorDir;
488 }
489 else if (i == anchorPointsCountMinus1)
490 {
491 vectorDir = vector.Direction(anchorPoints[i - 1], anchorPoint);
493 vectorDir = -vectorDir;
494 }
495 else
496 {
497 vectorDir = vector.Direction(
498 vector.Direction(anchorPoint, anchorPoints[i - 1]).Normalized(),
499 vector.Direction(anchorPoint, anchorPoints[i + 1]).Normalized());
500 }
501
502 vectorDir = vectorDir.VectorToAngles(); // variable reuse
503 vectorDir = { 0, Math.Repeat(vectorDir[0] + junctionData.m_fYawOffset, 360), 0 };
504
505 if (junctionData.m_bApplyPitchAndRollRandomisation)
506 {
507 if (m_fRandomPitchAngle != 0) // pitch
508 vectorDir[0] = m_RandomGenerator.RandFloatXY(-m_fRandomPitchAngle, m_fRandomPitchAngle);
509
510 if (m_fRandomRollAngle != 0) // roll
511 vectorDir[2] = m_RandomGenerator.RandFloatXY(-m_fRandomRollAngle, m_fRandomRollAngle);
512 }
513
514 if (junctionData.m_fYOffset)
515 anchorPoint[1] = anchorPoint[1] + junctionData.m_fYOffset;
516
517 objectSource = worldEditorAPI.CreateEntity(junctionResourceName, string.Empty, m_iSourceLayerID, m_Source, anchorPoint, vectorDir);
518 if (!objectSource)
519 continue;
520
521 poleSource = GetPowerPoleSourceFromEntitySource(objectSource);
522 if (!poleSource)
523 {
524 worldEditorAPI.DeleteEntity(objectSource);
525 continue;
526 }
527
528 if (junctionData.m_bPowerSource)
529 worldEditorAPI.SetVariableValue(poleSource, null, "PowerSource", "1");
530
531 result.Insert(objectSource, poleSource);
532 }
533
534 return result;
535 }
536
537 //------------------------------------------------------------------------------------------------
538 protected override bool _WB_OnKeyChanged(BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent)
539 {
540 if (!super._WB_OnKeyChanged(src, key, ownerContainers, parent))
541 return false;
542
543 if (key == "coords")
544 return false;
545
546 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
547 if (!worldEditorAPI || worldEditorAPI.UndoOrRedoIsRestoring())
548 return false;
549
550 BaseContainerTools.WriteToInstance(this, worldEditorAPI.EntityToSource(this));
551
552 Generate();
553
554 return true;
555 }
556
557 //------------------------------------------------------------------------------------------------
561 protected void GeneratePoles(notnull map<IEntitySource, ref map<IEntitySource, vector>> junctionGeneratorAndPoleAndPosMap)
562 {
563 if (!m_ParentShapeSource)
564 {
565 Print("Parent Shape Source is null, cannot generate power poles", LogLevel.ERROR);
566 return;
567 }
568
569 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
570 if (!worldEditorAPI)
571 return;
572
573 if (!ShapeEntity.Cast(worldEditorAPI.SourceToEntity(m_ParentShapeSource))) // wat - parent shape... is not a shape
574 {
575 Print("Parent Shape is null, cannot generate power poles", LogLevel.ERROR);
576 return;
577 }
578
579 array<vector> anchorPointsRelPos = m_ShapeNextPointHelper.GetAnchorPoints();
580 if (anchorPointsRelPos.Count() < 2)
581 {
582 Print("Not enough shape points", LogLevel.ERROR);
583 return;
584 }
585
586 // POLES'S POSITION
587 array<vector> polePointsRelPos = {};
588 map<int, ref SCR_PowerlineGeneratorPointData> pointDataMap = new map<int, ref SCR_PowerlineGeneratorPointData>();
589
590 if (!GetPolesRelPosition(polePointsRelPos, pointDataMap))
591 return;
592
593 if (polePointsRelPos.IsEmpty())
594 {
595 Print("Power Line Generator shape is invalid; no poles will be generated", LogLevel.ERROR);
596 return;
597 }
598
599 // POLES (and cables) CREATION
600 CreatePolesAndCables(polePointsRelPos, anchorPointsRelPos, pointDataMap, junctionGeneratorAndPoleAndPosMap);
601 }
602
603 //------------------------------------------------------------------------------------------------
608 protected bool GetPolesRelPosition(
609 notnull out array<vector> polePointsRelPos,
610 notnull out map<int, ref SCR_PowerlineGeneratorPointData> pointDataMap)
611 {
612 if (!m_ShapeNextPointHelper || !m_ShapeNextPointHelper.IsValid())
613 {
614 Print("[SCR_PowerlineGeneratorEntity.GetPolesRelPosition] invalid shapeNextPointHelper (" + __FILE__ + " L" + __LINE__ + ")", LogLevel.ERROR);
615 return false;
616 }
617
618 if (m_fDistance < POLE_SAFEZONE_DISTANCE)
619 m_fDistance = POLE_SAFEZONE_DISTANCE;
620
621 // ANCHORS DATA COLLECTION
622 map<int, ref ShapePointDataScriptBase> pointsData = GetFirstPointDataMap(SCR_PowerlineGeneratorPointData);
623
624 IEntity parentShapeEntity = _WB_GetEditorAPI().SourceToEntity(m_ParentShapeSource);
625
626 float poleOffset;
627 bool generatePerPoint;
628 vector lastPoleRelPos;
629 SCR_PowerlineGeneratorPointData pointData;
630 array<vector> anchorPointsRelPos = m_ShapeNextPointHelper.GetAnchorPoints();
631 int anchorPointsCountMinus1 = anchorPointsRelPos.Count() - 1;
632 foreach (int i, vector anchorPointRelPos : anchorPointsRelPos)
633 {
634 bool isStart = i == 0;
635 bool isEnd = i == anchorPointsCountMinus1;
636 bool hasJunction;
637
638 bool wasGeneratedPerPoint = generatePerPoint;
639 float previousOffset = poleOffset;
640
641 pointData = SCR_PowerlineGeneratorPointData.Cast(pointsData.Get(i));
642 if (pointData)
643 {
644 poleOffset = pointData.m_fPoleOffset;
645 generatePerPoint = pointData.m_bGeneratePerPoint;
646
647 // hardcoded safety
648 if (m_fDistance + poleOffset < POLE_SAFEZONE_DISTANCE)
649 poleOffset = POLE_SAFEZONE_DISTANCE - m_fDistance;
650
651 hasJunction = pointData.m_JunctionData != null && (m_DefaultJunctionPole || pointData.m_JunctionData.m_sJunctionPrefab);
652 }
653 else
654 {
655 poleOffset = 0;
656 }
657
658 if (!isStart && !wasGeneratedPerPoint)
659 {
660 // add offset if it exists
661 if (previousOffset != 0 && m_ShapeNextPointHelper.GetNextPoint(m_fDistance + previousOffset, lastPoleRelPos, i, xzMode: m_bNextPointMeasurementXZ))
662 polePointsRelPos.Insert(lastPoleRelPos);
663
664 // straight to the current anchor point!
665 while (m_ShapeNextPointHelper.GetNextPoint(m_fDistance, lastPoleRelPos, i, xzMode: m_bNextPointMeasurementXZ))
666 {
667 polePointsRelPos.Insert(lastPoleRelPos);
668 }
669 }
670
671 if (wasGeneratedPerPoint || generatePerPoint || hasJunction || isEnd || isStart || vector.DistanceSq(lastPoleRelPos, anchorPointRelPos) == 0)
672 {
673 int pointsCount = polePointsRelPos.Count();
674 if (hasJunction)
675 {
676 if (pointData)
677 pointDataMap.Insert(pointsCount, pointData);
678 }
679 else
680 {
681 if (pointsCount > 0 && vector.DistanceSq(lastPoleRelPos, anchorPointRelPos) < POLE_SAFEZONE_DISTANCE)
682 polePointsRelPos.Remove(pointsCount - 1);
683 }
684
685 polePointsRelPos.Insert(anchorPointRelPos);
686
687 m_ShapeNextPointHelper.SetOnAnchor(i);
688 lastPoleRelPos = anchorPointRelPos;
689 }
690 }
691
692 return true;
693 }
694
695 //------------------------------------------------------------------------------------------------
701 protected void CreatePolesAndCables(
702 notnull array<vector> polePointsRelPos,
703 notnull array<vector> anchorPointsRelPos,
704 notnull map<int, ref SCR_PowerlineGeneratorPointData> pointsData,
705 notnull map<IEntitySource, ref map<IEntitySource, vector>> junctionGeneratorAndPoleAndPosMap)
706 {
707 bool snapToGround = m_bSnapOffsetShapeToTheGround && !m_bSculptTerrain;
708 WorldEditorAPI worldEditorAPI = _WB_GetEditorAPI();
709 int poleWorldPointsCountMinus1 = polePointsRelPos.Count() - 1;
710 vector prevPoleRelPoint;
711 ResourceName prefab;
712 vector vectorDir; // no need for it to ever be normalised as it is .ToYaw()'d
713
714 IEntitySource currPoleObject;
715 IEntitySource prevPoleSource;
716 IEntitySource currPoleSource;
717
718 SCR_PowerPole prevPoleEntity;
719 SCR_PowerPole currPoleEntity;
720 foreach (int i, vector currPoleRelPoint : polePointsRelPos)
721 {
722 bool applyRandomPitchAndRoll;
723 bool isStart = i == 0;
724 bool isEnd = i == poleWorldPointsCountMinus1;
725 bool isAnchor = isStart || isEnd;
726 if (!isAnchor)
727 {
728 foreach (vector anchorPointRelPos : anchorPointsRelPos)
729 {
730 if (currPoleRelPoint == anchorPointRelPos) // a bit rough, but it works for now
731 {
732 isAnchor = true;
733 break;
734 }
735 }
736 }
737
738 bool isSameLine = true;
739
740 if (isAnchor) // anchor: start, end or potential junction
741 {
742 // one must look for junction on all points
743 // as another generator could make a junction here
744 currPoleSource = FindJunctionOnPosXZ(junctionGeneratorAndPoleAndPosMap, m_Source, CoordToParent(currPoleRelPoint), isSameLine);
745
746 if (currPoleSource) // found junction
747 {
748 if (s_DebugShapeManager)
749 s_DebugShapeManager.AddSphere(CoordToParent(currPoleRelPoint), 2, 0x88800080);
750
751 applyRandomPitchAndRoll = isSameLine;
752
753 // cannot set junction's yaw here
754 // as other lines joining before this line is initialised would not match the new junction's direction
755 }
756 else
757 {
758 if (isStart) // start pole
759 {
760 vectorDir = vector.Direction(currPoleRelPoint, polePointsRelPos[1]);
761 prefab = m_StartPole;
762 applyRandomPitchAndRoll = m_bApplyPitchAndRollStart;
764 vectorDir = -vectorDir;
765 }
766 else if (isEnd) // end pole
767 {
768 vectorDir = vector.Direction(prevPoleRelPoint, currPoleRelPoint);
769 prefab = m_EndPole;
770 applyRandomPitchAndRoll = m_bApplyPitchAndRollEnd;
772 vectorDir = -vectorDir;
773 }
774 else // normal pole that happens to be EXACTLY on an anchor pos
775 {
776 vectorDir = vector.Direction(
777 vector.Direction(currPoleRelPoint, prevPoleRelPoint).Normalized(),
778 vector.Direction(currPoleRelPoint, polePointsRelPos[i + 1]).Normalized());
779
780 prefab = m_DefaultPole;
781 applyRandomPitchAndRoll = m_bApplyPitchAndRollDefault;
782 }
783 }
784 }
785 else // normal pole
786 {
787 vectorDir = vector.Direction(
788 vector.Direction(currPoleRelPoint, prevPoleRelPoint).Normalized(),
789 vector.Direction(currPoleRelPoint, polePointsRelPos[i + 1]).Normalized());
790
791 prefab = m_DefaultPole;
792 applyRandomPitchAndRoll = m_bApplyPitchAndRollDefault;
793 }
794
795 if (!currPoleSource)
796 {
797 if (snapToGround)
798 {
799 vector worldPos = CoordToParent(currPoleRelPoint);
800 worldPos[1] = worldEditorAPI.GetTerrainSurfaceY(worldPos[0], worldPos[2]);
801 currPoleRelPoint = CoordToLocal(worldPos) + vector.Up * m_vShapeOffset[1];
802 }
803
804 vector angles = { 0, vectorDir.ToYaw(), 0 };
805
806 if (m_fRandomYawAngle != 0)
808
809 if (applyRandomPitchAndRoll)
810 {
811 if (m_fRandomPitchAngle != 0)
813
814 if (m_fRandomRollAngle != 0)
816 }
817
818 currPoleObject = worldEditorAPI.CreateEntity(prefab, string.Empty, m_iSourceLayerID, m_Source, currPoleRelPoint, angles);
819 currPoleSource = GetPowerPoleSourceFromEntitySource(currPoleObject);
820
821 if (s_DebugShapeManager)
822 {
823 s_DebugShapeManager.AddSphere(CoordToParent(currPoleRelPoint), DEBUG_POLE_SIZE, DEBUG_POLE_COLOUR_POS, ShapeFlags.NOOUTLINE);
824 s_DebugShapeManager.AddArrow(CoordToParent(currPoleRelPoint), CoordToParent(currPoleRelPoint + 3 * vector.FromYaw(angles[1])));
825 }
826 }
827
828 if (i > 0)
829 {
830 prevPoleEntity = SCR_PowerPole.Cast(worldEditorAPI.SourceToEntity(prevPoleSource));
831 if (prevPoleEntity)
832 {
833 currPoleEntity = SCR_PowerPole.Cast(worldEditorAPI.SourceToEntity(currPoleSource));
834 if (currPoleEntity && prevPoleEntity != currPoleEntity)
835 CreatePowerLines(m_Source, prevPoleSource, currPoleSource);
836 }
837 }
838
839 prevPoleSource = currPoleSource;
840 prevPoleRelPoint = currPoleRelPoint;
841 currPoleObject = null;
842 currPoleSource = null;
843 }
844 }
845
846 //------------------------------------------------------------------------------------------------
853 protected static IEntitySource FindJunctionOnPosXZ(
854 map<IEntitySource, ref map<IEntitySource, vector>> generatorAndPolesPlusParentWorldPosMap,
855 notnull IEntitySource generatorSource,
856 vector worldPos,
857 out bool isSameLine)
858 {
859 map<IEntitySource, vector> selfMap = generatorAndPolesPlusParentWorldPosMap.Get(generatorSource);
860 if (!selfMap)
861 return null;
862
863 foreach (IEntitySource poleSource, vector poleWorldPos : selfMap)
864 {
865 if (vector.DistanceSqXZ(worldPos, poleWorldPos) < JUNCTION_DISTANCEXZ_SQ)
866 {
867 isSameLine = true;
868 return poleSource;
869 }
870 }
871
872 foreach (IEntitySource otherGeneratorSource, map<IEntitySource, vector> junctionPosMap : generatorAndPolesPlusParentWorldPosMap)
873 {
874 if (otherGeneratorSource == generatorSource)
875 continue;
876
877 foreach (IEntitySource poleSource, vector poleWorldPos : junctionPosMap)
878 {
879 if (vector.DistanceSqXZ(worldPos, poleWorldPos) < JUNCTION_DISTANCEXZ_SQ)
880 {
881 isSameLine = false;
882 return poleSource;
883 }
884 }
885 }
886
887 return null;
888 }
889
890 //------------------------------------------------------------------------------------------------
896 // TODO: fix cable bug on rotated shape
897 protected static array<IEntitySource> CreatePowerLines(notnull IEntitySource generatorSource, notnull IEntitySource existingPoleSource, notnull IEntitySource addedPoleSource)
898 {
899 WorldEditorAPI worldEditorAPI = GetWorldEditorAPI();
900 if (!worldEditorAPI)
901 return null;
902
903 SCR_PowerPole existingPole = SCR_PowerPole.Cast(worldEditorAPI.SourceToEntity(existingPoleSource));
904 SCR_PowerPole addedPole = SCR_PowerPole.Cast(worldEditorAPI.SourceToEntity(addedPoleSource));
905 vector existingPoleWorldPos = existingPole.GetOrigin();
906 vector addedPoleWorldPos = addedPole.GetOrigin();
907
908 bool isSameLine = existingPoleSource.GetParent() == addedPoleSource.GetParent();
909
910 map<SCR_EPoleCableType, ref SCR_PoleCableSlotGroup> existingPoleGroups = existingPole.GetClosestCableSlotGroupsForEachCableType(addedPoleWorldPos, isSameLine);
911 if (existingPoleGroups.IsEmpty())
912 {
913 Print("No valid slots found on the existing power pole, please check the setup of your power poles.", LogLevel.WARNING);
914
915 if (s_DebugShapeManager)
916 s_DebugShapeManager.AddSphere(existingPoleWorldPos + DEBUG_POLE_POS, DEBUG_POLE_SIZE, DEBUG_POLE_COLOUR_ERROR, ShapeFlags.NOOUTLINE);
917
918 return null;
919 }
920
921 map<SCR_EPoleCableType, ref SCR_PoleCableSlotGroup> addedPoleGroups = addedPole.GetClosestCableSlotGroupsForEachCableType(existingPoleWorldPos, isSameLine);
922 if (addedPoleGroups.IsEmpty())
923 {
924 Print("No valid slots found on the new power pole, please check the setup of your power poles.", LogLevel.WARNING);
925
926 if (s_DebugShapeManager)
927 s_DebugShapeManager.AddSphere(addedPoleWorldPos + DEBUG_POLE_POS, DEBUG_POLE_SIZE, DEBUG_POLE_COLOUR_ERROR, ShapeFlags.NOOUTLINE);
928
929 return null;
930 }
931
932 array<SCR_EPoleCableType> cableTypes = {};
933 if (SCR_Enum.GetEnumValues(SCR_EPoleCableType, cableTypes) < 1) // like, wat
934 return null;
935
936 array<SCR_EPoleCableType> cableTypesInCommon = {};
937 foreach (SCR_EPoleCableType cableType : cableTypes)
938 {
939 if (existingPoleGroups.Contains(cableType) && addedPoleGroups.Contains(cableType))
940 cableTypesInCommon.Insert(cableType);
941
942 // code below could be done here directly, but for clarity's sake, one foreach won't kill perfs
943 }
944
945 if (cableTypesInCommon.IsEmpty())
946 {
947 Print("No cable types in common between two poles - be sure to select compatible poles in Generator Prefab at " + Debug.GetEntityLinkString(existingPole), LogLevel.WARNING);
948
949 if (s_DebugShapeManager)
950 {
951 s_DebugShapeManager.AddSphere(existingPoleWorldPos + DEBUG_POLE_POS, DEBUG_POLE_SIZE, DEBUG_POLE_COLOUR_ERROR, ShapeFlags.NOOUTLINE);
952 s_DebugShapeManager.AddSphere(addedPoleWorldPos + DEBUG_POLE_POS, DEBUG_POLE_SIZE, DEBUG_POLE_COLOUR_ERROR, ShapeFlags.NOOUTLINE);
953 s_DebugShapeManager.AddLine(existingPoleWorldPos + DEBUG_POLE_POS, addedPoleWorldPos + DEBUG_POLE_POS, DEBUG_POLE_COLOUR_ERROR);
954 }
955
956 return null;
957 }
958
959 ResourceName powerLineMaterial;
960 generatorSource.Get("m_PowerlineMaterial", powerLineMaterial);
961
962 ResourceName cablePrefab;
963 generatorSource.Get("m_sPowerLinePrefab", cablePrefab);
964
965 int generatorLayerID = generatorSource.GetLayerID();
966
967 array<ref SCR_PoleCable> poleCables;
968 generatorSource.Get("m_aCables", poleCables);
969
970 string powerLineEntity;
971 if (cablePrefab) // !.IsEmpty()
972 powerLineEntity = cablePrefab;
973 else
974 powerLineEntity = DEFAULT_CABLE_PREFAB;
975
976 array<IEntitySource> result = {};
977
978 array<vector> startPoints = {}; // world position
979 array<vector> endPoints = {}; // world position
980 SCR_PoleCableSlotGroup existingGroup;
981 SCR_PoleCableSlotGroup addedGroup;
982 IEntitySource powerLineEntitySource;
983 IEntity referenceEntity;
984 array<ref ContainerIdPathEntry> containerPath;
985 foreach (SCR_EPoleCableType cableType : cableTypesInCommon)
986 {
987 existingGroup = existingPoleGroups.Get(cableType);
988 int existingPoleCableCount = existingGroup.m_aSlots.Count(); // cannot be zero
989
990 addedGroup = addedPoleGroups.Get(cableType);
991 int addedPoleCableCount = addedGroup.m_aSlots.Count(); // cannot be zero
992
993 int cableCount;
994 if (existingPoleCableCount < addedPoleCableCount)
995 cableCount = existingPoleCableCount;
996 else
997 cableCount = addedPoleCableCount;
998
999 startPoints.Clear();
1000 endPoints.Clear();
1001 for (int i; i < cableCount; ++i)
1002 {
1003 startPoints.Insert(existingPole.CoordToParent(existingGroup.m_aSlots[i].m_vPosition));
1004 endPoints.Insert(addedPole.CoordToParent(addedGroup.m_aSlots[i].m_vPosition));
1005 }
1006
1007 if (s_DebugShapeManager) // warn visually about unused slots
1008 {
1009 for (int i = cableCount; i < existingPoleCableCount; ++i)
1010 {
1011 s_DebugShapeManager.AddSphere(existingPole.CoordToParent(existingGroup.m_aSlots[i].m_vPosition), DEBUG_SLOT_SIZE, DEBUG_SLOT_COLOUR_UNUSED, ShapeFlags.NOOUTLINE);
1012 }
1013
1014 for (int i = cableCount; i < addedPoleCableCount; ++i)
1015 {
1016 s_DebugShapeManager.AddSphere(addedPole.CoordToParent(addedGroup.m_aSlots[i].m_vPosition), DEBUG_SLOT_SIZE, DEBUG_SLOT_COLOUR_UNUSED, ShapeFlags.NOOUTLINE);
1017 }
1018 }
1019
1020 float normalLengthSq;
1021 float reversedLengthSq;
1022 foreach (int i, vector startPoint : startPoints)
1023 {
1024 normalLengthSq += vector.DistanceSq(startPoint, endPoints[i]);
1025 reversedLengthSq += vector.DistanceSq(startPoint, endPoints[cableCount - i - 1]);
1026 }
1027
1028 // don't cross the streams!
1029 if (reversedLengthSq < normalLengthSq)
1030 SCR_ArrayHelperT<vector>.Reverse(endPoints);
1031
1032 if (s_DebugShapeManager)
1033 {
1034 foreach (int i, vector startPoint : startPoints)
1035 {
1036 s_DebugShapeManager.AddSphere(startPoint, DEBUG_SLOT_SIZE, DEBUG_SLOT_COLOUR_POS, ShapeFlags.NOOUTLINE);
1037 s_DebugShapeManager.AddSphere(endPoints[i], DEBUG_SLOT_SIZE, DEBUG_SLOT_COLOUR_POS, ShapeFlags.NOOUTLINE);
1038 s_DebugShapeManager.AddLine(startPoint, endPoints[i], DEBUG_CABLE_COLOUR_POS);
1039 }
1040 }
1041
1042 // power line preparation
1043 powerLineEntitySource = null;
1044
1045 vector startPos, endPos;
1046 // Create cables for the entity
1047
1048 int cableIndex;
1049 foreach (int i, vector startPoint : startPoints)
1050 {
1051 vector endPoint = endPoints[i];
1052
1053 if (powerLineEntitySource)
1054 {
1055 referenceEntity = worldEditorAPI.SourceToEntity(powerLineEntitySource);
1056 startPos = referenceEntity.CoordToLocal(startPoint);
1057 endPos = referenceEntity.CoordToLocal(endPoint);
1058 }
1059 else
1060 {
1061 referenceEntity = worldEditorAPI.SourceToEntity(generatorSource);
1062 startPos = referenceEntity.CoordToLocal(startPoint);
1063 endPos = referenceEntity.CoordToLocal(endPoint);
1064 }
1065
1066 if (vector.DistanceSqXZ(startPos, endPos) < MIN_CABLE_LENGTH_SQ)
1067 {
1068 PrintFormat("Cable's 2D length is too small! Skipping (%1m < %2m)", vector.DistanceXZ(startPos, endPos).ToString(-1, 2), Math.Sqrt(MIN_CABLE_LENGTH_SQ), level: LogLevel.WARNING);
1069
1070 if (s_DebugShapeManager)
1071 {
1072 float localX = existingPole.CoordToLocal(startPoint)[0];
1073 vector forward = vector.Direction(startPoint, endPoint); // not normalised
1074 vector right = -{
1075 forward[0] * Math.Cos(Math.PI_HALF) - forward[2] * Math.Sin(Math.PI_HALF),
1076 0, // Y rotation - 2D only
1077 forward[0] * Math.Sin(Math.PI_HALF) + forward[2] * Math.Cos(Math.PI_HALF),
1078 };
1079
1080 if (localX > DEBUG_CABLE_LENGTH_POS_MAX_DIST)
1081 right *= DEBUG_CABLE_LENGTH_POS_MAX_DIST;
1082 else
1083 right *= localX;
1084
1085 vector spherePos = (startPoint + endPoint) * 0.5 + DEBUG_CABLE_LENGTH_POS + right;
1086
1087 s_DebugShapeManager.AddSphere(spherePos, DEBUG_CABLE_LENGTH_SIZE, DEBUG_CABLE_LENGTH_COLOUR_ERROR_SPHERE, ShapeFlags.NOOUTLINE);
1088 s_DebugShapeManager.AddLine(spherePos, startPoint, DEBUG_CABLE_LENGTH_COLOUR_ERROR_LINE);
1089 s_DebugShapeManager.AddLine(spherePos, endPoint, DEBUG_CABLE_LENGTH_COLOUR_ERROR_LINE);
1090 }
1091
1092 continue;
1093 }
1094
1095 if (vector.DistanceSq(startPos, endPos) > MAX_CABLE_LENGTH_SQ)
1096 {
1097 PrintFormat("Cable's 3D length is too big! Skipping (%1m > %2m)", vector.Distance(startPos, endPos).ToString(-1, 2), Math.Sqrt(MAX_CABLE_LENGTH_SQ), level: LogLevel.WARNING);
1098
1099 if (s_DebugShapeManager)
1100 s_DebugShapeManager.AddLine(startPoint, endPoint, DEBUG_CABLE_LENGTH_COLOUR_ERROR_LINE);
1101
1102 continue;
1103 }
1104
1105 if (!powerLineEntitySource)
1106 {
1107 powerLineEntitySource = worldEditorAPI.CreateEntity(powerLineEntity, string.Empty, generatorLayerID, generatorSource, referenceEntity.CoordToLocal(existingPoleWorldPos), vector.Zero);
1108 if (!powerLineEntitySource)
1109 {
1110 if (powerLineEntity != POWER_LINE_CLASS)
1111 {
1112 Print("CreateEntity returned null trying to create " + powerLineEntity, LogLevel.ERROR);
1113 powerLineEntity = POWER_LINE_CLASS;
1114 powerLineEntitySource = worldEditorAPI.CreateEntity(powerLineEntity, string.Empty, generatorLayerID, generatorSource, referenceEntity.CoordToLocal(existingPoleWorldPos), vector.Zero);
1115 }
1116
1117 if (!powerLineEntitySource)
1118 {
1119 Print("CreateEntity returned null trying to create " + powerLineEntity, LogLevel.ERROR);
1120 return null; // it would fail for the other ones too
1121 }
1122 }
1123
1124 // get the most adapted material
1125 ResourceName cableMaterial;
1126 foreach (SCR_PoleCable cable : poleCables)
1127 {
1128 if (cable.m_eType == cableType && cable.m_sMaterial) // !.IsEmpty()
1129 {
1130 cableMaterial = cable.m_sMaterial;
1131 break;
1132 }
1133 }
1134
1135 if (!cableMaterial)
1136 cableMaterial = powerLineMaterial; // default value
1137
1138 worldEditorAPI.BeginEditSequence(powerLineEntitySource);
1139
1140 if (cableMaterial)
1141 worldEditorAPI.SetVariableValue(powerLineEntitySource, null, "Material", cableMaterial);
1142
1143 referenceEntity = worldEditorAPI.SourceToEntity(powerLineEntitySource);
1144 startPos = referenceEntity.CoordToLocal(startPoint);
1145 endPos = referenceEntity.CoordToLocal(endPoint);
1146 }
1147 else
1148 {
1149 worldEditorAPI.BeginEditSequence(powerLineEntitySource);
1150 }
1151
1152 worldEditorAPI.CreateObjectArrayVariableMember(powerLineEntitySource, null, "Cables", "Cable", cableIndex);
1153 containerPath = { new ContainerIdPathEntry("Cables", cableIndex) };
1154 worldEditorAPI.SetVariableValue(powerLineEntitySource, containerPath, "StartPoint", startPos.ToString(false));
1155 worldEditorAPI.SetVariableValue(powerLineEntitySource, containerPath, "EndPoint", endPos.ToString(false));
1156
1157 worldEditorAPI.EndEditSequence(powerLineEntitySource);
1158
1159 cableIndex++;
1160 }
1161
1162 if (powerLineEntitySource)
1163 result.Insert(powerLineEntitySource);
1164 }
1165
1166 return result;
1167 }
1168
1169 //------------------------------------------------------------------------------------------------
1173 protected static IEntitySource GetPowerPoleSourceFromEntitySource(notnull IEntitySource powerPoleEntitySource)
1174 {
1175 WorldEditorAPI worldEditorAPI = GetWorldEditorAPI();
1176 if (powerPoleEntitySource.GetClassName().ToType().IsInherited(SCR_PowerPole))
1177 return powerPoleEntitySource;
1178
1179 typename childType;
1180 for (int i, childrenCount = powerPoleEntitySource.GetNumChildren(); i < childrenCount; i++)
1181 {
1182 childType = powerPoleEntitySource.GetChild(i).GetClassName().ToType();
1183 if (childType && childType.IsInherited(SCR_PowerPole))
1184 return powerPoleEntitySource.GetChild(i);
1185 }
1186
1187 return null;
1188 }
1189
1190 //------------------------------------------------------------------------------------------------
1191 protected override void _WB_OnParentChange(IEntitySource src, IEntitySource prevParentSrc)
1192 {
1193 super._WB_OnParentChange(src, prevParentSrc);
1194 if (!m_ParentShapeSource)
1195 return;
1196
1197 Generate();
1198 }
1199
1200 //------------------------------------------------------------------------------------------------
1201 protected override void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array<vector> mins, array<vector> maxes)
1202 {
1203 super.OnShapeChangedInternal(shapeEntitySrc, shapeEntity, mins, maxes);
1204
1205 Generate();
1206 }
1207#endif // WORKBENCH
1208}
void ContainerIdPathEntry(string propertyName, int index=-1)
Definition worldEditor.c:30
bool m_bDrawDebugShapes
float m_fRandomRollAngle
float m_fRandomPitchAngle
ref array< string > angles
float m_fDistance
ref RandomGenerator m_RandomGenerator
enum SCR_ECompassType EntityEditorProps(category:"GameScripted/Gadgets", description:"Compass", color:"0 0 255 255")
Prefab data class for compass component.
override bool _WB_OnKeyChanged(IEntity owner, BaseContainer src, string key, BaseContainerList ownerContainers, IEntity parent)
Any property value has been changed. You can use editor API here and do some additional edit actions ...
SCR_EPoleCableType
void Generate()
void SCR_PowerPole(IEntitySource src, IEntity parent)
ResourceName m_sPowerLinePrefab
ResourceName m_PowerlineMaterial
float Clearance
Used by Obstacle Detector.
ResourceName m_StartPole
float m_fRandomYawAngle
bool m_bRotate180DegreeYawEndPole
ResourceName m_DefaultJunctionPole
ref array< ref SCR_PoleCable > m_aCables
ResourceName m_DefaultPole
bool m_bApplyPitchAndRollEnd
ResourceName m_EndPole
bool m_bApplyPitchAndRollDefault
bool m_bApplyPitchAndRollStart
bool m_bRotate180DegreeYawStartPole
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum EVehicleType IEntity
proto external vector GetOrigin()
proto external void GetWorldBounds(out vector mins, out vector maxs)
proto external IEntity GetSibling()
proto external vector CoordToLocal(vector coord)
proto external IEntity GetRootParent()
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
Definition LogLevel.c:14
ShapeFlags
Definition ShapeFlags.c:13
proto void PrintFormat(string fmt, void param1=NULL, void param2=NULL, void param3=NULL, void param4=NULL, void param5=NULL, void param6=NULL, void param7=NULL, void param8=NULL, void param9=NULL, LogLevel level=LogLevel.NORMAL)
SCR_FieldOfViewSettings Attribute
void OnShapeChangedInternal(IEntitySource shapeEntitySrc, ShapeEntity shapeEntity, array< vector > mins, array< vector > maxes)
void Debug()
Definition Types.c:327