using System; using System.Runtime.CompilerServices; using GlitchyEngine.Components; using GlitchyEngine.Core; using GlitchyEngine.Extensions; namespace GlitchyEngine; /// /// The base class for all entities and scripts in the current world. /// public class Entity : EngineObject { /// /// Don't call this constructor yourself. /// protected Entity() { // We don't do anything here. // This constructor will be called by the Engine to initialize the scripts fields. // Especially don't call Create here! Because Create would try and create a new entity. } /// /// Creates a new Entity. /// /// The name of the new entity. public Entity(string name = null) { Create(name, null); } /// /// Creates a new Entity. /// /// The name of the new entity. /// The components that the entity shall have. public Entity(string name, params Type[] components) { Create(name, components); } private void Create(string name, Type[] components) { ScriptGlue.Entity_Create(this, name, components, out _uuid); if (_uuid == UUID.Zero) { throw new InvalidOperationException("Failed to create the Entity. Received UUID.Zero from the engine."); } } /// /// Creates an instance that represents the Entity with the given id. /// /// The ID of the entity that belongs to this instance. internal Entity(UUID uuid) : base(uuid) { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent() => HasComponent(typeof(T)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool HasComponent(Type type) => ScriptGlue.Entity_HasComponent(_uuid, type); /// /// Gets the component with the specified type. /// /// The type of the component to get. /// The component or null, if the entity doesn't have a component of the specified type. public T GetComponent() where T : Component, new() { if (HasComponent()) { return new T { _uuid = _uuid }; } return null; } /// /// Gets the component with the given type. /// /// The type of the component to get. /// The component or null, if the entity doesn't have a component of the given type. /// For performance reasons it is recommended to use if possible. public Component GetComponent(Type componentType) { // TODO: probably throw! if (!componentType.IsSubclassOf(typeof(Component))) return null; if (HasComponent(componentType)) { return Activator.CreateInstance(componentType) as Component; } return null; } #region Add Components /// /// Adds the component with the specified type. /// /// The type of the component to add. /// The component. public T AddComponent() where T : Component, new() { ScriptGlue.Entity_AddComponent(_uuid, typeof(T)); return new T { _uuid = _uuid }; } /// /// Adds the component with the given type. /// /// The type of the component to add. /// The component or null, if the given type is not a valid component type. /// For performance reasons it is recommended to use if possible. public Component AddComponent(Type componentType) { if (!componentType.IsSubclassOf(typeof(Component))) { throw new ArgumentException($"Invalid component type \"{componentType}\". Must be a subclass of \"Component\".", nameof(componentType)); } ScriptGlue.Entity_AddComponent(_uuid, componentType); Component component = Activator.CreateInstance(componentType) as Component; if (component != null) { component._uuid = _uuid; } return component; } /// /// Adds components with the specified types to the entity. /// /// The types of the components to add. /// An array containing the components. public Component[] AddComponents(params Type[] componentTypes) { foreach ((Type componentType, int index) in componentTypes.WithIndex()) { if (!componentType.IsSubclassOf(typeof(Component))) { throw new ArgumentException($"Invalid component type \"{componentType}\" at index {index}. Must be a subclass of \"Component\".", nameof(componentTypes)); } } ScriptGlue.Entity_AddComponents(_uuid, componentTypes); Component[] components = new Component[componentTypes.Length]; foreach ((Type componentType, int index) in componentTypes.WithIndex()) { components[index] = Activator.CreateInstance(componentType) as Component; components[index]._uuid = _uuid; } return components; } /// /// Adds the specified components to the entity. /// /// A tuple containing the added components. public (T1, T2) AddComponents() where T1 : Component, new() where T2 : Component, new() { ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2)}); return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid }); } /// /// Adds the specified components to the entity. /// /// A tuple containing the added components. public (T1, T2, T3) AddComponents() where T1 : Component, new() where T2 : Component, new() where T3 : Component, new() { ScriptGlue.Entity_AddComponents(_uuid, new []{typeof(T1), typeof(T2), typeof(T3)}); return (new T1 { _uuid = _uuid }, new T2 { _uuid = _uuid }, new T3 { _uuid = _uuid }); } #endregion Add Components /// /// Removes the component with the specified component type. /// /// The type of the component to remove. public void RemoveComponent() where T : Component, new() { ScriptGlue.Entity_RemoveComponent(_uuid, typeof(T)); } /// /// Removes the component with the given component type. /// /// The type of the component to remove. /// For performance reasons it is recommended to use if possible. public void RemoveComponent(Type componentType) { if (!componentType.IsSubclassOf(typeof(Component))) { throw new ArgumentException($"Invalid component type \"{componentType}\". Must be a subclass of \"Component\".", nameof(componentType)); } ScriptGlue.Entity_RemoveComponent(_uuid, componentType); } /// /// Sets the script of the entity to the specified type. /// If necessary removes the existing script. /// /// The type of the script. public T SetScript() where T : Entity { return ScriptGlue.Entity_SetScript(_uuid, typeof(T)) as T; } /// /// Removes the script from the entity. /// public void RemoveScript() { ScriptGlue.Entity_RemoveScript(_uuid); } public Transform Transform => GetComponent(); //public Vector3 Translation //{ // get => Transform.Translation; // set => Transform.Translation = value; //} /// /// Returns the first entity with the given name. /// /// The name of the entity. /// The first entity with the given name, or null. public static Entity FindEntityWithName(string name) { ScriptGlue.Entity_FindEntityWithName(name, out UUID entityId); if (entityId == UUID.Zero) return null; return new Entity(entityId); } /// /// Returns true if the entity has a script component of the given type; false if the entity either has no script or the script is not of the specified type. /// /// The type of the script. public bool Is() where T : Entity { ScriptGlue.Entity_GetScriptInstance(_uuid, out object scriptInstance); return scriptInstance is T; } /// /// Returns the script of the given type, or null, if the entity has no script of the given type. /// /// The type of the script. /// The script instance or null. public T As() where T : Entity { ScriptGlue.Entity_GetScriptInstance(_uuid, out object scriptInstance); return scriptInstance as T; } /// /// Destroys the entity and all it's children. /// /// /// The destruction will not take place immediately. It will happen at the end of the current frame. /// public void Destroy() { ScriptGlue.Entity_Destroy(_uuid); } /// /// Instantiates a copy of the given entity and it's children. /// /// The entity to copy. /// The new entity. public static Entity CreateInstance(Entity entity) { ScriptGlue.Entity_CreateInstance(entity.UUID, out UUID newEntityId); return new Entity(newEntityId); } // Will be executed once after the entity has be created. // void OnCreate(); // Will be executed every frame. // void OnUpdate(GameTime); // Will be executed once when the entity is being destroyed. // void OnDestroy(); }