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

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();
}