using System;
using System.Runtime.CompilerServices;
using GlitchyEngine.Core;
using GlitchyEngine.Extensions;
namespace GlitchyEngine;
public class Entity : EngineObject
{
///
/// Only used by the Engine. Don't use!
///
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)
{
_uuid = 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
{
object scriptInstance = ScriptGlue.Entity_GetScriptInstance(_uuid);
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
{
object scriptInstance = ScriptGlue.Entity_GetScriptInstance(_uuid);
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();
}