diff --git a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs index d7bfbbad7..9da2ab180 100644 --- a/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs +++ b/Source/Editor/CustomEditors/Dedicated/ScriptsEditor.cs @@ -224,16 +224,44 @@ namespace FlaxEditor.CustomEditors.Dedicated private void AddScripts(List items) { - var actions = new List(4); + var actions = new List(); for (int i = 0; i < items.Count; i++) { var scriptType = items[i]; + List requiredScripts = new List(); + if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) + { + var attributes = new List(); + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireScriptAttribute requireScriptAttribute) + continue; + attributes.Add(requireScriptAttribute); + } + + if (attributes.Count > 0) + { + foreach (var attribute in attributes) + { + if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + continue; + requiredScripts.Add(new ScriptType(attribute.RequiredType)); + } + } + } var actors = ScriptsEditor.ParentEditor.Values; for (int j = 0; j < actors.Count; j++) { var actor = (Actor)actors[j]; actions.Add(AddRemoveScript.Add(actor, scriptType)); + // Check if actor has required scripts and add them if the actor does not. + foreach (var type in requiredScripts) + { + if (actor.GetScript(type.Type) != null) + continue; + actions.Add(AddRemoveScript.Add(actor, type)); + } } } @@ -584,10 +612,50 @@ namespace FlaxEditor.CustomEditors.Dedicated var values = new ScriptsContainer(elementType, i, Values); var scriptType = TypeUtils.GetObjectType(script); var editor = CustomEditorsUtil.CreateEditor(scriptType, false); + + // Check if actor has all the required scripts + bool hasAllRequiredScripts = true; + if (scriptType.HasAttribute(typeof(RequireScriptAttribute), false)) + { + var scriptTypesToCheck = new List(); + var attributes = new List(); + foreach (var e in scriptType.GetAttributes(false)) + { + if (e is not RequireScriptAttribute requireScriptAttribute) + continue; + attributes.Add(requireScriptAttribute); + } + + if (attributes.Count > 0) + { + foreach (var attribute in attributes) + { + if (!attribute.RequiredType.IsSubclassOf(typeof(Script))) + continue; + scriptTypesToCheck.Add(new ScriptType(attribute.RequiredType)); + } + } + + if (scriptTypesToCheck.Count > 0) + { + foreach (var type in scriptTypesToCheck) + { + var requiredScript = script.Actor.GetScript(type.Type); + if (requiredScript == null) + { + Editor.LogWarning($"{script} on {script.Actor} is missing required script of type {type}."); + hasAllRequiredScripts = false; + break; + } + } + } + } // Create group var title = Utilities.Utils.GetPropertyNameUI(scriptType.Name); var group = layout.Group(title, editor); + if (!hasAllRequiredScripts) + group.Panel.HeaderTextColor = FlaxEngine.GUI.Style.Current.Statusbar.Failed; if ((Presenter.Features & FeatureFlags.CacheExpandedGroups) != 0) { if (Editor.Instance.ProjectCache.IsCollapsedGroup(title)) diff --git a/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs new file mode 100644 index 000000000..eee529c5c --- /dev/null +++ b/Source/Engine/Scripting/Attributes/Editor/RequireScriptAttribute.cs @@ -0,0 +1,25 @@ +using System; + +namespace FlaxEngine; + +/// +/// This attribute is used to check for if a script requires another script type. +/// +[Serializable] +[AttributeUsage(AttributeTargets.Class)] +public class RequireScriptAttribute : Attribute +{ + /// + /// The required type. + /// + public Type RequiredType; + + /// + /// Initializes a new instance of the class. + /// + /// The required type. + public RequireScriptAttribute(Type type) + { + RequiredType = type; + } +}