ecspresso 0.12.0 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +18 -1005
  2. package/dist/ecspresso.d.ts +0 -1
  3. package/dist/index.js +4 -0
  4. package/dist/{src/index.js.map → index.js.map} +6 -6
  5. package/dist/{src/plugins → plugins}/renderers/renderer2D.js.map +2 -2
  6. package/dist/screen-manager.d.ts +9 -3
  7. package/package.json +10 -2
  8. package/dist/src/index.js +0 -4
  9. /package/dist/{src/plugins → plugins}/audio.js +0 -0
  10. /package/dist/{src/plugins → plugins}/audio.js.map +0 -0
  11. /package/dist/{src/plugins → plugins}/bounds.js +0 -0
  12. /package/dist/{src/plugins → plugins}/bounds.js.map +0 -0
  13. /package/dist/{src/plugins → plugins}/camera.js +0 -0
  14. /package/dist/{src/plugins → plugins}/camera.js.map +0 -0
  15. /package/dist/{src/plugins → plugins}/collision.js +0 -0
  16. /package/dist/{src/plugins → plugins}/collision.js.map +0 -0
  17. /package/dist/{src/plugins → plugins}/coroutine.js +0 -0
  18. /package/dist/{src/plugins → plugins}/coroutine.js.map +0 -0
  19. /package/dist/{src/plugins → plugins}/diagnostics.js +0 -0
  20. /package/dist/{src/plugins → plugins}/diagnostics.js.map +0 -0
  21. /package/dist/{src/plugins → plugins}/input.js +0 -0
  22. /package/dist/{src/plugins → plugins}/input.js.map +0 -0
  23. /package/dist/{src/plugins → plugins}/particles.js +0 -0
  24. /package/dist/{src/plugins → plugins}/particles.js.map +0 -0
  25. /package/dist/{src/plugins → plugins}/physics2D.js +0 -0
  26. /package/dist/{src/plugins → plugins}/physics2D.js.map +0 -0
  27. /package/dist/{src/plugins → plugins}/renderers/renderer2D.js +0 -0
  28. /package/dist/{src/plugins → plugins}/spatial-index.js +0 -0
  29. /package/dist/{src/plugins → plugins}/spatial-index.js.map +0 -0
  30. /package/dist/{src/plugins → plugins}/sprite-animation.js +0 -0
  31. /package/dist/{src/plugins → plugins}/sprite-animation.js.map +0 -0
  32. /package/dist/{src/plugins → plugins}/state-machine.js +0 -0
  33. /package/dist/{src/plugins → plugins}/state-machine.js.map +0 -0
  34. /package/dist/{src/plugins → plugins}/timers.js +0 -0
  35. /package/dist/{src/plugins → plugins}/timers.js.map +0 -0
  36. /package/dist/{src/plugins → plugins}/transform.js +0 -0
  37. /package/dist/{src/plugins → plugins}/transform.js.map +0 -0
  38. /package/dist/{src/plugins → plugins}/tween.js +0 -0
  39. /package/dist/{src/plugins → plugins}/tween.js.map +0 -0
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/plugins/renderers/renderer2D.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * 2D Renderer Plugin for ECSpresso\n *\n * An opt-in PixiJS-based 2D rendering plugin that automates scene graph wiring.\n * Import from 'ecspresso/plugins/renderers/renderer2D'\n *\n * This plugin includes transform propagation automatically.\n */\n\nimport type { Application, ApplicationOptions, Container, Sprite, Graphics } from 'pixi.js';\nimport { definePlugin, type Plugin } from 'ecspresso';\nimport type { WorldConfigFrom, EmptyConfig } from '../../type-utils';\nimport type ECSpresso from 'ecspresso';\nimport {\n\tcreateTransformPlugin,\n\ttype LocalTransform,\n\ttype WorldTransform,\n\ttype TransformComponentTypes,\n\ttype TransformPluginOptions,\n} from 'ecspresso/plugins/transform';\nimport { createBounds, type BoundsRect } from 'ecspresso/plugins/bounds';\nimport type { CameraResourceTypes, CameraState } from 'ecspresso/plugins/camera';\n\n// Re-export transform and bounds types for convenience\nexport type { LocalTransform, WorldTransform, TransformComponentTypes };\nexport type { BoundsRect };\nexport { createTransform, createLocalTransform, createWorldTransform, DEFAULT_LOCAL_TRANSFORM, DEFAULT_WORLD_TRANSFORM } from 'ecspresso/plugins/transform';\n\n// Dynamic import for Application to avoid requiring pixi.js at plugin creation time\n// when using managed mode (init options instead of pre-initialized app)\nasync function createPixiApplication(options: Partial<ApplicationOptions>): Promise<Application> {\n\tconst { Application } = await import('pixi.js');\n\tconst app = new Application();\n\tawait app.init(options);\n\treturn app;\n}\n\n// ==================== Component Types ====================\n\n/**\n * Visibility and alpha component\n */\nexport interface Visible {\n\tvisible: boolean;\n\talpha?: number;\n}\n\n/**\n * Aggregate component types for the 2D renderer plugin.\n * Included automatically via `.withPlugin(createRenderer2DPlugin({ ... }))`.\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withPlugin(createRenderer2DPlugin({ ... }))\n * .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()\n * .build();\n * ```\n */\nexport interface Renderer2DComponentTypes extends TransformComponentTypes {\n\tsprite: Sprite;\n\tgraphics: Graphics;\n\tcontainer: Container;\n\tvisible: Visible;\n\t/** Assigns the entity to a named render layer for z-ordering */\n\trenderLayer: string;\n}\n\n// ==================== Event Types ====================\n\n/**\n * Events emitted by the 2D renderer plugin\n */\nexport interface Renderer2DEventTypes {\n\thierarchyChanged: {\n\t\tentityId: number;\n\t\toldParent: number | null;\n\t\tnewParent: number | null;\n\t};\n}\n\n// ==================== Resource Types ====================\n\n/**\n * Resources provided by the 2D renderer plugin\n */\nexport interface Renderer2DResourceTypes {\n\tpixiApp: Application;\n\trootContainer: Container;\n\t/** Screen bounds derived from PixiJS screen dimensions, updated on resize */\n\tbounds: BoundsRect;\n}\n\n// ==================== Scale Mode Types ====================\n\nexport type ScaleMode = 'fit' | 'cover' | 'stretch';\n\nexport interface ScreenScaleOptions {\n\treadonly width: number;\n\treadonly height: number;\n\treadonly mode?: ScaleMode;\n}\n\nexport interface ViewportScale {\n\tscaleX: number;\n\tscaleY: number;\n\toffsetX: number;\n\toffsetY: number;\n\tphysicalWidth: number;\n\tphysicalHeight: number;\n\treadonly designWidth: number;\n\treadonly designHeight: number;\n}\n\nexport interface ViewportScaleResourceTypes {\n\tviewportScale: ViewportScale;\n}\n\n// ==================== Plugin Options ====================\n\n/**\n * Common options shared between both initialization modes\n */\ninterface Renderer2DPluginCommonOptions<G extends string = 'renderer2d'> {\n\t/** Optional custom root container (defaults to app.stage) */\n\trootContainer?: Container;\n\t/** System group name (default: 'renderer2d') */\n\tsystemGroup?: G;\n\t/** Priority for render sync system (default: 500) */\n\trenderSyncPriority?: number;\n\t/** Options for the included transform plugin */\n\ttransform?: TransformPluginOptions;\n\t/** When true, wires up pixiApp.ticker to drive ecs.update() automatically (default: true) */\n\tstartLoop?: boolean;\n\t/** Ordered render layer names (back-to-front). Entities with a renderLayer component are placed in the corresponding container. */\n\trenderLayers?: string[];\n\t/** Automatically apply cameraState resource to rootContainer each frame.\n\t * Requires the camera plugin to be installed. (default: false) */\n\tcamera?: boolean;\n\t/** Enforce a logical design resolution with automatic aspect-ratio-aware scaling.\n\t * When set, systems work in design-resolution coordinate space. */\n\tscreenScale?: ScreenScaleOptions;\n}\n\n/**\n * Options when providing a pre-initialized PixiJS Application\n */\nexport interface Renderer2DPluginAppOptions<G extends string = 'renderer2d'> extends Renderer2DPluginCommonOptions<G> {\n\t/** The PixiJS Application instance (already initialized) */\n\tapp: Application;\n\tinit?: never;\n\tcontainer?: never;\n}\n\n/**\n * Options when letting the plugin create and manage the PixiJS Application\n */\nexport interface Renderer2DPluginManagedOptions<G extends string = 'renderer2d'> extends Renderer2DPluginCommonOptions<G> {\n\tapp?: never;\n\t/** PixiJS ApplicationOptions - plugin will create and initialize the Application */\n\tinit: Partial<ApplicationOptions>;\n\t/** Container element to append the canvas to, or CSS selector string */\n\tcontainer?: HTMLElement | string;\n}\n\n/**\n * Configuration options for the 2D renderer plugin.\n *\n * Supports two modes:\n * 1. **Pre-initialized**: Pass an already-initialized Application via `app`\n * 2. **Managed**: Pass `init` options and the plugin creates the Application during `ecs.initialize()`\n *\n * This plugin includes transform propagation automatically - no need to add createTransformPlugin() separately.\n *\n * @example Pre-initialized mode (full control)\n * ```typescript\n * const app = new Application();\n * await app.init({ resizeTo: window });\n * const ecs = ECSpresso.create()\n * .withPlugin(createRenderer2DPlugin({ app }))\n * .withComponentTypes<{ player: true }>()\n * .build();\n * ```\n *\n * @example Managed mode (convenience)\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withPlugin(createRenderer2DPlugin({\n * init: { background: '#1099bb', resizeTo: window },\n * container: document.body,\n * }))\n * .withComponentTypes<{ player: true }>()\n * .build();\n * await ecs.initialize(); // Application created here\n * ```\n */\nexport type Renderer2DPluginOptions<G extends string = 'renderer2d'> = Renderer2DPluginAppOptions<G> | Renderer2DPluginManagedOptions<G>;\n\n// ==================== Helper Utilities ====================\n\ninterface PositionOption {\n\tx?: number;\n\ty?: number;\n}\n\ninterface TransformOptions {\n\trotation?: number;\n\tscale?: number | { x: number; y: number };\n\tvisible?: boolean;\n\talpha?: number;\n}\n\nfunction createLocalTransformInternal(\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): LocalTransform {\n\tconst scaleValue = options?.scale;\n\tconst scaleX = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.x ?? 1;\n\tconst scaleY = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.y ?? 1;\n\n\treturn {\n\t\tx: position?.x ?? 0,\n\t\ty: position?.y ?? 0,\n\t\trotation: options?.rotation ?? 0,\n\t\tscaleX,\n\t\tscaleY,\n\t};\n}\n\nfunction createWorldTransformInternal(\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): WorldTransform {\n\tconst scaleValue = options?.scale;\n\tconst scaleX = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.x ?? 1;\n\tconst scaleY = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.y ?? 1;\n\n\treturn {\n\t\tx: position?.x ?? 0,\n\t\ty: position?.y ?? 0,\n\t\trotation: options?.rotation ?? 0,\n\t\tscaleX,\n\t\tscaleY,\n\t};\n}\n\nfunction createVisibleComponent(options?: TransformOptions): Visible {\n\treturn {\n\t\tvisible: options?.visible ?? true,\n\t\talpha: options?.alpha,\n\t};\n}\n\n/**\n * Create components for a sprite entity.\n * Returns an object suitable for spreading into spawn().\n *\n * @example\n * ```typescript\n * const player = ecs.spawn({\n * ...createSpriteComponents(new Sprite(texture), { x: 100, y: 100 }),\n * velocity: { x: 0, y: 0 },\n * });\n * ```\n */\nexport function createSpriteComponents(\n\tsprite: Sprite,\n\tposition?: PositionOption,\n\toptions?: TransformOptions & { anchor?: { x: number; y: number } }\n): Pick<Renderer2DComponentTypes, 'sprite' | 'localTransform' | 'worldTransform' | 'visible'> {\n\tif (options?.anchor) {\n\t\tsprite.anchor.set(options.anchor.x, options.anchor.y);\n\t}\n\treturn {\n\t\tsprite,\n\t\tlocalTransform: createLocalTransformInternal(position, options),\n\t\tworldTransform: createWorldTransformInternal(position, options),\n\t\tvisible: createVisibleComponent(options),\n\t};\n}\n\n/**\n * Create components for a graphics entity.\n * Returns an object suitable for spreading into spawn().\n *\n * @example\n * ```typescript\n * const rect = ecs.spawn({\n * ...createGraphicsComponents(graphics, { x: 50, y: 50 }),\n * });\n * ```\n */\nexport function createGraphicsComponents(\n\tgraphics: Graphics,\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): Pick<Renderer2DComponentTypes, 'graphics' | 'localTransform' | 'worldTransform' | 'visible'> {\n\treturn {\n\t\tgraphics,\n\t\tlocalTransform: createLocalTransformInternal(position, options),\n\t\tworldTransform: createWorldTransformInternal(position, options),\n\t\tvisible: createVisibleComponent(options),\n\t};\n}\n\n/**\n * Create components for a container entity.\n * Returns an object suitable for spreading into spawn().\n *\n * @example\n * ```typescript\n * const group = ecs.spawn({\n * ...createContainerComponents(new Container(), { x: 0, y: 0 }),\n * });\n * ```\n */\nexport function createContainerComponents(\n\tcontainer: Container,\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): Pick<Renderer2DComponentTypes, 'container' | 'localTransform' | 'worldTransform' | 'visible'> {\n\treturn {\n\t\tcontainer,\n\t\tlocalTransform: createLocalTransformInternal(position, options),\n\t\tworldTransform: createWorldTransformInternal(position, options),\n\t\tvisible: createVisibleComponent(options),\n\t};\n}\n\n// ==================== Viewport Scale Utilities ====================\n\nconst scaleModeStrategy: Record<ScaleMode, (ratioX: number, ratioY: number) => { scaleX: number; scaleY: number }> = {\n\tfit: (ratioX, ratioY) => {\n\t\tconst s = Math.min(ratioX, ratioY);\n\t\treturn { scaleX: s, scaleY: s };\n\t},\n\tcover: (ratioX, ratioY) => {\n\t\tconst s = Math.max(ratioX, ratioY);\n\t\treturn { scaleX: s, scaleY: s };\n\t},\n\tstretch: (ratioX, ratioY) => ({ scaleX: ratioX, scaleY: ratioY }),\n};\n\nexport function computeViewportScale(\n\tphysicalW: number,\n\tphysicalH: number,\n\tdesignW: number,\n\tdesignH: number,\n\tmode: ScaleMode,\n): ViewportScale {\n\tconst ratioX = physicalW / designW;\n\tconst ratioY = physicalH / designH;\n\tconst { scaleX, scaleY } = scaleModeStrategy[mode](ratioX, ratioY);\n\n\treturn {\n\t\tscaleX,\n\t\tscaleY,\n\t\toffsetX: (physicalW - designW * scaleX) / 2,\n\t\toffsetY: (physicalH - designH * scaleY) / 2,\n\t\tphysicalWidth: physicalW,\n\t\tphysicalHeight: physicalH,\n\t\tdesignWidth: designW,\n\t\tdesignHeight: designH,\n\t};\n}\n\n/**\n * Convert physical canvas pixel coordinates to design-resolution (logical) coordinates.\n * Compose with camera `screenToWorld()` for full physical→world conversion.\n */\nexport function physicalToLogical(\n\tphysicalX: number,\n\tphysicalY: number,\n\tviewport: ViewportScale,\n): { x: number; y: number } {\n\treturn {\n\t\tx: (physicalX - viewport.offsetX) / viewport.scaleX,\n\t\ty: (physicalY - viewport.offsetY) / viewport.scaleY,\n\t};\n}\n\n// ==================== Plugin Factory ====================\n\n/**\n * Create a 2D rendering plugin for ECSpresso.\n *\n * This plugin provides:\n * - Transform propagation (localTransform -> worldTransform)\n * - Render sync system (updates PixiJS objects from ECS components)\n * - Scene graph management (mirrors ECS hierarchy in PixiJS scene graph)\n *\n * @example Pre-initialized mode\n * ```typescript\n * const app = new Application();\n * await app.init({ resizeTo: window });\n *\n * const ecs = ECSpresso.create<GameComponents, {}, {}>()\n * .withPlugin(createRenderer2DPlugin({ app }))\n * .build();\n * ```\n *\n * @example Managed mode\n * ```typescript\n * const ecs = ECSpresso.create<GameComponents, {}, {}>()\n * .withPlugin(createRenderer2DPlugin({\n * init: { background: '#1099bb', resizeTo: window },\n * container: document.body,\n * }))\n * .build();\n * await ecs.initialize();\n * ```\n */\ntype Renderer2DLabels = 'renderer2d-sync' | 'renderer2d-scene-graph' | 'renderer2d-camera-sync' | 'transform-propagation';\ntype Renderer2DReactiveQueryNames = 'renderer2d-sprites' | 'renderer2d-graphics' | 'renderer2d-containers';\n\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { screenScale: ScreenScaleOptions; camera: true }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { screenScale: ScreenScaleOptions }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { camera: true }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G>\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { camera?: boolean; screenScale?: ScreenScaleOptions }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>\n| Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>\n| Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>\n| Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames> {\n\tconst {\n\t\trootContainer: customRootContainer,\n\t\tsystemGroup = 'renderer2d',\n\t\trenderSyncPriority = 500,\n\t\ttransform: transformOptions,\n\t\tstartLoop = true,\n\t\trenderLayers = [],\n\t\tcamera = false,\n\t\tscreenScale,\n\t} = options;\n\n\tconst hasScreenScale = screenScale !== undefined;\n\tconst designWidth = screenScale?.width ?? 0;\n\tconst designHeight = screenScale?.height ?? 0;\n\tconst screenScaleMode: ScaleMode = screenScale?.mode ?? 'fit';\n\n\t// Entity ID -> PixiJS Container mapping for scene graph management\n\tconst entityToPixiObject = new Map<number, Container>();\n\n\t// Render layer name -> PixiJS Container mapping\n\tconst layerContainers = new Map<string, Container>();\n\n\t// Container constructor captured during initialization via dynamic import\n\t// Used by getOrCreateLayerContainer for lazy layer creation\n\tlet createLayerContainer: (label: string) => Container = () => {\n\t\tthrow new Error('renderer2D: createLayerContainer called before initialization');\n\t};\n\n\t// Helper to get or create a render layer container\n\tfunction getOrCreateLayerContainer(\n\t\tlayerName: string,\n\t\trootCont: Container\n\t): Container {\n\t\tconst existing = layerContainers.get(layerName);\n\t\tif (existing) return existing;\n\n\t\t// Lazy-create for undeclared layers, appended to end\n\t\tconst cont = createLayerContainer(`layer:${layerName}`);\n\t\tlayerContainers.set(layerName, cont);\n\t\trootCont.addChild(cont);\n\t\treturn cont;\n\t}\n\n\t// Helper to resolve the target container for an entity.\n\t// Scene graph stays flat (rootContainer or render layer) because the render\n\t// sync positions objects using absolute worldTransform. Nesting under a\n\t// parent's display object would double-apply the parent's transform.\n\tfunction resolveTargetContainer(\n\t\tentityId: number,\n\t\tecs: ECSpresso<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>>\n\t): Container {\n\t\tconst rootCont = ecs.getResource('rootContainer');\n\n\t\t// 1. Check render layer component\n\t\tconst layerName = ecs.getComponent(entityId, 'renderLayer');\n\t\tif (layerName) return getOrCreateLayerContainer(layerName, rootCont);\n\n\t\t// 2. Fall back to root container\n\t\treturn rootCont;\n\t}\n\n\t// Helper to add a PixiJS object to the scene graph\n\tfunction addToSceneGraph(\n\t\tentityId: number,\n\t\tpixiObject: Container,\n\t\tecs: ECSpresso<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>>\n\t): void {\n\t\tconst targetContainer = resolveTargetContainer(entityId, ecs);\n\n\t\t// Only add if not already a child\n\t\tif (pixiObject.parent !== targetContainer) {\n\t\t\ttargetContainer.addChild(pixiObject);\n\t\t}\n\t}\n\n\t// Helper to update parent in scene graph\n\tfunction updateSceneGraphParent(\n\t\tentityId: number,\n\t\tecs: ECSpresso<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>>\n\t): void {\n\t\tconst pixiObject = entityToPixiObject.get(entityId);\n\t\tif (!pixiObject) return;\n\n\t\tconst targetContainer = resolveTargetContainer(entityId, ecs);\n\n\t\tif (pixiObject.parent !== targetContainer) {\n\t\t\tpixiObject.removeFromParent();\n\t\t\ttargetContainer.addChild(pixiObject);\n\t\t}\n\t}\n\n\t// Determine mode and set up resource registration closures\n\tconst isManaged = 'init' in options && options.init !== undefined;\n\n\treturn definePlugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>({\n\t\tid: 'renderer2d',\n\t\tinstall(world) {\n\t\t\t// Install transform plugin (deduplicates if already installed)\n\t\t\tworld.installPlugin(createTransformPlugin(transformOptions));\n\n\t\t\t// Register resources based on mode\n\t\t\tif (isManaged) {\n\t\t\t\tconst initOptions = (options as Renderer2DPluginManagedOptions<G>).init;\n\t\t\t\tconst containerOption = (options as Renderer2DPluginManagedOptions<G>).container;\n\n\t\t\t\tworld.addResource('pixiApp', async () => {\n\t\t\t\t\tconst app = await createPixiApplication(initOptions);\n\n\t\t\t\t\tif (containerOption) {\n\t\t\t\t\t\tconst containerEl = typeof containerOption === 'string'\n\t\t\t\t\t\t\t? document.querySelector(containerOption)\n\t\t\t\t\t\t\t: containerOption;\n\n\t\t\t\t\t\tif (containerEl) {\n\t\t\t\t\t\t\tcontainerEl.appendChild(app.canvas);\n\t\t\t\t\t\t} else if (typeof containerOption === 'string') {\n\t\t\t\t\t\t\tconsole.warn(`Renderer2D plugin: container selector \"${containerOption}\" not found`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn app;\n\t\t\t\t});\n\n\t\t\t\tworld.addResource('rootContainer', {\n\t\t\t\t\tdependsOn: ['pixiApp'],\n\t\t\t\t\tfactory: (ecs) => customRootContainer ?? ecs.getResource('pixiApp').stage,\n\t\t\t\t});\n\n\t\t\t\tworld.addResource('bounds', {\n\t\t\t\t\tdependsOn: ['pixiApp'],\n\t\t\t\t\tfactory: (ecs) => {\n\t\t\t\t\t\tif (hasScreenScale) return createBounds(designWidth, designHeight);\n\t\t\t\t\t\tconst pixiApp = ecs.getResource('pixiApp');\n\t\t\t\t\t\treturn createBounds(pixiApp.screen.width, pixiApp.screen.height);\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\tworld.addResource('viewportScale' as keyof Renderer2DResourceTypes, {\n\t\t\t\t\t\tdependsOn: ['pixiApp'],\n\t\t\t\t\t\tfactory: (ecs: ECSpresso<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>>) => {\n\t\t\t\t\t\t\tconst pixiApp = ecs.getResource('pixiApp');\n\t\t\t\t\t\t\treturn computeViewportScale(pixiApp.screen.width, pixiApp.screen.height, designWidth, designHeight, screenScaleMode);\n\t\t\t\t\t\t},\n\t\t\t\t\t} as any);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst app = (options as Renderer2DPluginAppOptions<G>).app;\n\t\t\t\tworld.addResource('pixiApp', app);\n\t\t\t\tworld.addResource('rootContainer', customRootContainer ?? app.stage);\n\t\t\t\tworld.addResource('bounds', hasScreenScale\n\t\t\t\t\t? createBounds(designWidth, designHeight)\n\t\t\t\t\t: createBounds(app.screen.width, app.screen.height));\n\n\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\tworld.addResource('viewportScale' as keyof Renderer2DResourceTypes,\n\t\t\t\t\t\tcomputeViewportScale(app.screen.width, app.screen.height, designWidth, designHeight, screenScaleMode) as any);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Register dispose callbacks for display object components\n\t\t\tworld.registerDispose('sprite', ({ value: sprite }) => {\n\t\t\t\tsprite.removeFromParent();\n\t\t\t});\n\t\t\tworld.registerDispose('graphics', ({ value: graphics }) => {\n\t\t\t\tgraphics.removeFromParent();\n\t\t\t});\n\t\t\tworld.registerDispose('container', ({ value: container }) => {\n\t\t\t\tcontainer.removeFromParent();\n\t\t\t});\n\n\t\t\t// Display objects require localTransform and visible\n\t\t\tworld.registerRequired('sprite', 'localTransform', () => createLocalTransformInternal());\n\t\t\tworld.registerRequired('sprite', 'visible', () => createVisibleComponent());\n\t\t\tworld.registerRequired('graphics', 'localTransform', () => createLocalTransformInternal());\n\t\t\tworld.registerRequired('graphics', 'visible', () => createVisibleComponent());\n\t\t\tworld.registerRequired('container', 'localTransform', () => createLocalTransformInternal());\n\t\t\tworld.registerRequired('container', 'visible', () => createVisibleComponent());\n\n\t\t\t// ==================== Render Sync System ====================\n\t\t\tworld\n\t\t\t\t.addSystem('renderer2d-sync')\n\t\t\t\t.setPriority(renderSyncPriority)\n\t\t\t\t.inPhase('render')\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('sprites', {\n\t\t\t\t\twith: ['sprite', 'worldTransform'],\n\t\t\t\t\tchanged: ['worldTransform'],\n\t\t\t\t})\n\t\t\t\t.addQuery('graphics', {\n\t\t\t\t\twith: ['graphics', 'worldTransform'],\n\t\t\t\t\tchanged: ['worldTransform'],\n\t\t\t\t})\n\t\t\t\t.addQuery('containers', {\n\t\t\t\t\twith: ['container', 'worldTransform'],\n\t\t\t\t\tchanged: ['worldTransform'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, ecs }) => {\n\t\t\t\t\tfor (const entity of queries.sprites) {\n\t\t\t\t\t\tconst { sprite, worldTransform } = entity.components;\n\n\t\t\t\t\t\tsprite.position.set(worldTransform.x, worldTransform.y);\n\t\t\t\t\t\tsprite.rotation = worldTransform.rotation;\n\t\t\t\t\t\tsprite.scale.set(worldTransform.scaleX, worldTransform.scaleY);\n\n\t\t\t\t\t\tconst visibleComp = ecs.getComponent(entity.id, 'visible');\n\t\t\t\t\t\tif (visibleComp) {\n\t\t\t\t\t\t\tsprite.visible = visibleComp.visible;\n\t\t\t\t\t\t\tif (visibleComp.alpha !== undefined) {\n\t\t\t\t\t\t\t\tsprite.alpha = visibleComp.alpha;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const entity of queries.graphics) {\n\t\t\t\t\t\tconst { graphics, worldTransform } = entity.components;\n\n\t\t\t\t\t\tgraphics.position.set(worldTransform.x, worldTransform.y);\n\t\t\t\t\t\tgraphics.rotation = worldTransform.rotation;\n\t\t\t\t\t\tgraphics.scale.set(worldTransform.scaleX, worldTransform.scaleY);\n\n\t\t\t\t\t\tconst visibleComp = ecs.getComponent(entity.id, 'visible');\n\t\t\t\t\t\tif (visibleComp) {\n\t\t\t\t\t\t\tgraphics.visible = visibleComp.visible;\n\t\t\t\t\t\t\tif (visibleComp.alpha !== undefined) {\n\t\t\t\t\t\t\t\tgraphics.alpha = visibleComp.alpha;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const entity of queries.containers) {\n\t\t\t\t\t\tconst { container, worldTransform } = entity.components;\n\n\t\t\t\t\t\tcontainer.position.set(worldTransform.x, worldTransform.y);\n\t\t\t\t\t\tcontainer.rotation = worldTransform.rotation;\n\t\t\t\t\t\tcontainer.scale.set(worldTransform.scaleX, worldTransform.scaleY);\n\n\t\t\t\t\t\tconst visibleComp = ecs.getComponent(entity.id, 'visible');\n\t\t\t\t\t\tif (visibleComp) {\n\t\t\t\t\t\t\tcontainer.visible = visibleComp.visible;\n\t\t\t\t\t\t\tif (visibleComp.alpha !== undefined) {\n\t\t\t\t\t\t\t\tcontainer.alpha = visibleComp.alpha;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// ==================== Scene Graph Manager System ====================\n\t\t\tworld\n\t\t\t\t.addSystem('renderer2d-scene-graph')\n\t\t\t\t.setPriority(9999)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.setOnInitialize(async (ecs) => {\n\t\t\t\t\tconst pixiApp = ecs.getResource('pixiApp');\n\t\t\t\t\tlet rootCont = ecs.getResource('rootContainer');\n\n\t\t\t\t\tconst { Container: ContainerClass } = await import('pixi.js');\n\t\t\t\t\tcreateLayerContainer = (label: string) => {\n\t\t\t\t\t\tconst cont = new ContainerClass();\n\t\t\t\t\t\tcont.label = label;\n\t\t\t\t\t\treturn cont;\n\t\t\t\t\t};\n\n\t\t\t\t\tlet viewportContainer: Container | undefined;\n\t\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\t\tviewportContainer = new ContainerClass();\n\t\t\t\t\t\tviewportContainer.label = 'viewportContainer';\n\n\t\t\t\t\t\tconst vs = ecs.tryGetResource<ViewportScale>('viewportScale');\n\t\t\t\t\t\tif (!vs) throw new Error('renderer2D: viewportScale resource not found');\n\t\t\t\t\t\tviewportContainer.position.set(vs.offsetX, vs.offsetY);\n\t\t\t\t\t\tviewportContainer.scale.set(vs.scaleX, vs.scaleY);\n\n\t\t\t\t\t\tconst newRoot = new ContainerClass();\n\t\t\t\t\t\tnewRoot.label = 'rootContainer';\n\n\t\t\t\t\t\tpixiApp.stage.addChild(viewportContainer);\n\t\t\t\t\t\tviewportContainer.addChild(newRoot);\n\n\t\t\t\t\t\tecs.updateResource('rootContainer', () => newRoot);\n\t\t\t\t\t\trootCont = newRoot;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const layerName of renderLayers) {\n\t\t\t\t\t\tconst cont = createLayerContainer(`layer:${layerName}`);\n\t\t\t\t\t\tlayerContainers.set(layerName, cont);\n\t\t\t\t\t\trootCont.addChild(cont);\n\t\t\t\t\t}\n\n\t\t\t\t\tecs.addReactiveQuery('renderer2d-sprites', {\n\t\t\t\t\t\twith: ['sprite'],\n\t\t\t\t\t\tonEnter: (entity) => {\n\t\t\t\t\t\t\tconst pixiObject = entity.components.sprite;\n\t\t\t\t\t\t\tentityToPixiObject.set(entity.id, pixiObject);\n\t\t\t\t\t\t\taddToSceneGraph(entity.id, pixiObject, ecs);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonExit: (entityId) => {\n\t\t\t\t\t\t\tentityToPixiObject.delete(entityId);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.addReactiveQuery('renderer2d-graphics', {\n\t\t\t\t\t\twith: ['graphics'],\n\t\t\t\t\t\tonEnter: (entity) => {\n\t\t\t\t\t\t\tconst pixiObject = entity.components.graphics;\n\t\t\t\t\t\t\tentityToPixiObject.set(entity.id, pixiObject);\n\t\t\t\t\t\t\taddToSceneGraph(entity.id, pixiObject, ecs);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonExit: (entityId) => {\n\t\t\t\t\t\t\tentityToPixiObject.delete(entityId);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.addReactiveQuery('renderer2d-containers', {\n\t\t\t\t\t\twith: ['container'],\n\t\t\t\t\t\tonEnter: (entity) => {\n\t\t\t\t\t\t\tconst pixiObject = entity.components.container;\n\t\t\t\t\t\t\tentityToPixiObject.set(entity.id, pixiObject);\n\t\t\t\t\t\t\taddToSceneGraph(entity.id, pixiObject, ecs);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonExit: (entityId) => {\n\t\t\t\t\t\t\tentityToPixiObject.delete(entityId);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.on('hierarchyChanged', ({ entityId }) => {\n\t\t\t\t\t\tupdateSceneGraphParent(entityId, ecs);\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.onComponentAdded('renderLayer', ({ entity }) => {\n\t\t\t\t\t\tupdateSceneGraphParent(entity.id, ecs);\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.onComponentRemoved('renderLayer', ({ entity }) => {\n\t\t\t\t\t\tupdateSceneGraphParent(entity.id, ecs);\n\t\t\t\t\t});\n\n\t\t\t\t\tif (camera) {\n\t\t\t\t\t\tconst cameraState = ecs.tryGetResource<CameraState>('cameraState');\n\t\t\t\t\t\tif (!cameraState) throw new Error('renderer2D: cameraState resource not found');\n\t\t\t\t\t\tcameraState.viewportWidth = hasScreenScale ? designWidth : pixiApp.screen.width;\n\t\t\t\t\t\tcameraState.viewportHeight = hasScreenScale ? designHeight : pixiApp.screen.height;\n\t\t\t\t\t}\n\n\t\t\t\t\tpixiApp.renderer.on('resize', (width: number, height: number) => {\n\t\t\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\t\t\tconst vs = computeViewportScale(width, height, designWidth, designHeight, screenScaleMode);\n\t\t\t\t\t\t\tconst vpResource = ecs.tryGetResource<ViewportScale>('viewportScale');\n\t\t\t\t\t\t\tif (!vpResource) throw new Error('renderer2D: viewportScale resource not found');\n\t\t\t\t\t\t\tvpResource.scaleX = vs.scaleX;\n\t\t\t\t\t\t\tvpResource.scaleY = vs.scaleY;\n\t\t\t\t\t\t\tvpResource.offsetX = vs.offsetX;\n\t\t\t\t\t\t\tvpResource.offsetY = vs.offsetY;\n\t\t\t\t\t\t\tvpResource.physicalWidth = width;\n\t\t\t\t\t\t\tvpResource.physicalHeight = height;\n\n\t\t\t\t\t\t\tif (viewportContainer) {\n\t\t\t\t\t\t\t\tviewportContainer.position.set(vs.offsetX, vs.offsetY);\n\t\t\t\t\t\t\t\tviewportContainer.scale.set(vs.scaleX, vs.scaleY);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst bounds = ecs.getResource('bounds');\n\t\t\t\t\t\t\tbounds.width = width;\n\t\t\t\t\t\t\tbounds.height = height;\n\n\t\t\t\t\t\t\tif (camera) {\n\t\t\t\t\t\t\t\tconst cameraState = ecs.tryGetResource<CameraState>('cameraState');\n\t\t\t\t\t\t\t\tif (!cameraState) throw new Error('renderer2D: cameraState resource not found');\n\t\t\t\t\t\t\t\tcameraState.viewportWidth = width;\n\t\t\t\t\t\t\t\tcameraState.viewportHeight = height;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tif (startLoop) {\n\t\t\t\t\t\tpixiApp.ticker.add((ticker) => {\n\t\t\t\t\t\t\tecs.update(ticker.deltaMS / 1_000);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// ==================== Camera Sync System (opt-in) ====================\n\t\t\tif (camera) {\n\t\t\t\tworld\n\t\t\t\t\t.addSystem('renderer2d-camera-sync')\n\t\t\t\t\t.setPriority(900)\n\t\t\t\t\t.inPhase('render')\n\t\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t\t.setProcess(({ ecs }) => {\n\t\t\t\t\t\tconst state = ecs.tryGetResource<CameraState>('cameraState');\n\t\t\t\t\t\tif (!state) throw new Error('renderer2D: cameraState resource not found');\n\t\t\t\t\t\tconst root = ecs.getResource('rootContainer');\n\t\t\t\t\t\tconst [centerW, centerH] = hasScreenScale\n\t\t\t\t\t\t\t? [designWidth, designHeight]\n\t\t\t\t\t\t\t: [ecs.getResource('pixiApp').screen.width, ecs.getResource('pixiApp').screen.height];\n\n\t\t\t\t\t\troot.position.set(\n\t\t\t\t\t\t\tcenterW / 2 - (state.x + state.shakeOffsetX) * state.zoom,\n\t\t\t\t\t\t\tcenterH / 2 - (state.y + state.shakeOffsetY) * state.zoom,\n\t\t\t\t\t\t);\n\t\t\t\t\t\troot.scale.set(state.zoom);\n\t\t\t\t\t\troot.rotation = -(state.rotation + state.shakeRotation);\n\t\t\t\t\t});\n\t\t\t}\n\t\t},\n\t});\n}\n"
5
+ "/**\n * 2D Renderer Plugin for ECSpresso\n *\n * An opt-in PixiJS-based 2D rendering plugin that automates scene graph wiring.\n * Import from 'ecspresso/plugins/renderers/renderer2D'\n *\n * This plugin includes transform propagation automatically.\n */\n\nimport type { Application, ApplicationOptions, Container, Sprite, Graphics } from 'pixi.js';\nimport { definePlugin, type Plugin } from 'ecspresso';\nimport type { WorldConfigFrom, EmptyConfig } from '../../type-utils';\nimport type ECSpresso from 'ecspresso';\nimport {\n\tcreateTransformPlugin,\n\ttype LocalTransform,\n\ttype WorldTransform,\n\ttype TransformComponentTypes,\n\ttype TransformPluginOptions,\n} from 'ecspresso/plugins/transform';\nimport { createBounds, type BoundsRect } from 'ecspresso/plugins/bounds';\nimport type { CameraResourceTypes, CameraState } from 'ecspresso/plugins/camera';\n\n// Re-export transform and bounds types for convenience\nexport type { LocalTransform, WorldTransform, TransformComponentTypes };\nexport type { BoundsRect };\nexport { createTransform, createLocalTransform, createWorldTransform, DEFAULT_LOCAL_TRANSFORM, DEFAULT_WORLD_TRANSFORM } from 'ecspresso/plugins/transform';\n\n// Dynamic import for Application to avoid requiring pixi.js at plugin creation time\n// when using managed mode (init options instead of pre-initialized app)\nasync function createPixiApplication(options: Partial<ApplicationOptions>): Promise<Application> {\n\tconst { Application } = await import('pixi.js');\n\tconst app = new Application();\n\tawait app.init(options);\n\treturn app;\n}\n\n// ==================== Component Types ====================\n\n/**\n * Visibility and alpha component\n */\nexport interface Visible {\n\tvisible: boolean;\n\talpha?: number;\n}\n\n/**\n * Aggregate component types for the 2D renderer plugin.\n * Included automatically via `.withPlugin(createRenderer2DPlugin({ ... }))`.\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withPlugin(createRenderer2DPlugin({ ... }))\n * .withComponentTypes<{ velocity: { x: number; y: number }; player: true }>()\n * .build();\n * ```\n */\nexport interface Renderer2DComponentTypes extends TransformComponentTypes {\n\tsprite: Sprite;\n\tgraphics: Graphics;\n\tcontainer: Container;\n\tvisible: Visible;\n\t/** Assigns the entity to a named render layer for z-ordering */\n\trenderLayer: string;\n}\n\n// ==================== Event Types ====================\n\n/**\n * Events emitted by the 2D renderer plugin\n */\nexport interface Renderer2DEventTypes {\n\thierarchyChanged: {\n\t\tentityId: number;\n\t\toldParent: number | null;\n\t\tnewParent: number | null;\n\t};\n}\n\n// ==================== Resource Types ====================\n\n/**\n * Resources provided by the 2D renderer plugin\n */\nexport interface Renderer2DResourceTypes {\n\tpixiApp: Application;\n\trootContainer: Container;\n\t/** Screen bounds derived from PixiJS screen dimensions, updated on resize */\n\tbounds: BoundsRect;\n}\n\n// ==================== Scale Mode Types ====================\n\nexport type ScaleMode = 'fit' | 'cover' | 'stretch';\n\nexport interface ScreenScaleOptions {\n\treadonly width: number;\n\treadonly height: number;\n\treadonly mode?: ScaleMode;\n}\n\nexport interface ViewportScale {\n\tscaleX: number;\n\tscaleY: number;\n\toffsetX: number;\n\toffsetY: number;\n\tphysicalWidth: number;\n\tphysicalHeight: number;\n\treadonly designWidth: number;\n\treadonly designHeight: number;\n}\n\nexport interface ViewportScaleResourceTypes {\n\tviewportScale: ViewportScale;\n}\n\n// ==================== Plugin Options ====================\n\n/**\n * Common options shared between both initialization modes\n */\ninterface Renderer2DPluginCommonOptions<G extends string = 'renderer2d'> {\n\t/** Optional custom root container (defaults to app.stage) */\n\trootContainer?: Container;\n\t/** System group name (default: 'renderer2d') */\n\tsystemGroup?: G;\n\t/** Priority for render sync system (default: 500) */\n\trenderSyncPriority?: number;\n\t/** Options for the included transform plugin */\n\ttransform?: TransformPluginOptions;\n\t/** When true, wires up pixiApp.ticker to drive ecs.update() automatically (default: true) */\n\tstartLoop?: boolean;\n\t/** Ordered render layer names (back-to-front). Entities with a renderLayer component are placed in the corresponding container. */\n\trenderLayers?: string[];\n\t/** Automatically apply cameraState resource to rootContainer each frame.\n\t * Requires the camera plugin to be installed. (default: false) */\n\tcamera?: boolean;\n\t/** Enforce a logical design resolution with automatic aspect-ratio-aware scaling.\n\t * When set, systems work in design-resolution coordinate space. */\n\tscreenScale?: ScreenScaleOptions;\n}\n\n/**\n * Options when providing a pre-initialized PixiJS Application\n */\nexport interface Renderer2DPluginAppOptions<G extends string = 'renderer2d'> extends Renderer2DPluginCommonOptions<G> {\n\t/** The PixiJS Application instance (already initialized) */\n\tapp: Application;\n\tinit?: never;\n\tcontainer?: never;\n}\n\n/**\n * Options when letting the plugin create and manage the PixiJS Application\n */\nexport interface Renderer2DPluginManagedOptions<G extends string = 'renderer2d'> extends Renderer2DPluginCommonOptions<G> {\n\tapp?: never;\n\t/** PixiJS ApplicationOptions - plugin will create and initialize the Application */\n\tinit: Partial<ApplicationOptions>;\n\t/** Container element to append the canvas to, or CSS selector string */\n\tcontainer?: HTMLElement | string;\n}\n\n/**\n * Configuration options for the 2D renderer plugin.\n *\n * Supports two modes:\n * 1. **Pre-initialized**: Pass an already-initialized Application via `app`\n * 2. **Managed**: Pass `init` options and the plugin creates the Application during `ecs.initialize()`\n *\n * This plugin includes transform propagation automatically - no need to add createTransformPlugin() separately.\n *\n * @example Pre-initialized mode (full control)\n * ```typescript\n * const app = new Application();\n * await app.init({ resizeTo: window });\n * const ecs = ECSpresso.create()\n * .withPlugin(createRenderer2DPlugin({ app }))\n * .withComponentTypes<{ player: true }>()\n * .build();\n * ```\n *\n * @example Managed mode (convenience)\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withPlugin(createRenderer2DPlugin({\n * init: { background: '#1099bb', resizeTo: window },\n * container: document.body,\n * }))\n * .withComponentTypes<{ player: true }>()\n * .build();\n * await ecs.initialize(); // Application created here\n * ```\n */\nexport type Renderer2DPluginOptions<G extends string = 'renderer2d'> = Renderer2DPluginAppOptions<G> | Renderer2DPluginManagedOptions<G>;\n\n// ==================== Helper Utilities ====================\n\ninterface PositionOption {\n\tx?: number;\n\ty?: number;\n}\n\ninterface TransformOptions {\n\trotation?: number;\n\tscale?: number | { x: number; y: number };\n\tvisible?: boolean;\n\talpha?: number;\n}\n\nfunction createLocalTransformInternal(\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): LocalTransform {\n\tconst scaleValue = options?.scale;\n\tconst scaleX = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.x ?? 1;\n\tconst scaleY = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.y ?? 1;\n\n\treturn {\n\t\tx: position?.x ?? 0,\n\t\ty: position?.y ?? 0,\n\t\trotation: options?.rotation ?? 0,\n\t\tscaleX,\n\t\tscaleY,\n\t};\n}\n\nfunction createWorldTransformInternal(\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): WorldTransform {\n\tconst scaleValue = options?.scale;\n\tconst scaleX = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.x ?? 1;\n\tconst scaleY = typeof scaleValue === 'number'\n\t\t? scaleValue\n\t\t: scaleValue?.y ?? 1;\n\n\treturn {\n\t\tx: position?.x ?? 0,\n\t\ty: position?.y ?? 0,\n\t\trotation: options?.rotation ?? 0,\n\t\tscaleX,\n\t\tscaleY,\n\t};\n}\n\nfunction createVisibleComponent(options?: TransformOptions): Visible {\n\treturn {\n\t\tvisible: options?.visible ?? true,\n\t\talpha: options?.alpha,\n\t};\n}\n\n/**\n * Create components for a sprite entity.\n * Returns an object suitable for spreading into spawn().\n *\n * @example\n * ```typescript\n * const player = ecs.spawn({\n * ...createSpriteComponents(new Sprite(texture), { x: 100, y: 100 }),\n * velocity: { x: 0, y: 0 },\n * });\n * ```\n */\nexport function createSpriteComponents(\n\tsprite: Sprite,\n\tposition?: PositionOption,\n\toptions?: TransformOptions & { anchor?: { x: number; y: number } }\n): Pick<Renderer2DComponentTypes, 'sprite' | 'localTransform' | 'worldTransform' | 'visible'> {\n\tif (options?.anchor) {\n\t\tsprite.anchor.set(options.anchor.x, options.anchor.y);\n\t}\n\treturn {\n\t\tsprite,\n\t\tlocalTransform: createLocalTransformInternal(position, options),\n\t\tworldTransform: createWorldTransformInternal(position, options),\n\t\tvisible: createVisibleComponent(options),\n\t};\n}\n\n/**\n * Create components for a graphics entity.\n * Returns an object suitable for spreading into spawn().\n *\n * @example\n * ```typescript\n * const rect = ecs.spawn({\n * ...createGraphicsComponents(graphics, { x: 50, y: 50 }),\n * });\n * ```\n */\nexport function createGraphicsComponents(\n\tgraphics: Graphics,\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): Pick<Renderer2DComponentTypes, 'graphics' | 'localTransform' | 'worldTransform' | 'visible'> {\n\treturn {\n\t\tgraphics,\n\t\tlocalTransform: createLocalTransformInternal(position, options),\n\t\tworldTransform: createWorldTransformInternal(position, options),\n\t\tvisible: createVisibleComponent(options),\n\t};\n}\n\n/**\n * Create components for a container entity.\n * Returns an object suitable for spreading into spawn().\n *\n * @example\n * ```typescript\n * const group = ecs.spawn({\n * ...createContainerComponents(new Container(), { x: 0, y: 0 }),\n * });\n * ```\n */\nexport function createContainerComponents(\n\tcontainer: Container,\n\tposition?: PositionOption,\n\toptions?: TransformOptions\n): Pick<Renderer2DComponentTypes, 'container' | 'localTransform' | 'worldTransform' | 'visible'> {\n\treturn {\n\t\tcontainer,\n\t\tlocalTransform: createLocalTransformInternal(position, options),\n\t\tworldTransform: createWorldTransformInternal(position, options),\n\t\tvisible: createVisibleComponent(options),\n\t};\n}\n\n// ==================== Viewport Scale Utilities ====================\n\nconst scaleModeStrategy: Record<ScaleMode, (ratioX: number, ratioY: number) => { scaleX: number; scaleY: number }> = {\n\tfit: (ratioX, ratioY) => {\n\t\tconst s = Math.min(ratioX, ratioY);\n\t\treturn { scaleX: s, scaleY: s };\n\t},\n\tcover: (ratioX, ratioY) => {\n\t\tconst s = Math.max(ratioX, ratioY);\n\t\treturn { scaleX: s, scaleY: s };\n\t},\n\tstretch: (ratioX, ratioY) => ({ scaleX: ratioX, scaleY: ratioY }),\n};\n\nexport function computeViewportScale(\n\tphysicalW: number,\n\tphysicalH: number,\n\tdesignW: number,\n\tdesignH: number,\n\tmode: ScaleMode,\n): ViewportScale {\n\tconst ratioX = physicalW / designW;\n\tconst ratioY = physicalH / designH;\n\tconst { scaleX, scaleY } = scaleModeStrategy[mode](ratioX, ratioY);\n\n\treturn {\n\t\tscaleX,\n\t\tscaleY,\n\t\toffsetX: (physicalW - designW * scaleX) / 2,\n\t\toffsetY: (physicalH - designH * scaleY) / 2,\n\t\tphysicalWidth: physicalW,\n\t\tphysicalHeight: physicalH,\n\t\tdesignWidth: designW,\n\t\tdesignHeight: designH,\n\t};\n}\n\n/**\n * Convert physical canvas pixel coordinates to design-resolution (logical) coordinates.\n * Compose with camera `screenToWorld()` for full physical→world conversion.\n */\nexport function physicalToLogical(\n\tphysicalX: number,\n\tphysicalY: number,\n\tviewport: ViewportScale,\n): { x: number; y: number } {\n\treturn {\n\t\tx: (physicalX - viewport.offsetX) / viewport.scaleX,\n\t\ty: (physicalY - viewport.offsetY) / viewport.scaleY,\n\t};\n}\n\n// ==================== Plugin Factory ====================\n\n/**\n * Create a 2D rendering plugin for ECSpresso.\n *\n * This plugin provides:\n * - Transform propagation (localTransform -> worldTransform)\n * - Render sync system (updates PixiJS objects from ECS components)\n * - Scene graph management (mirrors ECS hierarchy in PixiJS scene graph)\n *\n * @example Pre-initialized mode\n * ```typescript\n * const app = new Application();\n * await app.init({ resizeTo: window });\n *\n * const ecs = ECSpresso.create<GameComponents, {}, {}>()\n * .withPlugin(createRenderer2DPlugin({ app }))\n * .build();\n * ```\n *\n * @example Managed mode\n * ```typescript\n * const ecs = ECSpresso.create<GameComponents, {}, {}>()\n * .withPlugin(createRenderer2DPlugin({\n * init: { background: '#1099bb', resizeTo: window },\n * container: document.body,\n * }))\n * .build();\n * await ecs.initialize();\n * ```\n */\ntype Renderer2DLabels = 'renderer2d-sync' | 'renderer2d-scene-graph' | 'renderer2d-camera-sync' | 'transform-propagation';\ntype Renderer2DReactiveQueryNames = 'renderer2d-sprites' | 'renderer2d-graphics' | 'renderer2d-containers';\n\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { screenScale: ScreenScaleOptions; camera: true }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { screenScale: ScreenScaleOptions }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { camera: true }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G>\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>;\nexport function createRenderer2DPlugin<G extends string = 'renderer2d'>(\n\toptions: Renderer2DPluginOptions<G> & { camera?: boolean; screenScale?: ScreenScaleOptions }\n): Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>\n| Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>\n| Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>\n| Plugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, Renderer2DResourceTypes & ViewportScaleResourceTypes & CameraResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames> {\n\tconst {\n\t\trootContainer: customRootContainer,\n\t\tsystemGroup = 'renderer2d',\n\t\trenderSyncPriority = 500,\n\t\ttransform: transformOptions,\n\t\tstartLoop = true,\n\t\trenderLayers = [],\n\t\tcamera = false,\n\t\tscreenScale,\n\t} = options;\n\n\tconst hasScreenScale = screenScale !== undefined;\n\tconst designWidth = screenScale?.width ?? 0;\n\tconst designHeight = screenScale?.height ?? 0;\n\tconst screenScaleMode: ScaleMode = screenScale?.mode ?? 'fit';\n\n\t// Entity ID -> PixiJS Container mapping for scene graph management\n\tconst entityToPixiObject = new Map<number, Container>();\n\n\t// Render layer name -> PixiJS Container mapping\n\tconst layerContainers = new Map<string, Container>();\n\n\t// Container constructor captured during initialization via dynamic import\n\t// Used by getOrCreateLayerContainer for lazy layer creation\n\tlet createLayerContainer: (label: string) => Container = () => {\n\t\tthrow new Error('renderer2D: createLayerContainer called before initialization');\n\t};\n\n\t// Helper to get or create a render layer container\n\tfunction getOrCreateLayerContainer(\n\t\tlayerName: string,\n\t\trootCont: Container\n\t): Container {\n\t\tconst existing = layerContainers.get(layerName);\n\t\tif (existing) return existing;\n\n\t\t// Lazy-create for undeclared layers, appended to end\n\t\tconst cont = createLayerContainer(`layer:${layerName}`);\n\t\tlayerContainers.set(layerName, cont);\n\t\trootCont.addChild(cont);\n\t\treturn cont;\n\t}\n\n\t// Helper to resolve the target container for an entity.\n\t// Scene graph stays flat (rootContainer or render layer) because the render\n\t// sync positions objects using absolute worldTransform. Nesting under a\n\t// parent's display object would double-apply the parent's transform.\n\ttype PluginResourceTypes = Renderer2DResourceTypes & ViewportScaleResourceTypes;\n\ttype PluginECS = ECSpresso<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, PluginResourceTypes>>;\n\n\tfunction resolveTargetContainer(\n\t\tentityId: number,\n\t\tecs: PluginECS\n\t): Container {\n\t\tconst rootCont = ecs.getResource('rootContainer');\n\n\t\t// 1. Check render layer component\n\t\tconst layerName = ecs.getComponent(entityId, 'renderLayer');\n\t\tif (layerName) return getOrCreateLayerContainer(layerName, rootCont);\n\n\t\t// 2. Fall back to root container\n\t\treturn rootCont;\n\t}\n\n\t// Helper to add a PixiJS object to the scene graph\n\tfunction addToSceneGraph(\n\t\tentityId: number,\n\t\tpixiObject: Container,\n\t\tecs: PluginECS\n\t): void {\n\t\tconst targetContainer = resolveTargetContainer(entityId, ecs);\n\n\t\t// Only add if not already a child\n\t\tif (pixiObject.parent !== targetContainer) {\n\t\t\ttargetContainer.addChild(pixiObject);\n\t\t}\n\t}\n\n\t// Helper to update parent in scene graph\n\tfunction updateSceneGraphParent(\n\t\tentityId: number,\n\t\tecs: PluginECS\n\t): void {\n\t\tconst pixiObject = entityToPixiObject.get(entityId);\n\t\tif (!pixiObject) return;\n\n\t\tconst targetContainer = resolveTargetContainer(entityId, ecs);\n\n\t\tif (pixiObject.parent !== targetContainer) {\n\t\t\tpixiObject.removeFromParent();\n\t\t\ttargetContainer.addChild(pixiObject);\n\t\t}\n\t}\n\n\t// Determine mode and set up resource registration closures\n\tconst isManaged = 'init' in options && options.init !== undefined;\n\n\treturn definePlugin<WorldConfigFrom<Renderer2DComponentTypes, Renderer2DEventTypes, PluginResourceTypes>, EmptyConfig, Renderer2DLabels, G, never, Renderer2DReactiveQueryNames>({\n\t\tid: 'renderer2d',\n\t\tinstall(world) {\n\t\t\t// Install transform plugin (deduplicates if already installed)\n\t\t\tworld.installPlugin(createTransformPlugin(transformOptions));\n\n\t\t\t// Register resources based on mode\n\t\t\tif (isManaged) {\n\t\t\t\tconst initOptions = (options as Renderer2DPluginManagedOptions<G>).init;\n\t\t\t\tconst containerOption = (options as Renderer2DPluginManagedOptions<G>).container;\n\n\t\t\t\tworld.addResource('pixiApp', async () => {\n\t\t\t\t\tconst app = await createPixiApplication(initOptions);\n\n\t\t\t\t\tif (containerOption) {\n\t\t\t\t\t\tconst containerEl = typeof containerOption === 'string'\n\t\t\t\t\t\t\t? document.querySelector(containerOption)\n\t\t\t\t\t\t\t: containerOption;\n\n\t\t\t\t\t\tif (containerEl) {\n\t\t\t\t\t\t\tcontainerEl.appendChild(app.canvas);\n\t\t\t\t\t\t} else if (typeof containerOption === 'string') {\n\t\t\t\t\t\t\tconsole.warn(`Renderer2D plugin: container selector \"${containerOption}\" not found`);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\treturn app;\n\t\t\t\t});\n\n\t\t\t\tworld.addResource('rootContainer', {\n\t\t\t\t\tdependsOn: ['pixiApp'],\n\t\t\t\t\tfactory: (ecs) => customRootContainer ?? ecs.getResource('pixiApp').stage,\n\t\t\t\t});\n\n\t\t\t\tworld.addResource('bounds', {\n\t\t\t\t\tdependsOn: ['pixiApp'],\n\t\t\t\t\tfactory: (ecs) => {\n\t\t\t\t\t\tif (hasScreenScale) return createBounds(designWidth, designHeight);\n\t\t\t\t\t\tconst pixiApp = ecs.getResource('pixiApp');\n\t\t\t\t\t\treturn createBounds(pixiApp.screen.width, pixiApp.screen.height);\n\t\t\t\t\t},\n\t\t\t\t});\n\n\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\tworld.addResource('viewportScale', {\n\t\t\t\t\t\tdependsOn: ['pixiApp'],\n\t\t\t\t\t\tfactory: (ecs) => {\n\t\t\t\t\t\t\tconst pixiApp = ecs.getResource('pixiApp');\n\t\t\t\t\t\t\treturn computeViewportScale(pixiApp.screen.width, pixiApp.screen.height, designWidth, designHeight, screenScaleMode);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconst app = (options as Renderer2DPluginAppOptions<G>).app;\n\t\t\t\tworld.addResource('pixiApp', app);\n\t\t\t\tworld.addResource('rootContainer', customRootContainer ?? app.stage);\n\t\t\t\tworld.addResource('bounds', hasScreenScale\n\t\t\t\t\t? createBounds(designWidth, designHeight)\n\t\t\t\t\t: createBounds(app.screen.width, app.screen.height));\n\n\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\tworld.addResource('viewportScale',\n\t\t\t\t\t\tcomputeViewportScale(app.screen.width, app.screen.height, designWidth, designHeight, screenScaleMode));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Register dispose callbacks for display object components\n\t\t\tworld.registerDispose('sprite', ({ value: sprite }) => {\n\t\t\t\tsprite.removeFromParent();\n\t\t\t});\n\t\t\tworld.registerDispose('graphics', ({ value: graphics }) => {\n\t\t\t\tgraphics.removeFromParent();\n\t\t\t});\n\t\t\tworld.registerDispose('container', ({ value: container }) => {\n\t\t\t\tcontainer.removeFromParent();\n\t\t\t});\n\n\t\t\t// Display objects require localTransform and visible\n\t\t\tworld.registerRequired('sprite', 'localTransform', () => createLocalTransformInternal());\n\t\t\tworld.registerRequired('sprite', 'visible', () => createVisibleComponent());\n\t\t\tworld.registerRequired('graphics', 'localTransform', () => createLocalTransformInternal());\n\t\t\tworld.registerRequired('graphics', 'visible', () => createVisibleComponent());\n\t\t\tworld.registerRequired('container', 'localTransform', () => createLocalTransformInternal());\n\t\t\tworld.registerRequired('container', 'visible', () => createVisibleComponent());\n\n\t\t\t// ==================== Render Sync System ====================\n\t\t\tworld\n\t\t\t\t.addSystem('renderer2d-sync')\n\t\t\t\t.setPriority(renderSyncPriority)\n\t\t\t\t.inPhase('render')\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('sprites', {\n\t\t\t\t\twith: ['sprite', 'worldTransform'],\n\t\t\t\t\tchanged: ['worldTransform'],\n\t\t\t\t})\n\t\t\t\t.addQuery('graphics', {\n\t\t\t\t\twith: ['graphics', 'worldTransform'],\n\t\t\t\t\tchanged: ['worldTransform'],\n\t\t\t\t})\n\t\t\t\t.addQuery('containers', {\n\t\t\t\t\twith: ['container', 'worldTransform'],\n\t\t\t\t\tchanged: ['worldTransform'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, ecs }) => {\n\t\t\t\t\tfor (const entity of queries.sprites) {\n\t\t\t\t\t\tconst { sprite, worldTransform } = entity.components;\n\n\t\t\t\t\t\tsprite.position.set(worldTransform.x, worldTransform.y);\n\t\t\t\t\t\tsprite.rotation = worldTransform.rotation;\n\t\t\t\t\t\tsprite.scale.set(worldTransform.scaleX, worldTransform.scaleY);\n\n\t\t\t\t\t\tconst visibleComp = ecs.getComponent(entity.id, 'visible');\n\t\t\t\t\t\tif (visibleComp) {\n\t\t\t\t\t\t\tsprite.visible = visibleComp.visible;\n\t\t\t\t\t\t\tif (visibleComp.alpha !== undefined) {\n\t\t\t\t\t\t\t\tsprite.alpha = visibleComp.alpha;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const entity of queries.graphics) {\n\t\t\t\t\t\tconst { graphics, worldTransform } = entity.components;\n\n\t\t\t\t\t\tgraphics.position.set(worldTransform.x, worldTransform.y);\n\t\t\t\t\t\tgraphics.rotation = worldTransform.rotation;\n\t\t\t\t\t\tgraphics.scale.set(worldTransform.scaleX, worldTransform.scaleY);\n\n\t\t\t\t\t\tconst visibleComp = ecs.getComponent(entity.id, 'visible');\n\t\t\t\t\t\tif (visibleComp) {\n\t\t\t\t\t\t\tgraphics.visible = visibleComp.visible;\n\t\t\t\t\t\t\tif (visibleComp.alpha !== undefined) {\n\t\t\t\t\t\t\t\tgraphics.alpha = visibleComp.alpha;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const entity of queries.containers) {\n\t\t\t\t\t\tconst { container, worldTransform } = entity.components;\n\n\t\t\t\t\t\tcontainer.position.set(worldTransform.x, worldTransform.y);\n\t\t\t\t\t\tcontainer.rotation = worldTransform.rotation;\n\t\t\t\t\t\tcontainer.scale.set(worldTransform.scaleX, worldTransform.scaleY);\n\n\t\t\t\t\t\tconst visibleComp = ecs.getComponent(entity.id, 'visible');\n\t\t\t\t\t\tif (visibleComp) {\n\t\t\t\t\t\t\tcontainer.visible = visibleComp.visible;\n\t\t\t\t\t\t\tif (visibleComp.alpha !== undefined) {\n\t\t\t\t\t\t\t\tcontainer.alpha = visibleComp.alpha;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// ==================== Scene Graph Manager System ====================\n\t\t\tworld\n\t\t\t\t.addSystem('renderer2d-scene-graph')\n\t\t\t\t.setPriority(9999)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.setOnInitialize(async (ecs) => {\n\t\t\t\t\tconst pixiApp = ecs.getResource('pixiApp');\n\t\t\t\t\tlet rootCont = ecs.getResource('rootContainer');\n\n\t\t\t\t\tconst { Container: ContainerClass } = await import('pixi.js');\n\t\t\t\t\tcreateLayerContainer = (label: string) => {\n\t\t\t\t\t\tconst cont = new ContainerClass();\n\t\t\t\t\t\tcont.label = label;\n\t\t\t\t\t\treturn cont;\n\t\t\t\t\t};\n\n\t\t\t\t\tlet viewportContainer: Container | undefined;\n\t\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\t\tviewportContainer = new ContainerClass();\n\t\t\t\t\t\tviewportContainer.label = 'viewportContainer';\n\n\t\t\t\t\t\tconst vs = ecs.tryGetResource('viewportScale');\n\t\t\t\t\t\tif (!vs) throw new Error('renderer2D: viewportScale resource not found');\n\t\t\t\t\t\tviewportContainer.position.set(vs.offsetX, vs.offsetY);\n\t\t\t\t\t\tviewportContainer.scale.set(vs.scaleX, vs.scaleY);\n\n\t\t\t\t\t\tconst newRoot = new ContainerClass();\n\t\t\t\t\t\tnewRoot.label = 'rootContainer';\n\n\t\t\t\t\t\tpixiApp.stage.addChild(viewportContainer);\n\t\t\t\t\t\tviewportContainer.addChild(newRoot);\n\n\t\t\t\t\t\tecs.updateResource('rootContainer', () => newRoot);\n\t\t\t\t\t\trootCont = newRoot;\n\t\t\t\t\t}\n\n\t\t\t\t\tfor (const layerName of renderLayers) {\n\t\t\t\t\t\tconst cont = createLayerContainer(`layer:${layerName}`);\n\t\t\t\t\t\tlayerContainers.set(layerName, cont);\n\t\t\t\t\t\trootCont.addChild(cont);\n\t\t\t\t\t}\n\n\t\t\t\t\tecs.addReactiveQuery('renderer2d-sprites', {\n\t\t\t\t\t\twith: ['sprite'],\n\t\t\t\t\t\tonEnter: (entity) => {\n\t\t\t\t\t\t\tconst pixiObject = entity.components.sprite;\n\t\t\t\t\t\t\tentityToPixiObject.set(entity.id, pixiObject);\n\t\t\t\t\t\t\taddToSceneGraph(entity.id, pixiObject, ecs);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonExit: (entityId) => {\n\t\t\t\t\t\t\tentityToPixiObject.delete(entityId);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.addReactiveQuery('renderer2d-graphics', {\n\t\t\t\t\t\twith: ['graphics'],\n\t\t\t\t\t\tonEnter: (entity) => {\n\t\t\t\t\t\t\tconst pixiObject = entity.components.graphics;\n\t\t\t\t\t\t\tentityToPixiObject.set(entity.id, pixiObject);\n\t\t\t\t\t\t\taddToSceneGraph(entity.id, pixiObject, ecs);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonExit: (entityId) => {\n\t\t\t\t\t\t\tentityToPixiObject.delete(entityId);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.addReactiveQuery('renderer2d-containers', {\n\t\t\t\t\t\twith: ['container'],\n\t\t\t\t\t\tonEnter: (entity) => {\n\t\t\t\t\t\t\tconst pixiObject = entity.components.container;\n\t\t\t\t\t\t\tentityToPixiObject.set(entity.id, pixiObject);\n\t\t\t\t\t\t\taddToSceneGraph(entity.id, pixiObject, ecs);\n\t\t\t\t\t\t},\n\t\t\t\t\t\tonExit: (entityId) => {\n\t\t\t\t\t\t\tentityToPixiObject.delete(entityId);\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.on('hierarchyChanged', ({ entityId }) => {\n\t\t\t\t\t\tupdateSceneGraphParent(entityId, ecs);\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.onComponentAdded('renderLayer', ({ entity }) => {\n\t\t\t\t\t\tupdateSceneGraphParent(entity.id, ecs);\n\t\t\t\t\t});\n\n\t\t\t\t\tecs.onComponentRemoved('renderLayer', ({ entity }) => {\n\t\t\t\t\t\tupdateSceneGraphParent(entity.id, ecs);\n\t\t\t\t\t});\n\n\t\t\t\t\tif (camera) {\n\t\t\t\t\t\tconst cameraState = ecs.tryGetResource<CameraState>('cameraState');\n\t\t\t\t\t\tif (!cameraState) throw new Error('renderer2D: cameraState resource not found');\n\t\t\t\t\t\tcameraState.viewportWidth = hasScreenScale ? designWidth : pixiApp.screen.width;\n\t\t\t\t\t\tcameraState.viewportHeight = hasScreenScale ? designHeight : pixiApp.screen.height;\n\t\t\t\t\t}\n\n\t\t\t\t\tpixiApp.renderer.on('resize', (width: number, height: number) => {\n\t\t\t\t\t\tif (hasScreenScale) {\n\t\t\t\t\t\t\tconst vs = computeViewportScale(width, height, designWidth, designHeight, screenScaleMode);\n\t\t\t\t\t\t\tconst vpResource = ecs.tryGetResource('viewportScale');\n\t\t\t\t\t\t\tif (!vpResource) throw new Error('renderer2D: viewportScale resource not found');\n\t\t\t\t\t\t\tvpResource.scaleX = vs.scaleX;\n\t\t\t\t\t\t\tvpResource.scaleY = vs.scaleY;\n\t\t\t\t\t\t\tvpResource.offsetX = vs.offsetX;\n\t\t\t\t\t\t\tvpResource.offsetY = vs.offsetY;\n\t\t\t\t\t\t\tvpResource.physicalWidth = width;\n\t\t\t\t\t\t\tvpResource.physicalHeight = height;\n\n\t\t\t\t\t\t\tif (viewportContainer) {\n\t\t\t\t\t\t\t\tviewportContainer.position.set(vs.offsetX, vs.offsetY);\n\t\t\t\t\t\t\t\tviewportContainer.scale.set(vs.scaleX, vs.scaleY);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst bounds = ecs.getResource('bounds');\n\t\t\t\t\t\t\tbounds.width = width;\n\t\t\t\t\t\t\tbounds.height = height;\n\n\t\t\t\t\t\t\tif (camera) {\n\t\t\t\t\t\t\t\tconst cameraState = ecs.tryGetResource<CameraState>('cameraState');\n\t\t\t\t\t\t\t\tif (!cameraState) throw new Error('renderer2D: cameraState resource not found');\n\t\t\t\t\t\t\t\tcameraState.viewportWidth = width;\n\t\t\t\t\t\t\t\tcameraState.viewportHeight = height;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\n\t\t\t\t\tif (startLoop) {\n\t\t\t\t\t\tpixiApp.ticker.add((ticker) => {\n\t\t\t\t\t\t\tecs.update(ticker.deltaMS / 1_000);\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t// ==================== Camera Sync System (opt-in) ====================\n\t\t\tif (camera) {\n\t\t\t\tworld\n\t\t\t\t\t.addSystem('renderer2d-camera-sync')\n\t\t\t\t\t.setPriority(900)\n\t\t\t\t\t.inPhase('render')\n\t\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t\t.setProcess(({ ecs }) => {\n\t\t\t\t\t\tconst state = ecs.tryGetResource<CameraState>('cameraState');\n\t\t\t\t\t\tif (!state) throw new Error('renderer2D: cameraState resource not found');\n\t\t\t\t\t\tconst root = ecs.getResource('rootContainer');\n\t\t\t\t\t\tconst [centerW, centerH] = hasScreenScale\n\t\t\t\t\t\t\t? [designWidth, designHeight]\n\t\t\t\t\t\t\t: [ecs.getResource('pixiApp').screen.width, ecs.getResource('pixiApp').screen.height];\n\n\t\t\t\t\t\troot.position.set(\n\t\t\t\t\t\t\tcenterW / 2 - (state.x + state.shakeOffsetX) * state.zoom,\n\t\t\t\t\t\t\tcenterH / 2 - (state.y + state.shakeOffsetY) * state.zoom,\n\t\t\t\t\t\t);\n\t\t\t\t\t\troot.scale.set(state.zoom);\n\t\t\t\t\t\troot.rotation = -(state.rotation + state.shakeRotation);\n\t\t\t\t\t});\n\t\t\t}\n\t\t},\n\t});\n}\n"
6
6
  ],
7
- "mappings": "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",
7
+ "mappings": "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",
8
8
  "debugId": "94CD9C9F93830E4A64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -2,9 +2,14 @@
2
2
  * Screen/State management for ECSpresso ECS framework
3
3
  */
4
4
  import type EventBus from './event-bus';
5
- import type AssetManager from './asset-manager';
6
- import type ECSpresso from './ecspresso';
7
5
  import type { ScreenDefinition, ScreenResource, ScreenEvents, ScreenConfigurator, ScreenConfig, ScreenState } from './screen-types';
6
+ /** Structural interface covering only the AssetManager methods ScreenManager uses. */
7
+ interface ScreenManagerAssetDeps {
8
+ isLoaded(key: string): boolean;
9
+ loadAsset(key: string): Promise<unknown>;
10
+ isGroupLoaded(group: string): boolean;
11
+ loadAssetGroup(group: string): Promise<void>;
12
+ }
8
13
  /**
9
14
  * Manages screen/state transitions for ECSpresso
10
15
  */
@@ -19,7 +24,7 @@ export default class ScreenManager<Screens extends Record<string, ScreenDefiniti
19
24
  * Set dependencies for screen transitions
20
25
  * @internal
21
26
  */
22
- setDependencies(eventBus: EventBus<ScreenEvents<keyof Screens & string>>, assetManager: AssetManager<any> | null, ecs: ECSpresso<any, any, any, any, any>): void;
27
+ setDependencies(eventBus: EventBus<ScreenEvents<keyof Screens & string>>, assetManager: ScreenManagerAssetDeps | null, ecs: unknown): void;
23
28
  private requireEcs;
24
29
  /**
25
30
  * Register a screen definition
@@ -120,3 +125,4 @@ export declare class ScreenConfiguratorImpl<Screens extends Record<string, Scree
120
125
  * Create a new ScreenConfigurator for builder pattern usage
121
126
  */
122
127
  export declare function createScreenConfigurator<Screens extends Record<string, ScreenDefinition<any, any>> = Record<string, never>, W = unknown>(manager?: ScreenManager<Screens>): ScreenConfiguratorImpl<Screens, W>;
128
+ export {};
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "ecspresso",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "homepage": "https://david0178418.github.io/ecspresso/",
7
8
  "description": "A minimal Entity-Component-System library for typescript and javascript.",
8
9
  "sideEffects": false,
9
10
  "exports": {
@@ -70,6 +71,10 @@
70
71
  "./plugins/particles": {
71
72
  "import": "./dist/plugins/particles.js",
72
73
  "types": "./dist/plugins/particles.d.ts"
74
+ },
75
+ "./plugins/coroutine": {
76
+ "import": "./dist/plugins/coroutine.js",
77
+ "types": "./dist/plugins/coroutine.d.ts"
73
78
  }
74
79
  },
75
80
  "publishConfig": {
@@ -95,7 +100,8 @@
95
100
  "@types/three": "^0.183.1",
96
101
  "howler": "^2.2.4",
97
102
  "pixi.js": "^8.16.0",
98
- "three": "^0.183.2"
103
+ "three": "^0.183.2",
104
+ "typedoc": "^0.28.17"
99
105
  },
100
106
  "peerDependencies": {
101
107
  "typescript": "^5.9.3",
@@ -121,6 +127,8 @@
121
127
  "check:types": "bun tsc --noEmit --skipLibCheck",
122
128
  "check": "bun run check:types && bun test",
123
129
  "examples": "bun ./examples/serve-examples.ts",
130
+ "docs": "typedoc && bun scripts/build-examples.ts && bun scripts/build-docs-index.ts",
131
+ "docs:serve": "bun run docs && bunx serve docs",
124
132
  "prepublishOnly": "bun run check && bun run build"
125
133
  },
126
134
  "type": "module"
package/dist/src/index.js DELETED
@@ -1,4 +0,0 @@
1
- var{defineProperty:C,getOwnPropertyNames:i,getOwnPropertyDescriptor:o}=Object,r=Object.prototype.hasOwnProperty;function a(j){return this[j]}var t=(j)=>{var $=(k??=new WeakMap).get(j),L;if($)return $;if($=C({},"__esModule",{value:!0}),j&&typeof j==="object"||typeof j==="function"){for(var J of i(j))if(!r.call($,J))C($,J,{get:a.bind(j,J),enumerable:!(L=o(j,J))||L.enumerable})}return k.set(j,$),$},k;var n=(j)=>j;function e(j,$){this[j]=n.bind(null,$)}var jj=(j,$)=>{for(var L in $)C(j,L,{get:$[L],enumerable:!0,configurable:!0,set:e.bind($,L)})};var V=(j,$)=>()=>(j&&($=j(j=0)),$);var Yj=((j)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(j,{get:($,L)=>(typeof require<"u"?require:$)[L]}):j)(function(j){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+j+'" is not supported')});class H{parentMap=new Map;childrenMap=new Map;setParent(j,$){if(j===$)throw Error(`Cannot set entity ${j} as its own parent`);if(this.wouldCreateCycle(j,$))throw Error("Cannot set parent: would create circular reference");let L=this.parentMap.get(j);if(L!==void 0){let X=this.childrenMap.get(L);if(X){let Y=X.indexOf(j);if(Y!==-1)X.splice(Y,1)}}this.parentMap.set(j,$);let J=this.childrenMap.get($);if(J)J.push(j);else this.childrenMap.set($,[j]);return this}removeParent(j){let $=this.parentMap.get(j);if($===void 0)return!1;let L=this.childrenMap.get($);if(L){let J=L.indexOf(j);if(J!==-1)L.splice(J,1)}return this.parentMap.delete(j),!0}getParent(j){return this.parentMap.get(j)??null}getChildren(j){let $=this.childrenMap.get(j);return $?[...$]:[]}getChildAt(j,$){if($<0)return null;let L=this.childrenMap.get(j);if(!L||$>=L.length)return null;return L[$]??null}getChildIndex(j,$){let L=this.childrenMap.get(j);if(!L)return-1;return L.indexOf($)}removeEntity(j){let $=this.parentMap.get(j)??null;if($!==null){let X=this.childrenMap.get($);if(X){let Y=X.indexOf(j);if(Y!==-1)X.splice(Y,1)}}this.parentMap.delete(j);let L=this.childrenMap.get(j)??[],J=[...L];for(let X of L)this.parentMap.delete(X);return this.childrenMap.delete(j),{oldParent:$,orphanedChildren:J}}getAncestors(j){let $=[],L=this.parentMap.get(j);while(L!==void 0)$.push(L),L=this.parentMap.get(L);return $}getDescendants(j){let $=[],L=this.childrenMap.get(j);if(!L)return $;let J=L.slice().reverse();while(J.length>0){let X=J.pop();$.push(X);let Y=this.childrenMap.get(X);if(Y)for(let Z=Y.length-1;Z>=0;Z--)J.push(Y[Z])}return $}getRoot(j){let $=j,L=this.parentMap.get($);while(L!==void 0)$=L,L=this.parentMap.get($);return $}getSiblings(j){let $=this.parentMap.get(j);if($===void 0)return[];let L=this.childrenMap.get($);if(!L)return[];return L.filter((J)=>J!==j)}isDescendantOf(j,$){if(j===$)return!1;let L=this.parentMap.get(j);while(L!==void 0){if(L===$)return!0;L=this.parentMap.get(L)}return!1}isAncestorOf(j,$){return this.isDescendantOf($,j)}getRootEntities(){let j=[];for(let $ of this.childrenMap.keys())if(!this.parentMap.has($))j.push($);return j}wouldCreateCycle(j,$){let L=$;while(L!==void 0){if(L===j)return!0;L=this.parentMap.get(L)}return!1}forEachInHierarchy(j,$){let L=$?.roots??this.getRootEntities(),J=[];for(let X of L)J.push({entityId:X,parentId:null,depth:0});for(let X of J){j(X.entityId,X.parentId,X.depth);let Y=this.childrenMap.get(X.entityId);if(Y)for(let Z of Y)J.push({entityId:Z,parentId:X.entityId,depth:X.depth+1})}}*hierarchyIterator(j){let $=j?.roots??this.getRootEntities(),L=[];for(let J of $)L.push({entityId:J,parentId:null,depth:0});for(let J of L){yield J;let X=this.childrenMap.get(J.entityId);if(X)for(let Y of X)L.push({entityId:Y,parentId:J.entityId,depth:J.depth+1})}}}class E{nextId=1;entities=new Map;componentIndices=new Map;addedCallbacks=new Map;removedCallbacks=new Map;hierarchyManager=new H;disposeCallbacks=new Map;changeSeqs=new Map;_changeSeq=0;_afterComponentAddedHooks=[];_afterEntityMutatedHooks=[];_afterComponentRemovedHooks=[];_beforeEntityRemovedHooks=[];_afterParentChangedHooks=[];_batchingDepth=0;_batchedEntityIds=new Set;_pendingBatchKeys=null;get entityCount(){return this.entities.size}createEntity(){let j=this.nextId++,$={id:j,components:{}};return this.entities.set(j,$),$}registerDispose(j,$){this.disposeCallbacks.set(j,$)}getDisposeCallbacks(){return this.disposeCallbacks}invokeDispose(j,$,L){let J=this.disposeCallbacks.get(j);if(!J)return;try{J({value:$,entityId:L})}catch(X){console.warn(`Component dispose callback for '${String(j)}' threw:`,X)}}addComponent(j,$,L){let J=this.entities.get(j);if(!J)throw Error(`Cannot add component '${String($)}': Entity with ID ${j} does not exist`);let X=J.components[$];if(X!==void 0)this.invokeDispose($,X,J.id);if(J.components[$]=L,!this.componentIndices.has($))this.componentIndices.set($,new Set);this.componentIndices.get($)?.add(J.id);let Y=this.addedCallbacks.get($);if(Y)for(let Z of[...Y])Z({value:L,entity:J});this._batchingDepth++;for(let Z of this._afterComponentAddedHooks)Z(J.id,$);if(this._batchedEntityIds.add(J.id),this._batchingDepth--,this._batchingDepth===0){for(let Z of this._batchedEntityIds)for(let B of this._afterEntityMutatedHooks)B(Z);this._batchedEntityIds.clear()}return this}addComponents(j,$){let L=this.entities.get(j);if(!L)throw Error(`Cannot add components: Entity with ID ${j} does not exist`);let J=this._pendingBatchKeys;this._pendingBatchKeys=new Set(Object.keys($)),this._batchingDepth++;for(let X in $)this.addComponent(L.id,X,$[X]);if(this._batchingDepth--,this._pendingBatchKeys=J,this._batchingDepth===0){for(let X of this._batchedEntityIds)for(let Y of this._afterEntityMutatedHooks)Y(X);this._batchedEntityIds.clear()}return this}removeComponent(j,$){let L=this.entities.get(j);if(!L)throw Error(`Cannot remove component '${String($)}': Entity with ID ${j} does not exist`);let J=L.components[$];if(J!==void 0)this.invokeDispose($,J,L.id);delete L.components[$];let X=this.removedCallbacks.get($);if(X&&J!==void 0)for(let Y of[...X])Y({value:J,entity:L});if(this.componentIndices.get($)?.delete(L.id),J!==void 0)for(let Y of this._afterComponentRemovedHooks)Y(L.id,$);return this}getComponent(j,$){let L=this.entities.get(j);if(!L)throw Error(`Cannot get component '${String($)}': Entity with ID ${j} does not exist`);return L.components[$]}getEntitiesWithQuery(j=[],$=[],L,J,X){let Y=L!==void 0&&L.length>0&&J!==void 0,Z=X!==void 0&&X.length>0;if(j.length===0){if($.length===0&&!Y&&!Z)return Array.from(this.entities.values());return Array.from(this.entities.values()).filter((D)=>{if($.length>0&&!$.every((_)=>!(_ in D.components)))return!1;if(Y){let _=this.changeSeqs.get(D.id);if(!_)return!1;if(!L.some((Q)=>(_.get(Q)??-1)>J))return!1}if(Z&&!this.parentHasComponents(D.id,X))return!1;return!0})}let B=j[0];if(B===void 0)return[];let K=j.reduce((D,_)=>{let Q=this.componentIndices.get(_)?.size??0,A=this.componentIndices.get(D)?.size??1/0;return Q<A?_:D},B),U=this.componentIndices.get(K);if(!U||U.size===0)return[];let W=[],F=$.length>0;for(let D of U){let _=this.entities.get(D);if(_&&j.every((Q)=>(Q in _.components))&&(!F||$.every((Q)=>!(Q in _.components)))){if(Y){let Q=this.changeSeqs.get(D);if(!Q||!L.some((A)=>(Q.get(A)??-1)>J))continue}if(Z&&!this.parentHasComponents(D,X))continue;W.push(_)}}return W}parentHasComponents(j,$){let L=this.hierarchyManager.getParent(j);if(L===null)return!1;let J=this.entities.get(L);if(!J)return!1;for(let X of $)if(!(X in J.components))return!1;return!0}removeEntity(j,$){let L=this.entities.get(j);if(!L)return!1;if($?.cascade??!0){let X=this.hierarchyManager.getDescendants(L.id);for(let Y=X.length-1;Y>=0;Y--){let Z=X[Y];if(Z===void 0)continue;for(let B of this._beforeEntityRemovedHooks)B(Z)}for(let Y of this._beforeEntityRemovedHooks)Y(L.id);for(let Y=X.length-1;Y>=0;Y--){let Z=X[Y];if(Z===void 0)continue;this.removeEntityInternal(Z)}}else for(let X of this._beforeEntityRemovedHooks)X(L.id);return this.removeEntityInternal(L.id)}removeEntityInternal(j){let $=this.entities.get(j);if(!$)return!1;this.hierarchyManager.removeEntity(j);for(let L of Object.keys($.components)){let J=$.components[L];if(J!==void 0){this.invokeDispose(L,J,$.id);let X=this.removedCallbacks.get(L);if(X)for(let Y of[...X])Y({value:J,entity:$})}this.componentIndices.get(L)?.delete($.id)}return this.changeSeqs.delete($.id),this.entities.delete($.id)}getEntity(j){return this.entities.get(j)}onComponentAdded(j,$){let L=$,J=this.addedCallbacks.get(j);if(!J)J=new Set,this.addedCallbacks.set(j,J);return J.add(L),()=>{this.addedCallbacks.get(j)?.delete(L)}}onComponentRemoved(j,$){let L=$,J=this.removedCallbacks.get(j);if(!J)J=new Set,this.removedCallbacks.set(j,J);return J.add(L),()=>{this.removedCallbacks.get(j)?.delete(L)}}onAfterComponentAdded(j){return this._afterComponentAddedHooks.push(j),()=>{let $=this._afterComponentAddedHooks.indexOf(j);if($!==-1)this._afterComponentAddedHooks.splice($,1)}}onAfterEntityMutated(j){return this._afterEntityMutatedHooks.push(j),()=>{let $=this._afterEntityMutatedHooks.indexOf(j);if($!==-1)this._afterEntityMutatedHooks.splice($,1)}}onAfterComponentRemoved(j){return this._afterComponentRemovedHooks.push(j),()=>{let $=this._afterComponentRemovedHooks.indexOf(j);if($!==-1)this._afterComponentRemovedHooks.splice($,1)}}onBeforeEntityRemoved(j){return this._beforeEntityRemovedHooks.push(j),()=>{let $=this._beforeEntityRemovedHooks.indexOf(j);if($!==-1)this._beforeEntityRemovedHooks.splice($,1)}}onAfterParentChanged(j){return this._afterParentChangedHooks.push(j),()=>{let $=this._afterParentChangedHooks.indexOf(j);if($!==-1)this._afterParentChangedHooks.splice($,1)}}get changeSeq(){return this._changeSeq}markChanged(j,$){let L=++this._changeSeq,J=this.changeSeqs.get(j);if(!J)J=new Map,this.changeSeqs.set(j,J);J.set($,L)}getChangeSeq(j,$){return this.changeSeqs.get(j)?.get($)??-1}spawnChild(j,$){let L=this.createEntity();return this.addComponents(L.id,$),this.setParent(L.id,j),L}setParent(j,$){this.hierarchyManager.setParent(j,$);for(let L of this._afterParentChangedHooks)L(j);return this}removeParent(j){let $=this.hierarchyManager.removeParent(j);if($)for(let L of this._afterParentChangedHooks)L(j);return $}getParent(j){return this.hierarchyManager.getParent(j)}getChildren(j){return this.hierarchyManager.getChildren(j)}getChildAt(j,$){return this.hierarchyManager.getChildAt(j,$)}getChildIndex(j,$){return this.hierarchyManager.getChildIndex(j,$)}getAncestors(j){return this.hierarchyManager.getAncestors(j)}getDescendants(j){return this.hierarchyManager.getDescendants(j)}getRoot(j){return this.hierarchyManager.getRoot(j)}getSiblings(j){return this.hierarchyManager.getSiblings(j)}isDescendantOf(j,$){return this.hierarchyManager.isDescendantOf(j,$)}isAncestorOf(j,$){return this.hierarchyManager.isAncestorOf(j,$)}getRootEntities(){return this.hierarchyManager.getRootEntities()}forEachInHierarchy(j,$){this.hierarchyManager.forEachInHierarchy(j,$)}hierarchyIterator(j){return this.hierarchyManager.hierarchyIterator(j)}}var h=()=>{};class w{handlers=new Map;subscribe(j,$){return this.addHandler(j,$,!1)}once(j,$){return this.addHandler(j,$,!0)}unsubscribe(j,$){let L=this.handlers.get(j);if(!L)return!1;let J=L.findIndex((X)=>X.callback===$);if(J===-1)return!1;return L.splice(J,1),!0}addHandler(j,$,L){let J=this.handlers.get(j);if(!J)J=[],this.handlers.set(j,J);let X={callback:$,once:L};return J.push(X),()=>{let Y=this.handlers.get(j);if(Y){let Z=Y.indexOf(X);if(Z!==-1)Y.splice(Z,1)}}}publish(...[j,$]){let L=this.handlers.get(j);if(!L||L.length===0)return;let J=!1,X=L.length;for(let Y=0;Y<X&&Y<L.length;Y++){let Z=L[Y];if(!Z)continue;if(Z.callback($),Z.once)J=!0}if(J){for(let Y=L.length-1;Y>=0;Y--)if(L[Y]?.once)L.splice(Y,1)}}clear(){this.handlers.clear()}clearEvent(j){this.handlers.delete(j)}}function $j(j){return{[q]:j}}function Lj(j){return typeof j==="object"&&j!==null&&"factory"in j&&typeof j.factory==="function"}function Jj(j){return typeof j==="object"&&j!==null&&q in j}function u(j,$){let L=[],J=new Set,X=new Set;function Y(Z,B=[]){if(J.has(Z))return;if(X.has(Z))throw Error(`Circular resource dependency: ${[...B,Z].join(" -> ")}`);X.add(Z);for(let K of $(Z)){let U=j.find((W)=>W===K);if(U)Y(U,[...B,Z])}X.delete(Z),J.add(Z),L.push(Z)}for(let Z of j)Y(Z);return L}class R{resources=new Map;resourceFactories=new Map;resourceDependencies=new Map;resourceDisposers=new Map;initializedResourceKeys=new Set;add(j,$){let L=(J)=>{this.resources.set(j,J),this.initializedResourceKeys.add(j),this.resourceDependencies.set(j,[])};if(Lj($)){if(this.resourceFactories.set(j,$.factory),this.resourceDependencies.set(j,$.dependsOn??[]),$.onDispose)this.resourceDisposers.set(j,$.onDispose)}else if(Jj($))L($[q]);else if(typeof $==="function")this.resourceFactories.set(j,$),this.resourceDependencies.set(j,[]);else L($);return this}tryGet(j,...$){if(!this.has(j))return;return this.get(j,...$)}get(j,...$){let L=this.resources.get(j);if(L!==void 0)return L;let J=this.resourceFactories.get(j);if(J===void 0)throw Error(`Resource ${String(j)} not found`);let X=$[0],Y=J(X);if(!(Y instanceof Promise))this.resources.set(j,Y),this.initializedResourceKeys.add(j);return Y}has(j){return this.resources.has(j)||this.resourceFactories.has(j)}remove(j){let $=this.resources.delete(j),L=this.resourceFactories.delete(j);return this.resourceDependencies.delete(j),this.resourceDisposers.delete(j),this.initializedResourceKeys.delete(j),$||L}getKeys(){let j=new Set([...this.resources.keys(),...this.resourceFactories.keys()]);return Array.from(j)}needsInitialization(j){return this.resourceFactories.has(j)&&!this.initializedResourceKeys.has(j)}getPendingInitializationKeys(){return Array.from(this.resourceFactories.keys()).filter((j)=>!this.initializedResourceKeys.has(j))}async initializeResource(j,...$){if(!this.resourceFactories.has(j)||this.initializedResourceKeys.has(j))return;let L=this.resourceFactories.get(j);if(!L)return;let J=$[0],X=await L(J);this.resources.set(j,X),this.initializedResourceKeys.add(j),this.resourceFactories.delete(j)}async initializeResources(...j){let $=j.slice(1),L=$.length===0?this.getPendingInitializationKeys():$;if(L.length===0)return;let J=u(L,(X)=>[...this.resourceDependencies.get(X)??[]]);for(let X of J)await this.initializeResource(X,...j.slice(0,1))}getDependencies(j){return this.resourceDependencies.get(j)??[]}async disposeResource(j,...$){if(!this.resources.has(j)&&!this.resourceFactories.has(j))return!1;if(this.initializedResourceKeys.has(j)){let L=this.resourceDisposers.get(j),J=this.resources.get(j);if(L&&J!==void 0){let X=$[0];await L(J,X)}}return this.resources.delete(j),this.resourceFactories.delete(j),this.resourceDependencies.delete(j),this.resourceDisposers.delete(j),this.initializedResourceKeys.delete(j),!0}async disposeResources(...j){let $=Array.from(this.initializedResourceKeys);if($.length===0)return;let L=u($,(J)=>[...this.resourceDependencies.get(J)??[]]).reverse();for(let J of L)await this.disposeResource(J,...j)}}var q;var v=V(()=>{q=Symbol("resource-direct")});class S{queries=new Map;entityManager;_hasParentHasQueries=!1;constructor(j){this.entityManager=j}get hasParentHasQueries(){return this._hasParentHasQueries}addQuery(j,$){let L={definition:$,matchingEntities:new Set};if(this.queries.set(j,L),$.parentHas?.length)this._hasParentHasQueries=!0;let J=this.entityManager.getEntitiesWithQuery($.with,$.without??[]);for(let X of J)if(this.entityMatchesQuery(X,L.definition))L.matchingEntities.add(X.id),L.definition.onEnter?.(X)}removeQuery(j){let $=this.queries.delete(j);if($)this._recalcParentHasFlag();return $}entityMatchesQuery(j,$){for(let L of $.with)if(!(L in j.components))return!1;if($.without){for(let L of $.without)if(L in j.components)return!1}if($.parentHas?.length){let L=this.entityManager.getParent(j.id);if(L===null)return!1;let J=this.entityManager.getEntity(L);if(!J)return!1;for(let X of $.parentHas)if(!(X in J.components))return!1}return!0}_applyQueryTransition(j,$){let L=$.matchingEntities.has(j.id),J=this.entityMatchesQuery(j,$.definition);if(!L&&J)$.matchingEntities.add(j.id),$.definition.onEnter?.(j);else if(L&&!J)$.matchingEntities.delete(j.id),$.definition.onExit?.(j.id)}onComponentAdded(j,$){for(let[,L]of this.queries)this._applyQueryTransition(j,L);if(this._hasParentHasQueries)this._recheckChildren(j.id)}onComponentRemoved(j,$){for(let[,L]of this.queries)this._applyQueryTransition(j,L);if(this._hasParentHasQueries)this._recheckChildren(j.id)}onEntityRemoved(j){for(let[$,L]of this.queries)if(L.matchingEntities.has(j))L.matchingEntities.delete(j),L.definition.onExit?.(j)}recheckEntity(j){for(let[,$]of this.queries)this._applyQueryTransition(j,$)}recheckEntityAndChildren(j){if(this.recheckEntity(j),this._hasParentHasQueries)this._recheckChildren(j.id)}_recheckChildren(j){let $=this.entityManager.getChildren(j);for(let L of $){let J=this.entityManager.getEntity(L);if(J)this.recheckEntity(J)}}_recalcParentHasFlag(){this._hasParentHasQueries=!1;for(let[,j]of this.queries)if(j.definition.parentHas?.length){this._hasParentHasQueries=!0;return}}}class x{commands=[];removeEntity(j,$){this.commands.push((L)=>{L.removeEntity(j,$)})}addComponent(j,$,L){this.commands.push((J)=>{J.addComponent(j,$,L)})}removeComponent(j,$){this.commands.push((L)=>{L.removeComponent(j,$)})}spawn(j){this.commands.push(($)=>{$.spawn(j)})}spawnChild(j,$){this.commands.push((L)=>{L.spawnChild(j,$)})}addComponents(j,$){this.commands.push((L)=>{L.addComponents(j,$)})}setParent(j,$){this.commands.push((L)=>{L.setParent(j,$)})}mutateComponent(j,$,L){this.commands.push((J)=>{J.mutateComponent(j,$,L)})}markChanged(j,$){this.commands.push((L)=>{L.markChanged(j,$)})}removeParent(j){this.commands.push(($)=>{$.removeParent(j)})}playback(j){for(let $ of this.commands)try{$(j)}catch(L){console.warn("CommandBuffer: Command failed during playback:",L)}this.commands=[]}clear(){this.commands=[]}get length(){return this.commands.length}}function z(j){return j}class T{_label;queries={};processFunction;detachFunction;initializeFunction;eventHandlers;_priority=0;_phase="update";_groups=[];_inScreens;_excludeScreens;_requiredAssets;_runWhenEmpty=!1;_entityEnterHandlers={};_resourceKeys;constructor(j){this._label=j}get label(){return this._label}_createSystemObject(){let j={label:this._label,entityQueries:this.queries,priority:this._priority,phase:this._phase};if(this.processFunction)j.process=this.processFunction;if(this.detachFunction)j.onDetach=this.detachFunction;if(this.initializeFunction)j.onInitialize=this.initializeFunction;if(this.eventHandlers)j.eventHandlers=this.eventHandlers;if(this._groups.length>0)j.groups=[...this._groups];if(this._inScreens)j.inScreens=this._inScreens;if(this._excludeScreens)j.excludeScreens=this._excludeScreens;if(this._requiredAssets)j.requiredAssets=this._requiredAssets;if(this._runWhenEmpty)j.runWhenEmpty=!0;if(Object.keys(this._entityEnterHandlers).length>0)j.onEntityEnter={...this._entityEnterHandlers};return j}setPriority(j){return this._priority=j,this}inPhase(j){return this._phase=j,this}inGroup(j){if(!this._groups.includes(j))this._groups.push(j);return this}inScreens(j){return this._inScreens=[...j],this}excludeScreens(j){return this._excludeScreens=[...j],this}requiresAssets(j){return this._requiredAssets=[...j],this}runWhenEmpty(){return this._runWhenEmpty=!0,this}withResources(j){return this._resourceKeys=[...j],this}addQuery(j,$){let L=this;return L.queries={...this.queries,[j]:$},L}setProcess(j){if(this._resourceKeys?.length){let $=this._resourceKeys,L;this.processFunction=(J)=>{if(!L){L={};for(let X of $)L[X]=J.ecs.getResource(X)}J.resources=L,j(J)}}else this.processFunction=j;return this}setOnEntityEnter(j,$){return this._entityEnterHandlers[j]=$,this}setOnDetach(j){return this.detachFunction=j,this}setOnInitialize(j){return this.initializeFunction=j,this}setEventHandlers(j){return this.eventHandlers=j,this}}function I(j,$,L){let J=new Set,X=[$];while(X.length>0){let Y=X.pop();if(Y===void 0)break;if(Y===j)throw Error(`Circular required component dependency: '${String(j)}' -> '${String($)}' -> ... -> '${String(j)}'`);if(J.has(Y))continue;J.add(Y);let Z=L(Y);if(Z)for(let B of Z)X.push(B.component)}}var m="0.12.0";var p=()=>{};class O{assets=new Map;groups=new Map;eventBus=null;setEventBus(j){this.eventBus=j}register(j,$){if(this.assets.set(j,{definition:$,status:"pending"}),$.group){let L=this.groups.get($.group)??new Set;L.add(j),this.groups.set($.group,L)}}async loadEagerAssets(){let j=[];for(let[$,L]of this.assets)if(L.definition.eager&&L.status==="pending")j.push($);await Promise.all(j.map(($)=>this.loadAsset($)))}async loadAsset(j){let $=this.assets.get(j);if(!$)throw Error(`Asset '${String(j)}' not found`);if($.status==="loaded"&&$.value!==void 0)return $.value;if($.status==="loading"&&$.loadPromise)return $.loadPromise;if($.status==="failed")$.status="pending";$.status="loading",$.loadPromise=$.definition.loader();try{let L=await $.loadPromise;return $.value=L,$.status="loaded",$.loadPromise=void 0,this.eventBus?.publish("assetLoaded",{key:j}),this.checkGroupProgress($.definition.group),L}catch(L){let J=L instanceof Error?L:Error(String(L));throw $.status="failed",$.error=J,$.loadPromise=void 0,this.eventBus?.publish("assetFailed",{key:j,error:J}),J}}async loadAssetGroup(j){let $=this.groups.get(j);if(!$||$.size===0)throw Error(`Asset group '${j}' not found or empty`);await Promise.all(Array.from($).map((L)=>this.loadAsset(L)))}get(j){let $=this.assets.get(j);if(!$)throw Error(`Asset '${String(j)}' not found`);if($.status!=="loaded"||$.value===void 0)throw Error(`Asset '${String(j)}' is not loaded (status: ${$.status})`);return $.value}tryGet(j){let $=this.assets.get(j);if(!$||$.status!=="loaded")return;return $.value}getHandle(j){let $=this.assets.get(j);if(!$)throw Error(`Asset '${String(j)}' not found`);let L=this;return{get status(){return $.status},get isLoaded(){return $.status==="loaded"},get(){return L.get(j)},tryGet(){return L.tryGet(j)}}}getStatus(j){let $=this.assets.get(j);if(!$)throw Error(`Asset '${String(j)}' not found`);return $.status}isLoaded(j){return this.assets.get(j)?.status==="loaded"}isGroupLoaded(j){let $=this.groups.get(j);if(!$||$.size===0)return!1;for(let L of $){let J=this.assets.get(L);if(!J||J.status!=="loaded")return!1}return!0}getGroupProgress(j){return this.getGroupProgressDetails(j).progress}getGroupProgressDetails(j){let $=this.groups.get(j);if(!$||$.size===0)return{loaded:0,total:0,progress:0};let L=0;for(let X of $)if(this.assets.get(X)?.status==="loaded")L++;let J=$.size;return{loaded:L,total:J,progress:L/J}}checkGroupProgress(j){if(!j||!this.eventBus)return;let $=j,L=this.getGroupProgressDetails($);if(this.eventBus.publish("assetGroupProgress",{group:$,...L}),L.loaded===L.total)this.eventBus.publish("assetGroupLoaded",{group:$})}createResource(){let j=this;return{getStatus($){return j.getStatus($)},isLoaded($){return j.isLoaded($)},isGroupLoaded($){return j.isGroupLoaded($)},getGroupProgress($){return j.getGroupProgress($)},get($){return j.get($)},tryGet($){return j.tryGet($)},getHandle($){return j.getHandle($)}}}getKeys(){return Array.from(this.assets.keys())}getGroupNames(){return Array.from(this.groups.keys())}getGroupKeys(j){let $=this.groups.get(j);return $?Array.from($):[]}}class l{manager;constructor(j){this.manager=j}add(j,$){return this.manager.register(j,{loader:$,eager:!0}),this}addWithConfig(j,$){return this.manager.register(j,$),this}addGroup(j,$){for(let[L,J]of Object.entries($))this.manager.register(L,{loader:J,eager:!1,group:j});return this}getManager(){return this.manager}}function f(j){return new l(j??new O)}class P{screens=new Map;currentScreen=null;screenStack=[];eventBus=null;assetManager=null;ecs=null;setDependencies(j,$,L){this.eventBus=j,this.assetManager=$,this.ecs=L}requireEcs(){if(!this.ecs)throw Error("ScreenManager: dependencies not set. Call setDependencies() first.");return this.ecs}register(j,$){this.screens.set(j,{definition:$})}async setScreen(j,$){let L=this.screens.get(j);if(!L)throw Error(`Screen '${String(j)}' not found`);await this.verifyRequiredAssets(L.definition);while(this.screenStack.length>0){let X=this.screenStack.pop();if(X)await this.exitScreen(X.name)}if(this.currentScreen)await this.exitScreen(this.currentScreen.name);let J=L.definition.initialState($);this.currentScreen={name:j,config:$,state:J},await L.definition.onEnter?.({config:$,ecs:this.requireEcs()}),this.eventBus?.publish("screenEnter",{screen:j,config:$})}async pushScreen(j,$){let L=this.screens.get(j);if(!L)throw Error(`Screen '${String(j)}' not found`);if(await this.verifyRequiredAssets(L.definition),this.currentScreen)this.screenStack.push(this.currentScreen);let J=L.definition.initialState($);this.currentScreen={name:j,config:$,state:J},await L.definition.onEnter?.({config:$,ecs:this.requireEcs()}),this.eventBus?.publish("screenPush",{screen:j,config:$})}async popScreen(){if(this.screenStack.length===0)throw Error("Cannot pop screen: stack is empty");if(this.currentScreen)await this.exitScreen(this.currentScreen.name),this.eventBus?.publish("screenPop",{screen:this.currentScreen.name});this.currentScreen=this.screenStack.pop()??null}async exitScreen(j){let $=this.screens.get(j);if($?.definition.onExit)await $.definition.onExit(this.requireEcs());this.eventBus?.publish("screenExit",{screen:j})}async verifyRequiredAssets(j){if(!this.assetManager)return;if(j.requiredAssets){for(let $ of j.requiredAssets)if(!this.assetManager.isLoaded($))await this.assetManager.loadAsset($)}if(j.requiredAssetGroups){for(let $ of j.requiredAssetGroups)if(!this.assetManager.isGroupLoaded($))await this.assetManager.loadAssetGroup($)}}getCurrentScreen(){return this.currentScreen?.name??null}getConfig(j){if(!this.currentScreen)throw Error("No current screen");if(j!==void 0&&this.currentScreen.name!==j)throw Error(`Expected current screen '${String(j)}', but current is '${String(this.currentScreen.name)}'`);return this.currentScreen.config}tryGetConfig(j){if(!this.currentScreen)return;if(j!==void 0&&this.currentScreen.name!==j)return;return this.currentScreen.config}getState(j){if(!this.currentScreen)throw Error("No current screen");if(j!==void 0&&this.currentScreen.name!==j)throw Error(`Expected current screen '${String(j)}', but current is '${String(this.currentScreen.name)}'`);return this.currentScreen.state}tryGetState(j){if(!this.currentScreen)return;if(j!==void 0&&this.currentScreen.name!==j)return;return this.currentScreen.state}updateState(j,$){if(!this.currentScreen)throw Error("No current screen");if($!==void 0&&this.currentScreen.name!==$)throw Error(`Expected current screen '${String($)}', but current is '${String(this.currentScreen.name)}'`);let L=typeof j==="function"?j(this.currentScreen.state):j;this.currentScreen.state={...this.currentScreen.state,...L}}getStackDepth(){return this.screenStack.length}isOverlay(){return this.screenStack.length>0}isActive(j){if(this.currentScreen?.name===j)return!0;return this.screenStack.some(($)=>$.name===j)}isCurrent(j){return this.currentScreen?.name===j}createResource(){let j=this;return{get current(){return j.getCurrentScreen()},get config(){return j.tryGetConfig()??null},get state(){return j.tryGetState()??null},set state($){if(j.currentScreen&&$!==null)j.currentScreen.state=$},get stack(){return j.screenStack},get isOverlay(){return j.isOverlay()},get stackDepth(){return j.getStackDepth()},isActive($){return j.isActive($)},isCurrent($){return j.isCurrent($)}}}getScreenNames(){return Array.from(this.screens.keys())}hasScreen(j){return this.screens.has(j)}}class s{manager;constructor(j){this.manager=j}add(j,$){return this.manager.register(j,$),this}getManager(){return this.manager}}function b(j){return new s(j??new P)}class N{ecspresso;assetConfigurator=null;screenConfigurator=null;pendingResources=[];pendingDisposeCallbacks=[];pendingRequiredComponents=[];pendingPlugins=[];_fixedDt=null;constructor(){let{default:j}=(g(),t(c));this.ecspresso=new j}withPlugin(j){return this.pendingPlugins.push(j),this}withComponentTypes(){return this}withEventTypes(){return this}withResourceTypes(){return this}withResource(j,$){return this.pendingResources.push({key:j,value:$}),this}withDispose(j,$){return this.pendingDisposeCallbacks.push({key:j,callback:$}),this}withRequired(j,$,L){return this.pendingRequiredComponents.push({trigger:j,required:$,factory:L}),this}withAssets(j){let $=f();return j($),this.assetConfigurator=$,this}withScreens(j){let $=b();return j($),this.screenConfigurator=$,this}withFixedTimestep(j){return this._fixedDt=j,this}withReactiveQueryNames(){return this}pluginFactory(){return z}build(){for(let j of this.pendingPlugins)this.ecspresso.installPlugin(j);for(let{key:j,value:$}of this.pendingResources)this.ecspresso.addResource(j,$);for(let{key:j,callback:$}of this.pendingDisposeCallbacks)this.ecspresso.registerDispose(j,$);for(let{trigger:j,required:$,factory:L}of this.pendingRequiredComponents)this.ecspresso.registerRequired(j,$,L);if(this.assetConfigurator)this.ecspresso._setAssetManager(this.assetConfigurator.getManager());else if(this.ecspresso._hasPendingPluginAssets())this.ecspresso._setAssetManager(new O);if(this.screenConfigurator)this.ecspresso._setScreenManager(this.screenConfigurator.getManager());else if(this.ecspresso._hasPendingPluginScreens())this.ecspresso._setScreenManager(new P);if(this._fixedDt!==null)this.ecspresso._setFixedDt(this._fixedDt);return this.ecspresso}}var y=()=>{};var c={};jj(c,{default:()=>M});var d,M;var g=V(()=>{h();v();p();y();d=["preUpdate","fixedUpdate","update","postUpdate","render"];M=class M{static VERSION=m;_entityManager;_eventBus;_resourceManager;_commandBuffer;_systems=[];_phaseSystems={preUpdate:[],fixedUpdate:[],update:[],postUpdate:[],render:[]};_installedPlugins=new Set;_disabledGroups=new Set;_assetManager=null;_screenManager=null;_reactiveQueryManager;_postUpdateHooks=[];_currentTick=0;_systemLastSeqs=new Map;_changeThreshold=0;_fixedDt=0.016666666666666666;_fixedAccumulator=0;_interpolationAlpha=0;_maxFixedSteps=8;_requiredComponents=new Map;_pendingPluginAssets=[];_pendingPluginScreens=[];_diagnosticsEnabled=!1;_systemTimings=new Map;_phaseTimings={preUpdate:0,fixedUpdate:0,update:0,postUpdate:0,render:0};_entityEnterTracking=new Map;_entityEnterFrameSet=new Set;_systemContexts=new WeakMap;_pendingFinalizers=[];_batchingRegistrations=!1;constructor(){this._entityManager=new E,this._eventBus=new w,this._resourceManager=new R,this._reactiveQueryManager=new S(this._entityManager),this._commandBuffer=new x,this._subscribeLifecycleHooks()}_subscribeLifecycleHooks(){this._entityManager.onAfterComponentAdded((j,$)=>{this._entityManager.markChanged(j,$);let L=this._requiredComponents.get($);if(L){let J=this._entityManager.getEntity(j);if(J){let X=J.components[$];for(let{component:Y,factory:Z}of L){if(this._entityManager._pendingBatchKeys?.has(Y))continue;if(!(Y in J.components))this._entityManager.addComponent(j,Y,Z(X))}}}}),this._entityManager.onAfterEntityMutated((j)=>{let $=this._entityManager.getEntity(j);if($)this._reactiveQueryManager.recheckEntityAndChildren($)}),this._entityManager.onAfterComponentRemoved((j,$)=>{let L=this._entityManager.getEntity(j);if(L)this._reactiveQueryManager.onComponentRemoved(L,$)}),this._entityManager.onBeforeEntityRemoved((j)=>{this._reactiveQueryManager.onEntityRemoved(j)}),this._entityManager.onAfterParentChanged((j)=>{if(this._reactiveQueryManager.hasParentHasQueries){let $=this._entityManager.getEntity(j);if($)this._reactiveQueryManager.recheckEntity($)}})}static create(){return new N}addSystem(j){let $=new T(j);return this._pendingFinalizers.push(()=>{this._registerSystem($._createSystemObject())}),$}_finalizePendingBuilders(){if(this._pendingFinalizers.length===0)return;this._batchingRegistrations=!0;while(this._pendingFinalizers.length>0){let j=this._pendingFinalizers;this._pendingFinalizers=[];for(let $ of j)$()}this._batchingRegistrations=!1,this._rebuildPhaseSystems()}update(j){this._finalizePendingBuilders();let $=this._screenManager?.getCurrentScreen()??null,L=this._diagnosticsEnabled;this._runPhase("preUpdate",j,$,L);let J=L?performance.now():0;this._fixedAccumulator+=j;let X=0;while(this._fixedAccumulator>=this._fixedDt&&X<this._maxFixedSteps)this._executePhase(this._phaseSystems.fixedUpdate,this._fixedDt,$),this._commandBuffer.playback(this),this._fixedAccumulator-=this._fixedDt,X++;if(this._fixedAccumulator>=this._fixedDt)this._fixedAccumulator=0;if(L)this._phaseTimings.fixedUpdate=performance.now()-J;this._interpolationAlpha=this._fixedAccumulator/this._fixedDt,this._runPhase("update",j,$,L),this._runPhase("postUpdate",j,$,L);for(let Y of this._postUpdateHooks)Y({ecs:this,dt:j});this._runPhase("render",j,$,L),this._changeThreshold=this._entityManager.changeSeq,this._currentTick++}_executePhase(j,$,L){for(let J of j){if(!J.process&&!J.onEntityEnter)continue;if(J.groups?.length){let W=!1;for(let F of J.groups)if(this._disabledGroups.has(F)){W=!0;break}if(W)continue}if(J.inScreens?.length){if(L===null||!J.inScreens.includes(L))continue}if(J.excludeScreens?.length){if(L!==null&&J.excludeScreens.includes(L))continue}if(J.requiredAssets?.length&&this._assetManager){let W=!0;for(let F of J.requiredAssets)if(!this._assetManager.isLoaded(F)){W=!1;break}if(!W)continue}let X=this._systemLastSeqs.get(J)??0;this._changeThreshold=X;let Y=this._systemContexts.get(J);if(!Y)Y={queries:{},dt:0,ecs:this},this._systemContexts.set(J,Y);Y.dt=$;let Z=Y.queries,B=!1,K=!1;if(J.entityQueries)for(let W in J.entityQueries){K=!0;let F=J.entityQueries[W];if(F){if(Z[W]=this._entityManager.getEntitiesWithQuery(F.with,F.without||[],F.changed,F.changed?this._changeThreshold:void 0,F.parentHas),Z[W].length)B=!0}}let U=this._entityEnterTracking.get(J);if(U&&J.onEntityEnter)for(let W in J.onEntityEnter){let F=Z[W],D=U.get(W);if(!F||!D)continue;let _=J.onEntityEnter[W];if(!_)continue;let Q=this._entityEnterFrameSet;Q.clear();for(let A of F)if(Q.add(A.id),!D.has(A.id))D.add(A.id),_({entity:A,ecs:this});for(let A of D)if(!Q.has(A))D.delete(A)}if(J.process){if(this._diagnosticsEnabled){let W=performance.now();if(B||J.runWhenEmpty)J.process(Y);else if(!K)J.process(Y);this._systemTimings.set(J.label,performance.now()-W)}else if(B||J.runWhenEmpty)J.process(Y);else if(!K)J.process(Y)}this._systemLastSeqs.set(J,this._entityManager.changeSeq)}}_runPhase(j,$,L,J){if(J){let X=performance.now();this._executePhase(this._phaseSystems[j],$,L),this._phaseTimings[j]=performance.now()-X}else this._executePhase(this._phaseSystems[j],$,L);this._commandBuffer.playback(this)}async initialize(){if(this._finalizePendingBuilders(),await this.initializeResources(),this._assetManager)this._assetManager.setEventBus(this._eventBus),await this._assetManager.loadEagerAssets(),this._resourceManager.add("$assets",this._assetManager.createResource());if(this._screenManager)this._screenManager.setDependencies(this._eventBus,this._assetManager,this),this._resourceManager.add("$screen",this._screenManager.createResource());for(let j of this._systems)await j.onInitialize?.(this)}async initializeResources(...j){await this._resourceManager.initializeResources(this,...j)}_rebuildPhaseSystems(){for(let j of d)this._phaseSystems[j]=[];for(let j of this._systems){let $=j.phase??"update";this._phaseSystems[$].push(j)}for(let j of d)this._phaseSystems[j].sort(($,L)=>{let J=$.priority??0;return(L.priority??0)-J})}updateSystemPriority(j,$){this._finalizePendingBuilders();let L=this._systems.find((J)=>J.label===j);if(!L)return!1;return L.priority=$,this._rebuildPhaseSystems(),!0}updateSystemPhase(j,$){this._finalizePendingBuilders();let L=this._systems.find((J)=>J.label===j);if(!L)return!1;return L.phase=$,this._rebuildPhaseSystems(),!0}get interpolationAlpha(){return this._interpolationAlpha}get fixedDt(){return this._fixedDt}disableSystemGroup(j){this._disabledGroups.add(j)}enableSystemGroup(j){this._disabledGroups.delete(j)}isSystemGroupEnabled(j){return!this._disabledGroups.has(j)}getSystemsInGroup(j){return this._finalizePendingBuilders(),this._systems.filter(($)=>$.groups?.includes(j)).map(($)=>$.label)}removeSystem(j){this._finalizePendingBuilders();let $=this._systems.findIndex((J)=>J.label===j);if($===-1)return!1;let L=this._systems[$];if(!L)return!1;if(L.onDetach)L.onDetach(this);return this._systems.splice($,1),this._systemLastSeqs.delete(L),this._entityEnterTracking.delete(L),this._rebuildPhaseSystems(),!0}_registerSystem(j){if(this._systems.push(j),this._systemLastSeqs.set(j,this._changeThreshold),!this._batchingRegistrations)this._rebuildPhaseSystems();if(j.onEntityEnter){let $=new Map;for(let L in j.onEntityEnter)$.set(L,new Set);this._entityEnterTracking.set(j,$)}if(!j.eventHandlers)return;for(let $ in j.eventHandlers){let L=j.eventHandlers[$];if(L)this._eventBus.subscribe($,(J)=>{L({data:J,ecs:this})})}}hasResource(j){return this._resourceManager.has(j)}getResource(j){if(!this._resourceManager.has(j))throw Error(`Resource '${String(j)}' not found. Available resources: [${this.getResourceKeys().map(($)=>String($)).join(", ")}]`);return this._resourceManager.get(j,this)}tryGetResource(j){let $=j;if(!this._resourceManager.has($))return;return this._resourceManager.get($,this)}addResource(j,$){return this._resourceManager.add(j,$),this}removeResource(j){return this._resourceManager.remove(j)}async disposeResource(j){return this._resourceManager.disposeResource(j,this)}async disposeResources(){return this._resourceManager.disposeResources(this)}updateResource(j,$){let L=this.getResource(j),J=$(L);return this._resourceManager.add(j,J),this}getResourceKeys(){return this._resourceManager.getKeys()}resourceNeedsInitialization(j){return this._resourceManager.needsInitialization(j)}getComponent(j,$){return this._entityManager.getComponent(j,$)}addComponent(j,$,L){this._entityManager.addComponent(j,$,L)}addComponents(j,$){this._entityManager.addComponents(j,$)}removeComponent(j,$){this._entityManager.removeComponent(j,$)}hasComponent(j,$){return this._entityManager.getComponent(j,$)!==void 0}spawn(j){let $=this._entityManager.createEntity();return this._entityManager.addComponents($.id,j),$}getEntitiesWithQuery(j,$=[],L,J){return this._entityManager.getEntitiesWithQuery(j,$,L,L?this._changeThreshold:void 0,J)}getSingleton(j,$=[]){let L=this._entityManager.getEntitiesWithQuery(j,$);if(L.length===0)throw Error(`getSingleton: no entity matches query with=[${String(j)}] without=[${String($)}]`);if(L.length>1)throw Error(`getSingleton: expected 1 entity but found ${L.length} matching query with=[${String(j)}] without=[${String($)}]`);let J=L[0];if(!J)throw Error("getSingleton: unexpected empty result");return J}tryGetSingleton(j,$=[]){let L=this._entityManager.getEntitiesWithQuery(j,$);if(L.length===0)return;if(L.length>1)throw Error(`tryGetSingleton: expected 0 or 1 entity but found ${L.length} matching query with=[${String(j)}] without=[${String($)}]`);return L[0]}removeEntity(j,$){return this._entityManager.removeEntity(j,$)}spawnChild(j,$){let L=this._entityManager.spawnChild(j,$);return this._emitHierarchyChanged(L.id,null,j),L}setParent(j,$){let L=this._entityManager.getParent(j);return this._entityManager.setParent(j,$),this._emitHierarchyChanged(j,L,$),this}removeParent(j){let $=this._entityManager.getParent(j),L=this._entityManager.removeParent(j);if(L)this._emitHierarchyChanged(j,$,null);return L}getParent(j){return this._entityManager.getParent(j)}getChildren(j){return this._entityManager.getChildren(j)}getChildAt(j,$){return this._entityManager.getChildAt(j,$)}getChildIndex(j,$){return this._entityManager.getChildIndex(j,$)}getAncestors(j){return this._entityManager.getAncestors(j)}getDescendants(j){return this._entityManager.getDescendants(j)}getRoot(j){return this._entityManager.getRoot(j)}getSiblings(j){return this._entityManager.getSiblings(j)}isDescendantOf(j,$){return this._entityManager.isDescendantOf(j,$)}isAncestorOf(j,$){return this._entityManager.isAncestorOf(j,$)}getRootEntities(){return this._entityManager.getRootEntities()}forEachInHierarchy(j,$){this._entityManager.forEachInHierarchy(j,$)}hierarchyIterator(j){return this._entityManager.hierarchyIterator(j)}_emitHierarchyChanged(j,$,L){this._eventBus.publish("hierarchyChanged",{entityId:j,oldParent:$,newParent:L})}get installedPlugins(){return Array.from(this._installedPlugins)}get entityManager(){return this._entityManager}get eventBus(){return this._finalizePendingBuilders(),this._eventBus}get commands(){return this._commandBuffer}get currentTick(){return this._currentTick}get changeThreshold(){return this._changeThreshold}enableDiagnostics(j){if(this._diagnosticsEnabled=j,!j)this._systemTimings.clear(),this._phaseTimings={preUpdate:0,fixedUpdate:0,update:0,postUpdate:0,render:0}}get diagnosticsEnabled(){return this._diagnosticsEnabled}get systemTimings(){return this._systemTimings}get phaseTimings(){return this._phaseTimings}get entityCount(){return this._entityManager.entityCount}mutateComponent(j,$,L){let J=this._entityManager.getComponent(j,$);if(J===void 0)throw Error(`Entity ${j} does not have component "${String($)}"`);return L(J),this._entityManager.markChanged(j,$),J}markChanged(j,$){this._entityManager.markChanged(j,$)}registerDispose(j,$){this._entityManager.registerDispose(j,$)}registerRequired(j,$,L){if(String(j)===String($))throw Error(`Cannot require a component to depend on itself: '${String(j)}'`);let J=this._requiredComponents.get(j)??[];if(J.some((X)=>X.component===$))throw Error(`Required component '${String($)}' already registered for trigger '${String(j)}'`);this._checkRequiredCycle(j,$),J.push({component:$,factory:L}),this._requiredComponents.set(j,J)}_checkRequiredCycle(j,$){I(j,$,(L)=>this._requiredComponents.get(L))}onComponentAdded(j,$){return this._entityManager.onComponentAdded(j,$)}onComponentRemoved(j,$){return this._entityManager.onComponentRemoved(j,$)}addReactiveQuery(j,$){this._reactiveQueryManager.addQuery(j,$)}removeReactiveQuery(j){return this._reactiveQueryManager.removeQuery(j)}on(j,$){return this._eventBus.subscribe(j,$)}off(j,$){return this._eventBus.unsubscribe(j,$)}onPostUpdate(j){return this._postUpdateHooks.push(j),()=>{let $=this._postUpdateHooks.indexOf(j);if($!==-1)this._postUpdateHooks.splice($,1)}}requireAssetManager(){if(!this._assetManager)throw Error("Asset manager not configured. Use withAssets() in builder.");return this._assetManager}getAsset(j){return this.requireAssetManager().get(j)}tryGetAsset(j){return this._assetManager?.tryGet(j)}getAssetHandle(j){return this.requireAssetManager().getHandle(j)}isAssetLoaded(j){return this._assetManager?.isLoaded(j)??!1}async loadAsset(j){return this.requireAssetManager().loadAsset(j)}async loadAssetGroup(j){return this.requireAssetManager().loadAssetGroup(j)}isAssetGroupLoaded(j){return this._assetManager?.isGroupLoaded(j)??!1}getAssetGroupProgress(j){return this._assetManager?.getGroupProgress(j)??0}requireScreenManager(){if(!this._screenManager)throw Error("Screen manager not configured. Use withScreens() in builder.");return this._screenManager}async setScreen(j,$){return this.requireScreenManager().setScreen(j,$)}async pushScreen(j,$){return this.requireScreenManager().pushScreen(j,$)}async popScreen(){return this.requireScreenManager().popScreen()}getCurrentScreen(){return this._screenManager?.getCurrentScreen()??null}getScreenConfig(j){return this.requireScreenManager().getConfig(j)}tryGetScreenConfig(j){return this._screenManager?.tryGetConfig(j)??void 0}getScreenState(j){return this.requireScreenManager().getState(j)}tryGetScreenState(j){return this._screenManager?.tryGetState(j)??void 0}updateScreenState(j,$){if(typeof j==="string")this.requireScreenManager().updateState($,j);else this.requireScreenManager().updateState(j)}isCurrentScreen(j){return this._screenManager?.isCurrent(j)??!1}isScreenActive(j){return this._screenManager?.isActive(j)??!1}getScreenStackDepth(){return this._screenManager?.getStackDepth()??0}_setAssetManager(j){this._assetManager=j;for(let[$,L]of this._pendingPluginAssets)this._assetManager.register($,L);this._pendingPluginAssets=[]}_setScreenManager(j){this._screenManager=j;for(let[$,L]of this._pendingPluginScreens)this._screenManager.register($,L);this._pendingPluginScreens=[]}_hasPendingPluginAssets(){return this._pendingPluginAssets.length>0}_hasPendingPluginScreens(){return this._pendingPluginScreens.length>0}_setFixedDt(j){this._fixedDt=j}_registerAsset(j,$){this._pendingPluginAssets.push([j,$])}_registerScreen(j,$){this._pendingPluginScreens.push([j,$])}installPlugin(j){if(this._installedPlugins.has(j.id))return this;return this._installedPlugins.add(j.id),j.install(this),this}pluginFactory(){return z}getHelpers(j){return j(this)}}});g();function kj(j){return j}function uj(j,$){return{x:j,y:$}}function Ij(){return{x:0,y:0}}function pj(j,$){return{x:j.x+$.x,y:j.y+$.y}}function mj(j,$){return{x:j.x-$.x,y:j.y-$.y}}function lj(j,$){return{x:j.x*$,y:j.y*$}}function sj(j){return{x:-j.x,y:-j.y}}function yj(j,$){return j.x*$.x+j.y*$.y}function cj(j,$){return j.x*$.y-j.y*$.x}function dj(j){return j.x*j.x+j.y*j.y}function ij(j){return Math.sqrt(j.x*j.x+j.y*j.y)}function oj(j){let $=Math.sqrt(j.x*j.x+j.y*j.y);if($===0)return{x:0,y:0};return{x:j.x/$,y:j.y/$}}function rj(j,$){let L=j.x-$.x,J=j.y-$.y;return L*L+J*J}function aj(j,$){let L=j.x-$.x,J=j.y-$.y;return Math.sqrt(L*L+J*J)}function tj(j,$,L=0.0000000001){return Math.abs(j.x-$.x)<=L&&Math.abs(j.y-$.y)<=L}v();var B2=M;export{Ij as vec2Zero,mj as vec2Sub,lj as vec2Scale,oj as vec2Normalize,sj as vec2Negate,dj as vec2LengthSq,ij as vec2Length,tj as vec2Equals,yj as vec2Dot,rj as vec2DistanceSq,aj as vec2Distance,cj as vec2Cross,pj as vec2Add,uj as vec2,$j as directValue,z as definePlugin,B2 as default,b as createScreenConfigurator,kj as createQueryDefinition,f as createAssetConfigurator,T as SystemBuilder,P as ScreenManager,O as AssetManager};
2
-
3
- //# debugId=A4AFCA1F564A5FB464756E2164756E21
4
- //# sourceMappingURL=index.js.map
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes