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 ]; } }; }