140 lines
4.3 KiB
Zig
140 lines
4.3 KiB
Zig
const std = @import("std");
|
|
const World = @import("world.zig");
|
|
const Archetype = @import("archetype.zig");
|
|
const QueryFilters = @import("query_filters.zig");
|
|
const TypeID = @import("../template/type_id.zig").of;
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn Query(
|
|
comptime Result: type,
|
|
comptime Filters: QueryFilters,
|
|
) type {
|
|
return struct {
|
|
const Self = @This();
|
|
|
|
const result_fields = std.meta.fields(Result);
|
|
|
|
//
|
|
// FIELDS
|
|
//
|
|
|
|
world: *World,
|
|
arch_cursor: usize,
|
|
row_cursor: usize,
|
|
|
|
current_arch: ?*Archetype,
|
|
current_row: usize,
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn init(world: *World) Self {
|
|
return .{
|
|
.world = world,
|
|
.arch_cursor = 0,
|
|
.row_cursor = 0,
|
|
|
|
.current_arch = null,
|
|
.current_row = 0,
|
|
};
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
fn matches(arch: *Archetype) bool {
|
|
// --- REQUIRED COMPONENTS ---
|
|
inline for (result_fields) |field| {
|
|
// TODO: Clean this shit up
|
|
const FieldT = field.type;
|
|
const is_optional = @typeInfo(FieldT) == .optional;
|
|
|
|
const PtrT = if (is_optional) @typeInfo(FieldT).optional.child else FieldT;
|
|
const T = std.meta.Child(PtrT);
|
|
|
|
if (!arch.type_map.contains(TypeID(T))) {
|
|
// --- SKIP OPTIONALS ---
|
|
if (!is_optional) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- WITH ---
|
|
inline for (Filters.with) |T| {
|
|
if (!arch.type_map.contains(TypeID(T))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// --- WITHOUT ---
|
|
inline for (Filters.without) |T| {
|
|
if (arch.type_map.contains(TypeID(T))) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn next(self: *Self) ?Result {
|
|
while (self.arch_cursor < self.world.archetypes.items.len) {
|
|
var arch = &self.world.archetypes.items[self.arch_cursor];
|
|
|
|
// --- SKIP ARCHETYPES ---
|
|
if (!matches(arch)) {
|
|
self.arch_cursor += 1;
|
|
self.row_cursor = 0;
|
|
continue;
|
|
}
|
|
|
|
// --- END ROWS ---
|
|
if (self.row_cursor >= arch.entities.items.len) {
|
|
self.arch_cursor += 1;
|
|
self.row_cursor = 0;
|
|
continue;
|
|
}
|
|
|
|
const row = self.row_cursor;
|
|
|
|
self.row_cursor += 1;
|
|
self.current_arch = arch;
|
|
self.current_row = row;
|
|
|
|
var result: Result = undefined;
|
|
|
|
inline for (result_fields) |field| {
|
|
// TODO: Clean this shit up
|
|
const FieldT = field.type;
|
|
|
|
const is_optional = @typeInfo(FieldT) == .optional;
|
|
|
|
const PtrT = if (is_optional) @typeInfo(FieldT).optional.child else FieldT;
|
|
|
|
const T = std.meta.Child(PtrT);
|
|
|
|
const ptr = arch.get(T, row);
|
|
|
|
if (is_optional) {
|
|
@field(result, field.name) = ptr;
|
|
} else {
|
|
@field(result, field.name) = ptr orelse return null;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// ----------------------------------------------------
|
|
/// ----------------------------------------------------
|
|
pub fn entity(self: *const Self) u64 {
|
|
return self.current_arch.?.entities.items[
|
|
self.current_row
|
|
];
|
|
}
|
|
};
|
|
}
|