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.
194 lines
6.6 KiB
194 lines
6.6 KiB
2 years ago
|
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();
|
||
|
}
|