import { Awaitable, ClassMap, Constructible, first, log_from, Prototyped, wait } from "../common/utils.ts"; const log = log_from(import.meta); export class Entity { identifier; components; engine; constructor(identifier: number, engine: Engine) { this.identifier = identifier; this.engine = engine; this.components = new ClassMap(); } insert(...components: Prototyped[]) { for (const c of components) this.components.insert(c); return this; } get(class_: Constructible) { return this.components.get(class_); } get_force(class_: Constructible) { const result = this.get(class_); if (result === null) throw new Error(); return result; } } export class Engine { next_identifier; entities; constructor() { this.next_identifier = 0; this.entities = new Map(); } start() { log("Started engine."); } spawn(op: (entity: Entity) => unknown) { const identifier = this.next_identifier++; const entity = new Entity(identifier, this); op(entity); this.entities.set(identifier, entity); return entity; } run(sys: (engine: Engine) => T) { return sys(this); } async global_system_loop(sys: (engine: Engine) => Awaitable, interval: number) { while (true) { await sys(this); await wait(interval); } } *query_all(query: Query) { yield* query.run(this.entities.values()); } query_one(query: Query) { return first(this.query_all(query)); } get(identifier: number) { const value = this.entities.get(identifier); if (value === undefined) return null; return value; } get_force(identifier: number) { const entity = this.get(identifier); if (entity === null) throw new Error(); return entity; } delete(identifier: number) { return this.entities.delete(identifier); } } export class Query { private test; constructor(test: (entity: Entity) => boolean) { this.test = test; } and(query: Query) { return new Query((entity) => this.test(entity) && query.test(entity)); } static with(...components: Constructible[]) { return new Query((entity) => { for (const comp of components) if (!entity.components.has(comp)) return false; return true; }); } with(...components: Constructible[]) { return this.and(Query.with(...components)); } static filter(component: Constructible, filter: (comp: I, entity: Entity) => boolean) { return new Query((entity) => { const comp = entity.get(component); if (comp === null) return false; return filter(comp, entity); }); } filter(component: Constructible, filter: (comp: I, entity: Entity) => boolean) { return this.and(Query.filter(component, filter)); } *run(entities: Iterable) { for (const entity of entities) if (this.test(entity)) yield entity; } }