4class GameModeSetupConfig
6 [
Attribute(
desc:
"Prefabs that will be created automatically by the plugin.\nTheir classes, and classes of their children, will be used as required classes for the game mode.")]
7 protected ref array<ref GameModeSetupConfigEntry> m_Prefabs;
9 [
Attribute(
desc:
"Template from which mission header will be created.")]
10 protected ref MissionHeader m_MissionHeader;
12 protected ref array<ref GameModeSetupConfigEntry> m_aRequiredTypes = {};
13 protected typename m_GameModeType;
14 protected int m_iLongestTypeLength;
15 protected string m_sWorldPath;
18 protected const string DESCRIPTION_ENTITY_PRESENT =
" ✅ %1\n";
19 protected const string DESCRIPTION_ENTITY_MISSING =
" ❌ %1\n";
21 protected const string DESCRIPTION_VALIDATION_SUCCESS =
"\n✅ The world is configured correctly.";
22 protected const string DESCRIPTION_VALIDATION_NO_SUBSCENE =
"\n⛔ Missing Subscene\nYou are attempting to add the game mode to the root world.\nPlease create a subscene for it instead, while leaving the root world intact.\nThat will allow others to create their own subscenes with different game modes.\n";
23 protected const string DESCRIPTION_VALIDATION_WRONG_GAME_MODE =
"\n⛔ Incompatible Game Mode\nThe scan found existing game mode of type %1.\nThat's incompatible with the expected type %2.\nThere can be only one game mode in the world, so if you wish to continue,\nyou have to manually remove the existing one. The plugin cannot automate this process.\n";
24 protected const string DESCRIPTION_VALIDATION_PRESENT_ENTITIES =
"\nPresent entity types:\n (NOT USED)";
25 protected const string DESCRIPTION_VALIDATION_MISSING_ENTITIES =
"\n⛔ Missing Entities\nOne or more required entity types are missing, listed below.\nThese can be generated automatically in the next step.\n";
27 protected const string DESCRIPTION_GENERATION_ENTITY =
" ❗ %1: %2\n";
28 protected const string DESCRIPTION_GENERATION_MORE_STEPS_1 =
"\nSome entities need to be manually configured or replaced by more specialized prefabs:\n";
29 protected const string DESCRIPTION_GENERATION_MORE_STEPS_2 =
"\nEach has a comment attached explaining necessary steps, so you can return to them later.\n";
31 protected const string DESCRIPTION_MISSION_HEADER_EXISTS =
"✅ Mission header config at '%1' is configured correctly.\n";
33 protected const string SCENARIOS_PATH =
"Missions";
40 bool ValidateWorld(out
string dialogMessage, out
bool canAutogenerate)
42 canAutogenerate =
true;
44 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
45 WorldEditorAPI api = worldEditor.GetApi();
48 api.GetWorldPath(worldPath);
50 set<typename> worldTypes = GetWorldEntityTypes();
55 bool isSuccess =
true;
56 foreach (GameModeSetupConfigEntry entry : m_aRequiredTypes)
59 if (worldTypes.Contains(entry.m_Type))
61 presentTypes +=
string.Format(DESCRIPTION_ENTITY_PRESENT, entry.m_Type);
65 missingTypes +=
string.Format(DESCRIPTION_ENTITY_MISSING, entry.m_Type);
71 typename wrongGameMode;
72 foreach (
typename type : worldTypes)
74 if (
type.IsInherited(BaseGameMode) && !
type.IsInherited(m_GameModeType))
78 canAutogenerate =
false;
85 if (api.GetNumSubScenes() == 1)
89 canAutogenerate =
false;
94 dialogMessage += DESCRIPTION_VALIDATION_SUCCESS;
99 dialogMessage += DESCRIPTION_VALIDATION_NO_SUBSCENE;
102 dialogMessage +=
string.Format(DESCRIPTION_VALIDATION_WRONG_GAME_MODE, wrongGameMode, m_GameModeType);
107 if (missingTypes && !noSubscene && !wrongGameMode)
108 dialogMessage += DESCRIPTION_VALIDATION_MISSING_ENTITIES + missingTypes;
118 bool GenerateWorld(out
string dialogMessage)
120 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
121 WorldEditorAPI api = worldEditor.GetApi();
123 BaseWorld world = api.GetWorld();
125 world.GetBoundBox(min, max);
126 vector worldCenter = vector.Lerp(min, max, 0.5);
128 api.BeginEntityAction(
"GameModeSetupPlugin");
129 int layerID = api.GetCurrentEntityLayerId();
131 int asciiLineBreak = 10;
132 string lineBreak = asciiLineBreak.AsciiToString();
134 IEntitySource entitySource;
135 foreach (
int i, GameModeSetupConfigEntry entry : m_Prefabs)
137 string entityName = FilePath.StripExtension(FilePath.StripPath(entry.m_Prefab));
139 worldCenter += vector.Forward;
140 worldCenter[1] = world.GetSurfaceY(worldCenter[0], worldCenter[2]);
143 entitySource = entry.CreateEntity(api, entityName, layerID, worldCenter, vector.Zero);
144 api.AddToEntitySelection(entitySource);
149 entitySource = api.CreateEntity(
"CommentEntity",
string.Empty, layerID, entitySource, vector.Up, vector.Zero);
150 api.SetVariableValue(entitySource, null,
"m_Comment", entityName +
": " + lineBreak + entry.m_Comment);
151 api.SetVariableValue(entitySource, null,
"m_Color",
"0 0 0 1");
152 api.SetVariableValue(entitySource, null,
"m_FaceCamera",
"1");
153 api.SetVariableValue(entitySource, null,
"m_TextBackground",
"1");
154 api.SetVariableValue(entitySource, null,
"m_BackgroundColor",
"1 0.6 0 1");
155 api.SetVariableValue(entitySource, null,
"m_BackgroundTransparency",
"0");
157 dialogMessage +=
string.Format(DESCRIPTION_GENERATION_ENTITY, entityName, entry.m_Comment);
160 api.UpdateSelectionGui();
161 api.EndEntityAction(
"GameModeSetupPlugin");
164 api.SetCamera(worldCenter +
Vector(0, 4, 2),
Vector(180, -60, 0).AnglesToVector());
167 dialogMessage = DESCRIPTION_GENERATION_MORE_STEPS_1 + dialogMessage + DESCRIPTION_GENERATION_MORE_STEPS_2;
176 bool ValidateMissionHeader(out
string dialogMessage)
178 array<ResourceName> resources = {};
181 SearchResourcesFilter filter =
new SearchResourcesFilter();
182 filter.fileExtensions = {
"conf" };
183 filter.rootPath = missionsPath;
184 ResourceDatabase.SearchResources(filter, resources.Insert);
186 Resource missionResource;
187 BaseContainer missionContainer;
188 ResourceName missionWorld;
190 for (
int i = resources.Count() - 1; i >= 0; i--)
192 missionResource = Resource.Load(resources[i]);
193 if (!missionResource.IsValid())
196 missionContainer = missionResource.GetResource().ToBaseContainer();
197 missionContainer.Get(
"World", missionWorld);
200 dialogMessage =
string.Format(DESCRIPTION_MISSION_HEADER_EXISTS, resources[i].GetPath());
212 bool GenerateMissionHeader(
string templatePath, out
string dialogMessage)
215 Resource templateResource = Resource.Load(templatePath);
216 BaseContainer templateContainer = templateResource.GetResource().ToBaseContainer();
217 BaseContainer missionHeaderContainer = templateContainer.GetObject(
"m_MissionHeader");
220 ResourceManager resourceManager = Workbench.GetModule(ResourceManager);
222 Workbench.GetAbsolutePath(m_sWorldPath, absWorldPath);
223 MetaFile worldMeta = resourceManager.GetMetaFile(absWorldPath);
224 string fullWorldPath = worldMeta.GetResourceID();
225 missionHeaderContainer.Set(
"World", fullWorldPath);
228 string worldName = FilePath.StripExtension(FilePath.StripPath(m_sWorldPath));
230 string absoluteDirPath;
231 if (!Workbench.GetAbsolutePath(relativeDirPath, absoluteDirPath,
true))
233 if (!Workbench.GetAbsolutePath(relativeDirPath, absoluteDirPath,
false))
235 Print(
"Unable to obtain the " + SCENARIOS_PATH +
" directory path at " + relativeDirPath,
LogLevel.ERROR);
239 if (!FileIO.MakeDirectory(absoluteDirPath))
241 Print(
"Unable to create the " + SCENARIOS_PATH +
" directory at " + absoluteDirPath,
LogLevel.ERROR);
245 Print(
"Successfully created the " + SCENARIOS_PATH +
" directory at " + absoluteDirPath,
LogLevel.NORMAL);
248 string missionHeaderPath = FilePath.Concat(relativeDirPath, worldName);
249 missionHeaderPath = FilePath.AppendExtension(missionHeaderPath,
"conf");
252 if (!BaseContainerTools.SaveContainer(missionHeaderContainer, ResourceName.Empty, missionHeaderPath))
254 Print(
string.Format(
"Unable to create mission header at %1!", missionHeaderPath),
LogLevel.ERROR);
259 string missionHeaderAbsPath;
260 Workbench.GetAbsolutePath(missionHeaderPath, missionHeaderAbsPath,
false);
261 resourceManager.RegisterResourceFile(missionHeaderAbsPath,
false);
262 resourceManager.SetOpenedResource(missionHeaderPath);
264 dialogMessage =
string.Format(DESCRIPTION_MISSION_HEADER_EXISTS, missionHeaderPath);
270 protected set<typename> GetWorldEntityTypes()
272 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
274 WBProgressDialog progress =
new WBProgressDialog(
"Validating world entities...", worldEditor);
276 set<typename> types =
new set<typename>();
277 IEntitySource entitySource;
278 array<IEntitySource> queue = {};
279 int containerCount = worldEditor.GetNumContainers();
281 float prevProgress, currProgress;
282 for (
int i; i < containerCount; i++)
284 entitySource = IEntitySource.Cast(worldEditor.GetContainer(i));
286 if (entitySource.GetNumChildren() > 0)
287 queue.Insert(entitySource);
289 types.Insert(entitySource.GetClassName().ToType());
291 currProgress = i / containerCount;
292 if (currProgress - prevProgress >= 0.01)
294 progress.SetProgress(currProgress);
295 prevProgress = currProgress;
299 while (!queue.IsEmpty())
301 entitySource = queue[0];
304 types.Insert(entitySource.GetClassName().ToType());
306 for (
int i = 0, count = entitySource.GetNumChildren(); i < count; i++)
308 queue.Insert(entitySource.GetChild(i));
320 WorldEditor worldEditor = Workbench.GetModule(WorldEditor);
321 WorldEditorAPI api = worldEditor.GetApi();
323 api.GetWorldPath(m_sWorldPath);
324 m_sFileSystem = FilePath.FileSystemNameFromFileName(m_sWorldPath);
330 void GameModeSetupConfig()
335 array<ref Resource> prefabResources = {};
336 Resource prefabResource;
337 IEntitySource prefabSource;
338 array<IEntitySource> queue = {};
339 array<ref GameModeSetupConfigEntry> queuePrefabs = {};
341 foreach (GameModeSetupConfigEntry entry : m_Prefabs)
343 prefabResource = Resource.Load(entry.m_Prefab);
344 if (!prefabResource.IsValid())
347 prefabResources.Insert(prefabResource);
348 prefabSource = prefabResource.GetResource().ToEntitySource();
349 queue.Insert(prefabSource);
350 queuePrefabs.Insert(entry);
353 map<string, ref GameModeSetupConfigEntry> entries =
new map<string, ref GameModeSetupConfigEntry>();
354 GameModeSetupConfigEntry entry;
355 string requiredNamesSortedStatic[256];
358 while (!queue.IsEmpty())
360 prefabSource = queue[0];
361 entry = queuePrefabs[0];
364 queuePrefabs.Remove(0);
366 className = prefabSource.GetClassName();
367 m_iLongestTypeLength = Math.Max(m_iLongestTypeLength, className.Length());
368 requiredNamesSortedStatic[r] = className;
371 type = className.ToType();
373 entries.Insert(className, entry);
375 if (
type.IsInherited(BaseGameMode))
376 m_GameModeType =
type;
378 for (
int i, count = prefabSource.GetNumChildren(); i < count; i++)
380 queue.Insert(prefabSource.GetChild(i));
381 queuePrefabs.Insert(
new GameModeSetupConfigEntry());
386 Print(
"No game mode entity found among prefabs to be auto-generated!",
LogLevel.WARNING);
389 StaticArray.Sort(requiredNamesSortedStatic);
390 string sortedClassName;
391 for (
int i = 0; i < 256; i++)
393 sortedClassName = requiredNamesSortedStatic[i];
394 if (!sortedClassName.IsEmpty())
395 m_aRequiredTypes.Insert(entries[sortedClassName]);
401class GameModeSetupConfigEntry
406 [
Attribute(
desc:
"When defined, user will be notified that the entity requires additional configuration\nand a comment with this text will be created next to the entity.")]
420 IEntitySource CreateEntity(WorldEditorAPI api,
string entityName,
int layerID, vector pos, vector dir)
422 return api.CreateEntity(
m_Prefab, entityName, layerID, null, pos, dir);
SCR_AIAnimation_Loitering BaseContainerProps
Commanding menu commanding element class.
SCR_CampaignMilitaryBaseComponent SCR_MilitaryBaseComponent SCR_BaseContainerCustomTitleResourceName("m_sBaseName", true)
UI Textures DeployMenu Briefing conflict_HintBanner_1_UI desc
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 native vector Vector(float x, float y, float z)