|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
|
|
|
using gaemstone.Utility;
|
|
|
|
using static flecs_hub.flecs;
|
|
|
|
|
|
|
|
namespace gaemstone.ECS;
|
|
|
|
|
|
|
|
public class EntityBuilder
|
|
|
|
: EntityBase<EntityBuilder>
|
|
|
|
{
|
|
|
|
public override Universe Universe { get; }
|
|
|
|
|
|
|
|
/// <summary> Set to modify existing entity (optional). </summary>
|
|
|
|
public Entity ID { get; set; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Path of the entity. If no entity is provided, an entity with this path
|
|
|
|
/// will be looked up first. When an entity is provided, the path will be
|
|
|
|
/// verified with the existing entity.
|
|
|
|
/// </summary>
|
|
|
|
public EntityPath? Path { get; set; }
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Optional entity symbol. A symbol is an unscoped identifier that can
|
|
|
|
/// be used to lookup an entity. The primary use case for this is to
|
|
|
|
/// associate the entity with a language identifier, such as a type or
|
|
|
|
/// function name, where these identifiers differ from the name they are
|
|
|
|
/// registered with in flecs.
|
|
|
|
/// </summary>
|
|
|
|
public EntityBuilder Symbol(string symbol) { _symbol = symbol; return this; }
|
|
|
|
private string? _symbol = null;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// When set to true, a low id (typically reserved for components)
|
|
|
|
/// will be used to create the entity, if no id is specified.
|
|
|
|
/// </summary>
|
|
|
|
public bool UseLowID { get; set; }
|
|
|
|
|
|
|
|
/// <summary> IDs to add to the new or existing entity. </summary>
|
|
|
|
private readonly HashSet<Identifier> _toAdd = new();
|
|
|
|
private Entity _parent = Entity.None;
|
|
|
|
|
|
|
|
/// <summary> String expression with components to add. </summary>
|
|
|
|
public string? Expression { get; }
|
|
|
|
|
|
|
|
/// <summary> Actions to run once the entity has been created. </summary>
|
|
|
|
private readonly List<Action<EntityRef>> _toSet = new();
|
|
|
|
|
|
|
|
public EntityBuilder(Universe universe, EntityPath? path = null)
|
|
|
|
{ Universe = universe; Path = path; }
|
|
|
|
|
|
|
|
public override EntityBuilder Add(Identifier id)
|
|
|
|
{
|
|
|
|
// If adding a ChildOf relation, store the parent separately.
|
|
|
|
if (id.AsPair(Universe) is (EntityRef relation, EntityRef target) &&
|
|
|
|
(relation == Universe.ChildOf)) { _parent = target; return this; }
|
|
|
|
|
|
|
|
if (_toAdd.Count == 31) throw new NotSupportedException(
|
|
|
|
"Must not add more than 31 IDs at once with EntityBuilder");
|
|
|
|
_toAdd.Add(id);
|
|
|
|
|
|
|
|
return this;
|
|
|
|
}
|
|
|
|
public override EntityBuilder Remove(Identifier id)
|
|
|
|
=> throw new NotSupportedException();
|
|
|
|
public override bool Has(Identifier id)
|
|
|
|
=> !ecs_id_is_wildcard(id) ? _toAdd.Contains(id)
|
|
|
|
: throw new NotSupportedException(); // TODO: Support wildcard.
|
|
|
|
|
|
|
|
public override T Get<T>() => throw new NotSupportedException();
|
|
|
|
public override T? MaybeGet<T>() => throw new NotSupportedException();
|
|
|
|
public override T? MaybeGet<T>(T _ = null!) where T : class => throw new NotSupportedException();
|
|
|
|
public override ref T GetMut<T>() => throw new NotSupportedException();
|
|
|
|
public override ref T GetRefOrNull<T>() => throw new NotSupportedException();
|
|
|
|
public override ref T GetRefOrThrow<T>() => throw new NotSupportedException();
|
|
|
|
public override void Modified<T>() => throw new NotImplementedException();
|
|
|
|
|
|
|
|
public override EntityBuilder Set<T>(in T value)
|
|
|
|
// "in" can't be used with lambdas, so we make a local copy.
|
|
|
|
{ var copy = value; _toSet.Add(e => e.Set(copy)); return this; }
|
|
|
|
|
|
|
|
public override EntityBuilder Set<T>(T obj)
|
|
|
|
{ _toSet.Add(e => e.Set(obj)); return this; }
|
|
|
|
|
|
|
|
public unsafe EntityRef Build()
|
|
|
|
{
|
|
|
|
var parent = _parent;
|
|
|
|
|
|
|
|
if (Path != null) {
|
|
|
|
if (parent.IsSome && Path.IsAbsolute) throw new InvalidOperationException(
|
|
|
|
"Entity already has parent set (via ChildOf), so path must not be absolute");
|
|
|
|
// If path specifies more than just a name, ensure the parent entity exists.
|
|
|
|
if (Path.Count > 1) parent = EntityPath.EnsureEntityExists(Universe, parent, Path.Parent!);
|
|
|
|
}
|
|
|
|
|
|
|
|
using var alloc = TempAllocator.Use();
|
|
|
|
var desc = new ecs_entity_desc_t {
|
|
|
|
id = ID,
|
|
|
|
name = (Path != null) ? alloc.AllocateCString(Path.Name.AsSpan()) : default,
|
|
|
|
symbol = alloc.AllocateCString(_symbol),
|
|
|
|
add_expr = alloc.AllocateCString(Expression),
|
|
|
|
use_low_id = UseLowID,
|
|
|
|
sep = CStringExtensions.ETX,
|
|
|
|
};
|
|
|
|
|
|
|
|
var add = desc.add; var index = 0;
|
|
|
|
if (parent.IsSome) add[index++] = Identifier.Pair(Universe.ChildOf, parent);
|
|
|
|
foreach (var id in _toAdd) add[index++] = id;
|
|
|
|
|
|
|
|
var entityID = ecs_entity_init(Universe, &desc);
|
|
|
|
var entity = new EntityRef(Universe, new(entityID));
|
|
|
|
foreach (var action in _toSet) action(entity);
|
|
|
|
|
|
|
|
return entity;
|
|
|
|
}
|
|
|
|
}
|