2[WorkbenchToolAttribute(name:
"Entity Replacer", wbModules: {
"WorldEditor" }, shortcut:
"Ctrl+H", awesomeFontCode: 0xF362,
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"
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"
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
16 protected ref array<ref EntityReplacerTool_ToReplace> m_aToReplace;
19 protected ref array<ref EntityReplacerTool_ReplaceWith> m_aReplaceWith;
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;
24 [
Attribute(defvalue:
"0",
desc:
"Only search/replace in the currently active layer",
category:
"Replacement Options")]
25 protected bool m_bActiveLayerOnly;
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;
30 protected static const ref array<IEntitySource> ENTITIES_TO_PARSE = {};
35 protected void ReplaceFromSelectedEntities()
37 ReplaceFromEntities(
false);
42 protected void ReplaceFromAllEntities()
44 ReplaceFromEntities(
true);
48 protected void ReplaceFromEntities(
bool allEntities =
true)
57 RemoveInvalidResourceNames();
58 WorldEditorTool.UpdatePropertyPanel();
60 if (m_aToReplace.IsEmpty())
66 if (m_aReplaceWith.IsEmpty())
80 Debug.BeginTimeMeasure();
83 GetAllEntities(baseWorld);
87 ENTITIES_TO_PARSE.Clear();
88 for (
int i = 0, cnt = m_API.GetSelectedEntitiesCount(); i < cnt; i++)
90 ENTITIES_TO_PARSE.Insert(m_API.GetSelectedEntity(i));
93 Debug.EndTimeMeasure(
string.Format(
"%1 entities found", ENTITIES_TO_PARSE.Count()));
95 if (ENTITIES_TO_PARSE.IsEmpty())
102 array<IEntitySource> sourcesToDelete = {};
104 m_API.BeginEntityAction(
"Replace Entities");
107 Debug.BeginTimeMeasure();
108 array<IEntitySource> createdEntitySources = CreateReplacements(baseWorld, sourcesToDelete);
109 Debug.EndTimeMeasure(
string.Format(
"%1 entities created", createdEntitySources.Count()));
112 Debug.BeginTimeMeasure();
113 m_API.DeleteEntities(sourcesToDelete);
114 Debug.EndTimeMeasure(
string.Format(
"%1 entities deleted", createdEntitySources.Count()));
116 if (m_iMaxSelectedEntities > 0)
117 SelectEntities(createdEntitySources);
119 m_API.EndEntityAction();
120 ENTITIES_TO_PARSE.Clear();
126 protected array<IEntitySource> CreateReplacements(
BaseWorld baseWorld, notnull out array<IEntitySource> toDelete)
128 array<float> weights = {};
129 foreach (EntityReplacerTool_ReplaceWith replaceWith : m_aReplaceWith)
131 weights.Insert(replaceWith.m_fReplacementWeight);
134 int currentLayerId = m_API.GetCurrentEntityLayerId();
135 int replaceWithCount = m_aReplaceWith.Count();
137 IEntitySource parentSource, childSource, createdEntitySource;
140 int replaceIndex, entityLayerId;
144 array<IEntitySource> createdEntitySources = {};
147 int totalChildOperationTime, childTick;
148 int totalCreationOperationTime =
System.GetTickCount();
149 foreach (
IEntitySource originalEntitySource : ENTITIES_TO_PARSE)
151 entityLayerId = originalEntitySource.GetLayerID();
152 if (m_bActiveLayerOnly && entityLayerId != currentLayerId)
155 ancestor = originalEntitySource.GetAncestor();
161 if (replaceIndex < 0)
165 if (m_aToReplace[replaceIndex].m_fReplacementPercentage * 0.01 < RANDOM_GENERATOR.RandFloat01())
168 if (!m_bReplaceInOrder || replaceIndex > replaceWithCount - 1)
169 replaceIndex =
SCR_ArrayHelper.GetWeightedIndex(weights, RANDOM_GENERATOR.RandFloat01());
171 IEntity originalEntity = m_API.SourceToEntity(originalEntitySource);
173 parentSource = originalEntitySource.GetParent();
178 name = originalEntity.
GetName();
179 originalEntity.
SetName(
string.Empty);
182 createdEntitySource = m_API.CreateEntity(m_aReplaceWith[replaceIndex].
m_sResourceName, name, entityLayerId, parentSource,
coords,
angles);
185 worldPos = m_API.SourceToEntity(parentSource).CoordToParent(
coords);
189 if (m_API.SourceToEntity(createdEntitySource).GetFlags() &
EntityFlags.RELATIVE_Y)
191 terrainY = baseWorld.GetSurfaceY(worldPos[0], worldPos[2]);
196 finalCoords = worldPos;
198 finalCoords[1] = finalCoords[1] - terrainY;
199 m_API.SetVariableValue(createdEntitySource, null,
"coords", finalCoords.ToString(
false));
202 childTick =
System.GetTickCount();
204 for (
int j = 0, childrenCount = originalEntitySource.GetNumChildren(); j < childrenCount; j++)
206 childSource = originalEntitySource.GetChild(j);
210 m_API.ParentEntity(originalEntitySource, childSource,
true);
212 totalChildOperationTime +=
System.GetTickCount() - childTick;
214 createdEntitySources.Insert(createdEntitySource);
215 toDelete.Insert(originalEntitySource);
217 totalCreationOperationTime =
System.GetTickCount() - totalCreationOperationTime;
219 Print(
"creation operation time = " + totalCreationOperationTime +
"ms",
LogLevel.NORMAL);
222 if (totalCreationOperationTime > 0)
223 pct =
Math.Round(totalChildOperationTime * 10000.0 / totalCreationOperationTime) * 0.01;
225 Print(
"children operation time = " + totalChildOperationTime +
"ms (" + pct +
"pct)",
LogLevel.NORMAL);
227 return createdEntitySources;
231 protected override void OnMousePressEvent(
float x,
float y, WETMouseButtonFlag buttons)
233 if (buttons != WETMouseButtonFlag.LEFT)
237 m_API.GetWorldPath(worldPath);
238 if (worldPath.IsEmpty())
242 vector start, end, normal;
244 if (entity && entity.Type() == GenericTerrainEntity)
247 bool toReplaceOperation = GetModifierKeyState(ModifierKey.CONTROL);
248 bool replaceWithOperation = GetModifierKeyState(ModifierKey.ALT);
249 bool isShiftPressed = GetModifierKeyState(ModifierKey.SHIFT);
254 if (toReplaceOperation == replaceWithOperation)
256 if (toReplaceOperation && replaceWithOperation)
258 Print(
"Use Ctrl OR Alt, not both at the same time",
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);
269 m_API.AddToEntitySelection(entitySource);
271 m_API.SetEntitySelection(entitySource);
275 m_API.ClearEntitySelection();
278 m_API.UpdateSelectionGui();
305 RemoveInvalidResourceNames();
307 if (toReplaceOperation)
312 WorldEditorTool.UpdatePropertyPanel();
318 foreach (
int i, EntityReplacerTool_ToReplace toReplace : m_aToReplace)
329 foreach (
int i, EntityReplacerTool_ReplaceWith replaceWith : m_aReplaceWith)
344 m_aToReplace.RemoveOrdered(
index);
352 EntityReplacerTool_ToReplace toAdd =
new EntityReplacerTool_ToReplace();
354 toAdd.m_fReplacementPercentage = EntityReplacerTool_ToReplace.DEFAULT_REPLACEMENT_PERCENTAGE;
355 m_aToReplace.Insert(toAdd);
371 m_aReplaceWith.RemoveOrdered(
index);
379 EntityReplacerTool_ReplaceWith toAdd =
new EntityReplacerTool_ReplaceWith();
381 toAdd.m_fReplacementWeight = EntityReplacerTool_ReplaceWith.DEFAULT_REPLACEMENT_WEIGHT;
382 m_aReplaceWith.Insert(toAdd);
392 protected void RemoveInvalidResourceNames()
394 for (
int i = m_aToReplace.Count() - 1; i >= 0; i--)
397 m_aToReplace.Remove(i);
400 for (
int i = m_aReplaceWith.Count() - 1; i >= 0; i--)
403 m_aReplaceWith.Remove(i);
409 protected void GetAllEntities(
BaseWorld baseWorld)
411 ENTITIES_TO_PARSE.Clear();
413 baseWorld.GetBoundBox(minPos, maxPos);
414 baseWorld.QueryEntitiesByAABB(minPos, maxPos, InsertEntity);
418 protected bool InsertEntity(
IEntity entity)
421 ENTITIES_TO_PARSE.Insert(m_API.EntityToSource(entity));
426 protected void SelectEntities(notnull array<IEntitySource> entities)
428 m_API.ClearEntitySelection();
429 for (
int i, cnt =
Math.Min(m_iMaxSelectedEntities, entities.Count()); i < cnt; i++)
431 m_API.AddToEntitySelection(entities[i]);
433 m_API.UpdateSelectionGui();
437 protected array<IEntity> GetEntitiesFromSource(notnull array<IEntitySource> sources)
439 array<IEntity> result = {};
442 result.Insert(m_API.SourceToEntity(source));
450class EntityReplacerTool_ToReplace
452 static const float DEFAULT_REPLACEMENT_PERCENTAGE = 100;
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;
463class EntityReplacerTool_ReplaceWith
465 static const float DEFAULT_REPLACEMENT_WEIGHT = 50;
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;
ref array< string > coords
ref array< string > angles
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
ResourceName resourceName
SCR_CampaignMilitaryBaseComponent SCR_MilitaryBaseComponent SCR_BaseContainerCustomTitleResourceName("m_sBaseName", true)
SCR_DestructionSynchronizationComponentClass ScriptComponentClass int index
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
ResourceName m_sResourceName
class WorkbenchDialog_AbortRetryIgnore ButtonAttribute("OK", true)
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()
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
EntityFlags
Various entity flags.