Alternative managed wrapper around flecs-cs bindings for using the ECS framework Flecs in modern .NET.
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.

172 lines
8.3 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using gaemstone.ECS.Internal;
using static flecs_hub.flecs;
namespace gaemstone.ECS;
public unsafe readonly partial struct Entity<TContext>
: IEquatable<Entity<TContext>>
{
public static readonly Entity<TContext> None = default;
public readonly World<TContext> World;
public readonly Entity Value;
public uint NumericId => Value.NumericId;
public bool IsNone => Value.IsNone;
public bool IsSome => Value.IsSome;
public bool IsValid => EntityAccess.IsValid(World, this);
public bool IsAlive => IsSome && EntityAccess.IsAlive(World, this);
public string? Name { get => EntityAccess.GetName(World, this); set => EntityAccess.SetName(World, this, value); }
public string? Symbol { get => EntityAccess.GetSymbol(World, this); set => EntityAccess.SetSymbol(World, this, value); }
public EntityPath Path => EntityPath.From(World, this);
public EntityType<TContext> Type => new(World, ecs_get_type(World, this));
public Entity<TContext>? Parent
=> GetOrNull(World, GetTargets(FlecsBuiltIn.ChildOf).FirstOrDefault());
public IEnumerable<Entity<TContext>> Children
=> World.Term(new(FlecsBuiltIn.ChildOf, this)).GetAllEntities();
private Entity(World<TContext> world, Entity value)
{ World = world; Value = value; }
public static Entity<TContext> GetOrInvalid(World<TContext> world, Entity value)
=> new(world, value);
public static Entity<TContext>? GetOrNull(World<TContext> world, Entity value)
=> new Entity<TContext>(world, value).ValidOrNull();
public static Entity<TContext> GetOrThrow(World<TContext> world, Entity value)
=> new Entity<TContext>(world, value).ValidOrThrow();
public Entity<TContext>? ValidOrNull() => IsValid ? this : null;
public Entity<TContext> ValidOrThrow() => IsValid ? this
: throw new InvalidOperationException($"The entity {this} is not valid");
public Entity<TContext>? AliveOrNull() => IsAlive ? this : null;
public Entity<TContext> AliveOrThrow() => IsAlive ? this
: throw new InvalidOperationException($"The entity {this} is not alive");
public void Delete()
=> ecs_delete(World, this);
public Entity<TContext> CreateLookup<T>()
{
ref var lookup = ref Lookup<TContext>.Entity<T>.Value;
if (lookup == this) { /* Don't throw if lookup already has the same entity set. */ }
else if (lookup.IsSome) throw new InvalidOperationException(
$"The lookup for type {typeof(T)} in context {typeof(TContext)} is already in use by {lookup}");
lookup = this;
return this;
}
public EntityBuilder<TContext> NewChild(EntityPath? path = null)
=> World.New(path?.ThrowIfAbsolute(), this);
public Entity<TContext>? LookupChildOrNull(EntityPath path)
=> World.LookupPathOrNull(path.ThrowIfAbsolute(), this);
public Entity<TContext> LookupChildOrThrow(EntityPath path)
=> World.LookupPathOrThrow(path.ThrowIfAbsolute()!, this);
public Entity<TContext> ChildOf(Entity parent)
=> Add(FlecsBuiltIn.ChildOf, parent);
public bool IsDisabled => Has(FlecsBuiltIn.Disabled);
public bool IsEnabled => !Has(FlecsBuiltIn.Disabled);
public Entity<TContext> Disable() => Add(FlecsBuiltIn.Disabled);
public Entity<TContext> Enable() => Remove(FlecsBuiltIn.Disabled);
public Entity<TContext> Add(Id id) { EntityAccess.Add(World, this, id); return this; }
public Entity<TContext> Add(string symbol) => Add(World.LookupSymbolOrThrow(symbol));
public Entity<TContext> Add(Entity relation, Entity target) => Add(Id.Pair(relation, target));
public Entity<TContext> Add<TEntity>() => Add(World.Entity<TEntity>());
public Entity<TContext> Add<TRelation>(Entity target) => Add(World.Entity<TRelation>(), target);
public Entity<TContext> Add<TRelation, TTarget>() => Add(World.Entity<TRelation>(), World.Entity<TTarget>());
public Entity<TContext> Remove(Id id) { EntityAccess.Remove(World, this, id); return this; }
public Entity<TContext> Remove(string symbol) => Remove(World.LookupSymbolOrThrow(symbol));
public Entity<TContext> Remove(Entity relation, Entity target) => Remove(Id.Pair(relation, target));
public Entity<TContext> Remove<TEntity>() => Remove(World.Entity<TEntity>());
public Entity<TContext> Remove<TRelation>(Entity target) => Remove(World.Entity<TRelation>(), target);
public Entity<TContext> Remove<TRelation, TTarget>() => Remove(World.Entity<TRelation>(), World.Entity<TTarget>());
public bool Has(Id id) => EntityAccess.Has(World, this, id);
public bool Has(string symbol) => Has(World.LookupSymbolOrThrow(symbol));
public bool Has(Entity relation, Entity target) => Has(Id.Pair(relation, target));
public bool Has<TEntity>() => Has(World.Entity<TEntity>());
public bool Has<TRelation>(Entity target) => Has(World.Entity<TRelation>(), target);
public bool Has<TRelation, TTarget>() => Has(World.Entity<TRelation>(), World.Entity<TTarget>());
public T? GetOrNull<T>(Id id) where T : unmanaged => EntityAccess.GetOrNull<T>(World, this, id);
public T? GetOrNull<T>(Id id, T _ = null!) where T : class => EntityAccess.GetOrNull<T>(World, this, id);
public T GetOrThrow<T>(Id id) => EntityAccess.GetOrThrow<T>(World, this, id);
public ref T GetMut<T>(Id id) where T : unmanaged => ref EntityAccess.GetMut<T>(World, this, id);
public ref T GetRefOrNull<T>(Id id) where T : unmanaged => ref EntityAccess.GetRefOrNull<T>(World, this, id);
public ref T GetRefOrThrow<T>(Id id) where T : unmanaged => ref EntityAccess.GetRefOrThrow<T>(World, this, id);
public Entity<TContext> Modified(Id id) { EntityAccess.Modified(World, this, id); return this; }
public Entity<TContext> Set<T>(Id id, in T value) where T : unmanaged { EntityAccess.Set(World, this, id, value); return this; }
public Entity<TContext> Set<T>(Id id, T value) where T : class { EntityAccess.Set(World, this, id, value); return this; }
public T? GetOrNull<T>() where T : unmanaged => GetOrNull<T>(World.Entity<T>());
public T? GetOrNull<T>(T _ = null!) where T : class => GetOrNull<T>(World.Entity<T>());
public T GetOrThrow<T>() => GetOrThrow<T>(World.Entity<T>());
public ref T GetMut<T>() where T : unmanaged => ref GetMut<T>(World.Entity<T>());
public ref T GetRefOrNull<T>() where T : unmanaged => ref GetRefOrNull<T>(World.Entity<T>());
public ref T GetRefOrThrow<T>() where T : unmanaged => ref GetRefOrThrow<T>(World.Entity<T>());
public Entity<TContext> Modified<T>() => Modified(World.Entity<T>());
public Entity<TContext> Set<T>(in T value) where T : unmanaged => Set(World.Entity<T>(), value);
public Entity<TContext> Set<T>(T value) where T : class => Set(World.Entity<T>(), value);
public IEnumerable<Entity<TContext>> GetTargets(Entity relation)
{
foreach (var entity in EntityAccess.GetTargets(World, this, relation))
yield return new(World, entity);
}
public IEnumerable<Entity<TContext>> GetTargets(string symbol)
=> GetTargets(World.LookupSymbolOrThrow(symbol));
public IEnumerable<Entity<TContext>> GetTargets<TRelation>()
=> GetTargets(World.Entity<TRelation>());
public bool Equals(Entity<TContext> other)
{
#if DEBUG
// In DEBUG mode, we additionally check if the worlds the two compared
// values are from the same world. This accounts for the world being a
// stage, hence why it might not be the cheapest operation.
if (World != other.World) throw new ArgumentException(
"The specified values are not from the same world");
#endif
return Value == other.Value;
}
public override bool Equals(object? obj)
=> (obj is Entity<TContext> other) && Equals(other);
public override int GetHashCode()
=> Value.GetHashCode();
public override string? ToString()
=> Value.ToString();
public static bool operator ==(Entity<TContext> left, Entity<TContext> right) => left.Equals(right);
public static bool operator !=(Entity<TContext> left, Entity<TContext> right) => !left.Equals(right);
public static implicit operator Entity (Entity<TContext> entity) => entity.Value;
public static implicit operator ecs_entity_t(Entity<TContext> entity) => entity.Value.Value;
public static implicit operator Id<TContext>(Entity<TContext> entity) => Id<TContext>.GetUnsafe(entity.World, entity);
public static implicit operator Id (Entity<TContext> entity) => new(entity.Value.Value.Data);
public static implicit operator ecs_id_t (Entity<TContext> entity) => entity.Value.Value.Data;
public static implicit operator Term (Entity<TContext> entity) => new(entity.Value);
public static implicit operator TermId(Entity<TContext> entity) => new(entity.Value);
}