High-level wrapper around Flecs, a powerful ECS (Entity Component System) library, written in Zig language
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.

113 lines
4.2 KiB

const std = @import("std");
const c = @import("./c.zig");
const Id = @import("./id.zig").Id;
const Lookup = @import("./main.zig").Lookup;
const World = @import("./world.zig").World;
pub const EntityError = error{
/// Id is `0`.
IsNone,
/// Id is not valid.
IsInvalid,
/// Id is not an `Entity`.
IsNotEntity,
/// Generation doesn't match.
GenMismatch,
/// Entity is not alive.
IsNotAlive,
};
/// An `Entity` is an `Id` which represents a "thing" within the world. This
/// could be a traditional game object, but is also able to represent other
/// concepts such as component types, relationship types, systems, modules,
/// resources, and anything else you can make fit.
///
/// Each `Entity` can have a number of `Id`s added to it, which could be
/// components (such as `Position`), zero-size tags (such as `Disabled`) or
/// relationship `Pair`s (such as `(ChildOf, parent)`).
///
/// Entities can be created using `World.new()` and deleted with `delete()`.
/// When an entity is deleted it is no longer considered "alive". A world can
/// contain up to 4 billion alive entities.
///
/// An `Id` can be converted to an `Entity` using `Id.asEntity()` (fails if
/// the id isn't an entity), and back again with `asId()` (always succeeds).
pub fn Entity(comptime ctx: anytype) type {
return struct {
world: *World(ctx),
raw: c.ecs_entity_t,
const Self = @This();
pub fn fromRaw(world: *World(ctx), raw: c.ecs_entity_t) Self {
return .{ .world = world, .raw = raw };
}
/// Ensures this `Entity` is valid, returning an error otherwise.
/// Entities that are valid can be used with API functions.
///
/// An `Entity` is valid if ..
/// - .. it is not `0`.
/// - .. it does not have invalid bits set.
/// - .. it does not have any `Id` flags set.
/// - .. no entity with this id is alive, but this entity's generation is `0`.
/// - .. an entity with this exact id and generation is alive in the world.
pub fn ensureValid(self: Self) !Self {
// `0` is not a valid entity id.
if (self.raw == 0) return EntityError.IsNone;
// Entity ids should not contain data in dead zone bits.
const VALID_BITS_MASK: c.ecs_entity_t = 0xFF00FFFFFFFFFFFF;
if ((self.raw & ~VALID_BITS_MASK) != 0) return EntityError.IsInvalid;
// Entity ids should not contain `Id` flag bits.
if ((self.raw & c.ECS_ID_FLAGS_MASK) != 0) return EntityError.IsNotEntity;
// Get the currently alive entity entity with the same id.
// If entity is alive, the generation has to match.
// If entity is not alive, the generation has to be `0`.
const entity = c.ecs_get_alive(self.world.raw, self.raw);
if (c.ECS_GENERATION(entity) != c.ECS_GENERATION(self.raw))
return EntityError.GenMismatch;
return self;
}
/// Ensures this `Entity` is alive in this world, returning an error if not.
pub fn ensureAlive(self: Self) !Self {
return if ((try ensureValid(self)).isAlive()) self else EntityError.IsNotAlive;
}
pub fn isAlive(self: Self) bool {
return c.ecs_is_alive(self.world.raw, self.raw);
}
pub fn asId(self: Self) Id(ctx) {
return @bitCast(self);
}
pub fn getEntityId(self: Self) u32 {
return @intCast(self.raw & c.ECS_ENTITY_MASK);
}
pub fn getGeneration(self: Self) u16 {
return @intCast(c.ECS_GENERATION(self.raw));
}
/// Deletes this `Entity` and all of its components.
pub fn delete(self: Self) void {
c.ecs_delete(self.world.raw, self.raw);
}
pub fn get(self: Self, comptime T: type) ?*const T {
const id = Lookup(ctx, T).id;
return @alignCast(@ptrCast(c.ecs_get_id(self.world.raw, self.raw, id)));
}
pub fn set(self: Self, comptime T: type, value: T) void {
const id = Lookup(ctx, T).id;
_ = c.ecs_set_id(self.world.raw, self.raw, id, @sizeOf(T), &value);
}
};
}