|
|
|
@ -10,6 +10,7 @@ const Lookup = @import("./main.zig").Lookup; |
|
|
|
|
const Entity = @import("./entity.zig").Entity; |
|
|
|
|
const EntityError = @import("./entity.zig").EntityError; |
|
|
|
|
const Iter = @import("./iter.zig").Iter; |
|
|
|
|
const Path = @import("./path.zig").Path; |
|
|
|
|
|
|
|
|
|
pub fn World(comptime ctx: anytype) type { |
|
|
|
|
return struct { |
|
|
|
@ -59,6 +60,29 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
return result; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: Reconsider whether lookup functions should return errors or just optionals. |
|
|
|
|
// TODO: Reconsider whether "Entity" should always be in backticks in our doc comments. |
|
|
|
|
// TODO: We really need tests for this function. |
|
|
|
|
/// Returns the `Entity` at the specified path, or an error if the |
|
|
|
|
/// entity does not exist. If the path is not absolute, the operation |
|
|
|
|
/// will use the current scope, or the world root. |
|
|
|
|
pub fn lookupByPath(self: *Self, path: Path) !Entity(ctx) { |
|
|
|
|
var parent = if (path.absolute) 0 else c.ecs_get_scope(self.raw); |
|
|
|
|
var current: c.ecs_entity_t = undefined; |
|
|
|
|
for (path.parts) |part| { |
|
|
|
|
current = switch (part) { |
|
|
|
|
.id => |i| c.ecs_get_alive(self.raw, i), |
|
|
|
|
.name => |n| c.ecs_lookup_child(self.raw, parent, n.ptr), |
|
|
|
|
}; |
|
|
|
|
if (current == 0) return error.EntityNotFound; |
|
|
|
|
// If a part is looked up by ID, parent must match. |
|
|
|
|
// If a part is looked up by name, this check isn't necessary. |
|
|
|
|
if (part == .id and parent != c.ecs_get_parent(self.raw, current)) return error.ParentMismatch; |
|
|
|
|
parent = current; |
|
|
|
|
} |
|
|
|
|
return Entity(ctx).fromRaw(self, current); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Returns the component `Entity` registered for the specified |
|
|
|
|
/// type `T`, or an error if an association has not been made. |
|
|
|
|
pub fn lookupByType(self: *Self, comptime T: type) !Entity(ctx) { |
|
|
|
@ -95,6 +119,41 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
return Entity(ctx).fromRaw(self, result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Registers a singleton component of type `T` with the specified value. |
|
|
|
|
/// |
|
|
|
|
/// A singleton is a component which has itself added to its entity. |
|
|
|
|
/// This allows looking up a value of `T` using its own type. Only one |
|
|
|
|
/// singleton of each type can exist per world, but other entities may |
|
|
|
|
/// still have this component added to them. |
|
|
|
|
/// |
|
|
|
|
/// Use `get()` and `set()` to get and set the value of this singleton. |
|
|
|
|
/// |
|
|
|
|
/// Returns the created component entity. |
|
|
|
|
pub fn singleton(self: *Self, comptime T: type, value: T) !Entity(ctx) { |
|
|
|
|
const single = try component(self, T); |
|
|
|
|
single.set(T, value); |
|
|
|
|
return single; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Gets the value of the singleton component with type `T`. |
|
|
|
|
/// |
|
|
|
|
/// Returns `error.IsNotAlive` if the entity is missing. |
|
|
|
|
/// Returns `error.ComponentMissing` if the component is missing. |
|
|
|
|
/// Both of these may occur from not calling `singleton()` first. |
|
|
|
|
pub fn get(self: *Self, comptime T: type) !T { |
|
|
|
|
const e = try self.lookupByType(T); |
|
|
|
|
return e.get(T) orelse error.ComponentMissing; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Sets the value of the singleton component with type `T`. |
|
|
|
|
/// |
|
|
|
|
/// Returns `error.IsNotAlive` if the entity is missing. |
|
|
|
|
/// This may occur from not calling `singleton()` first. |
|
|
|
|
pub fn set(self: *Self, comptime T: type, value: T) !void { |
|
|
|
|
const e = try self.lookupByType(T); |
|
|
|
|
e.set(T, value); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub fn system( |
|
|
|
|
self: *Self, |
|
|
|
|
name: [:0]const u8, |
|
|
|
@ -150,6 +209,41 @@ pub fn World(comptime ctx: anytype) type { |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/// Creates a term iterator which allows querying for all entities |
|
|
|
|
/// that have a specific `Id`. This function supports wildcards. |
|
|
|
|
pub fn term(self: *Self, id: anytype) TermIterator { |
|
|
|
|
const id_ = util.anyToId(ctx, id); |
|
|
|
|
var term_ = std.mem.zeroInit(c.ecs_term_t, .{ .id = id_ }); |
|
|
|
|
const iter = c.ecs_term_iter(self.raw, &term_); |
|
|
|
|
return .{ .world = self, .iter = iter }; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: Move this logic to `Iter`, add `TermIter` type? |
|
|
|
|
// TODO: Rename `Iter` to `Iterator`? |
|
|
|
|
pub const TermIterator = struct { |
|
|
|
|
world: *World(ctx), |
|
|
|
|
iter: c.ecs_iter_t, |
|
|
|
|
index: i32 = 0, |
|
|
|
|
|
|
|
|
|
pub fn next(self: *TermIterator) ?Entity(ctx) { |
|
|
|
|
if (self.index >= self.iter.count) |
|
|
|
|
if (!c.ecs_term_next(&self.iter)) |
|
|
|
|
return null; |
|
|
|
|
|
|
|
|
|
const result = self.iter.entities[@intCast(self.index)]; |
|
|
|
|
self.index += 1; |
|
|
|
|
return Entity(ctx).fromRaw(self.world, result); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: Check where else in the codebase it would make sense to have `deinit` take a pointer? |
|
|
|
|
pub fn deinit(self: *TermIterator) void { |
|
|
|
|
// Finalize the iterator if it hasn't run to completion. |
|
|
|
|
if ((self.iter.flags & c.EcsIterIsValid) != 0) |
|
|
|
|
c.ecs_iter_fini(&self.iter); |
|
|
|
|
self.* = undefined; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
/// Gets the current scope of this `World`. See also: `setScope()`. |
|
|
|
|
pub fn getScope(self: *Self) ?Entity(ctx) { |
|
|
|
|
const result = c.ecs_get_scope(self.raw); |
|
|
|
|