You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
193 lines
6.6 KiB
193 lines
6.6 KiB
using System; |
|
using System.Collections; |
|
using System.Collections.Generic; |
|
using System.Runtime.CompilerServices; |
|
using System.Runtime.InteropServices; |
|
using static flecs_hub.flecs; |
|
|
|
namespace gaemstone.ECS; |
|
|
|
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] |
|
public class EntityAttribute : Attribute { } |
|
|
|
public unsafe readonly struct Entity |
|
{ |
|
public Universe Universe { get; } |
|
public ecs_entity_t Value { get; } |
|
|
|
public EntityType Type => new(Universe, ecs_get_type(Universe, Value)); |
|
public string Name => ecs_get_name(Universe, Value).ToStringAndFree(); |
|
public string FullPath => ecs_get_path_w_sep(Universe, default, Value, ".", default).ToStringAndFree(); |
|
|
|
public bool IsNone => Value.Data == 0; |
|
public bool IsAlive => ecs_is_alive(Universe, Value); |
|
|
|
public IEnumerable<Entity> Children { get { |
|
var term = new ecs_term_t { id = Universe.EcsChildOf & this }; |
|
var iter = Iterator.FromTerm(Universe, term); |
|
while (iter.Next()) |
|
for (var i = 0; i < iter.Count; i++) |
|
yield return iter.Entity(i); |
|
} } |
|
|
|
public Entity(Universe universe, ecs_entity_t value) |
|
{ Universe = universe; Value = value; } |
|
|
|
public void ThrowIfNone() { if (IsNone) throw new InvalidOperationException("Entity isn't valid"); } |
|
public void ThrowIfDead() { if (!IsAlive) throw new InvalidOperationException("Entity is dead"); } |
|
|
|
public void Delete() => ecs_delete(Universe, Value); |
|
|
|
|
|
public Entity Add(ecs_id_t id) { ecs_add_id(Universe, this, id); return this; } |
|
public Entity Add(Identifier id) { ecs_add_id(Universe, this, id); return this; } |
|
public Entity Add(Entity relation, Entity target) => Add(relation & target); |
|
|
|
public Entity Add<T>() |
|
=> Add(Universe.Lookup<T>()); |
|
public Entity Add<TRelation, TTarget>() |
|
=> Add(Universe.Lookup<TRelation>(), Universe.Lookup<TTarget>()); |
|
public Entity Add<TRelation>(Entity target) |
|
=> Add(Universe.Lookup<TRelation>(), target); |
|
|
|
|
|
public Entity Override(ecs_id_t id) { ecs_override_id(Universe, this, id); return this; } |
|
public Entity Override(Identifier id) { ecs_override_id(Universe, this, id); return this; } |
|
public Entity Override(Entity relation, Entity target) => Override(relation & target); |
|
|
|
public Entity Override<T>() |
|
=> Override(Universe.Lookup<T>()); |
|
public Entity Override<TRelation, TTarget>() |
|
=> Override(Universe.Lookup<TRelation>(), Universe.Lookup<TTarget>()); |
|
public Entity Override<TRelation>(Entity target) |
|
=> Override(Universe.Lookup<TRelation>(), target); |
|
|
|
|
|
public void Remove(ecs_id_t id) => ecs_remove_id(Universe, this, id); |
|
public void Remove(Identifier id) => ecs_remove_id(Universe, this, id); |
|
public void Remove<T>() => Remove(Universe.Lookup<T>()); |
|
|
|
|
|
public bool Has(ecs_id_t id) => ecs_has_id(Universe, this, id); |
|
public bool Has(Identifier id) => ecs_has_id(Universe, this, id); |
|
public bool Has(Entity relation, Entity target) => Has(relation & target); |
|
|
|
public bool Has<T>() |
|
=> Has(Universe.Lookup<T>()); |
|
public bool Has<TRelation, TTarget>() |
|
=> Has(Universe.Lookup<TRelation>(), Universe.Lookup<TTarget>()); |
|
public bool Has<TRelation>(Entity target) |
|
=> Has(Universe.Lookup<TRelation>(), target); |
|
|
|
|
|
/// <summary> |
|
/// Gets a component value from this entity. If the component is a value |
|
/// type, this will return a copy. If the component is a reference type, |
|
/// it will return the reference itself. |
|
/// When modifying a reference, consider calling <see cref="Modified"/>. |
|
/// </summary> |
|
public T Get<T>() |
|
{ |
|
var comp = Universe.Lookup<T>(); |
|
var ptr = ecs_get_id(Universe, this, comp); |
|
if (typeof(T).IsValueType) { |
|
return Unsafe.Read<T>(ptr); |
|
} else { |
|
var handle = (GCHandle)Unsafe.Read<nint>(ptr); |
|
return (T)handle.Target!; |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// Gets a reference to a component value from this entity. Only works for |
|
/// value types. When modifying, consider calling <see cref="Modified"/>. |
|
/// </summary> |
|
public ref T GetRef<T>() |
|
where T : unmanaged |
|
{ |
|
var comp = Universe.Lookup<T>(); |
|
var ptr = ecs_get_mut_id(Universe, this, comp); |
|
return ref Unsafe.AsRef<T>(ptr); |
|
} |
|
|
|
/// <summary> |
|
/// Marks a component as modified. Do this after getting a reference to |
|
/// it with <see cref="Get"/> or <see cref="GetRef"/>, making sure change |
|
/// detection will kick in. |
|
/// </summary> |
|
public void Modified<T>() |
|
{ |
|
var comp = Universe.Lookup<T>(); |
|
ecs_modified_id(Universe, this, comp); |
|
} |
|
|
|
|
|
public Entity Set<T>(in T value) |
|
where T : unmanaged |
|
{ |
|
var comp = Universe.Lookup<T>(); |
|
var size = (ulong)Unsafe.SizeOf<T>(); |
|
fixed (T* ptr = &value) ecs_set_id(Universe, this, comp, size, ptr); |
|
return this; |
|
} |
|
public Entity SetOverride<T>(in T value) |
|
where T : unmanaged |
|
{ |
|
var comp = Universe.Lookup<T>(); |
|
var size = (ulong)Unsafe.SizeOf<T>(); |
|
ecs_add_id(Universe, this, Universe.ECS_OVERRIDE | comp); |
|
fixed (T* ptr = &value) ecs_set_id(Universe, this, comp, size, ptr); |
|
return this; |
|
} |
|
|
|
public Entity Set(Type type, object obj) |
|
{ |
|
var comp = Universe.Lookup(type); |
|
var handle = (nint)GCHandle.Alloc(obj); |
|
ecs_set_id(Universe, this, comp, (ulong)sizeof(nint), &handle); |
|
// FIXME: Handle needs to be freed when component is removed! |
|
return this; |
|
} |
|
public Entity Set<T>(T obj) where T : class |
|
=> Set(typeof(T), obj); |
|
public Entity SetOverride<T>(T obj) |
|
where T : class |
|
{ |
|
var comp = Universe.Lookup<T>(); |
|
var handle = (nint)GCHandle.Alloc(obj); |
|
ecs_add_id(Universe, this, Universe.ECS_OVERRIDE | comp); |
|
ecs_set_id(Universe, this, comp, (ulong)sizeof(nint), &handle); |
|
// FIXME: Handle needs to be freed when component is removed! |
|
return this; |
|
} |
|
|
|
|
|
public static Identifier operator &(Entity first, Entity second) => Identifier.Pair(first, second); |
|
public static Identifier operator &(ecs_entity_t first, Entity second) => Identifier.Pair(first, second); |
|
public static Identifier operator |(ecs_id_t left, Entity right) => new(right.Universe, left | right.Value.Data); |
|
|
|
public static implicit operator ecs_id_t(Entity e) => e.Value.Data; |
|
public static implicit operator ecs_entity_t(Entity e) => e.Value; |
|
public static implicit operator Identifier(Entity e) => new(e.Universe, e); |
|
} |
|
|
|
public unsafe readonly struct EntityType |
|
: IEnumerable<Identifier> |
|
{ |
|
public Universe Universe { get; } |
|
public unsafe ecs_type_t* Handle { get; } |
|
|
|
public int Count => Handle->count; |
|
public Identifier this[int index] => new(Universe, Handle->array[index]); |
|
|
|
public EntityType(Universe universe, ecs_type_t* handle) |
|
{ Universe = universe; Handle = handle; } |
|
|
|
public IEnumerator<Identifier> GetEnumerator() |
|
{ for (var i = 0; i < Count; i++) yield return this[i]; } |
|
IEnumerator IEnumerable.GetEnumerator() |
|
=> GetEnumerator(); |
|
|
|
public override string ToString() |
|
=> ecs_type_str(Universe, Handle).ToStringAndFree(); |
|
}
|
|
|