Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_ObjectBrushTool.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[WorkbenchToolAttribute(
3 name: "Object Brush",
4 description: "Generate randomized compositions using a brush.\n" +
5 "- click and drag to draw\n" +
6 "- space to delete brush-created entities\n" +
7 "- alt+click then click to create a (wide) line of entities (ESC to cancel)",
8 // shortcut: "B", // unused
9 wbModules: { "WorldEditor" },
10 awesomeFontCode: 0xF1FC)]
11class SCR_ObjectBrushTool : WorldEditorTool
12{
13 protected static const float HECTARE_CONVERSION_FACTOR = 0.0001; // x/10000 (hectare is 100×100m)
14 protected static const float MAX_SCALE_THRESHOLD = 1000;
15
16 protected static const float RADIUS_STEP = 1;
17 protected static const float RADIUS_MAX = 100;
18 protected static const float RADIUS_MIN = 0.1;
19
20 protected static const float STRENGTH_STEP = 1;
21 protected static const float STRENGTH_MAX = 500;
22 protected static const float STRENGTH_MIN = 0;
23
24 protected static const float STRENGTH_RELATIVE_RADIUS_DISTANCE_TO_CREATE = 1 / 3;
25
26 /*
27 Category: OBJECT BRUSH
28 */
29
30 [Attribute(defvalue: "10", uiwidget: UIWidgets.Slider, desc: "Radius of the brush", params: string.Format("%1 %2 %3", RADIUS_MIN, RADIUS_MAX, RADIUS_STEP), category: "Object Brush")]
31 protected float m_fRadius;
32
33 [Attribute(defvalue: "10", uiwidget: UIWidgets.Slider, desc: "Strength of the brush (objects per hectare (100×100m))", params: string.Format("%1 %2 %3", STRENGTH_MIN, STRENGTH_MAX, STRENGTH_STEP), category: "Object Brush")]
34 protected float m_fStrength;
35
36 [Attribute(uiwidget: UIWidgets.CurveDialog, desc: "Used to determine the scale fall off", category: "Object Brush")]
37 protected ref Curve m_ScaleFallOffCurve;
38
39 [Attribute(defvalue: "0", desc: "Apply density fall off to the brush", category: "Object Brush")]
40 protected bool m_bDensityFallOffEnabled;
41
42 [Attribute(uiwidget: UIWidgets.CurveDialog, desc: "Used to determine the density fall off", category: "Object Brush")]
43 protected ref Curve m_DensityFallOffCurve;
44
45 [Attribute(defvalue: "10", uiwidget: UIWidgets.SpinBox, desc: "Sets how many sub areas will be made; higher amount of subareas will result in a more fluid density fall off", params: "1 100 1", category: "Object Brush")]
46 protected int m_iDensityFallOffSubareaCount;
47
48 [Attribute(defvalue: "0", desc: "Overwrite older brush strokes", category: "Object Brush")]
49 protected bool m_bOverrideBrush;
50
51 [Attribute(desc: "Define objects to paint - can take a SCR_ObjectBrushArrayConfig .conf", category: "Object Brush")]
52 protected ref SCR_ObjectBrushArrayConfig m_ObjectsConfig;
53
54 /*
55 Category: Obstacles
56 */
57
58 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid static objects", category: "Obstacles")]
59 protected bool m_bAvoidObjects;
60
61 [Attribute(defvalue: "0.1", uiwidget: UIWidgets.Slider, desc: "Object avoidance detection cylinder radius", params: "0 100 0.1", category: "Obstacles")]
62 protected float m_fAvoidObjectsDetectionRadius;
63
64 [Attribute(defvalue: "100", uiwidget: UIWidgets.Slider, desc: "Object avoidance detection cylinder height", params: "0 1000 100", category: "Obstacles")]
65 protected float m_fAvoidObjectsDetectionHeight;
66
67 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid roads", category: "Obstacles")]
68 protected bool m_bAvoidRoads;
69
70 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid rivers", category: "Obstacles")]
71 protected bool m_bAvoidRivers;
72
73 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid power lines", category: "Obstacles")]
74 protected bool m_bAvoidPowerLines;
75
76 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid land", category: "Obstacles")]
77 protected bool m_bAvoidLand;
78
79 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid ocean", category: "Obstacles")]
80 protected bool m_bAvoidOcean;
81
82 /*
83 Category: Obstacles - Area
84 */
85
86 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid forests. Depends on Area Detection Radius", category: "Obstacles - Area")]
87 protected bool m_bAvoidForests;
88
89 [Attribute(defvalue: "0", desc: "Objects generated by the brush will avoid lakes. Depends on Area Detection Radius", category: "Obstacles - Area")]
90 protected bool m_bAvoidLakes;
91
92 [Attribute(defvalue: "100", uiwidget: UIWidgets.Slider, "Radius to detect areas (forests and lakes) around the brush's starting point for avoidance - performance setting. Zero is world-wide detection", "0 1000 100", category: "Obstacles - Area")]
93 protected int m_iAreaDetectionRadius;
94
95#ifdef DEBUG
96 protected ref array<ref Shape> m_aDebugShapes = {};
97#endif // DEBUG
98
99 protected ref SCR_ObstacleDetector m_ObstacleDetector;
100
103
104 protected bool m_bIsMouseHeldDown;
105
107 // the parameter determines the granularity of the grid. This results in the granularity being world size / 10.
108 protected ref ForestGeneratorGrid m_Grid = new ForestGeneratorGrid(10);
109
110 protected ref Shape m_BrushShape;
111 protected ref array<ref Shape> m_aLineShapes = {};
112
113 protected bool m_bDeleteMode; //<! is Space pressed
114 protected bool m_bLineMode; //<! is Alt pressed
115 protected bool m_bAreasDetectedByWorld; //<! were the area detected by world's BBox
116 protected bool m_bManageEditAction; //<! is doing an action
117
118 protected vector m_vFirstLinePoint;
119 protected vector m_vSecondLinePoint;
120
121 protected vector m_vLastMousePosition;
122 protected vector m_vLastObjectCreationCentrePosition;
123
124 protected int m_iBrushShapeColor = ARGB(255, 0, 255, 0);
125
126 //------------------------------------------------------------------------------------------------
132 protected void CreateObjects(float mouseX, float mouseY, vector position)
133 {
134 if (!m_API)
135 {
136 Print("m_API is null", LogLevel.ERROR);
137 return;
138 }
139
140 // safety that should never trigger
141 if (!m_API.IsDoingEditAction())
142 {
143 Print("Workbench isn't performing edit action!", LogLevel.WARNING);
144 return;
145 }
146
147 if (!m_ObjectsConfig || !m_ObjectsConfig.m_aObjectArray || m_ObjectsConfig.m_aObjectArray.IsEmpty())
148 return; // silent stop - config warning managed in OnMousePressEvent
149
150 float oneDividedByFallOffCount = 1 / m_iDensityFallOffSubareaCount;
151 float radiusDividedByFallOffCount = m_fRadius * oneDividedByFallOffCount;
152
153 array<ref SCR_ObjectBrushObjectBase> objectsCreationData = {};
154
155 if (m_bDensityFallOffEnabled)
156 {
157 float subareaRadius = radiusDividedByFallOffCount;
158 float previousArea = 0;
159 float curvePoint = 0;
160 float totalArea = Math.PI * m_fRadius * m_fRadius * HECTARE_CONVERSION_FACTOR;
161 int objectGenerationAttemptMaximumAmount = Math.Ceil(totalArea * m_fStrength);
162
163 SCR_ObjectBrushObjectBase obj;
164 for (int i = 0; i < m_iDensityFallOffSubareaCount; i++)
165 {
166 float area = Math.PI * subareaRadius * subareaRadius * HECTARE_CONVERSION_FACTOR;
167 area -= previousArea;
168 previousArea += area;
169 subareaRadius += radiusDividedByFallOffCount;
170
171 float densityFallOffMultiplier = LegacyCurve.Curve(ECurveType.CurveProperty2D, Math.Clamp(curvePoint, 0, 1), m_DensityFallOffCurve)[1];
172
173 int objectGenerationAttemptsForSubarea = Math.Ceil(area * m_fStrength * densityFallOffMultiplier);
174 if (objectGenerationAttemptMaximumAmount < objectGenerationAttemptsForSubarea)
175 objectGenerationAttemptsForSubarea = objectGenerationAttemptMaximumAmount;
176
177 for (int x = 0; x < objectGenerationAttemptsForSubarea; x++)
178 {
179 obj = GetRandomBrushObjectData();
180 if (obj)
181 {
182 obj.m_iSubareaIndex = i + 1;
183 objectsCreationData.Insert(obj);
184 }
185 }
186
187 curvePoint += oneDividedByFallOffCount;
188 }
189 }
190 else
191 {
192 float area = Math.PI * m_fRadius * m_fRadius * HECTARE_CONVERSION_FACTOR;
193 int objectGenerationAttempts = Math.Ceil(area * m_fStrength);
194
195 SCR_ObjectBrushObjectBase obj;
196 for (int x = 0; x < objectGenerationAttempts; x++)
197 {
198 obj = GetRandomBrushObjectData();
199 if (obj)
200 {
201 obj.m_iSubareaIndex = 1;
202 objectsCreationData.Insert(obj);
203 }
204 }
205 }
206
207 if (m_bOverrideBrush)
208 DeleteObjects(position);
209
210 IEntity entity;
211 IEntitySource entitySource;
212 EntityID entityID;
213
215
216 vector traceStart;
217 vector traceEndManual;
218 vector traceDir;
219 vector point;
220 vector transformTemp[4];
221
222 if (position == vector.Zero)
223 m_API.TraceWorldPos(mouseX, mouseY, TraceFlags.WORLD, traceStart, traceEndManual, traceDir);
224 else
225 traceEndManual = position;
226
227 m_vLastObjectCreationCentrePosition = traceEndManual;
228
229 // area splines detected in OnMousePressEvent
230 // non-area splines sphere detection here
231 m_ObstacleDetector.RefreshRoadObstaclesBySphere(traceEndManual, m_fRadius);
232
233 // if not world bbox detection (that is eventually done in OnMousePressEvent), refresh by sphere
234 if (m_iAreaDetectionRadius > 0 && (m_bAvoidForests || m_bAvoidLakes))
235 {
236 m_ObstacleDetector.RefreshAreaObstaclesBySphere(traceEndManual, m_iAreaDetectionRadius);
237 m_bAreasDetectedByWorld = false;
238 }
239
240 BaseWorld world = m_API.GetWorld();
241
242 foreach (SCR_ObjectBrushObjectBase obj : objectsCreationData)
243 {
244 if (!obj.m_Prefab) // .IsEmpty() is slower (-:
245 continue;
246
247 if (m_bDensityFallOffEnabled)
248 {
249 float minDistanceFromCenter = radiusDividedByFallOffCount * (obj.m_iSubareaIndex - 1);
250 float maxDistanceFromCenter = radiusDividedByFallOffCount * obj.m_iSubareaIndex;
251 point = m_RandomGenerator.GenerateRandomPointInRadius(minDistanceFromCenter, maxDistanceFromCenter, traceEndManual);
252 }
253 else
254 {
255 point = m_RandomGenerator.GenerateRandomPointInRadius(0, m_fRadius, traceEndManual);
256 }
257
258#ifdef DEBUG
259 m_aDebugShapes.Insert(CreateCircle(point, vector.Up, 0.5, ARGB(255, 0, 0, 255), 4, ShapeFlags.NOZBUFFER));
260#endif // DEBUG
261
262 if (m_Grid.IsColliding(point, obj))
263 continue;
264
265 float terrainY = world.GetSurfaceY(point[0], point[2]);
266 point[1] = terrainY;
267
268 if (m_ObstacleDetector.HasObstacle(point))
269 continue;
270
271 float scale;
272
273 if (!obj.m_bOverrideRandomization)
274 {
275 entitySource = m_API.CreateEntityExt(obj.m_Prefab, "", m_API.GetCurrentEntityLayerId(), null, point, vector.Zero, TraceFlags.WORLD);
276 if (!entitySource)
277 {
278 Print("Could not create entity from prefab", LogLevel.WARNING);
279 continue;
280 }
281
282 scale = m_API.SourceToEntity(entitySource).GetScale();
283 }
284 else
285 {
286 entitySource = m_API.CreateEntity(obj.m_Prefab, "", m_API.GetCurrentEntityLayerId(), null, point, vector.Zero);
287 if (!entitySource)
288 {
289 Print("Could not create entity from prefab", LogLevel.WARNING);
290 continue;
291 }
292
293 if (obj.m_fMinScale == obj.m_fMaxScale)
294 {
295 scale = obj.m_fMinScale;
296 }
297 else
298 {
299 if (obj.m_fMinScale < obj.m_fMaxScale)
300 scale = m_RandomGenerator.RandFloatXY(obj.m_fMinScale, obj.m_fMaxScale);
301 else
302 scale = m_RandomGenerator.RandFloatXY(obj.m_fMaxScale, obj.m_fMinScale);
303 }
304 }
305
306 if (obj.m_bScaleFalloff)
307 {
308 if (m_fRadius <= 0)
309 {
310 Print("Please set radius to a value higher than 0", LogLevel.WARNING);
311 return;
312 }
313
314 float distanceFromCenter = vector.DistanceXZ(traceEndManual, point);
315 float distanceFromCenterInPercent = distanceFromCenter / (m_fRadius * 0.01);
316 float scaleFallOffMultiplier = LegacyCurve.Curve(ECurveType.CurveProperty2D, Math.Clamp(distanceFromCenterInPercent * 0.01, 0, 1), m_ScaleFallOffCurve)[1];
317
318 scale *= scaleFallOffMultiplier;
319
320 if (obj.m_fLowestScaleFalloffValue > obj.m_fMaxScale)
321 Print("Lowest scale fall off value for " + obj.m_Prefab + " is higher than max scale value! Check parameters!", LogLevel.WARNING);
322
323 if (scale < obj.m_fLowestScaleFalloffValue)
324 scale = obj.m_fLowestScaleFalloffValue;
325
326 if (scale > MAX_SCALE_THRESHOLD)
327 scale = MAX_SCALE_THRESHOLD;
328 }
329
330 int flags;
331 if (entitySource.Get("Flags", flags)) // set point's Y value depending on entitySource's RELATIVE_Y flag
332 { // 0 if relative, otherwise terrain surface
333 if (flags & EntityFlags.RELATIVE_Y)
334 point[1] = 0;
335 else
336 point[1] = terrainY;
337 }
338
339 point[1] = point[1] + obj.m_fPrefabOffsetY;
340 if (obj.m_fMinRandomVerticalOffset != 0 || obj.m_fMaxRandomVerticalOffset != 0)
341 point[1] = point[1] + m_RandomGenerator.RandFloatXY(obj.m_fMinRandomVerticalOffset, obj.m_fMaxRandomVerticalOffset);
342
343 m_API.SetVariableValue(entitySource, null, "coords", point.ToString(false));
344
345 angles = vector.Zero;
346
347 if (obj.m_bAlignToNormal)
348 {
349 // allow for random Yaw
350 if (obj.m_bOverrideRandomization && obj.m_fRandomYawAngle > 0)
351 {
352 if (entitySource.Get("angles", angles))
353 {
354 angles[1] = m_RandomGenerator.RandFloatXY(-obj.m_fRandomYawAngle, obj.m_fRandomYawAngle);
355 m_API.SetVariableValue(entitySource, null, "angles", string.Format("%1 %2 %3", angles[0], angles[1], angles[2]));
356 }
357 }
358
359 vector mat[4];
360 m_API.SourceToEntity(entitySource).GetWorldTransform(mat);
362 angles = Math3D.MatrixToAngles(mat);
363 }
364 else if (obj.m_bOverrideRandomization)
365 {
366 if (obj.m_fRandomPitchAngle > 0)
367 angles[1] = m_RandomGenerator.RandFloatXY(-obj.m_fRandomPitchAngle, obj.m_fRandomPitchAngle);
368
369 if (obj.m_fRandomYawAngle > 0)
370 angles[0] = m_RandomGenerator.RandFloatXY(-obj.m_fRandomYawAngle, obj.m_fRandomYawAngle);
371
372 if (obj.m_fRandomRollAngle > 0)
373 angles[2] = m_RandomGenerator.RandFloatXY(-obj.m_fRandomRollAngle, obj.m_fRandomRollAngle);
374 }
375
376 m_Grid.AddEntry(obj, point);
377
378 if (scale != 1)
379 m_API.SetVariableValue(entitySource, null, "scale", scale.ToString());
380
381 if (angles != vector.Zero)
382 m_API.SetVariableValue(entitySource, null, "angles", string.Format("%1 %2 %3", angles[1], angles[0], angles[2]));
383
384 entityID = m_API.SourceToEntity(entitySource).GetID();
385 m_mCreatedObjects.Insert(obj, entityID);
386 m_mActiveBrushObjects.Insert(obj, entityID);
387 }
388 }
389
390 //------------------------------------------------------------------------------------------------
393 protected void DeleteObjects(vector position)
394 {
395 if (m_mCreatedObjects.IsEmpty() || position == vector.Zero)
396 return;
397
399 BaseWorld world = m_API.GetWorld();
400
401 IEntity entity;
402 vector entOrigin;
403 foreach (SCR_ObjectBrushObjectBase obj, EntityID entityID : m_mCreatedObjects)
404 {
405 entity = world.FindEntityByID(entityID);
406 if (!entity)
407 {
408 objectsToDelete.Insert(obj, entityID);
409 continue;
410 }
411
412 entOrigin = entity.GetOrigin();
413 float diff = vector.DistanceXZ(entOrigin, position);
414
415 if (diff <= m_fRadius)
416 {
417 if (m_bOverrideBrush)
418 {
419 if (!m_mActiveBrushObjects.Contains(obj))
420 objectsToDelete.Insert(obj, entityID);
421 }
422 else
423 {
424 objectsToDelete.Insert(obj, entityID);
425 }
426 }
427 }
428
429 if (objectsToDelete.IsEmpty())
430 return;
431
432 bool manageEditAction = SCR_WorldEditorToolHelper.BeginEntityAction();
433
434 foreach (SCR_ObjectBrushObjectBase obj, EntityID entID : objectsToDelete)
435 {
436 entity = world.FindEntityByID(entID);
437
438 m_Grid.RemoveEntry(obj);
439 if (entity)
440 m_API.DeleteEntity(m_API.EntityToSource(entity));
441
442 m_mCreatedObjects.Remove(obj);
443 }
444
445 SCR_WorldEditorToolHelper.EndEntityAction(manageEditAction);
446 }
447
448 //------------------------------------------------------------------------------------------------
450 [ButtonAttribute("Delete created entities")]
451 protected void DeleteEntities()
452 {
453 if (m_mCreatedObjects.IsEmpty())
454 return;
455
456 bool manageEditAction = SCR_WorldEditorToolHelper.BeginEntityAction();
457
458 BaseWorld world = m_API.GetWorld();
459 if (!world)
460 {
461 Print("World is null", LogLevel.ERROR);
462 return;
463 }
464
465 IEntity entity;
466 foreach (EntityID entityID : m_mCreatedObjects)
467 {
468 entity = world.FindEntityByID(entityID);
469 if (entity)
470 m_API.DeleteEntity(m_API.EntityToSource(entity));
471 }
472
473 OnActivate();
474
475 m_mCreatedObjects.Clear();
476
477#ifdef DEBUG
478 m_aDebugShapes.Clear();
479#endif // DEBUG
480
481 SCR_WorldEditorToolHelper.EndEntityAction(manageEditAction);
482 }
483
484 //------------------------------------------------------------------------------------------------
491 override void OnMousePressEvent(float x, float y, WETMouseButtonFlag buttons)
492 {
493 if (buttons != WETMouseButtonFlag.LEFT)
494 return;
495
496 string worldPath;
497 m_API.GetWorldPath(worldPath);
498 if (worldPath.IsEmpty())
499 {
500 Print("A proper world is not loaded", LogLevel.WARNING);
501 return;
502 }
503
504 if (!m_ObjectsConfig || !m_ObjectsConfig.m_aObjectArray || m_ObjectsConfig.m_aObjectArray.IsEmpty())
505 {
506 Print("Object Brush's Objects Config is not filled", LogLevel.WARNING);
507 return;
508 }
509
510 vector traceStart, traceEnd, traceDir;
511 m_API.TraceWorldPos(x, y, TraceFlags.WORLD, traceStart, traceEnd, traceDir);
512
513 m_bIsMouseHeldDown = true;
515
516 BaseWorld world = m_API.GetWorld();
517
518 // this very operation loop takes some time (~500ms for ~2000 entities) when Ctrl+Z a big amount of entities
519 foreach (SCR_ObjectBrushObjectBase objBase, EntityID entityID : m_mCreatedObjects)
520 {
521 if (world.FindEntityByID(entityID))
522 continue;
523
524 m_Grid.RemoveEntry(objBase);
525 m_mCreatedObjects.Remove(objBase);
526 }
527
528 // create/check the Obstacle Detector on each click to consider any settings change
529 CreateAndInitialiseObstacleDetector();
530
531 // non-area splines sphere detected in CreateObjects
532 // area splines detected here
533 if (!m_bAreasDetectedByWorld && (m_bAvoidForests || m_bAvoidLakes) && m_iAreaDetectionRadius < 1)
534 {
535 m_ObstacleDetector.RefreshAreaObstaclesByWorld();
536 m_bAreasDetectedByWorld = true;
537 }
538
539 // normal click
540 if (!m_bLineMode && !GetModifierKeyState(ModifierKey.ALT))
541 {
542 m_bManageEditAction = SCR_WorldEditorToolHelper.BeginEntityAction(); // ended in OnMouseRelease
543
544 m_mActiveBrushObjects.Clear();
545
546 if (m_bDeleteMode)
547 DeleteObjects(traceEnd);
548 else
549 CreateObjects(x, y, traceEnd);
550 }
551 else // in lineMode or Alt is pressed, draw the entity line
552 {
553 if (m_vFirstLinePoint == vector.Zero)
554 {
555 m_bLineMode = true;
556 m_vFirstLinePoint = traceEnd;
557 return;
558 }
559
560 m_bLineMode = false;
561
562 bool manageEditAction = SCR_WorldEditorToolHelper.BeginEntityAction();
563
564 m_mActiveBrushObjects.Clear();
565
566 m_vSecondLinePoint = traceEnd;
567
568 float dist = vector.Distance(m_vFirstLinePoint, m_vSecondLinePoint);
569 vector dir = vector.Direction(m_vFirstLinePoint, m_vSecondLinePoint);
570 dir.Normalize();
571
572 vector point = m_vFirstLinePoint;
573
574#ifdef DEBUG
575 vector p[2];
576 p[0] = m_vFirstLinePoint;
577 p[1] = m_vSecondLinePoint;
578 m_aDebugShapes.Insert(Shape.CreateLines(ARGB(255, 255, 0, 0), ShapeFlags.NOZBUFFER, p, 2));
579 m_aDebugShapes.Insert(CreateCircle(m_vFirstLinePoint, vector.Up, 2, ARGB(255, 0, 255, 0), 4, ShapeFlags.NOZBUFFER));
580 m_aDebugShapes.Insert(CreateCircle(m_vSecondLinePoint, vector.Up, 2, ARGB(255, 0, 255, 0), 4, ShapeFlags.NOZBUFFER));
581#endif // DEBUG
582
583 if (m_bDeleteMode)
584 DeleteObjects(point);
585 else
586 CreateObjects(x, y, point);
587
588 float halfRadius = m_fRadius * 0.5;
589 while (dist - halfRadius > 0)
590 {
591 point = point + dir * halfRadius;
592 dist -= halfRadius;
593
594#ifdef DEBUG
595 m_aDebugShapes.Insert(CreateCircle(point, vector.Up, 1, ARGB(255, 0, 255, 0), 12, ShapeFlags.NOZBUFFER));
596#endif // DEBUG
597
598 if (m_bDeleteMode)
599 DeleteObjects(point);
600 else
601 CreateObjects(x, y, point);
602 }
603
604 if (dist > m_fRadius * 0.25)
605 {
606#ifdef DEBUG
607 m_aDebugShapes.Insert(CreateCircle(m_vSecondLinePoint, vector.Up, 1, ARGB(255, 0, 255, 0), 12, ShapeFlags.NOZBUFFER));
608#endif // DEBUG
609
610 if (m_bDeleteMode)
611 DeleteObjects(m_vSecondLinePoint);
612 else
613 CreateObjects(x, y, m_vSecondLinePoint);
614 }
615
616 m_vFirstLinePoint = vector.Zero;
617 m_vSecondLinePoint = vector.Zero;
618
619 SCR_WorldEditorToolHelper.EndEntityAction(manageEditAction);
620 }
621 }
622
623 //------------------------------------------------------------------------------------------------
630 override void OnMouseReleaseEvent(float x, float y, WETMouseButtonFlag buttons)
631 {
632 if (buttons != WETMouseButtonFlag.LEFT)
633 return;
634
635 m_bIsMouseHeldDown = false;
636 m_vLastObjectCreationCentrePosition = vector.Zero;
637
638 SCR_WorldEditorToolHelper.EndEntityAction(m_bManageEditAction); // ending OnMousePressEvent's normal click
639 }
640
641 //------------------------------------------------------------------------------------------------
647 override void OnMouseMoveEvent(float x, float y)
648 {
649 m_aLineShapes.Clear();
650
651 vector traceStart, traceEnd, traceDir;
652 m_API.TraceWorldPos(x, y, TraceFlags.WORLD, traceStart, traceEnd, traceDir);
653
654 m_vLastMousePosition = traceEnd;
655
656 if (m_bDeleteMode)
657 m_iBrushShapeColor = ARGB(255, 255, 0, 0);
658 else
659 m_iBrushShapeColor = ARGB(255, 0, 255, 0);
660
661 m_BrushShape = CreateCircle(traceEnd, vector.Up, m_fRadius, m_iBrushShapeColor, 50, ShapeFlags.NOZBUFFER);
662
663 // in Alt+Click mode
664 if (m_vFirstLinePoint != vector.Zero)
665 {
666 m_aLineShapes.Insert(CreateCircle(m_vFirstLinePoint, vector.Up, m_fRadius, m_iBrushShapeColor, 50, ShapeFlags.NOZBUFFER));
667
668 vector fromTo = (traceEnd - m_vFirstLinePoint).Normalized();
669 fromTo = fromTo * m_fRadius;
670
671 vector points[2];
672 points[0] = m_vFirstLinePoint + fromTo;
673 points[1] = traceEnd - fromTo;
674
675 // middle line
676 if (vector.DistanceXZ(m_vFirstLinePoint, traceEnd) > m_fRadius * 2)
677 m_aLineShapes.Insert(Shape.CreateLines(m_iBrushShapeColor, ShapeFlags.NOZBUFFER, points, 2));
678
679 // left border
680 vector offset = { -fromTo[2], 0, fromTo[0] };
681 points[0] = m_vFirstLinePoint + offset;
682 points[1] = traceEnd + offset;
683 m_aLineShapes.Insert(Shape.CreateLines(m_iBrushShapeColor, ShapeFlags.NOZBUFFER, points, 2));
684
685 // right border
686 offset = { fromTo[2], 0, -fromTo[0] };
687 points[0] = m_vFirstLinePoint + offset;
688 points[1] = traceEnd + offset;
689 m_aLineShapes.Insert(Shape.CreateLines(m_iBrushShapeColor, ShapeFlags.NOZBUFFER, points, 2));
690 }
691
692 if (!m_bIsMouseHeldDown)
693 return;
694
695 if (m_bDeleteMode)
696 DeleteObjects(traceEnd);
697 else if (m_vLastObjectCreationCentrePosition != vector.Zero && vector.DistanceXZ(m_vLastMousePosition, m_vLastObjectCreationCentrePosition) >= STRENGTH_RELATIVE_RADIUS_DISTANCE_TO_CREATE * m_fRadius)
698 CreateObjects(x, y, traceEnd);
699 }
700
701 //------------------------------------------------------------------------------------------------
705 override void OnWheelEvent(int delta)
706 {
707 // adjusts m_fRadius value using a CTRL + Scrollwheel keybind
708 if (GetModifierKeyState(ModifierKey.CONTROL))
709 {
710 m_fRadius = AdjustValueUsingScrollwheel(delta, m_fRadius, RADIUS_MIN, RADIUS_MAX, RADIUS_STEP);
711 m_BrushShape = CreateCircle(m_vLastMousePosition, vector.Up, m_fRadius, m_iBrushShapeColor, 50, ShapeFlags.NOZBUFFER);
712 UpdatePropertyPanel();
713 }
714
715 // adjusts m_fStrength value using a SHIFT + Scrollwheel keybind
716 if (GetModifierKeyState(ModifierKey.SHIFT))
717 {
718 m_fStrength = AdjustValueUsingScrollwheel(delta, m_fStrength, STRENGTH_MIN, STRENGTH_MAX, STRENGTH_STEP);
719 UpdatePropertyPanel();
720 }
721 }
722
723 //------------------------------------------------------------------------------------------------
728 override void OnKeyPressEvent(KeyCode key, bool isAutoRepeat)
729 {
730 if (key == KeyCode.KC_SPACE)
731 {
732 m_bDeleteMode = true;
733 m_iBrushShapeColor = ARGB(255, 255, 0, 0);
734 m_BrushShape = CreateCircle(m_vLastMousePosition, vector.Up, m_fRadius, m_iBrushShapeColor, 50, ShapeFlags.NOZBUFFER);
735 }
736 else if (key == KeyCode.KC_ESCAPE)
737 {
738 if (m_bLineMode) // Alt+click line-drawing mode
739 {
740 m_bLineMode = false;
741 m_vFirstLinePoint = vector.Zero;
742 m_aLineShapes.Clear();
743 }
744 }
745 }
746
747 //------------------------------------------------------------------------------------------------
752 override void OnKeyReleaseEvent(KeyCode key, bool isAutoRepeat)
753 {
754 if (key == KeyCode.KC_SPACE)
755 {
756 m_bDeleteMode = false;
757 m_iBrushShapeColor = ARGB(255, 0, 255, 0);
758 m_BrushShape = CreateCircle(m_vLastMousePosition, vector.Up, m_fRadius, m_iBrushShapeColor, 50, ShapeFlags.NOZBUFFER);
759 }
760 }
761
762 //------------------------------------------------------------------------------------------------
765 override void OnDeActivate()
766 {
767 m_BrushShape = null;
768 m_bAreasDetectedByWorld = false; // one could go create a Forest or a Lake, let's re-check
769 }
770
771 //------------------------------------------------------------------------------------------------
774 override void OnActivate()
775 {
776 m_Grid.Clear();
777
778 if (!m_API.GetWorld())
779 return;
780
781 vector terrainMin, terrainMax;
782 m_API.GetWorld().GetBoundBox(terrainMin, terrainMax);
783
784 float x = terrainMax[0] - terrainMin[0];
785 float z = terrainMax[2] - terrainMin[2];
786
787 m_Grid.Resize(x, z);
788 }
789
790 //------------------------------------------------------------------------------------------------
794 protected SCR_ObjectBrushObjectBase GetRandomBrushObjectData()
795 {
796 if (m_ObjectsConfig.m_aObjectArray.Count() == 1)
797 {
798 if (m_ObjectsConfig.m_aObjectArray[0].m_fWeight <= 0)
799 return null;
800
801 return SCR_ObjectBrushObjectBase.Cast(m_ObjectsConfig.m_aObjectArray[0].Clone());
802 }
803
804 array<float> weights = {};
805 foreach (SCR_ObjectBrushObjectBase obj : m_ObjectsConfig.m_aObjectArray)
806 {
807 weights.Insert(obj.m_fWeight);
808 }
809
810 int index = SCR_ArrayHelper.GetWeightedIndex(weights, m_RandomGenerator.RandFloat01());
811 if (!m_ObjectsConfig.m_aObjectArray.IsIndexValid(index))
812 return null;
813
814 SCR_ObjectBrushObjectBase result = m_ObjectsConfig.m_aObjectArray[index];
815 if (!result)
816 return null;
817
818 return SCR_ObjectBrushObjectBase.Cast(result.Clone());
819 }
820
821 //------------------------------------------------------------------------------------------------
829 protected float AdjustValueUsingScrollwheel(float delta, float currentValue, float min, float max, float step)
830 {
831 // delta returns multiples of 120 - converting it into a more useable value of multiples of 1
832 float value = currentValue + (delta / 120) * step;
833
834 if (value < min)
835 return min;
836
837 if (value > max)
838 return max;
839
840 return value;
841 }
842
843 //------------------------------------------------------------------------------------------------
846 protected void CreateAndInitialiseObstacleDetector()
847 {
848 if (!m_ObstacleDetector)
849 {
850 m_ObstacleDetector = new SCR_ObstacleDetector();
851 m_bAreasDetectedByWorld = false;
852 }
853
854 m_ObstacleDetector.SetAvoidObjects(m_bAvoidObjects);
855 m_ObstacleDetector.SetAvoidObjectsDetectionRadius(m_fAvoidObjectsDetectionRadius);
856 m_ObstacleDetector.SetAvoidObjectsDetectionHeight(m_fAvoidObjectsDetectionHeight);
857 m_ObstacleDetector.SetAvoidRoads(m_bAvoidRoads);
858 m_ObstacleDetector.SetAvoidRivers(m_bAvoidRivers);
859 m_ObstacleDetector.SetAvoidPowerLines(m_bAvoidPowerLines);
860 m_ObstacleDetector.SetAvoidForests(m_bAvoidForests);
861 m_ObstacleDetector.SetAvoidLakes(m_bAvoidLakes);
862 m_ObstacleDetector.SetAvoidLand(m_bAvoidLand);
863 m_ObstacleDetector.SetAvoidOcean(m_bAvoidOcean);
864 }
865}
866#endif // WORKBENCH
SCR_EAIThreatSectorFlags flags
vector scale
Shape CreateCircle(vector pos, vector aroundDir, float radius, int color, int subdivisions, ShapeFlags flags)
Definition DebugShapes.c:91
ref array< string > angles
ref RandomGenerator m_RandomGenerator
override void OnActivate()
vector position
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
Definition Math.c:22
proto external vector GetOrigin()
Definition Math.c:13
static bool OrientToTerrain(out vector transform[4], BaseWorld world=null, bool noUnderwater=false, TraceParam trace=null)
Instance of created debug visualizer.
Definition Shape.c:14
Definition Types.c:486
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
SCR_FieldOfViewSettings Attribute
EntityFlags
Various entity flags.
Definition EntityFlags.c:14
KeyCode
Definition KeyCode.c:13
ECurveType
Definition ECurveType.c:13
TraceFlags
Definition TraceFlags.c:13
proto int ARGB(int a, int r, int g, int b)