Arma Reforger Explorer 1.7.0.54
Arma Reforger Code Explorer by Zeroy - Thanks to MisterOutofTime
Loading...
Searching...
No Matches
EditablePrefabsConfig.c
Go to the documentation of this file.
1#ifdef WORKBENCH
2[BaseContainerProps(configRoot: true)]
3class EditablePrefabsConfig
4{
5 [Attribute(defvalue: "Prefabs", desc: "Source directory where all prefabs are placed.", params: "unregFolders")]
6 protected ResourceName m_SourceDirectory;
7
8 [Attribute(defvalue: "PrefabsEditable", desc: "Target directory where editable sub-prefabs will be crated.\nFolder structure inside will mimic the structure in the source directory.", params: "folders")]
9 protected ResourceName m_TargetDirectory;
10
11 [Attribute(defvalue: "Auto", desc: "Auto-generated Prefabs folder name")]
12 protected string m_sAutoFolderName;
13
14 [Attribute(defvalue: "E_", desc: "Prefix added before file name of each prefab (i.e., editable entity prefab of 'Car.et' will be 'E_Car.et')")]
15 protected string m_sFileNamePrefix;
16
17 [Attribute(defvalue: "", desc: "Ignore file names containing this string. To define mutliple, separate them by a comma (without a space afterwards).")]
18 protected string m_sSourceBlacklist;
19
20 [Attribute(desc: "Entities that will be created in temporary world to ensure correct functionality (e.g., AIWorld for AI groups)", params: "et")]
21 protected ref array<ResourceName> m_SupportEntities;
22
23 [Attribute()]
24 protected ref array<ref EditablePrefabsSetting> m_Settings;
25
26 protected bool m_bIsValid;
27 protected string m_sSourcePath;
28 protected string m_sTargetPath;
29 protected string m_sTargetPathAuto;
30 protected ref map<string, string> m_mLinksFromTarget;
31 protected ref map<string, string> m_mLinksFromSource;
32 protected ref array<string> m_aSourceBlacklist = {};
33 protected string m_sCurrentAddon = "$ArmaReforger:";
34
35 protected static const string ADDON_PREFIX = "$profile:";
36 protected static const string WORLD_PATH = "worlds/Plugins/EditablePrefabsConfig";
37 protected static const string WORLD_NAME = "EditablePrefabsWorld";
38 protected static const string META_EXTENSION = ".meta";
39 protected static const string COPY_EXTENSION = "_copy";
40
42 //--- Prefab management
44
45 //------------------------------------------------------------------------------------------------
49 void CreateEditablePrefab(WorldEditorAPI api, ResourceName prefab, bool onlyUpdate = false, map<string, SCR_EEditablePrefabResult> results = null)
50 {
51 if (prefab.IsEmpty())
52 return;
53
54 if (IsBlacklisted(prefab))
55 {
56 if (results)
57 results.Set(string.Format("@\"%1\"", prefab.GetPath()), SCR_EEditablePrefabResult.IGNORED);
58 return;
59 }
60
61 if (!m_mLinksFromSource)
62 GetLinks();
63
64 string addonName = SCR_AddonTool.GetResourceLastAddon(prefab);
65 m_sCurrentAddon = SCR_AddonTool.ToFileSystem(addonName);
66
67 bool exists = false;
68 string prefabPath = prefab.GetPath();
69 if (prefabPath.StartsWith(m_sTargetPath))
70 {
71 //--- From target
72 if (prefabPath.StartsWith(m_sTargetPathAuto))
73 {
74 //--- Update prefab in target folder
75 prefab = GetSourcePrefab(prefab);
76 if (prefab.IsEmpty())
77 return;
78 exists = true;
79 }
80 else
81 {
83 Print(string.Format("Cannot update editable prefab @\"%1\", it's not auto-generated (i.e., not in '%2' folder)!", prefabPath, m_sTargetPathAuto), LogLevel.WARNING);
84 return;
85 }
86 }
87 else
88 {
89 //--- From source
90 Resource prefabResource = Resource.Load(prefab);
91 if (prefabResource && SCR_EditableEntityComponentClass.GetEditableEntitySource(prefabResource))
92 {
93 //--- Is already configured as editable entity - proceed, but only update components
94 UpdateEditablePrefab(api, prefab);
95 return;
96 }
97 else
98 {
99 //--- Not an editable entity, find its editable variant
100 ResourceName existingPrefab;
101 exists = GetLinkFromSource(SCR_ConfigHelper.GetGUID(prefab, true), existingPrefab);
102
103 if (existingPrefab.IsEmpty())
104 {
105 //--- Create
106 if (onlyUpdate)
107 return;
108 string prefabName = FilePath.StripPath(prefabPath);
109 prefabPath.Replace(prefabName, m_sFileNamePrefix + prefabName);
110 prefabPath.Replace(m_sSourcePath, m_sTargetPathAuto);
111 }
112 else
113 {
114 //--- Update
115 prefabPath = existingPrefab.GetPath();
116 }
117 }
118 }
119
120 //--- Create prefab directory
121 string directoryPath = FilePath.StripFileName(prefabPath);
122 if (!CreateDirectoryFor(directoryPath))
123 return;
124
125 IEntitySource entitySource = api.CreateEntity(prefab, "", api.GetCurrentEntityLayerId(), null, vector.Zero, vector.Zero);
126 if (!entitySource)
127 {
128 Print(string.Format("Unable to create entity %1!", prefab), LogLevel.ERROR);
129 return;
130 }
131
132 //--- Remove coordinates which were added when the entity was spawned
133 entitySource.ClearVariable("coords");
134
135 //--- Reset placement type, e.g., "slopelandcintact". Would clash with editor positioning on clients
136 api.SetVariableValue(entitySource, null, "placement", "none");
137
138 //--- Update source composition
139 bool needSave;
140 bool editableChildren = true;
141 IEntityComponentSource compositionComponentSource = SCR_BaseContainerTools.FindComponentSource(entitySource, SCR_SlotCompositionComponent);
142 if (compositionComponentSource)
143 {
144 compositionComponentSource.Get("m_bEditableChildren", editableChildren);
145 SCR_SlotCompositionComponent compositionComponent = SCR_SlotCompositionComponent.Cast(api.SourceToEntity(entitySource).FindComponent(SCR_SlotCompositionComponent));
146#ifdef WORKBENCH
147 if (compositionComponent)
148 needSave = compositionComponent._WB_ConfigureComposition(api, entitySource.GetAncestor());
149#endif // WORKBENCH
150 }
151
152 //--- Get existing target prefab
153 IEntitySource currentEntitySource;
154 Resource currentResource = Resource.Load(prefabPath);
155 if (currentResource && currentResource.IsValid())
156 currentEntitySource = currentResource.GetResource().ToEntitySource();
157
158 //--- Update children
159 array<ref SCR_EditorLinkEntry> links = {};
160 if (editableChildren && !UpdateChildPrefabs(api, entitySource, links, results: results))
161 {
162 if (results)
163 results.Set(string.Format("@\"%1\"", prefab.GetPath()), SCR_EEditablePrefabResult.FAILED);
164
165 //Print(string.Format("Editable entity creation FAILED: from @\"%1\"", prefab.GetPath()), LogLevel.WARNING);
166 api.DeleteEntity(entitySource);
167 entitySource.Release(true);
168 return;
169 }
170
171 //--- Add link component
172 if (links.Count() != 0)
173 {
174 int hasVariants;
175 IEntityComponentSource linkComponent = api.CreateComponent(entitySource, "SCR_EditorLinkComponent");
176 foreach (int i, SCR_EditorLinkEntry entry : links)
177 {
178 if (api.CreateObjectArrayVariableMember(entitySource, {ContainerIdPathEntry("SCR_EditorLinkComponent")}, "m_aEntries", "SCR_EditorLinkEntry", i))
179 {
180 array<ref ContainerIdPathEntry> entryPath = {ContainerIdPathEntry("SCR_EditorLinkComponent"), ContainerIdPathEntry("m_aEntries", i)};
181 api.SetVariableValue(entitySource, entryPath, "m_Prefab", entry.m_Prefab);
182 api.SetVariableValue(entitySource, entryPath, "m_vPosition", entry.m_vPosition.ToString(false));
183 api.SetVariableValue(entitySource, entryPath, "m_vAngles", entry.m_vAngles.ToString(false));
184 api.SetVariableValue(entitySource, entryPath, "m_fScale", entry.m_fScale.ToString());
185
186 //~ Check if entry has random variant
187 if (hasVariants == 0)
188 hasVariants = SCR_EditableEntityComponentClass.HasVariants(entry.m_Prefab);
189 }
190 }
191
192 api.SetVariableValue(entitySource, {ContainerIdPathEntry("SCR_EditorLinkComponent")}, "m_bRandomizeVariants", hasVariants.ToString());
193
194 //api.SetVariableValue(entitySource, {ContainerIdPathEntry("SCR_EditorLinkComponent"), ContainerIdPathEntry("m_aEntries")}, "Name", string.Format(m_sNameFormat, prefabName));
195 }
196
197 //--- Add components
198 EditablePrefabsSetting settings = GetSettings(prefabPath);
199 if (settings)
200 settings.AddComponents(this, api, prefab, prefabPath, entitySource, currentEntitySource, entitySource);
201
202 //--- Create prefab file
203 string absolutePath;
204 Workbench.GetAbsolutePath(m_sCurrentAddon + prefabPath, absolutePath, false);
205
206 //--- Back up prefab and meta files
207 string metaPath = prefabPath + META_EXTENSION;
208 string metaPathCopy = metaPath + COPY_EXTENSION;
209 array<string> backup = {};
210 if (exists)
211 {
212 BackupFile(prefabPath, backup);
213 FileIO.CopyFile(metaPath, metaPathCopy);
214 }
215
216 //--- Create new prefab
217 api.CreateEntityTemplate(entitySource, absolutePath);
218
219 //--- Restore meta file (prefab creation overwrites it) and supress 'dummy' changes of component prefabs GUIDs
220 if (exists)
221 {
222 CompareBackup(prefabPath, backup);
223 FileIO.CopyFile(metaPathCopy, metaPath);
224 FileIO.DeleteFile(metaPathCopy);
225 }
226
227 //--- Save changes in the original entity
228 if (needSave)
229 Workbench.GetModule(WorldEditor).Save();
230
231 api.DeleteEntity(entitySource);
232 entitySource.Release(true);
233
234 if (exists)
235 {
236 if (results)
237 results.Set(string.Format("@\"%1\" from @\"%2\"", prefabPath, prefab.GetPath()), SCR_EEditablePrefabResult.UPDATED);
238 //Print(string.Format("Editable entity UPDATED: @\"%1\" from @\"%2\"", prefabPath, prefab.GetPath()), LogLevel.DEBUG);
239 }
240 else
241 {
242 if (results)
243 results.Set(string.Format("@\"%1\" from @\"%2\"", prefabPath, prefab.GetPath()), SCR_EEditablePrefabResult.CREATED);
244 //Print(string.Format("Editable entity ADDED: @\"%1\" from @\"%1\"", prefabPath, prefab.GetPath()), LogLevel.DEBUG);
245 }
246 }
247
248 //------------------------------------------------------------------------------------------------
252 void UpdateEditablePrefab(WorldEditorAPI api, ResourceName prefab)
253 {
254 IEntitySource entitySource = api.CreateEntity(prefab, "", api.GetCurrentEntityLayerId(), null, vector.Zero, vector.Zero);
255 IEntitySource prefabSource = entitySource.GetAncestor();
256 if (!prefabSource)
257 return;
258
259 string prefabPath = prefab.GetPath();
260 EditablePrefabsSetting settings = GetSettings(prefabPath);
261 if (settings)
262 settings.AddComponents(this, api, prefab, prefabPath, prefabSource, prefabSource, entitySource);
263
264 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
265 worldEditor.Save();
266
267 api.DeleteEntity(entitySource);
268 entitySource.Release(true);
269
270 Print(string.Format("Editable entity UPDATED: @\"%1\"", prefabPath), LogLevel.DEBUG);
271 }
272
273 //------------------------------------------------------------------------------------------------
279 string VerifyEditablePrefab(WorldEditorAPI api, ResourceName prefab, bool onlyFileChanges = false)
280 {
281 Resource prefabResource = Resource.Load(prefab);
282 if (!prefabResource || !prefabResource.IsValid())
283 return string.Empty;
284
285 BaseResourceObject prefabBase = prefabResource.GetResource();
286 if (!prefabBase)
287 return string.Empty;
288
289 IEntitySource prefabEntity = prefabBase.ToEntitySource();
290 if (!prefabEntity)
291 return string.Empty;
292
293 string prefabPath = prefab.GetPath();
294 IEntitySource ancestorEntity = prefabEntity.GetAncestor();
295 if (ancestorEntity)
296 {
297 string correctPath = ancestorEntity.GetResourceName().GetPath();
298 correctPath.Replace(m_sSourcePath, m_sTargetPathAuto);
299
300 string correctName = FilePath.StripPath(correctPath);
301 correctPath.Replace(correctName, m_sFileNamePrefix + correctName);
302
303 //--- Source name changed, rename the prefab as well
304 if (prefabPath != correctPath)
305 prefab = MoveEditablePrefab(api, prefabPath, correctPath);
306
307 //--- Update prefab from the source
308 if (!onlyFileChanges)
309 CreateEditablePrefab(api, prefab);
310
311 return correctPath;
312 }
313 else
314 {
315 //--- Source deleted, delete the prefab as well
316 DeleteEditablePrefab(api, prefabPath);
317 }
318
319 return string.Empty;
320 }
321
322 //------------------------------------------------------------------------------------------------
327 protected ResourceName MoveEditablePrefab(WorldEditorAPI api, string prevPath, string newPath)
328 {
329 if (!MoveFile(prevPath, newPath))
330 return ResourceName.Empty;
331
332 EditablePrefabsSetting settings = GetSettings(newPath);
333 if (settings)
334 settings.Move(this, api, prevPath, newPath);
335
336 GetLinks(true);
337
338 string absPath;
339 Workbench.GetAbsolutePath(newPath, absPath);
340 if (absPath.IsEmpty())
341 {
342 Print(string.Format("Cannot MOVE '%1', unable to find absolute path for '%2'!", prevPath, newPath), LogLevel.ERROR);
343 return ResourceName.Empty;
344 }
345
346 Print(string.Format("Editable entity MOVED:\n Old: '%1'\n New: '%2'", prevPath, newPath), LogLevel.WARNING);
347
348 ResourceManager resourceManager = Workbench.GetModule(ResourceManager);
349 resourceManager.RegisterResourceFile(absPath, false);
350 return Workbench.GetResourceName(absPath);
351 }
352
353 //------------------------------------------------------------------------------------------------
355 protected void DeleteEditablePrefab(WorldEditorAPI api, string prefabPath)
356 {
357 FileIO.DeleteFile(prefabPath);
358 FileIO.DeleteFile(prefabPath + META_EXTENSION);
359
360 EditablePrefabsSetting settings = GetSettings(prefabPath);
361 if (settings)
362 settings.Delete(this, api, prefabPath);
363
364 Print(string.Format("Editable entity DELETED '%1' (its source no longer exists)!", prefabPath), LogLevel.WARNING);
365 }
366
367// //------------------------------------------------------------------------------------------------
368// //! Change all child prefabs to their editable variants.
369// //! \param[in] prefab editable entity prefab
370// //! \param[in] toEditable true to convert sourc entities to editable, false to do it the other way around
371// void UpdateEditableChildPrefabs(ResourceName prefab, bool toEditable = true)
372// {
373// string prefabPath = prefab.GetPath();
374// if (!FileIO.FileExist(prefabPath))
375// return;
376//
377// Print(string.Format("--- Processing prefab '%1'", prefab), LogLevel.DEBUG);
378//
379// //--- Replace prefab paths directly in the file (there is no API to do it 'cleanly')
380// FileHandle file = FileIO.OpenFile(prefabPath, FileMode.READ);
381//
382// string text, line, substring, replacement;
383// int n = 0;
384// int index, length;
385// bool canReplace;
386// while (file.ReadLine(line) > 0)
387// {
388// if (line.Contains(".et\" {"))
389// {
390// index = line.IndexOf("{") + 1;
391// length = line.LastIndexOf("}") - index;
392// substring = line.Substring(index, length);
393//
394// if (toEditable)
395// canReplace = GetLinkFromSource(substring, replacement);
396// else
397// canReplace = GetLinkFromTarget(substring, replacement);
398//
399// if (canReplace)
400// {
401// index = line.IndexOf("\"") + 1;
402// length = line.LastIndexOf("\"") - index;
403// substring = line.Substring(index, length);
404// line.Replace(substring, replacement);
405// Print(string.Format("'%1' converted to '%2'", substring, replacement), LogLevel.DEBUG);
406// }
407// else
408// {
409// if (toEditable)
410// canReplace = GetLinkFromTarget(substring, replacement);
411// else
412// canReplace = GetLinkFromSource(substring, replacement);
413//
414// if (!canReplace && n != 0)
415// {
416// index = line.IndexOf("\"") + 1;
417// length = line.LastIndexOf("\"") - index;
418// substring = line.Substring(index, length);
419// Print(string.Format("Cannot convert '%1', matching prefab not found!", substring), LogLevel.WARNING);
420// }
421// }
422// }
423//
424// if (n != 0)
425// text += "\n";
426// text += line;
427// n++;
428// }
429// file.Close();
430//
431// file = FileIO.OpenFile(prefabPath, FileMode.WRITE);
432// FPrint(file, text);
433// file.Close();
434// }
435
436 //------------------------------------------------------------------------------------------------
441 bool MoveFile(string prevPath, string newPath)
442 {
443 if (prevPath == newPath)
444 {
445 Print(string.Format("Cannot MOVE '%1', target path is the same as current path!", prevPath), LogLevel.ERROR);
446 return false;
447 }
448 if (newPath.IsEmpty())
449 {
450 Print(string.Format("Cannot MOVE '%1', target path is empty!", prevPath), LogLevel.ERROR);
451 return false;
452 }
453
454 string newDirectory = FilePath.StripFileName(newPath);
455 if (!CreateDirectoryFor(newDirectory))
456 return false;
457
458 //--- Copy prefab to new directory and delete the old one
459 FileIO.CopyFile(prevPath, newPath);
460 FileIO.DeleteFile(prevPath);
461
462 //--- Copy meta file (use intermediary file to prevent warning about duplicate GUID)
463 string prevMetaPath = prevPath + META_EXTENSION;
464 string newMetaPath = newPath + META_EXTENSION;
465 if (FileIO.FileExists(prevMetaPath))
466 {
467 FileIO.CopyFile(prevMetaPath, newMetaPath + COPY_EXTENSION);
468 FileIO.DeleteFile(prevMetaPath);
469 FileIO.CopyFile(newMetaPath + COPY_EXTENSION, newMetaPath);
470 FileIO.DeleteFile(newMetaPath + COPY_EXTENSION);
471 }
472
473 return true;
474 }
475
476 //------------------------------------------------------------------------------------------------
479 void LogResults(notnull map<string, SCR_EEditablePrefabResult> results)
480 {
481 map<SCR_EEditablePrefabResult, LogLevel> logLevels = new map<SCR_EEditablePrefabResult, LogLevel>();
482 logLevels.Insert(SCR_EEditablePrefabResult.FAILED, LogLevel.ERROR);
483 logLevels.Insert(SCR_EEditablePrefabResult.CREATED, LogLevel.DEBUG);
484 logLevels.Insert(SCR_EEditablePrefabResult.UPDATED, LogLevel.DEBUG);
485 logLevels.Insert(SCR_EEditablePrefabResult.MOVED, LogLevel.DEBUG);
486 logLevels.Insert(SCR_EEditablePrefabResult.DELETED, LogLevel.WARNING);
487 logLevels.Insert(SCR_EEditablePrefabResult.NON_EDITABLE, LogLevel.VERBOSE);
488 logLevels.Insert(SCR_EEditablePrefabResult.IGNORED, LogLevel.VERBOSE);
489
490 typename resultTypeName = SCR_EEditablePrefabResult;
491 SCR_EEditablePrefabResult resultQueried;
492 string resultName;
493 LogLevel logLevel;
494 for (int i = 0, count = resultTypeName.GetVariableCount(); i < count; i++)
495 {
496 resultTypeName.GetVariableValue(null, i, resultQueried);
497 resultName = resultTypeName.GetVariableName(i);
498 logLevel = logLevels.Get(resultQueried);
499
500 foreach (string resourceName, SCR_EEditablePrefabResult result : results)
501 {
502 if (result == resultQueried)
503 Print(string.Format("Editable Entity %1: %2", resultName, resourceName), logLevel);
504 }
505 }
506 }
507
509 //--- Support functions
511
512 //------------------------------------------------------------------------------------------------
513 protected bool UpdateChildPrefabs(WorldEditorAPI api, IEntitySource entitySource, array<ref SCR_EditorLinkEntry> links, bool forceDisable = false, map<string, SCR_EEditablePrefabResult> results = null, int depth = 0)
514 {
515 ResourceName ancestor, prefabEditable;
516 IEntitySource child;
517 BaseContainer ancestorSource;
518 bool prefabFound, sourcePrefabFound;
519 bool isValid = true;
520 for (int e = 0, childrenCount = entitySource.GetNumChildren(); e < childrenCount; e++)
521 {
522 child = entitySource.GetChild(e);
523 prefabFound = false;
524 sourcePrefabFound = false;
525 bool forceDisableChildren = forceDisable;
526
527 if (forceDisable)
528 {
529 //--- Disable, one of the parents is a link
530 api.SetVariableValue(child, null, "Flags", EntityFlags.EDITOR_ONLY.ToString());
531 }
532 else
533 {
534 //--- Find editable prefab variant
535 ancestorSource = child.GetAncestor();
536 while (ancestorSource)
537 {
538 ancestor = ancestorSource.GetResourceName();
539 if (ancestor.Contains("/"))
540 {
541 ancestor = Workbench.GetResourceName(ancestor);
542
543 if (SCR_BaseContainerTools.FindComponentSource(child, SCR_EditableEntityComponent))
544 {
545 prefabFound = true;
546 prefabEditable = ancestor;
547 }
548 else
549 {
550 prefabFound = GetLinkFromSource(SCR_ConfigHelper.GetGUID(ancestor, true), prefabEditable);
551 sourcePrefabFound = GetLinkFromTarget(SCR_ConfigHelper.GetGUID(ancestor, true));
552 }
553 break;
554 }
555 ancestorSource = ancestorSource.GetAncestor();
556 }
557
558 if (prefabFound)
559 {
560 //--- Editable prefab - add a source link
561 vector position;
562 child.Get("coords", position);
563
564 vector angles;
565 child.Get("angles", angles);
566
567 float scale;
568 child.Get("scale", scale);
569
570 //--- Add to the list of links
571 links.Insert(new SCR_EditorLinkEntry(prefabEditable, position, angles, scale));
572
573 //--- Mark the original entity as not-editable (override existing flags)
574 api.SetVariableValue(child, null, "Flags", EntityFlags.EDITOR_ONLY.ToString());
575 forceDisableChildren = true; //--- Disable all children
576 }
577 else if (sourcePrefabFound)
578 {
579 //--- ERROR: Is editable entity
580 Print(string.Format("Child entity of type '%1' is already auto-generated! Source compositions must not contain any editable prefabs from '%2/%3'!", GetResourceNameLink(child), m_TargetDirectory, m_sAutoFolderName), LogLevel.WARNING);
581 isValid = false;
582 }
583 /*else if (SCR_BaseContainerTools.FindComponentSource(child, RplComponent))
584 {
585 //--- ERROR: Contains RplComponent
586 Print(string.Format("Child entity '%1' contains RplComponent, but does not have editable variant! Children must not be replicated!", GetResourceNameLink(child)), LogLevel.WARNING);
587 isValid = false;
588 }*/
589 else
590 {
591 if (results)
592 results.Set(GetResourceNameLink(child), SCR_EEditablePrefabResult.NON_EDITABLE);
593
594 if (!SCR_BaseContainerTools.FindComponentSource(child, "Hierarchy"))
595 {
596 //--- Non-editable prefab or no prefab at all - add to hierarchy
597 api.CreateComponent(child, "Hierarchy");
598 }
599 }
600 }
601
602 //--- We need to go deeper
603 isValid &= UpdateChildPrefabs(api, child, links, forceDisableChildren, results, depth + 1);
604 }
605 return isValid;
606 }
607
608 //------------------------------------------------------------------------------------------------
609 protected void DisableComponent(WorldEditorAPI api, IEntitySource entitySource, BaseContainer componentSource, array<ref ContainerIdPathEntry> componentsPath)
610 {
611 if (!componentSource)
612 return;
613
614 array<ref ContainerIdPathEntry> componentsPathNew = {};
615 foreach (ContainerIdPathEntry entry : componentsPath)
616 {
617 componentsPathNew.Insert(entry);
618 }
619 componentsPathNew.Insert(new ContainerIdPathEntry(componentSource.GetClassName()));
620
621 if (componentSource.GetClassName() != "Hierarchy") //--- Don't disable hierarchy, dummy entities should still be tied to their parent
622 api.SetVariableValue(entitySource, componentsPathNew, "Enabled", "0");
623
624 BaseContainerList components = componentSource.GetObjectArray("components");
625 if (components)
626 {
627 int componentsCount = components.Count();
628 for (int i = 0; i < componentsCount; i++)
629 {
630 DisableComponent(api, entitySource, components.Get(i), componentsPathNew);
631 }
632 }
633 }
634
635 //------------------------------------------------------------------------------------------------
636 protected void BackupFile(string filePath, out notnull array<string> backup)
637 {
638 FileHandle file = FileIO.OpenFile(filePath, FileMode.READ);
639 string line;
640 while (file.ReadLine(line) > 0)
641 {
642 backup.Insert(line);
643 }
644 file.Close();
645 }
646
647 //------------------------------------------------------------------------------------------------
648 protected void CompareBackup(string filePath, notnull array<string> backup)
649 {
650 FileHandle file = FileIO.OpenFile(filePath, FileMode.READ);
651 string line, text;
652 int i = 0;
653 int backupCount = backup.Count();
654 bool apply = false;
655 while (file.ReadLine(line) > 0)
656 {
657 if (i >= backupCount)
658 {
659 apply = false;
660 break;
661 }
662
663 if (line != backup[i])
664 {
665 if (line.Contains(".ct"))
666 {
667 //--- Component GUID updated, restore the original to prevent false-flag SVN diffs
668 line = backup[i];
669 apply = true;
670 }
671 else
672 {
673 //--- Major file change, don't restore the backup
674 apply = false;
675 break;
676 }
677 }
678 if (i != 0)
679 text += "\n";
680 text += line;
681 i++;
682 }
683 file.Close();
684
685 if (apply)
686 {
687 file = FileIO.OpenFile(filePath, FileMode.WRITE);
688 file.Write(text);
689 file.Close();
690 }
691 }
692
693 //------------------------------------------------------------------------------------------------
694 protected ResourceName GetResourceNameLink(BaseContainer container)
695 {
696 string resourceName = container.GetClassName();
697 while (container)
698 {
699 if (container.GetResourceName().Contains("/"))
700 {
701 resourceName = string.Format("@\"%1\"", container.GetResourceName().GetPath());
702 break;
703 }
704 container = container.GetAncestor();
705 }
706 return resourceName;
707 }
708
709 //------------------------------------------------------------------------------------------------
710 protected EditablePrefabsSetting GetSettings(string prefabPath)
711 {
712 EditablePrefabsSetting settings = null;
713 int pathLengthMax = -1;
714 foreach (EditablePrefabsSetting settingsTemp : m_Settings)
715 {
716 string targetPath = settingsTemp.GetTarget();
717 int pathLength = targetPath.Length();
718 if (prefabPath.StartsWith(targetPath) && pathLength > pathLengthMax)
719 {
720 settings = settingsTemp;
721 pathLengthMax = pathLength;
722 }
723 }
724 return settings;
725 }
726
727 //------------------------------------------------------------------------------------------------
728 protected bool IsBlacklisted(string path)
729 {
730 foreach (string entry : m_aSourceBlacklist)
731 {
732 if (path.Contains(entry))
733 return true;
734 }
735 return false;
736 }
737
739 //--- Attribute getters
741
742 //------------------------------------------------------------------------------------------------
745 string GetSourcePath()
746 {
747 return m_sSourcePath;
748 }
749
750 //------------------------------------------------------------------------------------------------
753 string GetTargetPath()
754 {
755 return m_sTargetPath;
756 }
757
758 //------------------------------------------------------------------------------------------------
761 string GetTargetPathAuto()
762 {
763 return m_sTargetPathAuto;
764 }
765
766 //------------------------------------------------------------------------------------------------
769 string GetPrefix()
770 {
771 return m_sFileNamePrefix;
772 }
773
775 //--- General getters
777
778 //------------------------------------------------------------------------------------------------
782 static EditablePrefabsConfig GetConfig(ResourceName configPath)
783 {
784 Resource configResource = Resource.Load(configPath);
785 if (!configResource.IsValid())
786 {
787 Print(string.Format("Cannot load config '%1'!", configPath), LogLevel.ERROR);
788 return null;
789 }
790
791 BaseResourceObject configContainer = configResource.GetResource();
792 if (!configContainer)
793 return null;
794
795 BaseContainer configBase = configContainer.ToBaseContainer();
796 if (!configBase)
797 return null;
798
799 if (configBase.GetClassName() != "EditablePrefabsConfig")
800 {
801 Print(string.Format("Config '%1' is of type '%2', must be 'EditablePrefabsConfig'!", configPath, configBase.GetClassName()), LogLevel.ERROR);
802 return null;
803 }
804
805 return EditablePrefabsConfig.Cast(BaseContainerTools.CreateInstanceFromContainer(configBase));
806 }
807
808 //------------------------------------------------------------------------------------------------
811 bool IsValid()
812 {
813 return m_bIsValid;
814 }
815
816 //------------------------------------------------------------------------------------------------
821 bool CreateDirectoryFor(out string filePath, string addon = "")
822 {
823 array<string> directories = {};
824 filePath.Split("/", directories, true);
825
826 int directoriesCount = directories.Count();
827 if (directoriesCount == 0)
828 return false;
829
830 if (addon.IsEmpty())
831 addon = m_sCurrentAddon;
832
833 filePath = addon;
834 for (int i = 0; i < directoriesCount; i++)
835 {
836 filePath = FilePath.Concat(filePath, directories[i]);
837
838 if (!FileIO.MakeDirectory(filePath))
839 {
840 Print(string.Format("Unable to create directory '%1'!", filePath), LogLevel.ERROR);
841 return false;
842 }
843 }
844
845 return true;
846 }
847
848 //------------------------------------------------------------------------------------------------
851 bool CreateWorld()
852 {
853 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
854 if (!worldEditor)
855 return false;
856
857 WorldEditorAPI api = worldEditor.GetApi();
858 if (!api)
859 return false;
860
861 //--- World already opened, skip
862 string currentWorld;
863 api.GetWorldPath(currentWorld);
864 if (currentWorld == ADDON_PREFIX + WORLD_PATH + "/" + WORLD_NAME + ".ent")
865 return true;
866
867 string worldPath = SCR_WorldFilesHelper.CreateWorld(ADDON_PREFIX + WORLD_PATH, WORLD_NAME, m_SupportEntities, false, true);
868 if (worldPath.IsEmpty())
869 {
870 Print("Cannot create Editable Prefabs world files", LogLevel.WARNING);
871 return false;
872 }
873
874 Sleep(1); // required to open the world right after creation
875 if (!worldEditor.SetOpenedResource(worldPath))
876 {
877 Print("Cannot open Editable Prefabs world - if the world was just created, try restarting Workbench - " + worldPath, LogLevel.NORMAL);
878 return false;
879 }
880
881 return true;
882 }
883
884 //------------------------------------------------------------------------------------------------
887 void GetLinks(bool forced = false)
888 {
889 //--- Already defined
890 if (!forced && m_mLinksFromTarget && m_mLinksFromSource)
891 return;
892
893 m_mLinksFromTarget = new map<string, string>();
894 m_mLinksFromSource = new map<string, string>();
895
896 ref array<ResourceName> resources = {};
897 SearchResourcesFilter filter = new SearchResourcesFilter();
898 filter.fileExtensions = { "et" };
899 ResourceDatabase.SearchResources(filter, resources.Insert);
900
901 string prefabPath, prefabGUID, ancestorGUID;
902 ResourceName ancestor;
903 Resource prefabResource;
904 IEntitySource entitySource, ancestorSource;
905 foreach (ResourceName prefab : resources)
906 {
907 prefabPath = prefab.GetPath();
908 if (!prefabPath.StartsWith(m_sTargetPathAuto))
909 continue;
910
911 prefabResource = Resource.Load(prefab);
912 if (!prefabResource || !prefabResource.IsValid())
913 continue;
914
915 entitySource = SCR_BaseContainerTools.FindEntitySource(prefabResource);
916 if (!entitySource)
917 continue;
918
919 ancestorSource = entitySource.GetAncestor();
920 if (!ancestorSource)
921 continue;
922
923 ancestor = ancestorSource.GetResourceName();
924
925 prefabGUID = SCR_ConfigHelper.GetGUID(prefab, true);
926 ancestorGUID = SCR_ConfigHelper.GetGUID(ancestor, true);
927
928 m_mLinksFromTarget.Insert(prefabGUID, ancestor);
929 m_mLinksFromSource.Insert(ancestorGUID, prefab);
930 }
931 }
932
933 //------------------------------------------------------------------------------------------------
938 bool GetLinkFromSource(string guid, out ResourceName linkedPrefab = ResourceName.Empty)
939 {
940 return m_mLinksFromSource.Find(guid, linkedPrefab);
941 }
942
943 //------------------------------------------------------------------------------------------------
949 bool GetLinkFromTarget(string guid, out ResourceName linkedPrefab = ResourceName.Empty)
950 {
951 return m_mLinksFromTarget.Find(guid, linkedPrefab);
952 }
953
954 //------------------------------------------------------------------------------------------------
957 bool IsEditableEntity(ResourceName prefab)
958 {
959 return prefab.GetPath().StartsWith(m_sTargetPath);
960 }
961
962 //------------------------------------------------------------------------------------------------
966 ResourceName GetSourcePrefab(ResourceName prefab)
967 {
968 if (!IsEditableEntity(prefab))
969 return ResourceName.Empty;
970
971 Resource prefabResource = Resource.Load(prefab);
972 if (!prefabResource.IsValid())
973 return ResourceName.Empty;
974
975 IEntitySource prefabEntity = SCR_BaseContainerTools.FindEntitySource(prefabResource);
976 if (!prefabEntity)
977 return ResourceName.Empty;
978
979 IEntitySource ancestorEntity = prefabEntity.GetAncestor();
980 if (!ancestorEntity)
981 return ResourceName.Empty;
982
983 return ancestorEntity.GetResourceName();
984 }
985
986 //------------------------------------------------------------------------------------------------
987 // constructor
988 void EditablePrefabsConfig()
989 {
990 if (m_SourceDirectory.IsEmpty())
991 Print("Cannot use config EditablePrefabsConfig, m_SourceDirectory attribute is empty!", LogLevel.ERROR);
992
993 if (m_TargetDirectory.IsEmpty())
994 Print("Cannot use config EditablePrefabsConfig, m_TargetDirectory attribute is empty!", LogLevel.ERROR);
995
996 m_bIsValid = true;
997 m_sSourcePath = m_SourceDirectory.GetPath();
998 m_sTargetPath = m_TargetDirectory.GetPath();
999 m_sTargetPathAuto = m_sTargetPath + "/" + m_sAutoFolderName;
1000 m_sSourceBlacklist.Split(",", m_aSourceBlacklist, true);
1001 }
1002}
1003
1005class EditablePrefabsSetting
1006{
1007 [Attribute(params: "unregFolders et")]
1008 protected ResourceName m_Target;
1009
1010 [Attribute()]
1011 protected ref array<ref EditablePrefabsComponent_Base> m_Components;
1012
1013 //------------------------------------------------------------------------------------------------
1015 string GetTarget()
1016 {
1017 return m_Target.GetPath();
1018 }
1019
1020 //------------------------------------------------------------------------------------------------
1029 void AddComponents(EditablePrefabsConfig config, WorldEditorAPI api, ResourceName prefab, string targetPath, IEntitySource entitySource, IEntitySource currentEntitySource, IEntitySource instanceEntitySource)
1030 {
1031 //--- Get existing components on source file (to prevent duplicates)
1032 BaseContainer componentContainer, componentCurrent;
1033 map<string, BaseContainer> sourceComponentClasses = new map<string, BaseContainer>;
1034 int componentsCount = entitySource.GetComponentCount();
1035 for (int i = 0; i < componentsCount; i++)
1036 {
1037 componentContainer = entitySource.GetComponent(i);
1038 sourceComponentClasses.Insert(componentContainer.GetClassName(), componentContainer);
1039 }
1040
1041 //--- Get existing components on target file (to avoid overriding certain values)
1042 map<string, BaseContainer> currentComponentClasses = new map<string, BaseContainer>;
1043 if (currentEntitySource)
1044 {
1045 componentsCount = currentEntitySource.GetComponentCount();
1046 for (int i = 0; i < componentsCount; i++)
1047 {
1048 componentContainer = currentEntitySource.GetComponent(i);
1049 currentComponentClasses.Insert(componentContainer.GetClassName(), componentContainer);
1050 }
1051 }
1052
1053 //--- Add components from prefabs
1054 Resource componentResource;
1055 BaseResourceObject componentBase;
1056 foreach (EditablePrefabsComponent_Base component : m_Components)
1057 {
1058 //--- Get component on current target prefab
1059 if (!sourceComponentClasses.Find(component.GetClassName(), componentContainer))
1060 componentContainer = component.AddComponent(config, api, prefab, targetPath, entitySource);
1061
1062 if (componentContainer)
1063 {
1064 //--- If the component exists in source prefab, but is disabled, make sure to enable it here
1065 bool enabled;
1066 componentContainer.Get("Enabled", enabled);
1067 if (!enabled)
1068 api.SetVariableValue(entitySource, { new ContainerIdPathEntry(componentContainer.GetClassName()) }, "Enabled", "1");
1069
1070 currentComponentClasses.Find(component.GetClassName(), componentCurrent);
1071 component.EOnCreate(config, api, prefab, targetPath, entitySource, instanceEntitySource, componentContainer, componentCurrent);
1072 sourceComponentClasses.Insert(componentContainer.GetClassName(), componentContainer);
1073 }
1074 }
1075 }
1076
1077 //------------------------------------------------------------------------------------------------
1082 void Delete(EditablePrefabsConfig config, WorldEditorAPI api, string prefabPath)
1083 {
1084 foreach (EditablePrefabsComponent_Base component : m_Components)
1085 {
1086 component.EOnDelete(config, api, prefabPath);
1087 }
1088 }
1089
1090 //------------------------------------------------------------------------------------------------
1096 void Move(EditablePrefabsConfig config, WorldEditorAPI api, string currentPath, string newPath)
1097 {
1098 foreach (EditablePrefabsComponent_Base component : m_Components)
1099 {
1100 component.EOnMove(config, api, currentPath, newPath);
1101 }
1102 }
1103}
1104
1105enum SCR_EEditablePrefabResult
1106{
1107 FAILED,
1108 CREATED,
1109 UPDATED,
1110 MOVED,
1111 DELETED,
1112 NON_EDITABLE,
1113 IGNORED
1114}
1115#endif // WORKBENCH
string path
vector scale
CheckGUIDRequest exists
void ContainerIdPathEntry(string propertyName, int index=-1)
Definition worldEditor.c:30
ref array< string > angles
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
ResourceName resourceName
Definition SCR_AIGroup.c:66
class SCR_AIPolar m_Target
@ FAILED
Job failed during its processing and can be retried.
SCR_CampaignMilitaryBaseComponent SCR_MilitaryBaseComponent SCR_BaseContainerCustomTitleResourceName("m_sBaseName", true)
vector position
@ DELETED
Character was deleted.
override bool Delete(bool changedByUser=false, bool updateNavmesh=true)
string WORLD_NAME
ResourceName GetConfig()
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
@ UPDATED
proto external Managed FindComponent(typename typeName)
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
FileMode
Mode for opening file. See FileSystem::Open.
Definition FileMode.c:14