Comparing version 0.2.2 to 0.3.0
import { component } from '../Component2.js'; | ||
import { makeEffect } from '../Effect.js'; | ||
import { effect } from '../Effect.js'; | ||
import { changed, not } from '../filters.js'; | ||
import { Game } from '../Game.js'; | ||
import { logger } from '../logger.js'; | ||
import { makeSystem } from '../System.js'; | ||
import { system } from '../System.js'; | ||
import { describe, it, expect } from 'vitest'; | ||
@@ -17,3 +17,3 @@ const delta = 16 + 2 / 3; | ||
const stepsTillToggle = 3; | ||
const SetFlagEffect = makeEffect([RemovableComponent, OutputComponent], function (ent) { | ||
const SetFlagEffect = effect([RemovableComponent, OutputComponent], function (ent) { | ||
logger.debug('Setting removablePresent: true'); | ||
@@ -30,7 +30,7 @@ const output = ent.get(OutputComponent); | ||
}); | ||
const ReAddRemovableEffect = makeEffect([not(RemovableComponent)], function (ent, game) { | ||
const ReAddRemovableEffect = effect([not(RemovableComponent)], function (ent, game) { | ||
logger.debug('Adding RemovableComponent'); | ||
game.add(ent.id, RemovableComponent); | ||
}); | ||
const IncrementRemoveTimerSystem = makeSystem([RemovableComponent], (ent) => { | ||
const IncrementRemoveTimerSystem = system([RemovableComponent], (ent) => { | ||
logger.debug('Incrementing stepsSinceAdded'); | ||
@@ -42,3 +42,3 @@ const comp = ent.get(RemovableComponent); | ||
}); | ||
const RemoveSystem = makeSystem([changed(RemovableComponent)], (ent, game) => { | ||
const RemoveSystem = system([changed(RemovableComponent)], (ent, game) => { | ||
if (ent.get(RemovableComponent).stepsSinceAdded >= stepsTillToggle) { | ||
@@ -50,11 +50,3 @@ logger.debug('Removing RemovableComponent'); | ||
it('adds and removes components, and queries for those operations', () => { | ||
const game = new Game({ | ||
components: [OutputComponent, RemovableComponent], | ||
systems: [ | ||
SetFlagEffect, | ||
ReAddRemovableEffect, | ||
IncrementRemoveTimerSystem, | ||
RemoveSystem, | ||
], | ||
}); | ||
const game = new Game(); | ||
const a = game.create(); | ||
@@ -61,0 +53,0 @@ game.add(a, OutputComponent); |
@@ -29,2 +29,5 @@ import { EventSubscriber } from '@a-type/utils'; | ||
[Symbol.iterator](): IterableIterator<Entity<T[number], any>>; | ||
private setLookup; | ||
private getLookup; | ||
private clearLookup; | ||
addEntity(entity: Entity<any, any>): void; | ||
@@ -31,0 +34,0 @@ /** |
import { EventSubscriber } from '@a-type/utils'; | ||
import { getIdSignifier } from './ids.js'; | ||
/** | ||
@@ -49,2 +50,11 @@ * Archetype is a group of Entities which share a common component signature. | ||
} | ||
setLookup(entityId, index) { | ||
this.entityIndexLookup[getIdSignifier(entityId)] = index; | ||
} | ||
getLookup(entityId) { | ||
return this.entityIndexLookup[getIdSignifier(entityId)]; | ||
} | ||
clearLookup(entityId) { | ||
this.entityIndexLookup[getIdSignifier(entityId)] = undefined; | ||
} | ||
addEntity(entity) { | ||
@@ -54,3 +64,3 @@ // this is the index ("column") of this entity in the table | ||
// for lookup later when presented with an entityId | ||
this.entityIndexLookup[entity.id] = index; | ||
this.setLookup(entity.id, index); | ||
// add entity data to the column of all data arrays | ||
@@ -65,7 +75,7 @@ this.entities[index] = entity; | ||
removeEntity(entityId) { | ||
const index = this.entityIndexLookup[entityId]; | ||
const index = this.getLookup(entityId); | ||
if (index === undefined) { | ||
throw new Error(`Tried to remove ${entityId} from archetype ${this.id}, but was not present`); | ||
} | ||
this.entityIndexLookup[entityId] = undefined; | ||
this.clearLookup(entityId); | ||
const [entity] = this.entities.splice(index, 1); | ||
@@ -83,3 +93,3 @@ // FIXME: improve this!!! Maybe look into a linked list like that one blog post... | ||
getEntity(entityId) { | ||
const index = this.entityIndexLookup[entityId]; | ||
const index = this.getLookup(entityId); | ||
if (index === undefined) { | ||
@@ -86,0 +96,0 @@ throw new Error(`Could not find entity ${entityId} in archetype ${this.id}`); |
@@ -18,2 +18,5 @@ import { EventSubscriber } from '@a-type/utils'; | ||
constructor(game: Game); | ||
private lookupEntityArchetype; | ||
private setEntityArchetype; | ||
private clearEntityArchetype; | ||
createEntity(entityId: number): void; | ||
@@ -20,0 +23,0 @@ addComponent(entityId: number, instance: ComponentInstanceInternal): void; |
import { EventSubscriber } from '@a-type/utils'; | ||
import { Archetype } from './Archetype.js'; | ||
import { logger } from './logger.js'; | ||
import { getIdSignifier } from './ids.js'; | ||
export class ArchetypeManager extends EventSubscriber { | ||
@@ -14,3 +15,3 @@ constructor(game) { | ||
// should be more elegant | ||
this.emptyId = new Array(this.game.componentManager.componentHandles.length + 1) | ||
this.emptyId = new Array(this.game.componentManager.count + 1) | ||
.fill('0') | ||
@@ -20,5 +21,17 @@ .join(''); | ||
} | ||
lookupEntityArchetype(entityId) { | ||
const lookupIndex = getIdSignifier(entityId); | ||
return this.entityLookup[lookupIndex]; | ||
} | ||
setEntityArchetype(entityId, archetypeId) { | ||
const lookupIndex = getIdSignifier(entityId); | ||
this.entityLookup[lookupIndex] = archetypeId; | ||
} | ||
clearEntityArchetype(entityId) { | ||
const lookupIndex = getIdSignifier(entityId); | ||
this.entityLookup[lookupIndex] = undefined; | ||
} | ||
createEntity(entityId) { | ||
logger.debug(`Creating entity ${entityId}`); | ||
this.entityLookup[entityId] = this.emptyId; | ||
this.setEntityArchetype(entityId, this.emptyId); | ||
// allocate an Entity | ||
@@ -32,7 +45,8 @@ const entity = this.game.entityPool.acquire(); | ||
logger.debug(`Adding ${Object.getPrototypeOf(instance).constructor.name} to entity ${entityId}`); | ||
const oldArchetypeId = this.entityLookup[entityId]; | ||
const oldArchetypeId = this.lookupEntityArchetype(entityId); | ||
if (oldArchetypeId === undefined) { | ||
throw new Error(`Tried to add component ${instance.$.type.name} to ${entityId}, but it was not found in the archetype registry`); | ||
} | ||
const newArchetypeId = (this.entityLookup[entityId] = this.flipBit(oldArchetypeId, instance.$.type.id)); | ||
const newArchetypeId = this.flipBit(oldArchetypeId, instance.$.type.id); | ||
this.setEntityArchetype(entityId, newArchetypeId); | ||
if (oldArchetypeId === newArchetypeId) { | ||
@@ -54,3 +68,3 @@ // not currently supported... | ||
logger.debug(`Removing ${this.game.componentManager.getTypeName(componentType)} from entity ${entityId}`); | ||
const oldArchetypeId = this.entityLookup[entityId]; | ||
const oldArchetypeId = this.lookupEntityArchetype(entityId); | ||
if (oldArchetypeId === undefined) { | ||
@@ -63,3 +77,4 @@ logger.warn(`Tried to remove component ${this.game.componentManager.getTypeName(componentType)} from ${entityId}, but it was not found in the archetype registry`); | ||
const removed = entity.__removeComponent(componentType); | ||
const newArchetypeId = (this.entityLookup[entityId] = this.flipBit(oldArchetypeId, componentType)); | ||
const newArchetypeId = this.flipBit(oldArchetypeId, componentType); | ||
this.setEntityArchetype(entityId, newArchetypeId); | ||
const archetype = this.getOrCreate(newArchetypeId); | ||
@@ -73,7 +88,7 @@ archetype.addEntity(entity); | ||
logger.debug(`Destroying entity ${entityId}`); | ||
const archetypeId = this.entityLookup[entityId]; | ||
const archetypeId = this.lookupEntityArchetype(entityId); | ||
if (archetypeId === undefined) { | ||
throw new Error(`Tried to destroy ${entityId}, but it was not found in archetype registry`); | ||
} | ||
this.entityLookup[entityId] = undefined; | ||
this.clearEntityArchetype(entityId); | ||
const archetype = this.archetypes[archetypeId]; | ||
@@ -85,3 +100,3 @@ const entity = archetype.removeEntity(entityId); | ||
getEntity(entityId) { | ||
const archetypeId = this.entityLookup[entityId]; | ||
const archetypeId = this.lookupEntityArchetype(entityId); | ||
if (archetypeId === undefined) { | ||
@@ -88,0 +103,0 @@ logger.debug(`Could not find Archetype for Entity ${entityId}`); |
export declare const COMPONENT_CHANGE_HANDLE: unique symbol; | ||
export type BaseShape = Record<string, unknown>; | ||
export type ComponentHandle<Shape extends BaseShape = any, Ext extends Extensions<Shape> = any> = { | ||
sym: symbol; | ||
id: number; | ||
@@ -40,4 +39,9 @@ name: string; | ||
}; | ||
export declare const componentTypeMap: Map<number, ComponentHandle<any, any>>; | ||
export declare function component<Shape extends BaseShape, Ext extends Extensions<Shape>>(name: string, init: () => Shape, options?: ComponentOptions<Shape, Ext>): ComponentHandle<Shape, Ext>; | ||
export declare function state<Shape extends BaseShape, Ext extends Extensions<Shape>>(name: string, init: () => Shape, options?: Omit<ComponentOptions<Shape, Ext>, 'serialize' | 'deserialize'>): ComponentHandle<Shape, Ext>; | ||
export declare function namespace(ns: string): { | ||
component: <Shape extends BaseShape, Ext extends Extensions<Shape>>(name: string, init: () => Shape, options?: ComponentOptions<Shape, Ext>) => ComponentHandle<Shape, Ext>; | ||
state: <Shape_1 extends BaseShape, Ext_1 extends Extensions<Shape_1>>(name: string, init: () => Shape_1, options?: Omit<ComponentOptions<Shape_1, Ext_1>, "serialize" | "deserialize"> | undefined) => ComponentHandle<Shape_1, Ext_1>; | ||
}; | ||
export {}; |
@@ -0,1 +1,2 @@ | ||
import { componentTypeIds } from './IdManager.js'; | ||
export const COMPONENT_CHANGE_HANDLE = Symbol('Component change handle'); | ||
@@ -25,10 +26,14 @@ const defaultSerialize = (instance) => { | ||
}; | ||
export const componentTypeMap = new Map(); | ||
const componentNameSet = new Set(); | ||
function createComponentDefinition(name, init, options) { | ||
const sym = Symbol(name); | ||
if (componentNameSet.has(name)) { | ||
throw new Error(`Component name "${name}" already exists. Names must be unique. Use "namespace" to avoid conflicts.`); | ||
} | ||
const handle = { | ||
id: 0, | ||
id: componentTypeIds.get(), | ||
name, | ||
sym, | ||
defaults: init, | ||
}; | ||
componentTypeMap.set(handle.id, handle); | ||
function reset(instance) { | ||
@@ -83,2 +88,14 @@ Object.assign(instance, init()); | ||
} | ||
export function namespace(ns) { | ||
function namespacedComponent(name, init, options) { | ||
return component(`${ns}:${name}`, init, options); | ||
} | ||
function namespacedState(name, init, options) { | ||
return state(`${ns}:${name}`, init, options); | ||
} | ||
return { | ||
component: namespacedComponent, | ||
state: namespacedState, | ||
}; | ||
} | ||
//# sourceMappingURL=Component2.js.map |
@@ -1,2 +0,2 @@ | ||
import { ComponentInstance, ComponentInstanceInternal, ComponentHandle } from './Component2.js'; | ||
import { ComponentInstance, ComponentInstanceInternal } from './Component2.js'; | ||
import { Game } from './Game.js'; | ||
@@ -8,3 +8,2 @@ /** | ||
export declare class ComponentManager { | ||
componentHandles: ComponentHandle[]; | ||
private game; | ||
@@ -14,3 +13,5 @@ private pools; | ||
private unsubscribes; | ||
constructor(componentHandles: ComponentHandle[], game: Game); | ||
private componentIds; | ||
constructor(game: Game); | ||
get count(): number; | ||
acquire: (typeId: number, initialValues: any) => ComponentInstanceInternal; | ||
@@ -17,0 +18,0 @@ release: (instance: ComponentInstanceInternal) => void; |
import { ComponentPool } from './ComponentPool.js'; | ||
import { COMPONENT_CHANGE_HANDLE, } from './Component2.js'; | ||
import { COMPONENT_CHANGE_HANDLE, componentTypeMap, } from './Component2.js'; | ||
import { IdManager } from './IdManager.js'; | ||
/** | ||
@@ -8,4 +9,3 @@ * Manages pools of Components based on their Type, and | ||
export class ComponentManager { | ||
constructor(componentHandles, game) { | ||
this.componentHandles = componentHandles; | ||
constructor(game) { | ||
this.game = game; | ||
@@ -15,2 +15,3 @@ this.pools = new Array(); | ||
this.unsubscribes = new Array(); | ||
this.componentIds = new IdManager(); | ||
this.acquire = (typeId, initialValues) => { | ||
@@ -20,3 +21,3 @@ if (!this.pools[typeId]) { | ||
} | ||
const component = this.pools[typeId].acquire(initialValues, this.game.idManager.get()); | ||
const component = this.pools[typeId].acquire(initialValues, this.componentIds.get()); | ||
component.$[COMPONENT_CHANGE_HANDLE] = this.onComponentChanged; | ||
@@ -27,2 +28,3 @@ return component; | ||
delete instance.$[COMPONENT_CHANGE_HANDLE]; | ||
this.componentIds.release(instance.$.id); | ||
return this.pools[instance.$.type.id].release(instance); | ||
@@ -52,13 +54,13 @@ }; | ||
}; | ||
// initialize pools, one for each ComponentType by ID. ComponentType IDs are incrementing integers. | ||
Object.values(componentHandles).forEach((handle) => { | ||
// assign an ID | ||
handle.id = game.idManager.get(); | ||
// create a pool | ||
this.pools[handle.id] = new ComponentPool(handle, this.game); | ||
}); | ||
// pre-allocate pools for each ComponentType | ||
for (const [id, val] of componentTypeMap) { | ||
this.pools[id] = new ComponentPool(val, this.game); | ||
} | ||
// TODO: right time to do this? | ||
this.unsubscribes.push(game.subscribe('preApplyOperations', this.resetChanged)); | ||
} | ||
get count() { | ||
return componentTypeMap.size; | ||
} | ||
} | ||
//# sourceMappingURL=ComponentManager.js.map |
@@ -6,5 +6,7 @@ import { Game } from './Game.js'; | ||
type CleanupResult = Promise<CleanupFn | void> | CleanupFn | void; | ||
export declare function makeEffect<Filter extends QueryComponentFilter>(filter: Filter, effect: (entity: EntityImpostorFor<Filter>, game: Game, info: { | ||
export declare function effect<Filter extends QueryComponentFilter>(filter: Filter, effect: (entity: EntityImpostorFor<Filter>, game: Game, info: { | ||
abortSignal: AbortSignal; | ||
}) => CleanupResult): (game: Game) => () => void; | ||
/** @deprecated - use effect */ | ||
export declare const makeEffect: typeof effect; | ||
export {}; |
@@ -1,3 +0,4 @@ | ||
export function makeEffect(filter, effect) { | ||
return function (game) { | ||
import { allSystems } from './System.js'; | ||
export function effect(filter, effect) { | ||
function eff(game) { | ||
const query = game.queryManager.create(filter); | ||
@@ -44,4 +45,8 @@ const abortControllers = new Array(); | ||
}; | ||
}; | ||
} | ||
allSystems.push(eff); | ||
return eff; | ||
} | ||
/** @deprecated - use effect */ | ||
export const makeEffect = effect; | ||
//# sourceMappingURL=Effect.js.map |
@@ -23,10 +23,12 @@ import { ComponentHandle } from './Component2.js'; | ||
export declare const changed: <Comp extends ComponentHandle>(Component: Comp) => Changed<Comp>; | ||
export type Any<Comps extends ComponentHandle[]> = { | ||
export type OneOf<Comps extends ComponentHandle[]> = { | ||
Components: Comps; | ||
kind: 'any'; | ||
kind: 'oneOf'; | ||
__isFilter: true; | ||
toString(): string; | ||
}; | ||
export declare const any: <Comps extends ComponentHandle[]>(...Components: Comps) => Any<Comps>; | ||
export type Filter<Comp extends ComponentHandle> = Not<Comp> | Has<Comp> | Changed<Comp> | Any<Comp[]>; | ||
export declare const oneOf: <Comps extends ComponentHandle[]>(...Components: Comps) => OneOf<Comps>; | ||
/** @deprecated - use oneOf */ | ||
export declare const any: <Comps extends ComponentHandle[]>(...Components: Comps) => OneOf<Comps>; | ||
export type Filter<Comp extends ComponentHandle> = Not<Comp> | Has<Comp> | Changed<Comp> | OneOf<Comp[]>; | ||
export declare const isFilter: (thing: any) => thing is Filter<any>; | ||
@@ -36,2 +38,2 @@ export declare const isNotFilter: (fil: Filter<any>) => fil is Not<ComponentHandle>; | ||
export declare const isChangedFilter: (fil: Filter<any>) => fil is Changed<ComponentHandle>; | ||
export declare const isAnyFilter: (fil: Filter<any>) => fil is Any<ComponentHandle[]>; | ||
export declare const isOneOfFilter: (fil: Filter<any>) => fil is OneOf<ComponentHandle[]>; |
@@ -25,10 +25,12 @@ export const has = (Component) => ({ | ||
}); | ||
export const any = (...Components) => ({ | ||
export const oneOf = (...Components) => ({ | ||
Components, | ||
kind: 'any', | ||
kind: 'oneOf', | ||
__isFilter: true, | ||
toString() { | ||
return `any(${Components.map((Comp) => Comp.name).join(', ')})`; | ||
return `oneOf(${Components.map((Comp) => Comp.name).join(', ')})`; | ||
}, | ||
}); | ||
/** @deprecated - use oneOf */ | ||
export const any = oneOf; | ||
export const isFilter = (thing) => thing.__isFilter === true; | ||
@@ -38,3 +40,3 @@ export const isNotFilter = (fil) => fil.kind === 'not'; | ||
export const isChangedFilter = (fil) => fil.kind === 'changed'; | ||
export const isAnyFilter = (fil) => fil.kind === 'any'; | ||
export const isOneOfFilter = (fil) => fil.kind === 'oneOf'; | ||
//# sourceMappingURL=filters.js.map |
@@ -12,3 +12,3 @@ import { QueryManager } from './QueryManager.js'; | ||
import { EntityImpostorFor } from './QueryIterator.js'; | ||
import type { AssetLoaders, BaseShape, Globals } from './index.js'; | ||
import { type AssetLoaders, type BaseShape, type Globals } from './index.js'; | ||
import { EventSubscriber } from '@a-type/utils'; | ||
@@ -30,3 +30,3 @@ import { ComponentHandle } from './Component2.js'; | ||
private _queryManager; | ||
private _idManager; | ||
private _entityIds; | ||
private _archetypeManager; | ||
@@ -44,8 +44,7 @@ private _operationQueue; | ||
private _constants; | ||
constructor({ components, systems, assetLoaders, }: { | ||
components: ComponentHandle[]; | ||
systems?: ((game: Game) => () => void)[]; | ||
constructor({ assetLoaders, ignoreSystemsWarning, }?: { | ||
assetLoaders?: AssetLoaders; | ||
ignoreSystemsWarning?: boolean; | ||
}); | ||
get idManager(): IdManager; | ||
get entityIds(): IdManager; | ||
get componentManager(): ComponentManager; | ||
@@ -71,7 +70,7 @@ get archetypeManager(): ArchetypeManager; | ||
*/ | ||
add: <ComponentShape extends BaseShape>(entityId: number, handle: ComponentHandle<ComponentShape>, initial?: Partial<ComponentShape>) => void; | ||
add: <ComponentShape extends BaseShape>(entity: number | Entity, handle: ComponentHandle<ComponentShape>, initial?: Partial<ComponentShape>) => void; | ||
/** | ||
* Remove a component by type from an entity | ||
*/ | ||
remove: <T extends ComponentHandle>(entityId: number, Type: T) => void; | ||
remove: <T extends ComponentHandle>(entity: number | Entity, Type: T) => void; | ||
/** | ||
@@ -78,0 +77,0 @@ * Get a single entity by its known ID |
@@ -11,6 +11,7 @@ import { QueryManager } from './QueryManager.js'; | ||
import { EventSubscriber } from '@a-type/utils'; | ||
import { allSystems } from './System.js'; | ||
export class Game extends EventSubscriber { | ||
constructor({ components, systems = [], assetLoaders = {}, }) { | ||
constructor({ assetLoaders = {}, ignoreSystemsWarning, } = {}) { | ||
super(); | ||
this._idManager = new IdManager(); | ||
this._entityIds = new IdManager((...msgs) => console.debug('Entity IDs:', ...msgs)); | ||
this._operationQueue = []; | ||
@@ -32,3 +33,3 @@ this._globals = new Resources(); | ||
this.create = () => { | ||
const id = this.idManager.get(); | ||
const id = this.entityIds.get(); | ||
this._operationQueue.push({ | ||
@@ -52,3 +53,4 @@ op: 'createEntity', | ||
*/ | ||
this.add = (entityId, handle, initial) => { | ||
this.add = (entity, handle, initial) => { | ||
const entityId = typeof entity === 'number' ? entity : entity.id; | ||
this._operationQueue.push({ | ||
@@ -64,3 +66,4 @@ op: 'addComponent', | ||
*/ | ||
this.remove = (entityId, Type) => { | ||
this.remove = (entity, Type) => { | ||
const entityId = typeof entity === 'number' ? entity : entity.id; | ||
this._operationQueue.push({ | ||
@@ -153,2 +156,3 @@ op: 'removeComponent', | ||
entity = this.archetypeManager.destroyEntity(operation.entityId); | ||
this.entityIds.release(operation.entityId); | ||
this._removedList.add(entity); | ||
@@ -161,10 +165,16 @@ break; | ||
}; | ||
this._componentManager = new ComponentManager(components, this); | ||
this._componentManager = new ComponentManager(this); | ||
this._assets = new Assets(assetLoaders); | ||
this._queryManager = new QueryManager(this); | ||
this._archetypeManager = new ArchetypeManager(this); | ||
this._runnableCleanups = systems.map((sys) => sys(this)); | ||
if (allSystems.length === 0 && !ignoreSystemsWarning) { | ||
throw new Error('No systems are defined at the type of game construction. You have to define systems before calling the Game constructor. Did you forget to import modules which define your systems?'); | ||
} | ||
this._runnableCleanups = allSystems | ||
.map((sys) => sys(this)) | ||
.filter(Boolean); | ||
console.debug(`Registered ${allSystems.length} systems`); | ||
} | ||
get idManager() { | ||
return this._idManager; | ||
get entityIds() { | ||
return this._entityIds; | ||
} | ||
@@ -171,0 +181,0 @@ get componentManager() { |
@@ -11,3 +11,3 @@ import { component } from './Component2.js'; | ||
game = new Game({ | ||
components: [A, B, C], | ||
ignoreSystemsWarning: true, | ||
}); | ||
@@ -14,0 +14,0 @@ }); |
@@ -6,7 +6,10 @@ /** | ||
export declare class IdManager { | ||
private log?; | ||
private recycled; | ||
private active; | ||
private allocatedCount; | ||
constructor(log?: ((...msg: any[]) => void) | undefined); | ||
get(): number; | ||
release(id: number): void; | ||
} | ||
export declare const componentTypeIds: IdManager; |
@@ -1,2 +0,2 @@ | ||
import { incrementIdVersion, SIGNIFIER_MASK } from './ids.js'; | ||
import { incrementIdVersion, setIdVersion, SIGNIFIER_MASK } from './ids.js'; | ||
/** | ||
@@ -7,3 +7,4 @@ * Provides monotonically increasing ID numbers. Allows | ||
export class IdManager { | ||
constructor() { | ||
constructor(log) { | ||
this.log = log; | ||
this.recycled = new Array(); | ||
@@ -14,2 +15,3 @@ this.active = new Array(); | ||
get() { | ||
var _a; | ||
let id; | ||
@@ -21,5 +23,8 @@ id = this.recycled.shift(); | ||
} | ||
// FIXME: incrementing first means ids start at 1 | ||
// incrementing first means ids start at 1 | ||
id = ++this.allocatedCount; | ||
id = setIdVersion(id, 0); | ||
(_a = this.log) === null || _a === void 0 ? void 0 : _a.call(this, 'New ID allocated, total:', this.allocatedCount); | ||
} | ||
this.active.push(id); | ||
return id; | ||
@@ -32,6 +37,8 @@ } | ||
} | ||
this.active.splice(index); | ||
this.active.splice(index, 1); | ||
this.recycled.push(incrementIdVersion(id)); | ||
} | ||
} | ||
// global, since component() and state() use it | ||
export const componentTypeIds = new IdManager(); | ||
//# sourceMappingURL=IdManager.js.map |
@@ -5,7 +5,8 @@ import { AssetLoader } from './Assets.js'; | ||
export * from './Component2.js'; | ||
export * from './System.js'; | ||
export { system } from './System.js'; | ||
export * from './filters.js'; | ||
export * from './Effect.js'; | ||
export { effect } from './Effect.js'; | ||
export * from './compose.js'; | ||
export { Entity } from './Entity.js'; | ||
export { setup } from './Setup.js'; | ||
export interface Globals { | ||
@@ -12,0 +13,0 @@ [key: string]: any; |
export * from './Game.js'; | ||
export * from './Query.js'; | ||
export * from './Component2.js'; | ||
export * from './System.js'; | ||
export { system } from './System.js'; | ||
export * from './filters.js'; | ||
export * from './Effect.js'; | ||
export { effect } from './Effect.js'; | ||
export * from './compose.js'; | ||
export { Entity } from './Entity.js'; | ||
export { setup } from './Setup.js'; | ||
//# sourceMappingURL=index.js.map |
@@ -7,3 +7,3 @@ export class Keyboard { | ||
this.handleKeyDown = (ev) => { | ||
if (ev.target === document.body) { | ||
if (ev.target === document.body && ev.key !== 'F5' && ev.key !== 'F12') { | ||
ev.preventDefault(); | ||
@@ -10,0 +10,0 @@ } |
@@ -33,3 +33,3 @@ import { isFilter, has } from './filters.js'; | ||
break; | ||
case 'any': | ||
case 'oneOf': | ||
match = filter.Components.some((Comp) => archetype.includes(Comp)); | ||
@@ -36,0 +36,0 @@ } |
@@ -26,5 +26,3 @@ import { ArchetypeManager } from './ArchetypeManager.js'; | ||
componentManager: { | ||
componentHandles: { | ||
length: 10, | ||
}, | ||
count: 10, | ||
getTypeName: () => 'TEST MOCK', | ||
@@ -31,0 +29,0 @@ }, |
import { ComponentHandle } from './Component2.js'; | ||
import { Entity } from './Entity.js'; | ||
import { Any, Filter, Not } from './filters.js'; | ||
import { OneOf, Filter, Not } from './filters.js'; | ||
import { Game } from './Game.js'; | ||
import { Query, QueryComponentFilter } from './Query.js'; | ||
type FilterNots<CompUnion extends Filter<ComponentHandle> | ComponentHandle> = CompUnion extends Not<any> ? never : CompUnion; | ||
type UnwrapAnys<CompUnion extends Filter<ComponentHandle> | ComponentHandle> = CompUnion extends Any<any> ? never : CompUnion; | ||
type UnwrapAnys<CompUnion extends Filter<ComponentHandle> | ComponentHandle> = CompUnion extends OneOf<any> ? never : CompUnion; | ||
type OnlyNots<CompUnion extends Filter<ComponentHandle> | ComponentHandle> = CompUnion extends Not<infer C> ? C : never; | ||
@@ -9,0 +9,0 @@ type UnwrapFilters<CompUnion extends Filter<ComponentHandle> | ComponentHandle> = CompUnion extends Filter<infer C> ? C : CompUnion; |
import { Game } from './Game.js'; | ||
import { QueryComponentFilter } from './Query.js'; | ||
import { EntityImpostorFor } from './QueryIterator.js'; | ||
export declare function makeSystem<Filter extends QueryComponentFilter>(filter: Filter, run: (entity: EntityImpostorFor<Filter>, game: Game) => void, phase?: 'step' | 'preStep' | 'postStep'): (game: Game) => () => void; | ||
export declare const allSystems: ((game: Game) => void | (() => void))[]; | ||
export declare function system<Filter extends QueryComponentFilter>(filter: Filter, run: (entity: EntityImpostorFor<Filter>, game: Game) => void, phase?: 'step' | 'preStep' | 'postStep'): (game: Game) => () => void; | ||
/** @deprecated - use system */ | ||
export declare const makeSystem: typeof system; |
@@ -1,3 +0,4 @@ | ||
export function makeSystem(filter, run, phase = 'step') { | ||
return function (game) { | ||
export const allSystems = new Array(); | ||
export function system(filter, run, phase = 'step') { | ||
function sys(game) { | ||
const query = game.queryManager.create(filter); | ||
@@ -11,4 +12,8 @@ function onPhase() { | ||
return game.subscribe(phase, onPhase); | ||
}; | ||
} | ||
allSystems.push(sys); | ||
return sys; | ||
} | ||
/** @deprecated - use system */ | ||
export const makeSystem = system; | ||
//# sourceMappingURL=System.js.map |
{ | ||
"name": "0g", | ||
"version": "0.2.2", | ||
"version": "0.3.0", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "type": "module", |
import { component } from '../Component2.js'; | ||
import { makeEffect } from '../Effect.js'; | ||
import { effect } from '../Effect.js'; | ||
import { Entity } from '../Entity.js'; | ||
@@ -7,3 +7,3 @@ import { changed, not } from '../filters.js'; | ||
import { logger } from '../logger.js'; | ||
import { makeSystem } from '../System.js'; | ||
import { system } from '../System.js'; | ||
import { describe, it, expect } from 'vitest'; | ||
@@ -24,3 +24,3 @@ | ||
const SetFlagEffect = makeEffect( | ||
const SetFlagEffect = effect( | ||
[RemovableComponent, OutputComponent], | ||
@@ -42,3 +42,3 @@ function (ent) { | ||
const ReAddRemovableEffect = makeEffect( | ||
const ReAddRemovableEffect = effect( | ||
[not(RemovableComponent)], | ||
@@ -51,3 +51,3 @@ function (ent, game) { | ||
const IncrementRemoveTimerSystem = makeSystem([RemovableComponent], (ent) => { | ||
const IncrementRemoveTimerSystem = system([RemovableComponent], (ent) => { | ||
logger.debug('Incrementing stepsSinceAdded'); | ||
@@ -60,22 +60,11 @@ const comp = ent.get(RemovableComponent); | ||
const RemoveSystem = makeSystem( | ||
[changed(RemovableComponent)], | ||
(ent, game) => { | ||
if (ent.get(RemovableComponent).stepsSinceAdded >= stepsTillToggle) { | ||
logger.debug('Removing RemovableComponent'); | ||
game.remove(ent.id, RemovableComponent); | ||
} | ||
}, | ||
); | ||
const RemoveSystem = system([changed(RemovableComponent)], (ent, game) => { | ||
if (ent.get(RemovableComponent).stepsSinceAdded >= stepsTillToggle) { | ||
logger.debug('Removing RemovableComponent'); | ||
game.remove(ent.id, RemovableComponent); | ||
} | ||
}); | ||
it('adds and removes components, and queries for those operations', () => { | ||
const game = new Game({ | ||
components: [OutputComponent, RemovableComponent], | ||
systems: [ | ||
SetFlagEffect, | ||
ReAddRemovableEffect, | ||
IncrementRemoveTimerSystem, | ||
RemoveSystem, | ||
], | ||
}); | ||
const game = new Game(); | ||
@@ -82,0 +71,0 @@ const a = game.create(); |
import { EventSubscriber } from '@a-type/utils'; | ||
import { ComponentHandle } from './Component2.js'; | ||
import { Entity } from './Entity.js'; | ||
import { getIdSignifier } from './ids.js'; | ||
@@ -39,2 +40,14 @@ export type ArchetypeEvents = { | ||
private setLookup(entityId: number, index: number) { | ||
this.entityIndexLookup[getIdSignifier(entityId)] = index; | ||
} | ||
private getLookup(entityId: number) { | ||
return this.entityIndexLookup[getIdSignifier(entityId)]; | ||
} | ||
private clearLookup(entityId: number) { | ||
this.entityIndexLookup[getIdSignifier(entityId)] = undefined; | ||
} | ||
addEntity(entity: Entity<any, any>) { | ||
@@ -44,3 +57,3 @@ // this is the index ("column") of this entity in the table | ||
// for lookup later when presented with an entityId | ||
this.entityIndexLookup[entity.id] = index; | ||
this.setLookup(entity.id, index); | ||
@@ -57,3 +70,3 @@ // add entity data to the column of all data arrays | ||
removeEntity(entityId: number) { | ||
const index = this.entityIndexLookup[entityId]; | ||
const index = this.getLookup(entityId); | ||
if (index === undefined) { | ||
@@ -64,3 +77,3 @@ throw new Error( | ||
} | ||
this.entityIndexLookup[entityId] = undefined; | ||
this.clearLookup(entityId); | ||
@@ -81,3 +94,3 @@ const [entity] = this.entities.splice(index, 1); | ||
getEntity(entityId: number) { | ||
const index = this.entityIndexLookup[entityId]; | ||
const index = this.getLookup(entityId); | ||
if (index === undefined) { | ||
@@ -84,0 +97,0 @@ throw new Error( |
@@ -6,2 +6,3 @@ import { EventSubscriber } from '@a-type/utils'; | ||
import { logger } from './logger.js'; | ||
import { getIdSignifier } from './ids.js'; | ||
@@ -33,5 +34,3 @@ export type ArchetypeManagerEvents = { | ||
// should be more elegant | ||
this.emptyId = new Array( | ||
this.game.componentManager.componentHandles.length + 1, | ||
) | ||
this.emptyId = new Array(this.game.componentManager.count + 1) | ||
.fill('0') | ||
@@ -42,5 +41,18 @@ .join(''); | ||
private lookupEntityArchetype(entityId: number) { | ||
const lookupIndex = getIdSignifier(entityId); | ||
return this.entityLookup[lookupIndex]; | ||
} | ||
private setEntityArchetype(entityId: number, archetypeId: string) { | ||
const lookupIndex = getIdSignifier(entityId); | ||
this.entityLookup[lookupIndex] = archetypeId; | ||
} | ||
private clearEntityArchetype(entityId: number) { | ||
const lookupIndex = getIdSignifier(entityId); | ||
this.entityLookup[lookupIndex] = undefined; | ||
} | ||
createEntity(entityId: number) { | ||
logger.debug(`Creating entity ${entityId}`); | ||
this.entityLookup[entityId] = this.emptyId; | ||
this.setEntityArchetype(entityId, this.emptyId); | ||
// allocate an Entity | ||
@@ -59,3 +71,3 @@ const entity = this.game.entityPool.acquire(); | ||
); | ||
const oldArchetypeId = this.entityLookup[entityId]; | ||
const oldArchetypeId = this.lookupEntityArchetype(entityId); | ||
if (oldArchetypeId === undefined) { | ||
@@ -66,6 +78,4 @@ throw new Error( | ||
} | ||
const newArchetypeId = (this.entityLookup[entityId] = this.flipBit( | ||
oldArchetypeId, | ||
instance.$.type.id, | ||
)); | ||
const newArchetypeId = this.flipBit(oldArchetypeId, instance.$.type.id); | ||
this.setEntityArchetype(entityId, newArchetypeId); | ||
if (oldArchetypeId === newArchetypeId) { | ||
@@ -97,3 +107,3 @@ // not currently supported... | ||
); | ||
const oldArchetypeId = this.entityLookup[entityId]; | ||
const oldArchetypeId = this.lookupEntityArchetype(entityId); | ||
if (oldArchetypeId === undefined) { | ||
@@ -112,6 +122,4 @@ logger.warn( | ||
const newArchetypeId = (this.entityLookup[entityId] = this.flipBit( | ||
oldArchetypeId, | ||
componentType, | ||
)); | ||
const newArchetypeId = this.flipBit(oldArchetypeId, componentType); | ||
this.setEntityArchetype(entityId, newArchetypeId); | ||
const archetype = this.getOrCreate(newArchetypeId); | ||
@@ -127,3 +135,3 @@ archetype.addEntity(entity); | ||
logger.debug(`Destroying entity ${entityId}`); | ||
const archetypeId = this.entityLookup[entityId]; | ||
const archetypeId = this.lookupEntityArchetype(entityId); | ||
if (archetypeId === undefined) { | ||
@@ -134,3 +142,3 @@ throw new Error( | ||
} | ||
this.entityLookup[entityId] = undefined; | ||
this.clearEntityArchetype(entityId); | ||
const archetype = this.archetypes[archetypeId]; | ||
@@ -143,3 +151,3 @@ const entity = archetype.removeEntity(entityId); | ||
getEntity(entityId: number) { | ||
const archetypeId = this.entityLookup[entityId]; | ||
const archetypeId = this.lookupEntityArchetype(entityId); | ||
if (archetypeId === undefined) { | ||
@@ -146,0 +154,0 @@ logger.debug(`Could not find Archetype for Entity ${entityId}`); |
@@ -0,5 +1,6 @@ | ||
import { componentTypeIds } from './IdManager.js'; | ||
export const COMPONENT_CHANGE_HANDLE = Symbol('Component change handle'); | ||
export type BaseShape = Record<string, unknown>; | ||
type Empty = Record<string, never>; | ||
@@ -10,3 +11,2 @@ export type ComponentHandle< | ||
> = { | ||
sym: symbol; | ||
id: number; | ||
@@ -114,2 +114,5 @@ name: string; | ||
export const componentTypeMap = new Map<number, ComponentHandle>(); | ||
const componentNameSet = new Set<string>(); | ||
function createComponentDefinition< | ||
@@ -123,9 +126,15 @@ Shape extends BaseShape, | ||
): ComponentHandle<Shape, Ext> { | ||
const sym = Symbol(name); | ||
if (componentNameSet.has(name)) { | ||
throw new Error( | ||
`Component name "${name}" already exists. Names must be unique. Use "namespace" to avoid conflicts.`, | ||
); | ||
} | ||
const handle = { | ||
id: 0, | ||
id: componentTypeIds.get(), | ||
name, | ||
sym, | ||
defaults: init, | ||
} as ComponentHandle<Shape, Ext>; | ||
componentTypeMap.set(handle.id, handle); | ||
function reset(instance: Shape) { | ||
@@ -202,5 +211,23 @@ Object.assign(instance, init()); | ||
type Blobby = Record<string, any>; | ||
type Defined = { 3: 'a' }; | ||
type Test = Blobby[keyof Blobby]; | ||
export function namespace(ns: string) { | ||
function namespacedComponent< | ||
Shape extends BaseShape, | ||
Ext extends Extensions<Shape>, | ||
>(name: string, init: () => Shape, options?: ComponentOptions<Shape, Ext>) { | ||
return component<Shape, Ext>(`${ns}:${name}`, init, options); | ||
} | ||
function namespacedState< | ||
Shape extends BaseShape, | ||
Ext extends Extensions<Shape>, | ||
>( | ||
name: string, | ||
init: () => Shape, | ||
options?: Omit<ComponentOptions<Shape, Ext>, 'serialize' | 'deserialize'>, | ||
) { | ||
return state<Shape, Ext>(`${ns}:${name}`, init, options); | ||
} | ||
return { | ||
component: namespacedComponent, | ||
state: namespacedState, | ||
}; | ||
} |
@@ -6,5 +6,6 @@ import { ComponentPool } from './ComponentPool.js'; | ||
ComponentInstanceInternal, | ||
ComponentHandle, | ||
componentTypeMap, | ||
} from './Component2.js'; | ||
import { Game } from './Game.js'; | ||
import { IdManager } from './IdManager.js'; | ||
@@ -19,14 +20,9 @@ /** | ||
private unsubscribes = new Array<() => void>(); | ||
private componentIds = new IdManager(); | ||
constructor( | ||
public componentHandles: ComponentHandle[], | ||
private game: Game, | ||
) { | ||
// initialize pools, one for each ComponentType by ID. ComponentType IDs are incrementing integers. | ||
Object.values(componentHandles).forEach((handle) => { | ||
// assign an ID | ||
handle.id = game.idManager.get(); | ||
// create a pool | ||
this.pools[handle.id] = new ComponentPool(handle, this.game); | ||
}); | ||
constructor(private game: Game) { | ||
// pre-allocate pools for each ComponentType | ||
for (const [id, val] of componentTypeMap) { | ||
this.pools[id] = new ComponentPool(val, this.game); | ||
} | ||
@@ -39,2 +35,6 @@ // TODO: right time to do this? | ||
public get count() { | ||
return componentTypeMap.size; | ||
} | ||
acquire = (typeId: number, initialValues: any) => { | ||
@@ -46,3 +46,3 @@ if (!this.pools[typeId]) { | ||
initialValues, | ||
this.game.idManager.get(), | ||
this.componentIds.get(), | ||
); | ||
@@ -55,2 +55,3 @@ component.$[COMPONENT_CHANGE_HANDLE] = this.onComponentChanged; | ||
delete instance.$[COMPONENT_CHANGE_HANDLE]; | ||
this.componentIds.release(instance.$.id); | ||
return this.pools[instance.$.type.id].release(instance); | ||
@@ -57,0 +58,0 @@ }; |
import { Game } from './Game.js'; | ||
import { QueryComponentFilter } from './Query.js'; | ||
import { EntityImpostorFor } from './QueryIterator.js'; | ||
import { allSystems } from './System.js'; | ||
@@ -8,3 +9,3 @@ type CleanupFn = () => void | Promise<void>; | ||
export function makeEffect<Filter extends QueryComponentFilter>( | ||
export function effect<Filter extends QueryComponentFilter>( | ||
filter: Filter, | ||
@@ -17,3 +18,3 @@ effect: ( | ||
) { | ||
return function (game: Game) { | ||
function eff(game: Game) { | ||
const query = game.queryManager.create(filter); | ||
@@ -64,3 +65,9 @@ const abortControllers = new Array<AbortController>(); | ||
}; | ||
}; | ||
} | ||
allSystems.push(eff); | ||
return eff; | ||
} | ||
/** @deprecated - use effect */ | ||
export const makeEffect = effect; |
@@ -57,5 +57,5 @@ import { ComponentHandle } from './Component2.js'; | ||
export type Any<Comps extends ComponentHandle[]> = { | ||
export type OneOf<Comps extends ComponentHandle[]> = { | ||
Components: Comps; | ||
kind: 'any'; | ||
kind: 'oneOf'; | ||
__isFilter: true; | ||
@@ -65,12 +65,14 @@ toString(): string; | ||
export const any = <Comps extends ComponentHandle[]>( | ||
export const oneOf = <Comps extends ComponentHandle[]>( | ||
...Components: Comps | ||
): Any<Comps> => ({ | ||
): OneOf<Comps> => ({ | ||
Components, | ||
kind: 'any', | ||
kind: 'oneOf', | ||
__isFilter: true, | ||
toString() { | ||
return `any(${Components.map((Comp) => Comp.name).join(', ')})`; | ||
return `oneOf(${Components.map((Comp) => Comp.name).join(', ')})`; | ||
}, | ||
}); | ||
/** @deprecated - use oneOf */ | ||
export const any = oneOf; | ||
@@ -81,3 +83,3 @@ export type Filter<Comp extends ComponentHandle> = | ||
| Changed<Comp> | ||
| Any<Comp[]>; | ||
| OneOf<Comp[]>; | ||
@@ -97,3 +99,4 @@ export const isFilter = (thing: any): thing is Filter<any> => | ||
export const isAnyFilter = (fil: Filter<any>): fil is Any<ComponentHandle[]> => | ||
fil.kind === 'any'; | ||
export const isOneOfFilter = ( | ||
fil: Filter<any>, | ||
): fil is OneOf<ComponentHandle[]> => fil.kind === 'oneOf'; |
@@ -13,3 +13,3 @@ import { component } from './Component2.js'; | ||
game = new Game({ | ||
components: [A, B, C], | ||
ignoreSystemsWarning: true, | ||
}); | ||
@@ -16,0 +16,0 @@ }); |
@@ -13,10 +13,11 @@ import { QueryManager } from './QueryManager.js'; | ||
import { EntityImpostorFor } from './QueryIterator.js'; | ||
import type { | ||
AssetLoaders, | ||
BaseShape, | ||
ComponentInstanceInternal, | ||
Globals, | ||
import { | ||
type AssetLoaders, | ||
type BaseShape, | ||
type ComponentInstanceInternal, | ||
type Globals, | ||
} from './index.js'; | ||
import { EventSubscriber } from '@a-type/utils'; | ||
import { ComponentHandle } from './Component2.js'; | ||
import { allSystems } from './System.js'; | ||
@@ -39,3 +40,5 @@ export type GameConstants = { | ||
private _queryManager: QueryManager; | ||
private _idManager = new IdManager(); | ||
private _entityIds = new IdManager((...msgs) => | ||
console.debug('Entity IDs:', ...msgs), | ||
); | ||
private _archetypeManager: ArchetypeManager; | ||
@@ -65,20 +68,24 @@ private _operationQueue: OperationQueue = []; | ||
constructor({ | ||
components, | ||
systems = [], | ||
assetLoaders = {}, | ||
}: { | ||
components: ComponentHandle[]; | ||
systems?: ((game: Game) => () => void)[]; | ||
assetLoaders?: AssetLoaders; | ||
}) { | ||
ignoreSystemsWarning, | ||
}: { assetLoaders?: AssetLoaders; ignoreSystemsWarning?: boolean } = {}) { | ||
super(); | ||
this._componentManager = new ComponentManager(components, this); | ||
this._componentManager = new ComponentManager(this); | ||
this._assets = new Assets(assetLoaders); | ||
this._queryManager = new QueryManager(this); | ||
this._archetypeManager = new ArchetypeManager(this); | ||
this._runnableCleanups = systems.map((sys) => sys(this)); | ||
if (allSystems.length === 0 && !ignoreSystemsWarning) { | ||
throw new Error( | ||
'No systems are defined at the type of game construction. You have to define systems before calling the Game constructor. Did you forget to import modules which define your systems?', | ||
); | ||
} | ||
this._runnableCleanups = allSystems | ||
.map((sys) => sys(this)) | ||
.filter(Boolean) as (() => void)[]; | ||
console.debug(`Registered ${allSystems.length} systems`); | ||
} | ||
get idManager() { | ||
return this._idManager; | ||
get entityIds() { | ||
return this._entityIds; | ||
} | ||
@@ -117,3 +124,3 @@ get componentManager() { | ||
create = () => { | ||
const id = this.idManager.get(); | ||
const id = this.entityIds.get(); | ||
this._operationQueue.push({ | ||
@@ -140,6 +147,7 @@ op: 'createEntity', | ||
add = <ComponentShape extends BaseShape>( | ||
entityId: number, | ||
entity: number | Entity, | ||
handle: ComponentHandle<ComponentShape>, | ||
initial?: Partial<ComponentShape>, | ||
) => { | ||
const entityId = typeof entity === 'number' ? entity : entity.id; | ||
this._operationQueue.push({ | ||
@@ -156,3 +164,4 @@ op: 'addComponent', | ||
*/ | ||
remove = <T extends ComponentHandle>(entityId: number, Type: T) => { | ||
remove = <T extends ComponentHandle>(entity: number | Entity, Type: T) => { | ||
const entityId = typeof entity === 'number' ? entity : entity.id; | ||
this._operationQueue.push({ | ||
@@ -269,2 +278,3 @@ op: 'removeComponent', | ||
this.entityIds.release(operation.entityId); | ||
this._removedList.add(entity); | ||
@@ -271,0 +281,0 @@ break; |
@@ -1,2 +0,2 @@ | ||
import { incrementIdVersion, SIGNIFIER_MASK } from './ids.js'; | ||
import { incrementIdVersion, setIdVersion, SIGNIFIER_MASK } from './ids.js'; | ||
@@ -12,2 +12,4 @@ /** | ||
constructor(private log?: (...msg: any[]) => void) {} | ||
get() { | ||
@@ -20,5 +22,9 @@ let id: number | undefined; | ||
} | ||
// FIXME: incrementing first means ids start at 1 | ||
// incrementing first means ids start at 1 | ||
id = ++this.allocatedCount; | ||
id = setIdVersion(id, 0); | ||
this.log?.('New ID allocated, total:', this.allocatedCount); | ||
} | ||
this.active.push(id); | ||
return id!; | ||
@@ -32,5 +38,8 @@ } | ||
} | ||
this.active.splice(index); | ||
this.active.splice(index, 1); | ||
this.recycled.push(incrementIdVersion(id)); | ||
} | ||
} | ||
// global, since component() and state() use it | ||
export const componentTypeIds = new IdManager(); |
@@ -6,7 +6,8 @@ import { AssetLoader } from './Assets.js'; | ||
export * from './Component2.js'; | ||
export * from './System.js'; | ||
export { system } from './System.js'; | ||
export * from './filters.js'; | ||
export * from './Effect.js'; | ||
export { effect } from './Effect.js'; | ||
export * from './compose.js'; | ||
export { Entity } from './Entity.js'; | ||
export { setup } from './Setup.js'; | ||
@@ -13,0 +14,0 @@ export interface Globals { |
@@ -14,3 +14,3 @@ export type KeyboardKey = string; | ||
private handleKeyDown = (ev: KeyboardEvent) => { | ||
if (ev.target === document.body) { | ||
if (ev.target === document.body && ev.key !== 'F5' && ev.key !== 'F12') { | ||
ev.preventDefault(); | ||
@@ -17,0 +17,0 @@ } |
@@ -37,5 +37,3 @@ import { ArchetypeManager } from './ArchetypeManager.js'; | ||
componentManager: { | ||
componentHandles: { | ||
length: 10, | ||
}, | ||
count: 10, | ||
getTypeName: () => 'TEST MOCK', | ||
@@ -42,0 +40,0 @@ }, |
@@ -101,3 +101,3 @@ import { Game } from './Game.js'; | ||
break; | ||
case 'any': | ||
case 'oneOf': | ||
match = filter.Components.some((Comp) => archetype.includes(Comp)); | ||
@@ -104,0 +104,0 @@ } |
import { ComponentHandle } from './Component2.js'; | ||
import { Entity } from './Entity.js'; | ||
import { Any, Changed, Filter, not, Not } from './filters.js'; | ||
import { OneOf, Changed, Filter, not, Not } from './filters.js'; | ||
import { Game } from './Game.js'; | ||
@@ -11,3 +11,3 @@ import { Query, QueryComponentFilter } from './Query.js'; | ||
type UnwrapAnys<CompUnion extends Filter<ComponentHandle> | ComponentHandle> = | ||
CompUnion extends Any<any> ? never : CompUnion; | ||
CompUnion extends OneOf<any> ? never : CompUnion; | ||
@@ -14,0 +14,0 @@ type OnlyNots<CompUnion extends Filter<ComponentHandle> | ComponentHandle> = |
@@ -5,3 +5,5 @@ import { Game } from './Game.js'; | ||
export function makeSystem<Filter extends QueryComponentFilter>( | ||
export const allSystems = new Array<(game: Game) => void | (() => void)>(); | ||
export function system<Filter extends QueryComponentFilter>( | ||
filter: Filter, | ||
@@ -11,3 +13,3 @@ run: (entity: EntityImpostorFor<Filter>, game: Game) => void, | ||
) { | ||
return function (game: Game) { | ||
function sys(game: Game) { | ||
const query = game.queryManager.create(filter); | ||
@@ -23,3 +25,8 @@ | ||
return game.subscribe(phase, onPhase); | ||
}; | ||
} | ||
allSystems.push(sys); | ||
return sys; | ||
} | ||
/** @deprecated - use system */ | ||
export const makeSystem = system; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
249150
143
4690