Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
SCR_EntityReplacerTool.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[WorkbenchToolAttribute(name: "Entity Replacer", wbModules: { "WorldEditor" }, shortcut: "Ctrl+H", awesomeFontCode: 0xF362,
3description:
4"- Use Ctrl+Click to add to the 'To Replace' array\n"
5+ "- Use Ctrl+Shift+Click to remove from the 'To Replace' array\n"
6+ "\n"
7+ "- Use Alt+Click to add to the 'Replace With' array\n"
8+ "- Use Alt+Shift+Click to remove from the 'Replace With' array\n"
9+ "\n"
10+ "- Use Shift+Click to add to selection\n"
11+ "- Click anywhere to deselect everything\n"
12+ "- In order to Replace from Selected Entities, selected objects types must still be added to the 'To Replace' array!")]
13class SCR_EntityReplacerTool : WorldEditorTool
14{
15 [Attribute(desc: "Which entities will be ", category: "Replacement Options")]
16 protected ref array<ref EntityReplacerTool_ToReplace> m_aToReplace;
17
18 [Attribute(category: "Replacement Options")]
19 protected ref array<ref EntityReplacerTool_ReplaceWith> m_aReplaceWith;
20
21 [Attribute(defvalue: "0", desc: "If checked, will replace in ToReplace → ReplaceWith order (item 1 replaced with item 1, etc). If not, select a random ReplaceWith entry.\nIf there are more ToReplace than ReplaceWith entries, the overflow will be selected randomly.", category: "Replacement Options")]
22 protected bool m_bReplaceInOrder;
23
24 [Attribute(defvalue: "0", desc: "Only search/replace in the currently active layer", category: "Replacement Options")]
25 protected bool m_bActiveLayerOnly;
26
27 [Attribute(defvalue: "2000", desc: "Maximum replacement entities selected after the operation. If set to 0, will not change selection.", params: "0 10000 100", category: "Performance")]
28 protected int m_iMaxSelectedEntities;
29
30 protected static const ref array<IEntitySource> ENTITIES_TO_PARSE = {};
31 protected static const ref RandomGenerator RANDOM_GENERATOR = new RandomGenerator();
32
33 //------------------------------------------------------------------------------------------------
34 [ButtonAttribute("Replace from Selected Entities")]
35 protected void ReplaceFromSelectedEntities()
36 {
37 ReplaceFromEntities(false);
38 }
39
40 //------------------------------------------------------------------------------------------------
41 [ButtonAttribute("Replace from All Entities")]
42 protected void ReplaceFromAllEntities()
43 {
44 ReplaceFromEntities(true);
45 }
46
47 //------------------------------------------------------------------------------------------------
48 protected void ReplaceFromEntities(bool allEntities = true)
49 {
50 // can't happen with a WorldEditorTool?
52 {
53 Print("World Editor not in Edit mode", LogLevel.WARNING);
54 return;
55 }
56
57 RemoveInvalidResourceNames();
58 WorldEditorTool.UpdatePropertyPanel();
59
60 if (m_aToReplace.IsEmpty())
61 {
62 Print("No Entities to replace defined", LogLevel.WARNING);
63 return;
64 }
65
66 if (m_aReplaceWith.IsEmpty())
67 {
68 Print("No replacement Entities defined", LogLevel.WARNING);
69 return;
70 }
71
72 if (!m_API)
73 {
74 Print("No World Editor API", LogLevel.WARNING);
75 return;
76 }
77
78 BaseWorld baseWorld = m_API.GetWorld();
79
80 Debug.BeginTimeMeasure();
81 if (allEntities)
82 {
83 GetAllEntities(baseWorld);
84 }
85 else
86 {
87 ENTITIES_TO_PARSE.Clear();
88 for (int i = 0, cnt = m_API.GetSelectedEntitiesCount(); i < cnt; i++)
89 {
90 ENTITIES_TO_PARSE.Insert(m_API.GetSelectedEntity(i));
91 }
92 }
93 Debug.EndTimeMeasure(string.Format("%1 entities found", ENTITIES_TO_PARSE.Count()));
94
95 if (ENTITIES_TO_PARSE.IsEmpty())
96 {
97 Print("No entities found, stopping", LogLevel.NORMAL);
98 return;
99 }
100
101 // array of entities to be LATER deleted (children references etc)
102 array<IEntitySource> sourcesToDelete = {};
103
104 m_API.BeginEntityAction("Replace Entities");
105
106 // create first
107 Debug.BeginTimeMeasure();
108 array<IEntitySource> createdEntitySources = CreateReplacements(baseWorld, sourcesToDelete);
109 Debug.EndTimeMeasure(string.Format("%1 entities created", createdEntitySources.Count()));
110
111 // delete then
112 Debug.BeginTimeMeasure();
113 m_API.DeleteEntities(sourcesToDelete);
114 Debug.EndTimeMeasure(string.Format("%1 entities deleted", createdEntitySources.Count()));
115
116 if (m_iMaxSelectedEntities > 0)
117 SelectEntities(createdEntitySources);
118
119 m_API.EndEntityAction();
120 ENTITIES_TO_PARSE.Clear();
121
122 Print("Replacement done", LogLevel.NORMAL);
123 }
124
125 //------------------------------------------------------------------------------------------------
126 protected array<IEntitySource> CreateReplacements(BaseWorld baseWorld, notnull out array<IEntitySource> toDelete)
127 {
128 array<float> weights = {};
129 foreach (EntityReplacerTool_ReplaceWith replaceWith : m_aReplaceWith)
130 {
131 weights.Insert(replaceWith.m_fReplacementWeight);
132 }
133
134 int currentLayerId = m_API.GetCurrentEntityLayerId();
135 int replaceWithCount = m_aReplaceWith.Count();
136
137 IEntitySource parentSource, childSource, createdEntitySource;
138 BaseContainer ancestor;
140 int replaceIndex, entityLayerId;
141 string name;
142 vector coords, angles, worldPos, finalCoords;
143
144 array<IEntitySource> createdEntitySources = {};
145 float terrainY;
146
147 int totalChildOperationTime, childTick;
148 int totalCreationOperationTime = System.GetTickCount();
149 foreach (IEntitySource originalEntitySource : ENTITIES_TO_PARSE)
150 {
151 entityLayerId = originalEntitySource.GetLayerID();
152 if (m_bActiveLayerOnly && entityLayerId != currentLayerId)
153 continue;
154
155 ancestor = originalEntitySource.GetAncestor();
156 if (!ancestor)
157 continue;
158
159 resourceName = ancestor.GetResourceName();
160 replaceIndex = GetToReplaceIndex(resourceName);
161 if (replaceIndex < 0)
162 continue;
163
164 // chances of replacement
165 if (m_aToReplace[replaceIndex].m_fReplacementPercentage * 0.01 < RANDOM_GENERATOR.RandFloat01())
166 continue;
167
168 if (!m_bReplaceInOrder || replaceIndex > replaceWithCount - 1)
169 replaceIndex = SCR_ArrayHelper.GetWeightedIndex(weights, RANDOM_GENERATOR.RandFloat01());
170
171 IEntity originalEntity = m_API.SourceToEntity(originalEntitySource);
172 coords = originalEntity.GetOrigin();
173 parentSource = originalEntitySource.GetParent();
174 if (parentSource)
175 coords = SCR_BaseContainerTools.GetLocalCoords(parentSource, coords);
176 angles = originalEntity.GetAngles();
177
178 name = originalEntity.GetName();
179 originalEntity.SetName(string.Empty); // prevents conflict
180
181 // using CreateEntity and not CreateEntityExt for exact placement
182 createdEntitySource = m_API.CreateEntity(m_aReplaceWith[replaceIndex].m_sResourceName, name, entityLayerId, parentSource, coords, angles);
183
184 if (parentSource)
185 worldPos = m_API.SourceToEntity(parentSource).CoordToParent(coords);
186 else
187 worldPos = coords;
188
189 if (m_API.SourceToEntity(createdEntitySource).GetFlags() & EntityFlags.RELATIVE_Y)
190 {
191 terrainY = baseWorld.GetSurfaceY(worldPos[0], worldPos[2]);
192
193 if (parentSource)
194 finalCoords = coords;
195 else
196 finalCoords = worldPos;
197
198 finalCoords[1] = finalCoords[1] - terrainY;
199 m_API.SetVariableValue(createdEntitySource, null, "coords", finalCoords.ToString(false));
200 }
201
202 childTick = System.GetTickCount();
203 // move children to new parent
204 for (int j = 0, childrenCount = originalEntitySource.GetNumChildren(); j < childrenCount; j++)
205 {
206 childSource = originalEntitySource.GetChild(j);
207 if (!childSource)
208 continue;
209
210 m_API.ParentEntity(originalEntitySource, childSource, true);
211 }
212 totalChildOperationTime += System.GetTickCount() - childTick;
213
214 createdEntitySources.Insert(createdEntitySource);
215 toDelete.Insert(originalEntitySource);
216 }
217 totalCreationOperationTime = System.GetTickCount() - totalCreationOperationTime;
218
219 Print("creation operation time = " + totalCreationOperationTime + "ms", LogLevel.NORMAL);
220
221 int pct;
222 if (totalCreationOperationTime > 0)
223 pct = Math.Round(totalChildOperationTime * 10000.0 / totalCreationOperationTime) * 0.01;
224
225 Print("children operation time = " + totalChildOperationTime + "ms (" + pct + "pct)", LogLevel.NORMAL);
226
227 return createdEntitySources;
228 }
229
230 //------------------------------------------------------------------------------------------------
231 protected override void OnMousePressEvent(float x, float y, WETMouseButtonFlag buttons)
232 {
233 if (buttons != WETMouseButtonFlag.LEFT)
234 return;
235
236 string worldPath;
237 m_API.GetWorldPath(worldPath);
238 if (worldPath.IsEmpty())
239 return;
240
241 IEntity entity;
242 vector start, end, normal;
243 m_API.TraceWorldPos(x, y, TraceFlags.ENTS | TraceFlags.WORLD, start, end, normal, entity);
244 if (entity && entity.Type() == GenericTerrainEntity)
245 entity = null;
246
247 bool toReplaceOperation = GetModifierKeyState(ModifierKey.CONTROL);
248 bool replaceWithOperation = GetModifierKeyState(ModifierKey.ALT);
249 bool isShiftPressed = GetModifierKeyState(ModifierKey.SHIFT);
250
251 IEntitySource entitySource = m_API.EntityToSource(entity);
252
253 // allow selection - also explains how to use the tool on wrong controls combination
254 if (toReplaceOperation == replaceWithOperation)
255 {
256 if (toReplaceOperation && replaceWithOperation) // both bools kept for readability
257 {
258 Print("Use Ctrl OR Alt, not both at the same time", LogLevel.NORMAL);
259 Print(string.Empty, LogLevel.NORMAL);
260 Print("Use Ctrl to add to the 'To Replace' array", LogLevel.NORMAL);
261 Print("Use Alt to add to the 'Replace With' array", LogLevel.NORMAL);
262 Print("Use Shift -with- Ctrl/Alt to remove from said array", LogLevel.NORMAL);
263 return;
264 }
265
266 if (entitySource)
267 {
268 if (isShiftPressed)
269 m_API.AddToEntitySelection(entitySource);
270 else
271 m_API.SetEntitySelection(entitySource);
272 }
273 else
274 {
275 m_API.ClearEntitySelection();
276 }
277
278 m_API.UpdateSelectionGui();
279 return;
280 }
281
282 if (!entity)
283 return;
284
285 if (!entitySource)
286 {
287 Print("no entity source", LogLevel.NORMAL);
288 return;
289 }
290
291 BaseContainer ancestor = entitySource.GetAncestor();
292 if (!ancestor)
293 {
294 Print("no entity ancestor", LogLevel.NORMAL);
295 return;
296 }
297
298 ResourceName resourceName = ancestor.GetResourceName();
299 if (resourceName.IsEmpty())
300 {
301 Print("empty resource name", LogLevel.NORMAL);
302 return;
303 }
304
305 RemoveInvalidResourceNames();
306
307 if (toReplaceOperation)
308 ToReplaceOperation(resourceName, isShiftPressed);
309 else
310 ReplaceWithOperation(resourceName, isShiftPressed);
311
312 WorldEditorTool.UpdatePropertyPanel();
313 }
314
315 //------------------------------------------------------------------------------------------------
316 protected int GetToReplaceIndex(ResourceName resourceName)
317 {
318 foreach (int i, EntityReplacerTool_ToReplace toReplace : m_aToReplace)
319 {
320 if (toReplace.m_sResourceName == resourceName)
321 return i;
322 }
323 return -1;
324 }
325
326 //------------------------------------------------------------------------------------------------
327 protected int GetReplaceWithIndex(ResourceName resourceName)
328 {
329 foreach (int i, EntityReplacerTool_ReplaceWith replaceWith : m_aReplaceWith)
330 {
331 if (replaceWith.m_sResourceName == resourceName)
332 return i;
333 }
334 return -1;
335 }
336
337 //------------------------------------------------------------------------------------------------
338 protected void ToReplaceOperation(ResourceName resourceName, bool isDeletion)
339 {
340 int index = GetToReplaceIndex(resourceName);
341 if (isDeletion)
342 {
343 if (index > -1)
344 m_aToReplace.RemoveOrdered(index);
345 else
346 Print(resourceName + " not present in 'To Replace' array", LogLevel.NORMAL);
347 }
348 else
349 {
350 if (index < 0)
351 {
352 EntityReplacerTool_ToReplace toAdd = new EntityReplacerTool_ToReplace();
353 toAdd.m_sResourceName = resourceName;
354 toAdd.m_fReplacementPercentage = EntityReplacerTool_ToReplace.DEFAULT_REPLACEMENT_PERCENTAGE;
355 m_aToReplace.Insert(toAdd);
356 }
357 else
358 {
359 Print(resourceName + " already present in 'To Replace' array", LogLevel.NORMAL);
360 }
361 }
362 }
363
364 //------------------------------------------------------------------------------------------------
365 protected void ReplaceWithOperation(ResourceName resourceName, bool isDeletion)
366 {
367 int index = GetReplaceWithIndex(resourceName);
368 if (isDeletion)
369 {
370 if (index > -1)
371 m_aReplaceWith.RemoveOrdered(index);
372 else
373 Print(resourceName + " not present in 'Replace With' array", LogLevel.NORMAL);
374 }
375 else
376 {
377 if (index < 0)
378 {
379 EntityReplacerTool_ReplaceWith toAdd = new EntityReplacerTool_ReplaceWith();
380 toAdd.m_sResourceName = resourceName;
381 toAdd.m_fReplacementWeight = EntityReplacerTool_ReplaceWith.DEFAULT_REPLACEMENT_WEIGHT;
382 m_aReplaceWith.Insert(toAdd);
383 }
384 else
385 {
386 Print(resourceName + " already present in 'Replace With' array", LogLevel.NORMAL);
387 }
388 }
389 }
390
391 //------------------------------------------------------------------------------------------------
392 protected void RemoveInvalidResourceNames()
393 {
394 for (int i = m_aToReplace.Count() - 1; i >= 0; i--)
395 {
396 if (!m_aToReplace[i] || m_aToReplace[i].m_sResourceName.IsEmpty())
397 m_aToReplace.Remove(i);
398 }
399
400 for (int i = m_aReplaceWith.Count() - 1; i >= 0; i--)
401 {
402 if (!m_aReplaceWith[i] || m_aReplaceWith[i].m_sResourceName.IsEmpty())
403 m_aReplaceWith.Remove(i);
404 }
405 }
406
407 //------------------------------------------------------------------------------------------------
409 protected void GetAllEntities(BaseWorld baseWorld)
410 {
411 ENTITIES_TO_PARSE.Clear();
412 vector minPos, maxPos;
413 baseWorld.GetBoundBox(minPos, maxPos);
414 baseWorld.QueryEntitiesByAABB(minPos, maxPos, InsertEntity);
415 }
416
417 //------------------------------------------------------------------------------------------------
418 protected bool InsertEntity(IEntity entity)
419 {
420 if (entity)
421 ENTITIES_TO_PARSE.Insert(m_API.EntityToSource(entity));
422 return true;
423 }
424
425 //------------------------------------------------------------------------------------------------
426 protected void SelectEntities(notnull array<IEntitySource> entities)
427 {
428 m_API.ClearEntitySelection();
429 for (int i, cnt = Math.Min(m_iMaxSelectedEntities, entities.Count()); i < cnt; i++)
430 {
431 m_API.AddToEntitySelection(entities[i]);
432 }
433 m_API.UpdateSelectionGui();
434 }
435
436 //------------------------------------------------------------------------------------------------
437 protected array<IEntity> GetEntitiesFromSource(notnull array<IEntitySource> sources)
438 {
439 array<IEntity> result = {};
440 foreach (IEntitySource source : sources)
441 {
442 result.Insert(m_API.SourceToEntity(source));
443 }
444 return result;
445 }
446}
447
448// TODO: SCR_
450class EntityReplacerTool_ToReplace
451{
452 static const float DEFAULT_REPLACEMENT_PERCENTAGE = 100;
453
454 [Attribute(uiwidget: UIWidgets.ResourcePickerThumbnail, params: "et")]
455 ResourceName m_sResourceName;
456
457 [Attribute(desc: "Defines how many of detected Entities of said type will be replaced", defvalue: DEFAULT_REPLACEMENT_PERCENTAGE.ToString(), uiwidget: UIWidgets.Slider, params: "0 100 0.1", precision: 1)]
458 float m_fReplacementPercentage;
459}
460
461// TODO: SCR_
463class EntityReplacerTool_ReplaceWith
464{
465 static const float DEFAULT_REPLACEMENT_WEIGHT = 50;
466
467 [Attribute(uiwidget: UIWidgets.ResourcePickerThumbnail, params: "et")]
468 ResourceName m_sResourceName;
469
470 [Attribute(desc: "Weight in replacement randomisation (more weight = more chance to be selected)", defvalue: DEFAULT_REPLACEMENT_WEIGHT.ToString(), params: "0 100 0.1")]
471 float m_fReplacementWeight;
472}
473#endif // WORKBENCH
ref array< string > coords
ref array< string > angles
params precision
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
ResourceName resourceName
Definition SCR_AIGroup.c:66
SCR_CampaignMilitaryBaseComponent SCR_MilitaryBaseComponent SCR_BaseContainerCustomTitleResourceName("m_sBaseName", true)
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
Definition Debug.c:13
proto external vector GetAngles()
Same as GetYawPitchRoll(), but returns rotation vector around X, Y and Z axis.
proto external vector GetOrigin()
proto external void SetName(string name)
proto external string GetName()
Definition Math.c:13
static bool IsEditMode()
Definition Functions.c:1566
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
SCR_FieldOfViewSettings Attribute
EntityFlags
Various entity flags.
Definition EntityFlags.c:14
TraceFlags
Definition TraceFlags.c:13