Second commit
This commit is contained in:
commit
36840f3ad4
999 files changed
+61364
No files matched your search
@@ -0,0 +1,158 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
[ManualPage("Entities/Brush Entity Builder", showInTree: false)]
|
||||
public class BrushBuilderPage : BuilderPageBase<BrushBuilderPage.SampleData>
|
||||
{
|
||||
public class SampleData : ScriptableObject
|
||||
{
|
||||
[SerializeField] internal string m_UnityName = "MyCoolBrush";
|
||||
[SerializeField] internal string m_MapName = "";
|
||||
[SerializeField] internal string m_Category = "";
|
||||
[SerializeField] internal BrushType m_Type = BrushType.Solid;
|
||||
}
|
||||
|
||||
protected override void OnPropertiesGUI()
|
||||
{
|
||||
RenderProperty(nameof(SampleData.m_UnityName),
|
||||
title: "MonoBehaviour Name",
|
||||
description: "The name of the main MonoBehaviour script for your brush entity.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_MapName),
|
||||
title: "TrenchBroom name",
|
||||
description: "(optional) The entity name to use in TrenchBroom. Leave this blank to use the default.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_Category),
|
||||
title: "Category Name",
|
||||
description: "(optional) The category/group name to use in TrenchBroom. Leave this blank to use the default.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_Type),
|
||||
title: "Brush Type",
|
||||
description: "The brush type to use. Defaults to 'Solid'.",
|
||||
extraContent: () =>
|
||||
{
|
||||
Foldout("Available Brush Types", () =>
|
||||
{
|
||||
PropertyDescription("Solid",
|
||||
"A Solid brush with a texture.",
|
||||
"Example: a moving platform.");
|
||||
PropertyDescription("Liquid",
|
||||
"A brush with a texture, but with a trigger collision instead of a solid one.",
|
||||
"Example: a volume of water/lava, which damages players when entering.");
|
||||
PropertyDescription("Trigger",
|
||||
"A completely invisible brush which has a trigger collision.",
|
||||
"Example: a volume which triggers an event when players walk into the area.");
|
||||
PropertyDescription("Invisible",
|
||||
"A completely invisible brush which has solid collision.",
|
||||
"Example: an invisible wall to stop players going out of bounds.");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnPostPropertiesGUI()
|
||||
{
|
||||
List<string> attributeParts = new(16);
|
||||
|
||||
if (!Sample.m_MapName.IsNullOrEmpty())
|
||||
{
|
||||
attributeParts.Add($"\"{Sample.m_MapName}\"");
|
||||
}
|
||||
|
||||
if (!Sample.m_Category.IsNullOrEmpty())
|
||||
{
|
||||
attributeParts.Add($"category: \"{Sample.m_Category}\"");
|
||||
}
|
||||
|
||||
if (Sample.m_Type != BrushType.Solid)
|
||||
{
|
||||
attributeParts.Add($"type: {nameof(BrushType)}.{Sample.m_Type}");
|
||||
}
|
||||
|
||||
string attrName = FormatAttributeName(typeof(BrushEntityAttribute));
|
||||
string attributeLine = attributeParts.Count == 0
|
||||
? $"[{attrName}]"
|
||||
: $"[{attrName}({String.Join(", ", attributeParts)})]";
|
||||
|
||||
string unityName = Sample.m_UnityName.IsNullOrEmpty()
|
||||
? "<error>"
|
||||
: Sample.m_UnityName;
|
||||
|
||||
string catName = Sample.m_Category.IsNullOrEmpty() ? FgdConsts.BRUSH_PREFIX : Sample.m_Category;
|
||||
string entName = Sample.m_MapName.IsNullOrEmpty() ? Sample.m_UnityName.ToNamingConvention(SyncSettings.TypeNamingConvention) : Sample.m_MapName;
|
||||
|
||||
HorizontalLine();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
{
|
||||
H3("Create New Entity");
|
||||
|
||||
string code =
|
||||
"// Generated by Tremble v1.7.0\n" +
|
||||
$"// Brush entity '{catName}_{entName}'\n" +
|
||||
"\n" +
|
||||
"using UnityEngine;\n" +
|
||||
$"using {nameof(TinyGoose)}.{nameof(Tremble)};\n" +
|
||||
"\n" +
|
||||
attributeLine + "\n" +
|
||||
$"public class {unityName} : MonoBehaviour\n" +
|
||||
"{\n" +
|
||||
" // Your cool class here ;)\n" +
|
||||
"}";
|
||||
|
||||
CodeWithHeaderNoCopy($"Full source for {unityName}.cs", code);
|
||||
|
||||
ActionBar_SingleAction("Save to new MonoBehaviour class", () =>
|
||||
{
|
||||
string assetsFolder = Path.Combine(Directory.GetCurrentDirectory(), "Assets");
|
||||
|
||||
string filename = EditorUtility.SaveFilePanel($"Choose where to save {unityName}", assetsFolder, $"{unityName}.cs", "cs");
|
||||
if (!filename.IsNullOrEmpty())
|
||||
{
|
||||
File.WriteAllText(filename, code);
|
||||
EditorCompileUtil.CompileThen(typeof(BrushBuilderPage), nameof(OnPostCompile));
|
||||
}
|
||||
});
|
||||
}
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.Label("-or-", EditorStyles.centeredGreyMiniLabel, GUILayout.ExpandHeight(true));
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
{
|
||||
H3("Add to existing MonoBehaviour");
|
||||
CodeWithHeaderNoCopy("Attribute line only (for existing MonoBehaviour)", attributeLine);
|
||||
ActionBar_SingleAction("Copy to clipboard", () => EditorGUIUtility.systemCopyBuffer = attributeLine);
|
||||
}
|
||||
GUILayout.EndVertical();
|
||||
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private static void OnPostCompile()
|
||||
{
|
||||
string message = TrenchBroomUtil.IsTrenchBroomRunning
|
||||
? "Be sure to press F6 in TrenchBroom to reload entity definitions, so that your new entity appears!"
|
||||
: "All done!";
|
||||
EditorUtility.DisplayDialog("MonoBehaviour created!", message, "Great!");
|
||||
|
||||
ManualWindow mw = EditorWindow.GetWindow<ManualWindow>();
|
||||
if (mw)
|
||||
{
|
||||
mw.GoToPage(typeof(BrushEntitiesPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a19bc95fc7d74231b15345de8d617a86
|
||||
timeCreated: 1723368005
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/Manual/Pages/Entities/Builders/BrushBuilderPage.cs
|
||||
uploadId: 939664
|
||||
@@ -0,0 +1,163 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
[ManualPage("Entities/Point Entity Builder", showInTree: false)]
|
||||
public class PointBuilderPage : BuilderPageBase<PointBuilderPage.SampleData>
|
||||
{
|
||||
public class SampleData : ScriptableObject
|
||||
{
|
||||
[SerializeField] internal string m_UnityName = "MyCoolPoint";
|
||||
[SerializeField] internal string m_MapName = "";
|
||||
[SerializeField] internal string m_Category = "";
|
||||
[SerializeField] internal Color m_Colour = Color.white;
|
||||
[SerializeField] internal float m_Size = 0.25f;
|
||||
[SerializeField] internal Texture2D m_Sprite;
|
||||
}
|
||||
|
||||
protected override void OnPropertiesGUI()
|
||||
{
|
||||
RenderProperty(nameof(SampleData.m_UnityName),
|
||||
title: "MonoBehaviour Name",
|
||||
description: "The name of the main MonoBehaviour script of your point entity.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_MapName),
|
||||
title: "TrenchBroom name",
|
||||
description: "(optional) The entity name to use in TrenchBroom. Leave this blank to use the default.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_Category),
|
||||
title: "Category Name",
|
||||
description: "(optional) The category/group name to use in TrenchBroom. Leave this blank to use the default.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_Colour),
|
||||
title: "Preview Colour",
|
||||
description: "The colour of preview cube to represent this Point entity in TrenchBroom.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_Size),
|
||||
title: "Preview Size",
|
||||
description: "The size of preview cube to represent this Point entity in TrenchBroom.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_Sprite),
|
||||
title: "Preview Sprite",
|
||||
description: "(optional) A sprite to use to represent this Point entity in TrenchBroom.",
|
||||
objectReferenceType: typeof(Texture2D));
|
||||
}
|
||||
|
||||
protected override void OnPostPropertiesGUI()
|
||||
{
|
||||
List<string> attributeParts = new(16);
|
||||
|
||||
if (!Sample.m_MapName.IsNullOrEmpty())
|
||||
{
|
||||
attributeParts.Add($"\"{Sample.m_MapName}\"");
|
||||
}
|
||||
|
||||
if (!Sample.m_Category.IsNullOrEmpty())
|
||||
{
|
||||
attributeParts.Add($"category: \"{Sample.m_Category}\"");
|
||||
}
|
||||
|
||||
if (Sample.m_Sprite)
|
||||
{
|
||||
attributeParts.Add($"sprite: \"{Sample.m_Sprite.name}\"");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Sample.m_Colour != Color.white)
|
||||
{
|
||||
attributeParts.Add($"colour: \"{Sample.m_Colour.ToStringInvariant()}\"");
|
||||
}
|
||||
|
||||
if (Sample.m_Size != 0.25f)
|
||||
{
|
||||
attributeParts.Add($"size: {Sample.m_Size}");
|
||||
}
|
||||
}
|
||||
|
||||
string attrName = FormatAttributeName(typeof(PointEntityAttribute));
|
||||
|
||||
string attributeLine = attributeParts.Count == 0
|
||||
? $"[{attrName}]"
|
||||
: $"[{attrName}({String.Join(", ", attributeParts)})]";
|
||||
|
||||
string unityName = Sample.m_UnityName.IsNullOrEmpty()
|
||||
? "<error>"
|
||||
: Sample.m_UnityName;
|
||||
|
||||
string catName = Sample.m_Category.IsNullOrEmpty() ? FgdConsts.POINT_PREFIX : Sample.m_Category;
|
||||
string entName = Sample.m_MapName.IsNullOrEmpty() ? Sample.m_UnityName.ToNamingConvention(SyncSettings.TypeNamingConvention) : Sample.m_MapName;
|
||||
HorizontalLine();
|
||||
|
||||
GUILayout.BeginHorizontal();
|
||||
{
|
||||
GUILayout.BeginVertical();
|
||||
{
|
||||
H3("Create New Entity");
|
||||
|
||||
string code =
|
||||
"// Generated by Tremble v1.7.0\n" +
|
||||
$"// Point entity '{catName}_{entName}'\n" +
|
||||
"\n" +
|
||||
"using UnityEngine;\n" +
|
||||
$"using {nameof(TinyGoose)}.{nameof(Tremble)};\n" +
|
||||
"\n" +
|
||||
attributeLine + "\n" +
|
||||
$"public class {unityName} : MonoBehaviour\n" +
|
||||
"{\n" +
|
||||
" // Your cool class here ;)\n" +
|
||||
"}";
|
||||
|
||||
CodeWithHeaderNoCopy($"Full source for {unityName}.cs", code);
|
||||
|
||||
ActionBar_SingleAction("Save to new MonoBehaviour class", () =>
|
||||
{
|
||||
string assetsFolder = Path.Combine(Directory.GetCurrentDirectory(), "Assets");
|
||||
|
||||
string filename = EditorUtility.SaveFilePanel($"Choose where to save {unityName}", assetsFolder, $"{unityName}.cs", "cs");
|
||||
if (!filename.IsNullOrEmpty())
|
||||
{
|
||||
File.WriteAllText(filename, code);
|
||||
EditorCompileUtil.CompileThen(typeof(PointBuilderPage), nameof(OnPostCompile));
|
||||
}
|
||||
});
|
||||
}
|
||||
GUILayout.EndVertical();
|
||||
|
||||
GUILayout.Label("-or-", EditorStyles.centeredGreyMiniLabel, GUILayout.ExpandHeight(true));
|
||||
|
||||
GUILayout.BeginVertical();
|
||||
{
|
||||
H3("Add to existing MonoBehaviour");
|
||||
CodeWithHeaderNoCopy("Attribute line only (for existing MonoBehaviour)", attributeLine);
|
||||
ActionBar_SingleAction("Copy to clipboard", () => EditorGUIUtility.systemCopyBuffer = attributeLine);
|
||||
}
|
||||
GUILayout.EndVertical();
|
||||
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
|
||||
private static void OnPostCompile()
|
||||
{
|
||||
string message = TrenchBroomUtil.IsTrenchBroomRunning
|
||||
? "Be sure to press F6 in TrenchBroom to reload entity definitions, so that your new entity appears!"
|
||||
: "All done!";
|
||||
EditorUtility.DisplayDialog("MonoBehaviour created!", message, "Great!");
|
||||
|
||||
ManualWindow mw = EditorWindow.GetWindow<ManualWindow>();
|
||||
if (mw)
|
||||
{
|
||||
mw.GoToPage(typeof(PointEntitiesPage));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7c600b3e6a34d8a86cdaab529cef411
|
||||
timeCreated: 1723368625
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/Manual/Pages/Entities/Builders/PointBuilderPage.cs
|
||||
uploadId: 939664
|
||||
@@ -0,0 +1,592 @@
|
||||
//
|
||||
// This file is part of the Tremble package by Tiny Goose.
|
||||
// Copyright (c) 2024-2026 TinyGoose Ltd., All Rights Reserved.
|
||||
//
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace TinyGoose.Tremble.Editor
|
||||
{
|
||||
[ManualPage("Entities/Prefab Entity Builder", showInTree: false)]
|
||||
public class PrefabBuilderPage : BuilderPageBase<PrefabBuilderPage.SampleData>
|
||||
{
|
||||
private static readonly Dictionary<Type, string> s_ScriptFileLookup = new();
|
||||
|
||||
protected override bool HasInitialQuestions => true;
|
||||
|
||||
private bool m_MonoBehaviourListDirty = false;
|
||||
private readonly List<Type> m_CandidateTypes = new();
|
||||
|
||||
private readonly TrembleFieldConverterCollection m_FieldConverterCollection = new();
|
||||
|
||||
private Type m_UnityScriptType;
|
||||
private string m_NewUnityScriptName;
|
||||
private Action m_ResolutionAction;
|
||||
|
||||
private bool m_IsOnlyAddingSpawnablePrefab = false;
|
||||
|
||||
private bool m_IsAlreadyTrembleSpawnable = false;
|
||||
private bool m_IsAlreadyPrefabEntity = false;
|
||||
private bool m_IsMap = false;
|
||||
|
||||
private bool IsNewScript => !m_NewUnityScriptName.IsNullOrEmpty();
|
||||
private string ScriptName => m_UnityScriptType?.Name ?? m_NewUnityScriptName;
|
||||
|
||||
public class SampleData : ScriptableObject
|
||||
{
|
||||
[SerializeField] internal GameObject m_BasePrefab;
|
||||
[SerializeField] internal bool m_WantsData;
|
||||
|
||||
[SerializeField] internal string m_Category = "";
|
||||
[SerializeField] internal GameObject[] m_ExcludedPrefabs = Array.Empty<GameObject>();
|
||||
[SerializeField] internal GameObject[] m_IncludedPrefabs = Array.Empty<GameObject>();
|
||||
[SerializeField] internal bool m_OnlyVariants = false;
|
||||
}
|
||||
|
||||
protected override void OnInitialQuestionsGUI()
|
||||
{
|
||||
Text("Please select a prefab that you want to use in Tremble!");
|
||||
|
||||
m_MonoBehaviourListDirty = RenderProperty(nameof(SampleData.m_BasePrefab),
|
||||
title: "Target Prefab",
|
||||
description: "The prefab to use");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_WantsData),
|
||||
title: "I want to add data",
|
||||
description: "Whether to allow setting fields on the prefab from TrenchBroom. e.g. "+
|
||||
"variables such as Health, Colour, etc?");
|
||||
|
||||
if (Sample.m_BasePrefab)
|
||||
{
|
||||
if (m_MonoBehaviourListDirty)
|
||||
{
|
||||
Type[] monoBehaviours = Sample.m_BasePrefab
|
||||
.GetComponents<MonoBehaviour>()
|
||||
.Select(m => m.GetType())
|
||||
.ToArray();
|
||||
|
||||
m_CandidateTypes.Clear();
|
||||
m_CandidateTypes.AddRange(monoBehaviours
|
||||
.Where(t => t != typeof(TrembleSpawnablePrefab)));
|
||||
|
||||
m_IsAlreadyTrembleSpawnable = monoBehaviours.Contains(typeof(TrembleSpawnablePrefab));
|
||||
m_IsMap = monoBehaviours.Contains(typeof(MapDocument));
|
||||
m_IsAlreadyPrefabEntity = !m_IsAlreadyTrembleSpawnable && monoBehaviours.Any(t => t.HasCustomAttribute<PrefabEntityAttribute>());
|
||||
}
|
||||
|
||||
if (m_IsMap)
|
||||
{
|
||||
Text("Ha! No, that's a map prefab. We better not.");
|
||||
}
|
||||
else if (m_IsAlreadyPrefabEntity)
|
||||
{
|
||||
Text("Well, that's already spawnable in TrenchBroom. Try another prefab!");
|
||||
}
|
||||
else if (m_CandidateTypes.Count == 0)
|
||||
{
|
||||
if (m_IsAlreadyTrembleSpawnable)
|
||||
{
|
||||
Callout("Note, this prefab already has a TrembleSpawnablePrefab component",
|
||||
"allowing it to be spawned from TrenchBroom - but without field support.",
|
||||
"Converting this to a PrefabEntity will remove the TrembleSpawnablePrefab component!");
|
||||
}
|
||||
|
||||
if (m_IsAlreadyTrembleSpawnable)
|
||||
{
|
||||
if (Sample.m_WantsData)
|
||||
{
|
||||
Resolution("Create a new MonoBehaviour to use - this allows",
|
||||
"you to add fields which can be set in TrenchBroom",
|
||||
"- in order to be able to set data (i.e. Health, Type, etc).");
|
||||
ActionBar_SingleAction("Create New MonoBehaviour", () =>
|
||||
{
|
||||
m_NewUnityScriptName = GetScriptNameFromPrefab(Sample.m_BasePrefab);
|
||||
|
||||
if (Sample.m_BasePrefab.TryGetComponent(out TrembleSpawnablePrefab tsp))
|
||||
{
|
||||
Sample.m_Category = tsp.Category;
|
||||
Sample.m_OnlyVariants = tsp.OnlyVariants;
|
||||
}
|
||||
|
||||
SetResolution(Resolve_CreateNewClassAndAdd);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Text("Well, that's already spawnable in TrenchBroom. Tick the box above",
|
||||
"to add data using a [PrefabEntity].");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Sample.m_WantsData)
|
||||
{
|
||||
Resolution("Create a new MonoBehaviour to use - this allows",
|
||||
"you to add fields which can be set in TrenchBroom",
|
||||
"- in order to be able to set data (i.e. Health, Type, etc).");
|
||||
ActionBar_SingleAction("Create New MonoBehaviour", () =>
|
||||
{
|
||||
m_NewUnityScriptName = GetScriptNameFromPrefab(Sample.m_BasePrefab);
|
||||
|
||||
if (Sample.m_BasePrefab.TryGetComponent(out TrembleSpawnablePrefab tsp))
|
||||
{
|
||||
Sample.m_Category = tsp.Category;
|
||||
Sample.m_OnlyVariants = tsp.OnlyVariants;
|
||||
}
|
||||
|
||||
SetResolution(Resolve_CreateNewClassAndAdd);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Resolution("Add a 'TrembleSpawnablePrefab' component - this",
|
||||
"only allows you to spawn this prefab without being able to",
|
||||
"set any data on it.");
|
||||
|
||||
ActionBar_SingleAction("Add TrembleSpawnablePrefab component", () =>
|
||||
{
|
||||
m_IsOnlyAddingSpawnablePrefab = true;
|
||||
SetResolution(Resolve_AddTrembleSpawnablePrefab);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_IsAlreadyTrembleSpawnable)
|
||||
{
|
||||
// Already spawnable, but no candidates for [PrefabEntity] - make a new code class
|
||||
if (Sample.m_WantsData)
|
||||
{
|
||||
Resolution("Upgrade to a [PrefabEntity] - this allows",
|
||||
"you to add fields which can be set in TrenchBroom",
|
||||
"- in order to be able to set data (i.e. Health, Type, etc).");
|
||||
|
||||
foreach (Type type in m_CandidateTypes)
|
||||
{
|
||||
Bullet(type.Name);
|
||||
ActionBar_SingleAction($"Add [PrefabEntity] to {type.Name}", () =>
|
||||
{
|
||||
m_UnityScriptType = type;
|
||||
|
||||
if (Sample.m_BasePrefab.TryGetComponent(out TrembleSpawnablePrefab tsp))
|
||||
{
|
||||
Sample.m_Category = tsp.Category;
|
||||
Sample.m_OnlyVariants = tsp.OnlyVariants;
|
||||
}
|
||||
|
||||
SetResolution(Resolve_AddPrefabEntityAttribute);
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Text("This is already a TrembleSpawnablePrefab. Unless you want to add data,",
|
||||
"you don't need to do anything!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Sample.m_WantsData)
|
||||
{
|
||||
if (m_CandidateTypes.Count == 1)
|
||||
{
|
||||
Type type = m_CandidateTypes[0];
|
||||
|
||||
Resolution($"You can add a [PrefabEntity] attribute to {type.Name} on this",
|
||||
"prefab. This will enable setting the following fields:");
|
||||
|
||||
Indent(() =>
|
||||
{
|
||||
foreach (string fieldName in GetFieldNames(type))
|
||||
{
|
||||
Bullet(fieldName);
|
||||
}
|
||||
});
|
||||
|
||||
ActionBar_SingleAction($"Add [PrefabEntity]", () =>
|
||||
{
|
||||
m_UnityScriptType = type;
|
||||
SetResolution(Resolve_AddPrefabEntityAttribute);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Resolution("This prefab has MonoBehaviours which can be set to be a",
|
||||
"[PrefabEntity]. Select which one to add this attribute to.");
|
||||
|
||||
foreach (Type type in m_CandidateTypes)
|
||||
{
|
||||
string[] fieldNames = GetFieldNames(type);
|
||||
|
||||
string fields = (fieldNames is { Length: > 0 }) ? String.Join(", ", fieldNames) : "no fields";
|
||||
Bullet($"{type.Name} ({fields})");
|
||||
|
||||
ActionBar_SingleAction($"Add [PrefabEntity] to {type.Name}", () =>
|
||||
{
|
||||
m_UnityScriptType = type;
|
||||
SetResolution(Resolve_AddPrefabEntityAttribute);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Resolution("Simply add a TrembleSpawnablePrefab component to it, which will",
|
||||
"allow you to spawn the prefab in TrenchBroom, but not allow you to set any",
|
||||
"data on it.");
|
||||
ActionBar_SingleAction("Add TrembleSpawnablePrefab component", () =>
|
||||
{
|
||||
m_IsOnlyAddingSpawnablePrefab = true;
|
||||
SetResolution(Resolve_AddTrembleSpawnablePrefab);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private string[] GetFieldNames(Type type)
|
||||
{
|
||||
List<Type> soTypes= new();
|
||||
FgdClass dummyClass = new(FgdClassType.Point, type.Name, type.Name);
|
||||
TrembleSync.AddExposedFieldsToEntity(type, dummyClass, m_FieldConverterCollection, soTypes);
|
||||
|
||||
// Gather spawnflags
|
||||
NamingConvention spawnFlagNaming = TrembleSyncSettings.Get().SpawnFlagNamingConvention;
|
||||
|
||||
IEnumerable<string> spawnFlagNames = dummyClass.AllSpawnFlags
|
||||
.Select(f =>
|
||||
{
|
||||
string name = f.FieldName.ToNamingConvention(spawnFlagNaming);
|
||||
if (spawnFlagNaming == NamingConvention.HumanFriendly)
|
||||
{
|
||||
name += "?";
|
||||
}
|
||||
|
||||
return $"{name} (Boolean)";
|
||||
});
|
||||
|
||||
return dummyClass.AllFields
|
||||
.Select(f => $"{f.FieldName} ({f.FriendlyTypeName})")
|
||||
.Concat(spawnFlagNames)
|
||||
.ToArray();
|
||||
}
|
||||
|
||||
protected override void OnPropertiesGUI()
|
||||
{
|
||||
H2("Optional Extras");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_Category),
|
||||
title: "Category Name",
|
||||
description: "(optional) The category/group name to use in TrenchBroom. Leave this blank to use the default.");
|
||||
|
||||
Foldout("(optional) Include/Exclude specific Prefabs?", () =>
|
||||
{
|
||||
if (!m_IsOnlyAddingSpawnablePrefab)
|
||||
{
|
||||
Text("By default, any other prefabs containing your MonoBehaviour will be exported.");
|
||||
Text("Instead, you can choose to explicitly whitelist prefab(s) here.");
|
||||
Text("Or, you can choose to blacklist certain prefab(s) here.");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_IncludedPrefabs),
|
||||
title: "Included Prefabs",
|
||||
description: $"A whitelist of the ONLY prefab(s) to include, even though others may include your {ScriptName} MonoBehaviour?");
|
||||
|
||||
RenderProperty(nameof(SampleData.m_ExcludedPrefabs),
|
||||
title: "Excluded Prefabs",
|
||||
description: $"A blacklist of any prefab(s) to ignore, even though they include your {ScriptName} MonoBehaviour?");
|
||||
}
|
||||
|
||||
RenderProperty(nameof(SampleData.m_OnlyVariants),
|
||||
title: "Only Prefab Variants?",
|
||||
description: "Whether to only export VARIANTS of the prefab(s) found?");
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnPostPropertiesGUI()
|
||||
{
|
||||
string catName = Sample.m_Category.IsNullOrEmpty() ? FgdConsts.PREFAB_PREFIX : Sample.m_Category;
|
||||
//string entName = Sample.m_UnityName.ToNamingConvention(SyncSettings.TypeNamingConvention);
|
||||
string fullEntName = $"{catName}_{Sample.m_BasePrefab.name}";
|
||||
|
||||
if (m_IsOnlyAddingSpawnablePrefab)
|
||||
{
|
||||
H2("Tremble Spawnable Prefab Details");
|
||||
PropertyDescription("Prefab Name", labelWidth: 250f, Sample.m_BasePrefab.name);
|
||||
PropertyDescription("Exported TrenchBroom Name", labelWidth: 250f, fullEntName);
|
||||
}
|
||||
else
|
||||
{
|
||||
H2("C# Code");
|
||||
Text($"This is the code that will be used for {Sample.m_BasePrefab.name} if you click Apply below. You don't need to copy",
|
||||
"it manually (unless you want to!)");
|
||||
CodeWithHeader($"C# Script: {ScriptName}.cs (for TrenchBroom Prefab entity '{fullEntName}')", GenerateFullCodeSnippet());
|
||||
}
|
||||
|
||||
Space();
|
||||
|
||||
GUI.color = Color.green;
|
||||
LargeButton("Apply & Finish!", Resolve);
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
private void Resolution(params string[] resolution)
|
||||
{
|
||||
HorizontalLine();
|
||||
|
||||
H1("Action to take");
|
||||
Text(resolution);
|
||||
}
|
||||
|
||||
private string GetAttributeLine()
|
||||
{
|
||||
List<string> attributeParts = new(16);
|
||||
|
||||
if (!Sample.m_Category.IsNullOrEmpty())
|
||||
{
|
||||
attributeParts.Add($"category: \"{Sample.m_Category}\"");
|
||||
}
|
||||
|
||||
if (TryFormatPrefabNames("excludePrefab", Sample.m_ExcludedPrefabs, out string excludePart))
|
||||
{
|
||||
attributeParts.Add(excludePart);
|
||||
}
|
||||
|
||||
if (TryFormatPrefabNames("includePrefab", Sample.m_IncludedPrefabs, out string includePart))
|
||||
{
|
||||
attributeParts.Add(includePart);
|
||||
}
|
||||
|
||||
if (Sample.m_OnlyVariants)
|
||||
{
|
||||
attributeParts.Add("onlyVariants: true");
|
||||
}
|
||||
|
||||
string attrName = FormatAttributeName(typeof(PrefabEntityAttribute));
|
||||
|
||||
return attributeParts.Count == 0
|
||||
? $"[{attrName}]"
|
||||
: $"[{attrName}({String.Join(", ", attributeParts)})]";
|
||||
}
|
||||
|
||||
private string GenerateFullCodeSnippet()
|
||||
{
|
||||
return
|
||||
"using UnityEngine;\n" +
|
||||
$"using {nameof(TinyGoose)}.{nameof(Tremble)};\n" +
|
||||
"\n" +
|
||||
GetAttributeLine() + "\n" +
|
||||
$"public class {ScriptName} : MonoBehaviour\n" +
|
||||
"{\n" +
|
||||
(IsNewScript ? $" // Your new {ScriptName} class contents ;)" : $" // (your existing {ScriptName} code)") + "\n" +
|
||||
"}";
|
||||
}
|
||||
|
||||
private bool TryFormatPrefabNames(string propertyName, GameObject[] prefabs, out string formatted)
|
||||
{
|
||||
GameObject[] filteredPrefabs = prefabs.Where(p => p).ToArray();
|
||||
|
||||
switch (filteredPrefabs.Length)
|
||||
{
|
||||
case 1:
|
||||
formatted = $"{propertyName}: \"{filteredPrefabs[0].name}\"";
|
||||
return true;
|
||||
case > 1:
|
||||
{
|
||||
string prefabList = String.Join("\", \"", filteredPrefabs.Select(p => p.name));
|
||||
formatted = $"{propertyName}s: \"{prefabList}\"";
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
formatted = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetResolution(Action resolutionAction)
|
||||
{
|
||||
m_ResolutionAction = resolutionAction;
|
||||
MarkInitialQuestionsDone();
|
||||
}
|
||||
|
||||
private void Resolve()
|
||||
{
|
||||
m_ResolutionAction.Invoke();
|
||||
GoToPage(typeof(PrefabEntitiesPage));
|
||||
}
|
||||
|
||||
private void Resolve_AddTrembleSpawnablePrefab()
|
||||
{
|
||||
Sample.m_BasePrefab.AddComponent<TrembleSpawnablePrefab>().Modify(so =>
|
||||
{
|
||||
so.FindBackedProperty(nameof(TrembleSpawnablePrefab.OnlyVariants)).boolValue = Sample.m_OnlyVariants;
|
||||
so.FindBackedProperty(nameof(TrembleSpawnablePrefab.Category)).stringValue = Sample.m_Category;
|
||||
});
|
||||
|
||||
EditorUtility.SetDirty(Sample.m_BasePrefab);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
EditorApplication.delayCall += TrembleEditorAPI.SyncToTrenchBroom;
|
||||
|
||||
ShowCompleteDialog(
|
||||
title: "TrembleSpawnablePrefab component added!",
|
||||
message: $"{Sample.m_BasePrefab.name} now has a Tremble Spawnable Prefab component and should be available from TrenchBroom.");
|
||||
}
|
||||
|
||||
private void Resolve_CreateNewClassAndAdd()
|
||||
{
|
||||
// Write code and compile
|
||||
string assetsFolder = Path.Combine(Directory.GetCurrentDirectory(), "Assets");
|
||||
|
||||
string scriptFilePath = EditorUtility.SaveFilePanel($"Choose where to save {ScriptName}", assetsFolder, $"{ScriptName}.cs", "cs");
|
||||
if (scriptFilePath.IsNullOrEmpty())
|
||||
return;
|
||||
|
||||
File.WriteAllText(scriptFilePath, GenerateFullCodeSnippet());
|
||||
|
||||
EditorCompileUtil.CompileThen(typeof(PrefabBuilderPage), nameof(OnPostCompile_AddNewScript),
|
||||
ScriptName, AssetDatabase.GetAssetPath(Sample.m_BasePrefab));
|
||||
}
|
||||
|
||||
private void Resolve_AddPrefabEntityAttribute()
|
||||
{
|
||||
string scriptFileName = GetScriptFilename(m_UnityScriptType);
|
||||
if (scriptFileName.IsNullOrEmpty())
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
title: "Uh-oh!",
|
||||
message: $"Tremble could not find the source file for {ScriptName}.",
|
||||
"Oh no!");
|
||||
return;
|
||||
}
|
||||
|
||||
bool prefabEntityWasAdded = false;
|
||||
bool usingWasAdded = false;
|
||||
|
||||
string[] lines = File.ReadAllLines(scriptFileName);
|
||||
List<string> outLines = new(lines.Length+2);
|
||||
foreach (string line in lines)
|
||||
{
|
||||
if (!usingWasAdded && !line.ContainsInvariant("using", caseSensitive: true))
|
||||
{
|
||||
outLines.Add("using TinyGoose.Tremble;");
|
||||
usingWasAdded = true;
|
||||
}
|
||||
|
||||
if (line.ContainsInvariant($"class {ScriptName}", caseSensitive: true))
|
||||
{
|
||||
prefabEntityWasAdded = true;
|
||||
outLines.Add($"\t{GetAttributeLine()}");
|
||||
}
|
||||
|
||||
outLines.Add(line);
|
||||
}
|
||||
|
||||
if (!prefabEntityWasAdded)
|
||||
{
|
||||
EditorUtility.DisplayDialog(
|
||||
title: "Uh-oh!",
|
||||
message: $"Tremble could not find the class {ScriptName} in the source file {Path.GetFileName(scriptFileName)}.",
|
||||
"Oh no!");
|
||||
return;
|
||||
}
|
||||
|
||||
File.WriteAllLines(scriptFileName, outLines);
|
||||
|
||||
EditorCompileUtil.CompileThen(typeof(PrefabBuilderPage), nameof(OnPostCompile_AddAttribute), ScriptName);
|
||||
}
|
||||
|
||||
private static string GetScriptNameFromPrefab(GameObject prefab)
|
||||
{
|
||||
string newName = prefab.name;
|
||||
if (newName.StartsWithInvariant("P_", caseSensitive: true))
|
||||
return newName.Substring(2);
|
||||
|
||||
StringBuilder finalName = new(newName.Length);
|
||||
foreach (char c in newName)
|
||||
{
|
||||
if (c is '-')
|
||||
{
|
||||
// Swap characters
|
||||
finalName.Append('_');
|
||||
}
|
||||
else if (c is ' ')
|
||||
{
|
||||
// Omit characters
|
||||
}
|
||||
else
|
||||
{
|
||||
// Allowed characters
|
||||
finalName.Append(c);
|
||||
}
|
||||
}
|
||||
|
||||
return finalName.ToString();
|
||||
}
|
||||
|
||||
private static string GetScriptFilename(Type monoBehaviourType)
|
||||
{
|
||||
if (s_ScriptFileLookup.TryGetValue(monoBehaviourType, out string existingPath))
|
||||
return existingPath;
|
||||
|
||||
foreach (string path in AssetDatabase.GetAllAssetPaths())
|
||||
{
|
||||
// Check if the file name matches exactly (without the full path)
|
||||
if (Path.GetFileNameWithoutExtension(path).EqualsInvariant(monoBehaviourType.Name))
|
||||
{
|
||||
s_ScriptFileLookup[monoBehaviourType] = path;
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static void ShowCompleteDialog(string title, string message)
|
||||
{
|
||||
if (TrenchBroomUtil.IsTrenchBroomRunning)
|
||||
{
|
||||
message += "\n\n Note: Press F6 inside TrenchBroom to refresh Entity Definitions for it to appear!";
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog(title, message, "Great!");
|
||||
}
|
||||
|
||||
private static void OnPostCompile_AddAttribute(string scriptName)
|
||||
{
|
||||
TrembleEditorAPI.SyncToTrenchBroom();
|
||||
|
||||
ShowCompleteDialog(
|
||||
title: "[PrefabEntity] attribute added!",
|
||||
message: $"{scriptName} now has a [PrefabEntity] attribute and should be available from TrenchBroom.");
|
||||
}
|
||||
|
||||
private static void OnPostCompile_AddNewScript(string scriptName, string prefabPath)
|
||||
{
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(prefabPath);
|
||||
|
||||
Type newComponentType = Unsupported.GetTypeFromFullName(scriptName);
|
||||
prefab.AddComponent(newComponentType);
|
||||
|
||||
foreach (TrembleSpawnablePrefab tsp in prefab.GetComponents<TrembleSpawnablePrefab>())
|
||||
{
|
||||
Component.DestroyImmediate(tsp, true);
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(prefab);
|
||||
AssetDatabase.SaveAssets();
|
||||
|
||||
// Now sync
|
||||
EditorApplication.delayCall += TrembleEditorAPI.SyncToTrenchBroom;
|
||||
|
||||
// And tell the user!
|
||||
ShowCompleteDialog(
|
||||
title: "New Prefab Entity Created!",
|
||||
message: $"{scriptName}.cs was created and attached to {prefab.name}, and should be available from TrenchBroom.");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 76e837b79b44474bb99e6e62f09d2af6
|
||||
timeCreated: 1723206660
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 277805
|
||||
packageName: Tremble - Map Importer
|
||||
packageVersion: 1.11.1
|
||||
assetPath: Assets/Tremble/Editor/Manual/Pages/Entities/Builders/PrefabBuilderPage.cs
|
||||
uploadId: 939664
|
||||
Reference in new issue
Block a user