Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_FloatersFinderPlugin.c
Go to the documentation of this file.
1#ifdef WORKBENCH
3 name: "Floaters Finder",
4 category: SCR_PluginCategory.WORLDEDITOR_ENTITY_CHECK,
5 wbModules: { "WorldEditor" },
6 shortcut: "Ctrl+Alt+Page Up",
7 awesomeFontCode: 0xF338)] // 0xF338 = ↨
8class SCR_FloatersFinderPlugin : WorkbenchPlugin
9{
10 /*
11 Search
12 */
13
14 [Attribute(defvalue: "1", desc: "Only search in the currently active layer", category: "Search")]
15 protected bool m_bActiveLayerOnly;
16
17 [Attribute(defvalue: "1", desc: "Check for entities entirely below terrain by bounding box vertices' altitude", category: "Search")]
18 protected bool m_bSearchForEntitiesBelowTerrain;
19
20 [Attribute(defvalue: "1", desc: "Look for misplaced vegetation (trees & bushes) - floating up or too deep in the ground, etc.", category: "Search")]
21 protected bool m_bSearchForVegetation;
22
23// [Attribute(defvalue: "1", desc: "Check for out-of-bounds entities", category: "Search")]
24// protected bool m_bSearchForOutOfBoundsEntities;
25
26 // TODO - static buildings, destructible buildings
27// [Attribute(defvalue: "1", desc: "Look for buildings entering the terrain", category: "Search")]
28// protected bool m_bSearchForBuildings;
29
30 [Attribute(defvalue: "false", desc: "If set, uses the Prefab's random angle as acceptable margin; otherwise, uses below setting", category: "Angle")]
31 protected bool m_bUsePrefabAngles;
32
33 [Attribute(defvalue: "180", uiwidget: UIWidgets.Slider, desc: "Maximum allowed angle to not consider the tree as fallen. 0 = fully vertical tree, 90 = horizontal tree, 180 = no angle check (needs \"Use Prefab Angles\" unchecked)", params: "0 180 0.5", category: "Angle", precision: 1)]
34 protected float m_fMaxTreeAngle;
35
36 /*
37 Vertical Offset
38 */
39
40 [Attribute(defvalue: "1", desc: "Check object's altitude from OBJECTS below it (2× slower as it uses Trace)", category: "Vertical Offset")]
41 protected bool m_bCheckAboveEntitiesSurface;
42
43 [Attribute(defvalue: "0", desc: "If set, uses the Prefab's vertical offset range; otherwise, uses below settings", category: "Vertical Offset")]
44 protected bool m_bUsePrefabVerticalOffset;
45
46 [Attribute(defvalue: "-1", desc: "Minimum (included) vertical offset (needs \"Use Prefab Vertical Offset\" unchecked)", params: "-100 100 0.1", category: "Vertical Offset", precision: 1)]
47 protected float m_fMinVerticalOffset;
48
49 [Attribute(defvalue: "0.5", desc: "Maximum (included) vertical offset (needs \"Use Prefab Vertical Offset\" unchecked)", params: "-100 100 0.1", category: "Vertical Offset", precision: 1)]
50 protected float m_fMaxVerticalOffset;
51
52 [Attribute(defvalue: "0", desc: "Check for items being below water level (Ocean only for now). If under water, whatever the offset, the entity will be selected", category: "Vertical Offset")]
53 protected bool m_bCheckBelowWater;
54
55 /*
56 Performance
57 */
58
59 [Attribute(defvalue: "1000", uiwidget: UIWidgets.Slider, desc: "Search radius around camera position - 0 = all entities", params: "0 5000 1", category: "Performance")]
60 protected int m_iCameraSearchRadius;
61
62 [Attribute(defvalue: "1", desc: "Show radius sphere on entity search", category: "Performance")]
63 protected bool m_bShowSearchRadiusSphere;
64
65 [Attribute(defvalue: "2000", uiwidget: UIWidgets.Slider, desc: "Maximum number of selected entities (UI performance)", params: "1 5000 1", category: "Performance")]
66 protected int m_iMaxSelectedEntities;
67
68 [Attribute(defvalue: "1", desc: "[requires Search For Vegetation] Trace only checks for the vegetation's full height, allowing it to be below a bridge but may be missing a small tree entirely inside a rock", category: "Performance")]
69 protected bool m_bTraceVegetationPrecisely;
70
71 [Attribute(defvalue: "50", uiwidget: UIWidgets.Slider, desc: "Trace origin's distance (in metres) from above the entity to determine if it is below another entity AND Trace Vegetation Precisely is unchecked", params: "10 500 10", category: "Performance")]
72 protected int m_iTraceOriginDistance;
73
74 /*
75 Output
76 */
77
78 [Attribute(defvalue: "0", desc: "If ticked, write a file with links to all findings.", category: "Output")]
79 protected bool m_bOutputFindingsToFile;
80
81 [Attribute(defvalue: "0", desc: "Prefix file links with https prefix", category: "Output")]
82 protected bool m_bUseWebPrefix;
83
84 protected static const ref array<IEntitySource> WORLD_ENTITIES = {}; // non-nullable due to const
85 protected static ref Shape s_DetectionRadiusSphere;
86
87 protected static const int DEBUG_COLOUR = 0x99025D00;
88 protected static const int DEBUG_DURATION = 333; // ms
89 protected static const string OUTPUT_FILENAME = "FoundFloatingEntities.txt";
90
91 //------------------------------------------------------------------------------------------------
92 override void Run()
93 {
94 if (!Init())
95 return;
96
97 Print("Floaters Finder - Run method started", LogLevel.NORMAL);
98
99 int firstTick;
100 WORLD_ENTITIES.Clear();
101
102 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
103 BaseWorld baseWorld = worldEditorAPI.GetWorld();
104 bool useSelectedEntities = worldEditorAPI.GetSelectedEntity() != null;
105 if (useSelectedEntities) // use selected entities
106 {
107 for (int i, cnt = worldEditorAPI.GetSelectedEntitiesCount(); i < cnt; i++)
108 {
109 WORLD_ENTITIES.Insert(worldEditorAPI.GetSelectedEntity(i));
110 }
111
112 PrintFormat("Going with the current selection of %1 entities", WORLD_ENTITIES.Count(), level: LogLevel.NORMAL);
113 }
114 else // detect entities
115 {
116 firstTick = System.GetTickCount();
117 GetEntities(baseWorld);
118 PrintFormat("Entity getter duration: %1ms for %2 entities", System.GetTickCount() - firstTick, WORLD_ENTITIES.Count(), level: LogLevel.NORMAL);
119 }
120
121 // filter
122 array<IEntitySource> filteredEntities = {};
123 firstTick = System.GetTickCount();
124 FilterEntities(baseWorld, filteredEntities);
125 PrintFormat("Entity filter duration: %1ms for %2 entities (output: %3 entities)", System.GetTickCount() - firstTick, WORLD_ENTITIES.Count(), filteredEntities.Count(), level: LogLevel.NORMAL);
126
127 // select all found entities in UI
128 SelectEntities(filteredEntities);
129
130 // show after time-consuming operations in order to not "blink" it
131 if (m_bShowSearchRadiusSphere && !useSelectedEntities && m_iCameraSearchRadius > 0)
132 {
133 vector cameraMatrix[4];
134 baseWorld.GetCurrentCamera(cameraMatrix);
135 s_DetectionRadiusSphere = Shape.CreateSphere(DEBUG_COLOUR, ShapeFlags.BACKFACE | ShapeFlags.NOOUTLINE | ShapeFlags.TRANSP, cameraMatrix[3], m_iCameraSearchRadius);
136 }
137
138 // final report
139 if (worldEditorAPI.GetSelectedEntitiesCount() != filteredEntities.Count())
140 {
141 Print(
142 string.Format(
143 "Cannot select all entities: Treated %1, Detected %2, Selected %3",
144 WORLD_ENTITIES.Count(),
145 filteredEntities.Count(),
146 worldEditorAPI.GetSelectedEntitiesCount()),
147 LogLevel.WARNING);
148 }
149 else
150 {
151 Print(
152 string.Format(
153 "%1 entities treated, %2 selected properly",
154 WORLD_ENTITIES.Count(),
155 filteredEntities.Count()),
156 LogLevel.NORMAL);
157 }
158
159 // output to file if set to do so
160 if (m_bOutputFindingsToFile)
161 {
162 firstTick = System.GetTickCount();
163 OutputEntitiesToFile(filteredEntities);
164 Print(
165 string.Format(
166 "%1 entities successfully written to file %2 in %3ms",
167 filteredEntities.Count(),
168 FilePath.ToSystemFormat(OUTPUT_FILENAME),
169 System.GetTickCount() - firstTick),
170 LogLevel.NORMAL);
171 }
172
173 Print("Floaters Finder - Run method ended", LogLevel.NORMAL);
174
175 // delete
176 if (s_DetectionRadiusSphere)
177 {
178 Sleep(DEBUG_DURATION);
179 s_DetectionRadiusSphere = null;
180 }
181 }
182
183 //------------------------------------------------------------------------------------------------
184 protected bool Init()
185 {
186 if (!SCR_Global.IsEditMode())
187 {
188 Print("Floaters Finder - Run method stopped because non-Workbench run", LogLevel.NORMAL);
189 return false;
190 }
191
192 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
193 if (worldEditor.IsPrefabEditMode())
194 {
195 Print("Floaters Finder - Run method stopped because World Editor is in Prefab edit mode", LogLevel.NORMAL);
196 return false;
197 }
198
199 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
200 if (!worldEditorAPI)
201 {
202 Print("Floaters Finder - Run method stopped because World Editor API was not found", LogLevel.WARNING);
203 return false;
204 }
205
206 BaseWorld baseWorld = worldEditorAPI.GetWorld();
207 if (!baseWorld)
208 {
209 Print("Floaters Finder - Run method stopped because base world was not found", LogLevel.WARNING);
210 return false;
211 }
212
213 return true;
214 }
215
216 //------------------------------------------------------------------------------------------------
217 protected void GetEntities(BaseWorld baseWorld)
218 {
219 if (m_iCameraSearchRadius > 0)
220 {
221 vector cameraMatrix[4];
222 baseWorld.GetCurrentCamera(cameraMatrix);
223 baseWorld.QueryEntitiesBySphere(cameraMatrix[3], m_iCameraSearchRadius, InsertEntity);
224 }
225 else
226 {
227 vector minPos, maxPos;
228 baseWorld.GetBoundBox(minPos, maxPos);
229 baseWorld.QueryEntitiesByAABB(minPos, maxPos, InsertEntity);
230 }
231 }
232
233 //------------------------------------------------------------------------------------------------
234 protected bool InsertEntity(notnull IEntity entity)
235 {
236 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
237 WORLD_ENTITIES.Insert(worldEditorAPI.EntityToSource(entity));
238 return true;
239 }
240
241 //------------------------------------------------------------------------------------------------
242 protected void FilterEntities(notnull BaseWorld baseWorld, notnull out array<IEntitySource> filteredEntities)
243 {
244 BaseContainerList editorData;
245 BaseContainer firstEditorData;
246 BaseContainer ancestorContainer;
247 vector randomVerticalOffset;
248 bool isVegetation;
249 bool insertEntity;
250
251 int placementMode;
252
253 bool isOcean = baseWorld.IsOcean();
254 float oceanHeight = baseWorld.GetOceanBaseHeight();
255
256 vector entityPos, bboxMin, bboxMax, tempPos;
257 vector bboxCorners[15]; // 8 vertices + 6 face centres + 1 centre
258 float altitude, terrainY, tempTerrainY;
259
260 TraceParam traceParam = new TraceParam();
261 float traceRatio;
262 float minVerticalOffset = m_fMinVerticalOffset;
263 float maxVerticalOffset = m_fMaxVerticalOffset;
264 float maxPitch = m_fMaxTreeAngle;
265 float maxRoll = m_fMaxTreeAngle;
266
267 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
268
269 bool manyEntities = WORLD_ENTITIES.Count() > 10;
270 int currentLayerId = worldEditorAPI.GetCurrentEntityLayerId();
271 int underWaterNb, verticalOffsetNb, angleOffsetNb, fullyUndergroundNb;
272
273 filteredEntities.Clear();
274 foreach (IEntitySource entitySource : WORLD_ENTITIES)
275 {
276 if (!m_bOutputFindingsToFile && filteredEntities.Count() >= m_iMaxSelectedEntities)
277 break;
278
279 if (!entitySource)
280 continue;
281
282 if (m_bActiveLayerOnly && entitySource.GetLayerID() != currentLayerId)
283 continue;
284
285 IEntity entity = worldEditorAPI.SourceToEntity(entitySource);
286 isVegetation = Tree.Cast(entity) != null;
287
288 if (!(m_bSearchForEntitiesBelowTerrain || isVegetation))
289 continue;
290
291 if (isVegetation && entitySource.Get("placement", placementMode) && placementMode == 1) // 1 = slopelandcontact
292 continue;
293
294 minVerticalOffset = m_fMinVerticalOffset;
295 maxVerticalOffset = m_fMaxVerticalOffset;
296 maxPitch = m_fMaxTreeAngle;
297 maxRoll = m_fMaxTreeAngle;
298
299 editorData = entitySource.GetObjectArray("editorData");
300 if (editorData && editorData.Count() > 0)
301 {
302 firstEditorData = editorData.Get(0);
303
304 if (m_bUsePrefabVerticalOffset)
305 {
306 firstEditorData.Get("randomVertOffset", randomVerticalOffset);
307 minVerticalOffset = randomVerticalOffset[0];
308 maxVerticalOffset = randomVerticalOffset[1];
309 }
310
311 if (m_bUsePrefabAngles)
312 {
313 firstEditorData.Get("randomPitchAngle", maxPitch);
314 firstEditorData.Get("randomRollAngle", maxRoll);
315 }
316 }
317
318 insertEntity = false;
319
320 entityPos = entity.GetOrigin();
321 terrainY = worldEditorAPI.GetTerrainSurfaceY(entityPos[0], entityPos[2]);
322
323 // underwater check - needs WORLD coords
324 if (!insertEntity && m_bCheckBelowWater && isOcean && oceanHeight > entityPos[1])
325 {
326 if (!manyEntities)
327 Print("below water level", LogLevel.NORMAL);
328
329 underWaterNb++;
330 insertEntity = true;
331 }
332
333 // angles check - if out of angle, leave checks
334 if (!insertEntity && isVegetation && (m_bUsePrefabAngles || m_fMaxTreeAngle < 180))
335 {
336 vector angles;
337 if (entitySource.Get("angles", angles)
338 && (
339 angles[0] < -maxRoll || angles[2] < -maxPitch || angles[0] > maxRoll || angles[2] > maxPitch
340 )
341 )
342 {
343 if (!manyEntities)
344 PrintFormat("ignoring pitch %1/%2 / roll %3/%4", angles[2], maxPitch, angles[0], maxRoll, level: LogLevel.NORMAL);
345
346 angleOffsetNb++;
347 continue;
348 }
349 }
350
351 // terrain pos/neg Y distance check
352 altitude = entityPos[1] - terrainY;
353 entity.GetBounds(bboxMin, bboxMax);
354
355 // bbox checking
356 if (
357 !insertEntity &&
358 m_bSearchForEntitiesBelowTerrain &&
359 altitude < 0 &&
360 (bboxMin != vector.Zero || bboxMax != vector.Zero) &&
361 !LakeGeneratorEntity.Cast(entity) && // no lakes as some lakes' centre is not exposed
362 !ShapeEntity.Cast(entity) && // no spline or polyline
363 (
364 !entity.GetParent() ||
365 (
366 !entity.GetParent().IsInherited(BaseBuilding) &&
367 !entity.GetParent().IsInherited(StaticModelEntity) // no undeletable prefab's sub-entity
368 )
369 )
370 )
371 {
372 // vertices
373 bboxCorners[0] = Vector(bboxMin[0], bboxMax[1], bboxMax[2]); // top-front-left
374 bboxCorners[1] = Vector(bboxMax[0], bboxMax[1], bboxMax[2]); // top-front-right
375 bboxCorners[2] = Vector(bboxMin[0], bboxMax[1], bboxMin[2]); // top-back-left
376 bboxCorners[3] = Vector(bboxMax[0], bboxMax[1], bboxMin[2]); // top-back-right
377 bboxCorners[4] = Vector(bboxMin[0], bboxMin[1], bboxMax[2]); // bottom-front-left
378 bboxCorners[5] = Vector(bboxMax[0], bboxMin[1], bboxMax[2]); // bottom-front-right
379 bboxCorners[6] = Vector(bboxMin[0], bboxMin[1], bboxMin[2]); // bottom-back-left
380 bboxCorners[7] = Vector(bboxMax[0], bboxMin[1], bboxMin[2]); // bottom-back-right
381
382 // box centre
383 bboxCorners[14] = Vector((bboxMax[0] + bboxMin[1]) * 0.5, (bboxMax[1] + bboxMin[1]) * 0.5, (bboxMax[2] + bboxMin[2]) * 0.5);
384
385 // face centres
386 for (int i = 8; i < 14; i++) // creating then editing vectors is faster than inline-calculating each
387 {
388 bboxCorners[i] = bboxCorners[14];
389 }
390
391 bboxCorners[08][2] = bboxMax[2]; // front
392 bboxCorners[09][0] = bboxMax[0]; // right
393 bboxCorners[10][2] = bboxMin[2]; // back
394 bboxCorners[11][0] = bboxMin[0]; // left
395 bboxCorners[12][1] = bboxMax[1]; // top
396 bboxCorners[13][1] = bboxMin[1]; // bottom
397
398 insertEntity = true;
399 for (int i; i < 15; i++)
400 {
401 tempPos = entity.CoordToParent(bboxCorners[i]);
402 tempTerrainY = baseWorld.GetSurfaceY(tempPos[0], tempPos[2]);
403 if (tempPos[1] > tempTerrainY)
404 {
405 insertEntity = false;
406 break;
407 }
408 }
409
410 if (insertEntity)
411 fullyUndergroundNb++;
412 }
413
414 if (!insertEntity && isVegetation && m_bSearchForVegetation)
415 {
416 if (m_bCheckAboveEntitiesSurface && altitude >= minVerticalOffset) // trace trace
417 {
418 // TODO: align trace with entity's Y axis (entity.CoordToParent)… AND check relative altitude (two traces?)
419 traceParam.Start = entityPos;
420
421 if (m_bTraceVegetationPrecisely || bboxMax[1] > m_iTraceOriginDistance)
422 traceParam.Start[1] = traceParam.Start[1] + bboxMax[1];
423 else
424 traceParam.Start[1] = traceParam.Start[1] + m_iTraceOriginDistance;
425
426 if (traceParam.Start[1] > terrainY)
427 {
428 traceParam.End = entityPos;
429 traceParam.End[1] = terrainY;
430 traceParam.Flags = TraceFlags.ENTS;
431 traceParam.Exclude = entity;
432 traceRatio = baseWorld.TraceMove(traceParam, NoVegetationFilterCallback);
433 if (!manyEntities)
434 {
435 Print("----- Tracing -----", LogLevel.NORMAL);
436 PrintFormat("OBJ %1", entityPos, level: LogLevel.NORMAL);
437 PrintFormat("FROM %1", traceParam.Start, level: LogLevel.NORMAL);
438 PrintFormat("TO %1", traceParam.End, level: LogLevel.NORMAL);
440 "DONE %1pct (%2m/%3m)",
441 Math.Round(traceRatio * 10000) * 0.01,
442 vector.Distance(traceParam.Start, traceParam.End) * traceRatio,
443 vector.Distance(traceParam.Start, traceParam.End),
444 level: LogLevel.NORMAL);
445 }
446
447 altitude -= (traceParam.Start[1] - traceParam.End[1]) * (1 - traceRatio);
448 }
449 }
450
451 insertEntity = altitude > maxVerticalOffset || altitude < minVerticalOffset || traceParam.Start[1] <= terrainY;
452 if (insertEntity)
453 {
454 if (!manyEntities)
455 PrintFormat("altitude %1 DOES NOT MATCH the [%2, %3] range", altitude, minVerticalOffset, maxVerticalOffset, level: LogLevel.NORMAL);
456
457 verticalOffsetNb++;
458 }
459 }
460
461 if (!insertEntity)
462 continue;
463
464 filteredEntities.Insert(entitySource);
465 }
466
467 Print(
468 string.Format(
469 "Filtered: %1 below water level, %2 vertical offset, %3 entirely underground, ignored %4 angle offset entities",
470 underWaterNb,
471 verticalOffsetNb,
472 fullyUndergroundNb,
473 angleOffsetNb),
474 LogLevel.NORMAL);
475 }
476
477 //------------------------------------------------------------------------------------------------
478 protected bool NoVegetationFilterCallback(notnull IEntity entity, vector start = "0 0 0", vector dir = "0 0 0")
479 {
480 return Tree.Cast(entity) == null;
481 }
482
483 //------------------------------------------------------------------------------------------------
484 protected void SelectEntities(notnull array<IEntitySource> entities)
485 {
486 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
487 worldEditorAPI.ClearEntitySelection();
488 for (int i, cnt = Math.Min(m_iMaxSelectedEntities, entities.Count()); i < cnt; i++)
489 {
490 worldEditorAPI.AddToEntitySelection(entities[i]);
491 }
492
493 worldEditorAPI.UpdateSelectionGui();
494 }
495
496 //------------------------------------------------------------------------------------------------
497 protected void OutputEntitiesToFile(notnull array<IEntitySource> filteredEntities)
498 {
499 WorldEditorAPI worldEditorAPI = SCR_WorldEditorToolHelper.GetWorldEditorAPI();
500
501 string worldName;
502 worldEditorAPI.GetWorldPath(worldName);
503
504 string link;
505 if (filteredEntities.IsEmpty())
506 link = ".";
507 else
508 link = ":";
509
510 FileHandle fileHandle = FileIO.OpenFile(OUTPUT_FILENAME, FileMode.WRITE);
511 fileHandle.WriteLine("===========================================================================");
512 fileHandle.WriteLine("===== File generated by Floaters Finder Plugin on " + SCR_DateTimeHelper.GetDateTimeLocal() + " =====");
513 fileHandle.WriteLine("===========================================================================");
514 fileHandle.WriteLine(string.Empty);
515 fileHandle.WriteLine(filteredEntities.Count().ToString() + " misplaced entities were found on " + worldName + " terrain" + link);
516 if (!filteredEntities.IsEmpty())
517 fileHandle.WriteLine(string.Empty);
518
519 IEntity entity;
520 vector transformation[4];
521 transformation[0] = vector.Right;
522 transformation[1] = vector.Up;
523 transformation[2] = vector.Forward; // point North
524 vector bboxMin, bboxMax, centre;
525 foreach (IEntitySource entitySource : filteredEntities)
526 {
527 entity = worldEditorAPI.SourceToEntity(entitySource);
528 entity.GetBounds(bboxMin, bboxMax);
529 float diagonal = (bboxMax - bboxMin).Length();
530 if (diagonal < 5)
531 diagonal = 5;
532
533 centre = entity.CoordToParent((vector)(bboxMax + bboxMin) * 0.5);
534 transformation[3] = centre - 1.25 * diagonal * vector.Forward; // camera South by 1.25×'diagonal' metres
535
536 fileHandle.WriteLine(
537 SCR_WorldEditorToolHelper.GetCurrentWorldEditorLink(
538 transformation[3],
539 Math3D.MatrixToAngles(transformation),
540 m_bUseWebPrefix));
541 }
542
543 fileHandle.Close();
544 }
545
546 //------------------------------------------------------------------------------------------------
547 protected override void Configure()
548 {
549 Workbench.ScriptDialog("Configure 'Floaters Finder' plugin", "", this);
550 }
551
552 //------------------------------------------------------------------------------------------------
553 [ButtonAttribute("Close", true)]
554 protected bool ButtonOK()
555 {
556 return true;
557 }
558}
559#endif // WORKBENCH
override void Init()
GenerateFlowMaps WorkbenchPlugin WorkbenchPluginAttribute("Regenerate river flow-maps", "Generate and save/overwrite river flow-maps", "", "", {"WorldEditor"}, "", 0xf773)
Definition FlowmapTool.c:59
ref array< string > angles
params precision
override void Run()
bool ButtonOK()
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
enum EVehicleType IEntity
override void Configure()
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
proto external vector GetOrigin()
proto external void GetBounds(out vector mins, out vector maxs)
proto external IEntity GetParent()
proto external vector CoordToParent(vector coord)
@ Tree
Definition gameLib.c:47
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
FileMode
Mode for opening file. See FileSystem::Open.
Definition FileMode.c:14
proto native vector Vector(float x, float y, float z)
TraceFlags
Definition TraceFlags.c:13