2[WorkbenchToolAttribute(
4 "Turns polylines and splines into specific shapes"
5 +
"\nor creates a shape in the middle of the screen"
7 +
"\nTo make a \"diamond\" (rhombus),"
8 +
"\nuse Circle and set the number of points to 4"
10 +
"\nPoints are created clockwise.",
11 awesomeFontCode: 0xF5EE)]
12class SCR_ShapeAreaTool : WorldEditorTool
23 desc:
"Shape type to be created",
24 uiwidget: UIWidgets.ComboBox,
25 enums: SCR_ParamEnumArray.FromString(
"Circle,Can be used to create polygons;Rectangle;Star"),
27 protected int m_iShapeType;
30 protected float m_fWidth;
33 protected float m_fLength;
40 protected int m_iCircleSegmentsCount;
47 protected int m_iRectangleSegmentsPerSide;
54 protected int m_iStarBranchesCount;
56 [
Attribute(defvalue:
"0.5",
desc:
"Star inner radius ratio - e.g 0.75 * width-length radius", uiwidget: UIWidgets.Slider,
params:
"0.01 1",
category:
"Star")]
57 protected float m_fStarInnerRadiusRatio;
64 protected bool m_bSnapToTerrain;
66 [
Attribute(defvalue:
"1",
desc:
"Whether or not points should be around the shape entity's origin or if the points should be in positive position only (shape origin being bottom-left)",
category:
"Points")]
67 protected bool m_bCentreOnPosition;
69 [
Attribute(defvalue:
"0",
desc:
"Define whether to close, open or leave as is the selected shape(s)", uiwidget: UIWidgets.ComboBox, enums: SCR_ParamEnumArray.FromString(
"Leave as is;Open;Close"),
category:
"Points")]
70 protected int m_iShapeClosing;
74 protected void CreatePolyline()
81 protected void CreateSpline()
88 protected void ConvertSelectedShapes()
90 array<IEntitySource> shapeEntitySources = GetSelectedShapeEntitySources();
91 if (shapeEntitySources.IsEmpty())
93 Print(
"[SCR_ShapeAreaTool] No shapes were selected, no conversion needed",
LogLevel.NORMAL);
97 m_API.BeginEntityAction();
99 ConvertShapes(shapeEntitySources);
101 m_API.EndEntityAction();
107 protected void CreateShape(
bool isSpline)
109 vector worldStart, worldPos, worldNormal;
110 if (!m_API.TraceWorldPos(m_API.GetScreenWidth() * 0.5, m_API.GetScreenHeight() * 0.5,
TraceFlags.WORLD, worldStart, worldPos, worldNormal))
113 m_API.BeginEntityAction();
115 IEntitySource shapeEntitySource;
117 shapeEntitySource = m_API.CreateEntity(((
typename)SplineShapeEntity).
ToString(),
string.Empty, m_API.GetCurrentEntityLayerId(), null, worldPos, vector.Zero);
119 shapeEntitySource = m_API.CreateEntity(((
typename)PolylineShapeEntity).
ToString(),
string.Empty, m_API.GetCurrentEntityLayerId(), null, worldPos, vector.Zero);
121 m_API.AddToEntitySelection(shapeEntitySource);
123 ConvertShapes({ shapeEntitySource });
125 m_API.EndEntityAction();
129 protected array<IEntitySource> GetSelectedShapeEntitySources()
131 array<IEntitySource> result = SCR_WorldEditorToolHelper.GetSelectedWorldEntitySources();
134 for (
int i = result.Count() - 1; i >= 0; --i)
136 if (!m_API.SourceToEntity(result[i]).IsInherited(ShapeEntity))
146 protected void ConvertShapes(notnull array<IEntitySource> shapeEntitySources)
148 if (shapeEntitySources.IsEmpty())
151 array<vector> points = GetShapePoints();
153 foreach (IEntitySource shapeEntitySource : shapeEntitySources)
155 bool isSelected = m_API.IsEntitySelected(shapeEntitySource);
157 m_API.RemoveFromEntitySelection(shapeEntitySource);
159 SetShapePoints(shapeEntitySource, points);
161 if (m_iShapeClosing == 1)
162 m_API.ClearVariableValue(shapeEntitySource, null,
"IsClosed");
164 if (m_iShapeClosing == 2)
165 m_API.SetVariableValue(shapeEntitySource, null,
"IsClosed",
"1");
170 m_API.AddToEntitySelection(shapeEntitySource);
175 protected array<vector> GetShapePoints()
177 array<vector> result;
178 if (m_iShapeType == 0)
179 result = GetCirclePoints();
181 if (m_iShapeType == 2)
182 result = GetStarPoints();
184 result = GetRectanglePoints();
186 if (result.IsEmpty())
188 Print(
"Cannot modify shape(s), points calculation went wrong",
LogLevel.ERROR);
192 if (!m_bCentreOnPosition)
194 vector offset = { m_fWidth * 0.5, 0, m_fLength * 0.5 };
195 foreach (
int i, vector point : result)
197 result[i] = point + offset;
205 protected array<vector> GetCirclePoints()
207 array<vector> result = {};
208 float angleSize = Math.PI2 / m_iCircleSegmentsCount;
210 for (
float i = Math.PI_HALF + Math.PI2, min = Math.PI_HALF; i > min; i -= angleSize)
212 if (
float.AlmostEqual(i, min))
215 result.Insert({ Math.Cos(i) * m_fWidth * 0.5, 0, Math.Sin(i) * m_fLength * 0.5 });
222 protected array<vector> GetRectanglePoints()
224 if (m_iRectangleSegmentsPerSide)
226 { m_fWidth * 0.5, 0, m_fLength * 0.5 },
227 { m_fWidth * 0.5, 0, -m_fLength * 0.5 },
228 { -m_fWidth * 0.5, 0, -m_fLength * 0.5 },
229 { -m_fWidth * 0.5, 0, m_fLength * 0.5 },
234 array<vector>
data = {
235 { m_fWidth * 0.5, 0, m_fLength * 0.5 },
236 { 0, 0, -m_fLength / m_iRectangleSegmentsPerSide },
238 { m_fWidth * 0.5, 0, -m_fLength * 0.5 },
239 { -m_fWidth / m_iRectangleSegmentsPerSide, 0, 0 },
241 { -m_fWidth * 0.5, 0, -m_fLength * 0.5 },
242 { 0, 0, m_fLength / m_iRectangleSegmentsPerSide },
244 { -m_fWidth * 0.5, 0, m_fLength * 0.5 },
245 { m_fWidth / m_iRectangleSegmentsPerSide, 0, 0 },
248 array<vector> result = {};
249 for (
int i, count =
data.Count(); i < count; i += 2)
251 vector startPoint =
data[i];
252 vector offset =
data[i + 1];
254 for (
int j = 0; j < m_iRectangleSegmentsPerSide; ++j)
256 result.Insert(startPoint + (
float)j * offset);
264 protected array<vector> GetStarPoints()
266 array<vector> result = {};
267 float angleSize = Math.PI2 / m_iStarBranchesCount;
269 for (
float i = Math.PI_HALF + Math.PI2, min = Math.PI_HALF; i > min; i -= angleSize)
271 if (
float.AlmostEqual(i, min))
275 result.Insert({ Math.Cos(i) * m_fWidth * 0.5, 0, Math.Sin(i) * m_fLength * 0.5 });
276 result.Insert({ Math.Cos(i - 0.5 * angleSize) * m_fWidth * 0.5 * m_fStarInnerRadiusRatio, 0, Math.Sin(i - 0.5 * angleSize) * m_fLength * 0.5 * m_fStarInnerRadiusRatio });
285 protected void SetShapePoints(notnull IEntitySource entitySource, notnull array<vector> points)
287 int originalPointsCount = entitySource.GetObjectArray(
"Points").Count();
290 array<vector> pointsCopy = {};
291 pointsCopy.Copy(points);
292 if (m_bSnapToTerrain)
294 IEntity entity = m_API.SourceToEntity(entitySource);
297 foreach (
int i, vector point : pointsCopy)
301 if (!m_API.TryGetTerrainSurfaceY(terrainPos[0], terrainPos[2], y))
310 Print(
"Cannot snap shape(s) to terrain, entity not found from source",
LogLevel.WARNING);
314 foreach (
int i, vector point : pointsCopy)
316 m_API.CreateObjectArrayVariableMember(entitySource, null,
"Points",
"ShapePoint", i + originalPointsCount);
317 m_API.SetVariableValue(entitySource, {
new ContainerIdPathEntry(
"Points", i + originalPointsCount) },
"Position",
string.Format(
"%1 %2 %3", point[0], point[1], point[2]));
320 for (
int i = originalPointsCount - 1; i >= 0; --i)
322 m_API.RemoveObjectArrayVariableMember(entitySource, null,
"Points", i);
void ContainerIdPathEntry(string propertyName, int index=-1)
Get all prefabs that have the spawner data
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum EVehicleType IEntity
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
proto external vector CoordToParent(vector coord)
proto external vector CoordToLocal(vector coord)
proto void Print(void var, LogLevel level=LogLevel.NORMAL)
Prints content of variable to console/log.
LogLevel
Enum with severity of the logging message.
SCR_FieldOfViewSettings Attribute
proto external string ToString()
Plain C++ pointer, no weak pointers, no memory management.