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 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() => Add(Universe.Lookup()); public Entity Add() => Add(Universe.Lookup(), Universe.Lookup()); public Entity Add(Entity target) => Add(Universe.Lookup(), 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() => Override(Universe.Lookup()); public Entity Override() => Override(Universe.Lookup(), Universe.Lookup()); public Entity Override(Entity target) => Override(Universe.Lookup(), 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() => Remove(Universe.Lookup()); 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() => Has(Universe.Lookup()); public bool Has() => Has(Universe.Lookup(), Universe.Lookup()); public bool Has(Entity target) => Has(Universe.Lookup(), target); /// /// 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 . /// public T Get() { var comp = Universe.Lookup(); var ptr = ecs_get_id(Universe, this, comp); if (typeof(T).IsValueType) { return Unsafe.Read(ptr); } else { var handle = (GCHandle)Unsafe.Read(ptr); return (T)handle.Target!; } } /// /// Gets a reference to a component value from this entity. Only works for /// value types. When modifying, consider calling . /// public ref T GetRef() where T : unmanaged { var comp = Universe.Lookup(); var ptr = ecs_get_mut_id(Universe, this, comp); return ref Unsafe.AsRef(ptr); } /// /// Marks a component as modified. Do this after getting a reference to /// it with or , making sure change /// detection will kick in. /// public void Modified() { var comp = Universe.Lookup(); ecs_modified_id(Universe, this, comp); } public Entity Set(in T value) where T : unmanaged { var comp = Universe.Lookup(); var size = (ulong)Unsafe.SizeOf(); fixed (T* ptr = &value) ecs_set_id(Universe, this, comp, size, ptr); return this; } public Entity SetOverride(in T value) where T : unmanaged { var comp = Universe.Lookup(); var size = (ulong)Unsafe.SizeOf(); 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 obj) where T : class => Set(typeof(T), obj); public Entity SetOverride(T obj) where T : class { var comp = Universe.Lookup(); 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 { 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 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(); }