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); } } type QueryRes = readonly T[]; export class Query { extractor; constructor(extractor: (entity: Entity) => O | null) { this.extractor = extractor; } *run(entities: Iterable) { for (const entity of entities) { const extracted = this.extractor(entity); if (extracted !== null) yield extracted; } } and(other: Query) { const this_extractor = this.extractor; return new Query((entity) => { const this_extracted = this_extractor(entity); if (this_extracted === null) return null; const other_extracted = other.extractor(entity); if (other_extracted === null) return null; return [...this_extracted, ...other_extracted] as const; }); } static with(comp: Constructible) { function extract(entity: Entity) { const extracted = entity.get(comp); if (extracted === null) return null; return [extracted] as const; } return new Query(extract); } with(comp: Constructible) { return this.and(Query.with(comp)); } filter(predicate: (comps: O) => boolean) { const this_extract = this.extractor; return new Query((entity) => { const extracted = this_extract(entity); if (extracted === null) return null; if (predicate(extracted)) return extracted; else return null; }); } }