ecspresso 0.10.2 → 0.12.0
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.
- package/README.md +256 -148
- package/dist/asset-manager.d.ts +16 -16
- package/dist/asset-types.d.ts +18 -16
- package/dist/command-buffer.d.ts +30 -20
- package/dist/ecspresso-builder.d.ts +193 -0
- package/dist/ecspresso.d.ts +323 -209
- package/dist/entity-manager.d.ts +76 -30
- package/dist/event-bus.d.ts +6 -1
- package/dist/index.d.ts +6 -13
- package/dist/plugin.d.ts +61 -0
- package/dist/plugins/audio.d.ts +273 -0
- package/dist/{bundles/utils → plugins}/bounds.d.ts +20 -26
- package/dist/plugins/camera.d.ts +88 -0
- package/dist/plugins/collision.d.ts +285 -0
- package/dist/plugins/coroutine.d.ts +126 -0
- package/dist/plugins/diagnostics.d.ts +49 -0
- package/dist/{bundles/utils → plugins}/input.d.ts +22 -29
- package/dist/plugins/particles.d.ts +225 -0
- package/dist/plugins/physics2D.d.ts +163 -0
- package/dist/plugins/renderers/renderer2D.d.ts +262 -0
- package/dist/plugins/spatial-index.d.ts +58 -0
- package/dist/plugins/sprite-animation.d.ts +150 -0
- package/dist/plugins/state-machine.d.ts +244 -0
- package/dist/plugins/timers.d.ts +151 -0
- package/dist/{bundles/utils → plugins}/transform.d.ts +21 -22
- package/dist/plugins/tween.d.ts +162 -0
- package/dist/reactive-query-manager.d.ts +14 -3
- package/dist/resource-manager.d.ts +64 -23
- package/dist/screen-manager.d.ts +21 -15
- package/dist/screen-types.d.ts +15 -11
- package/dist/src/index.js +4 -0
- package/dist/src/index.js.map +25 -0
- package/dist/src/plugins/audio.js +4 -0
- package/dist/src/plugins/audio.js.map +10 -0
- package/dist/src/plugins/bounds.js +4 -0
- package/dist/src/plugins/bounds.js.map +10 -0
- package/dist/src/plugins/camera.js +4 -0
- package/dist/src/plugins/camera.js.map +10 -0
- package/dist/src/plugins/collision.js +4 -0
- package/dist/src/plugins/collision.js.map +11 -0
- package/dist/src/plugins/coroutine.js +4 -0
- package/dist/src/plugins/coroutine.js.map +10 -0
- package/dist/src/plugins/diagnostics.js +5 -0
- package/dist/src/plugins/diagnostics.js.map +10 -0
- package/dist/src/plugins/input.js +4 -0
- package/dist/src/plugins/input.js.map +10 -0
- package/dist/src/plugins/particles.js +4 -0
- package/dist/src/plugins/particles.js.map +10 -0
- package/dist/src/plugins/physics2D.js +4 -0
- package/dist/src/plugins/physics2D.js.map +11 -0
- package/dist/src/plugins/renderers/renderer2D.js +4 -0
- package/dist/src/plugins/renderers/renderer2D.js.map +10 -0
- package/dist/src/plugins/spatial-index.js +4 -0
- package/dist/src/plugins/spatial-index.js.map +11 -0
- package/dist/src/plugins/sprite-animation.js +4 -0
- package/dist/src/plugins/sprite-animation.js.map +10 -0
- package/dist/src/plugins/state-machine.js +4 -0
- package/dist/src/plugins/state-machine.js.map +10 -0
- package/dist/src/plugins/timers.js +4 -0
- package/dist/src/plugins/timers.js.map +10 -0
- package/dist/src/plugins/transform.js +4 -0
- package/dist/src/plugins/transform.js.map +10 -0
- package/dist/src/plugins/tween.js +4 -0
- package/dist/src/plugins/tween.js.map +11 -0
- package/dist/system-builder.d.ts +75 -112
- package/dist/type-utils.d.ts +247 -7
- package/dist/types.d.ts +58 -39
- package/dist/utils/check-required-cycle.d.ts +12 -0
- package/dist/utils/easing.d.ts +71 -0
- package/dist/utils/math.d.ts +67 -0
- package/dist/utils/narrowphase.d.ts +63 -0
- package/dist/utils/spatial-hash.d.ts +53 -0
- package/package.json +65 -27
- package/dist/bundle.d.ts +0 -123
- package/dist/bundles/renderers/renderer2D.d.ts +0 -220
- package/dist/bundles/renderers/renderer2D.js +0 -4
- package/dist/bundles/renderers/renderer2D.js.map +0 -10
- package/dist/bundles/utils/bounds.js +0 -4
- package/dist/bundles/utils/bounds.js.map +0 -10
- package/dist/bundles/utils/collision.d.ts +0 -204
- package/dist/bundles/utils/collision.js +0 -4
- package/dist/bundles/utils/collision.js.map +0 -10
- package/dist/bundles/utils/input.js +0 -4
- package/dist/bundles/utils/input.js.map +0 -10
- package/dist/bundles/utils/movement.d.ts +0 -86
- package/dist/bundles/utils/movement.js +0 -4
- package/dist/bundles/utils/movement.js.map +0 -10
- package/dist/bundles/utils/timers.d.ts +0 -172
- package/dist/bundles/utils/timers.js +0 -4
- package/dist/bundles/utils/timers.js.map +0 -10
- package/dist/bundles/utils/transform.js +0 -4
- package/dist/bundles/utils/transform.js.map +0 -10
- package/dist/index.js +0 -4
- package/dist/index.js.map +0 -22
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/plugins/transform.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Transform Plugin for ECSpresso\n *\n * Provides hierarchical transform propagation following Bevy's Transform/GlobalTransform pattern.\n * LocalTransform is modified by user code; WorldTransform is computed automatically.\n *\n * @see https://docs.rs/bevy/latest/bevy/transform/components/struct.GlobalTransform.html\n */\n\nimport { definePlugin, type Plugin, type BasePluginOptions } from 'ecspresso';\nimport type ECSpresso from 'ecspresso';\nimport type { WorldConfigFrom, EmptyConfig } from '../type-utils';\n\n// ==================== Component Types ====================\n\n/**\n * Local transform relative to parent (or world if no parent).\n * This is the transform you modify directly.\n */\nexport interface LocalTransform {\n\tx: number;\n\ty: number;\n\trotation: number;\n\tscaleX: number;\n\tscaleY: number;\n}\n\n/**\n * Computed world transform (accumulated from parent chain).\n * Read-only - managed by the transform propagation system.\n */\nexport interface WorldTransform {\n\tx: number;\n\ty: number;\n\trotation: number;\n\tscaleX: number;\n\tscaleY: number;\n}\n\n/**\n * Component types provided by the transform plugin.\n * Included automatically via `.withPlugin(createTransformPlugin())`.\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso.create()\n * .withPlugin(createTransformPlugin())\n * .withComponentTypes<{ sprite: Sprite; velocity: { x: number; y: number } }>()\n * .build();\n * ```\n */\nexport interface TransformComponentTypes {\n\tlocalTransform: LocalTransform;\n\tworldTransform: WorldTransform;\n}\n\n/**\n * WorldConfig representing the transform plugin's provided components.\n * Used as the `Requires` type parameter by plugins that depend on transform.\n */\nexport type TransformWorldConfig = WorldConfigFrom<TransformComponentTypes>;\n\n// ==================== Plugin Options ====================\n\n/**\n * Configuration options for the transform plugin.\n */\nexport interface TransformPluginOptions<G extends string = 'transform'> extends BasePluginOptions<G> {}\n\n// ==================== Default Values ====================\n\n/**\n * Default local transform values.\n */\nexport const DEFAULT_LOCAL_TRANSFORM: Readonly<LocalTransform> = {\n\tx: 0,\n\ty: 0,\n\trotation: 0,\n\tscaleX: 1,\n\tscaleY: 1,\n};\n\n/**\n * Default world transform values.\n */\nexport const DEFAULT_WORLD_TRANSFORM: Readonly<WorldTransform> = {\n\tx: 0,\n\ty: 0,\n\trotation: 0,\n\tscaleX: 1,\n\tscaleY: 1,\n};\n\n// ==================== Helper Functions ====================\n\n/**\n * Create a local transform component with position only.\n * Uses default rotation (0) and scale (1, 1).\n *\n * @param x The x coordinate\n * @param y The y coordinate\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createLocalTransform(100, 200),\n * sprite,\n * });\n * ```\n */\nexport function createLocalTransform(x: number, y: number): Pick<TransformComponentTypes, 'localTransform'> {\n\treturn {\n\t\tlocalTransform: {\n\t\t\tx,\n\t\t\ty,\n\t\t\trotation: 0,\n\t\t\tscaleX: 1,\n\t\t\tscaleY: 1,\n\t\t},\n\t};\n}\n\n/**\n * Create a world transform component with position only.\n * Typically used alongside createLocalTransform for initial state.\n *\n * @param x The x coordinate\n * @param y The y coordinate\n * @returns Component object suitable for spreading into spawn()\n */\nexport function createWorldTransform(x: number, y: number): Pick<TransformComponentTypes, 'worldTransform'> {\n\treturn {\n\t\tworldTransform: {\n\t\t\tx,\n\t\t\ty,\n\t\t\trotation: 0,\n\t\t\tscaleX: 1,\n\t\t\tscaleY: 1,\n\t\t},\n\t};\n}\n\n/**\n * Options for creating a full transform.\n */\nexport interface TransformOptions {\n\trotation?: number;\n\tscaleX?: number;\n\tscaleY?: number;\n\t/** Uniform scale (overrides scaleX/scaleY if provided) */\n\tscale?: number;\n}\n\n/**\n * Create both local and world transform components.\n * World transform is initialized to match local transform.\n *\n * @param x The x coordinate\n * @param y The y coordinate\n * @param options Optional rotation and scale\n * @returns Component object suitable for spreading into spawn()\n *\n * @example\n * ```typescript\n * ecs.spawn({\n * ...createTransform(100, 200),\n * sprite,\n * });\n *\n * // With rotation and scale\n * ecs.spawn({\n * ...createTransform(100, 200, { rotation: Math.PI / 4, scale: 2 }),\n * sprite,\n * });\n * ```\n */\nexport function createTransform(\n\tx: number,\n\ty: number,\n\toptions?: TransformOptions\n): TransformComponentTypes {\n\tconst scaleX = options?.scale ?? options?.scaleX ?? 1;\n\tconst scaleY = options?.scale ?? options?.scaleY ?? 1;\n\tconst rotation = options?.rotation ?? 0;\n\n\tconst transform = {\n\t\tx,\n\t\ty,\n\t\trotation,\n\t\tscaleX,\n\t\tscaleY,\n\t};\n\n\treturn {\n\t\tlocalTransform: { ...transform },\n\t\tworldTransform: { ...transform },\n\t};\n}\n\n// ==================== Plugin Factory ====================\n\n/**\n * Create a transform plugin for ECSpresso.\n *\n * This plugin provides:\n * - Transform propagation system that computes world transforms from local transforms\n * - Parent-first traversal ensures parents are processed before children\n * - Supports full transform hierarchy (position, rotation, scale)\n *\n * @example\n * ```typescript\n * const ecs = ECSpresso\n * .create<Components, Events, Resources>()\n * .withPlugin(createTransformPlugin())\n * .withPlugin(createPhysics2DPlugin())\n * .build();\n *\n * // Spawn entity with transform\n * ecs.spawn({\n * ...createTransform(100, 200),\n * velocity: { x: 50, y: 0 },\n * });\n * ```\n */\nexport function createTransformPlugin<G extends string = 'transform'>(\n\toptions?: TransformPluginOptions<G>\n): Plugin<WorldConfigFrom<TransformComponentTypes>, EmptyConfig, 'transform-propagation', G> {\n\tconst {\n\t\tsystemGroup = 'transform',\n\t\tpriority = 500,\n\t\tphase = 'postUpdate',\n\t} = options ?? {};\n\n\treturn definePlugin<WorldConfigFrom<TransformComponentTypes>, EmptyConfig, 'transform-propagation', G>({\n\t\tid: 'transform',\n\t\tinstall(world) {\n\t\t\t// localTransform requires worldTransform — initialize from localTransform values\n\t\t\tworld.registerRequired('localTransform', 'worldTransform', (lt) => ({\n\t\t\t\tx: lt.x, y: lt.y, rotation: lt.rotation, scaleX: lt.scaleX, scaleY: lt.scaleY,\n\t\t\t}));\n\n\t\t\tworld\n\t\t\t\t.addSystem('transform-propagation')\n\t\t\t\t.setPriority(priority)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.setProcess(({ ecs }) => {\n\t\t\t\t\tpropagateTransforms(ecs);\n\t\t\t\t});\n\t\t},\n\t});\n}\n\n/**\n * Propagate transforms through the hierarchy.\n * Parent-first traversal ensures parents are computed before children.\n *\n * Runs unconditionally for all entities with transforms — user code can\n * freely mutate localTransform without needing to call markChanged.\n * Marks worldTransform as changed so downstream systems (e.g. renderer\n * sync) pick up the updated values.\n */\nfunction propagateTransforms(ecs: ECSpresso<WorldConfigFrom<TransformComponentTypes>>): void {\n\tconst em = ecs.entityManager;\n\n\t// Use parent-first traversal for entities in hierarchy\n\tecs.forEachInHierarchy((entityId, parentId) => {\n\t\tconst localTransform = em.getComponent(entityId, 'localTransform');\n\t\tconst worldTransform = em.getComponent(entityId, 'worldTransform');\n\n\t\tif (!localTransform || !worldTransform) return;\n\n\t\tif (parentId === null) {\n\t\t\t// Root entity: world transform equals local transform\n\t\t\tcopyTransform(localTransform, worldTransform);\n\t\t} else {\n\t\t\t// Child entity: combine with parent's world transform\n\t\t\tconst parentWorld = em.getComponent(parentId, 'worldTransform');\n\t\t\tif (parentWorld) {\n\t\t\t\tcombineTransforms(parentWorld, localTransform, worldTransform);\n\t\t\t} else {\n\t\t\t\t// Parent has no world transform, treat as root\n\t\t\t\tcopyTransform(localTransform, worldTransform);\n\t\t\t}\n\t\t}\n\n\t\tecs.markChanged(entityId, 'worldTransform');\n\t});\n\n\t// Process orphaned entities (not in hierarchy but have transforms)\n\tconst orphanedEntities = ecs.getEntitiesWithQuery(['localTransform', 'worldTransform']);\n\tfor (const entity of orphanedEntities) {\n\t\tconst parentId = ecs.getParent(entity.id);\n\t\t// Only process if truly orphaned (no parent and not a root with children)\n\t\tif (parentId === null && ecs.getChildren(entity.id).length === 0) {\n\t\t\tconst { localTransform, worldTransform } = entity.components;\n\t\t\tcopyTransform(localTransform, worldTransform);\n\t\t\tecs.markChanged(entity.id, 'worldTransform');\n\t\t}\n\t}\n}\n\n/**\n * Copy transform values from source to destination.\n */\nfunction copyTransform(src: LocalTransform, dest: WorldTransform): void {\n\tdest.x = src.x;\n\tdest.y = src.y;\n\tdest.rotation = src.rotation;\n\tdest.scaleX = src.scaleX;\n\tdest.scaleY = src.scaleY;\n}\n\n/**\n * Combine parent world transform with child local transform into child world transform.\n */\nfunction combineTransforms(\n\tparent: WorldTransform,\n\tlocal: LocalTransform,\n\tworld: WorldTransform\n): void {\n\t// Apply parent's scale to local position\n\tconst scaledLocalX = local.x * parent.scaleX;\n\tconst scaledLocalY = local.y * parent.scaleY;\n\n\t// Rotate local position by parent's rotation\n\tconst cos = Math.cos(parent.rotation);\n\tconst sin = Math.sin(parent.rotation);\n\tconst rotatedX = scaledLocalX * cos - scaledLocalY * sin;\n\tconst rotatedY = scaledLocalX * sin + scaledLocalY * cos;\n\n\t// Add to parent's position\n\tworld.x = parent.x + rotatedX;\n\tworld.y = parent.y + rotatedY;\n\tworld.rotation = parent.rotation + local.rotation;\n\tworld.scaleX = parent.scaleX * local.scaleX;\n\tworld.scaleY = parent.scaleY * local.scaleY;\n}\n"
|
|
6
|
+
],
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "C63DD170F03CB2AB64756E2164756E21",
|
|
9
|
+
"names": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
var{defineProperty:$,getOwnPropertyNames:b,getOwnPropertyDescriptor:h}=Object,E=Object.prototype.hasOwnProperty;function K(z){return this[z]}var C=(z)=>{var D=(q??=new WeakMap).get(z),H;if(D)return D;if(D=$({},"__esModule",{value:!0}),z&&typeof z==="object"||typeof z==="function"){for(var J of b(z))if(!E.call(D,J))$(D,J,{get:K.bind(z,J),enumerable:!(H=h(z,J))||H.enumerable})}return q.set(z,D),D},q;var T=(z)=>z;function A(z,D){this[z]=T.bind(null,D)}var d=(z,D)=>{for(var H in D)$(z,H,{get:D[H],enumerable:!0,configurable:!0,set:A.bind(D,H)})};var m=(z,D)=>()=>(z&&(D=z(z=0)),D);var l=((z)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(z,{get:(D,H)=>(typeof require<"u"?require:D)[H]}):z)(function(z){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+z+'" is not supported')});import{definePlugin as x}from"ecspresso";function G(z){return z}var P=1.70158,c=P*1.525,w=P+1;var r=2*Math.PI/3,i=2*Math.PI/4.5;function _(z,D,H,J,M){let{from:N,easing:U=G,loop:W="once",loops:Y=1,onComplete:X}=M??{};return{tween:{steps:[{targets:[{component:z,path:D.split("."),from:N??null,to:H}],duration:J,easing:U}],currentStep:0,elapsed:0,loop:W,totalLoops:Y,completedLoops:0,direction:1,state:"pending",onComplete:X,justFinished:!1}}}function B(z,D){let{loop:H="once",loops:J=1,onComplete:M}=D??{};return{tween:{steps:z.map((N)=>({targets:N.targets.map((U)=>({component:U.component,path:U.field.split("."),from:U.from??null,to:U.to})),duration:N.duration,easing:N.easing??G})),currentStep:0,elapsed:0,loop:H,totalLoops:J,completedLoops:0,direction:1,state:"pending",onComplete:M,justFinished:!1}}}function s(z){return{createTween:_,createTweenSequence:B}}var V={parent:{},key:""};function j(z,D){let H=D.length-1,J=z;for(let N=0;N<H;N++){let U=D[N];if(U===void 0)return null;let W=J[U];if(W===null||W===void 0||typeof W!=="object")return null;J=W}let M=D[H];if(M===void 0)return null;if(!(M in J))return null;return V.parent=J,V.key=M,V}function F(z,D){let H=j(z,D);if(!H)return null;let J=H.parent[H.key];return typeof J==="number"?J:null}function Q(z,D,H){let J=j(z,D);if(!J)return!1;return J.parent[J.key]=H,!0}function O(z,D,H){return z<D?D:z>H?H:z}function v(z,D){for(let H of z.steps)for(let J of H.targets){if(J.from!==null)continue;let M=D[J.component];if(!M||typeof M!=="object")continue;let N=F(M,J.path);if(N!==null)J.from=N;else J.from=0}}function S(z,D,H,J,M){let N=z.easing(D);for(let U of z.targets){let W=H[U.component];if(!W||typeof W!=="object")continue;let Y=U.from??0,X=Y+(U.to-Y)*N;if(Q(W,U.path,X))M.markChanged(J,U.component)}}function k(z,D,H,J){for(let M of z.targets){let N=D[M.component];if(!N||typeof N!=="object")continue;if(Q(N,M.path,M.to))J.markChanged(H,M.component)}}function f(z){for(let D of z.steps)for(let H of D.targets){let J=H.from??0;H.from=H.to,H.to=J}}function L(z,D,H){z.state="complete",z.justFinished=!0,z.onComplete?.({entityId:D,stepCount:z.steps.length}),H.commands.removeComponent(D,"tween")}function g(z,D,H){if(z.completedLoops++,z.loop==="once")return L(z,D,H),!1;if(z.totalLoops>0&&z.completedLoops>=z.totalLoops)return L(z,D,H),!1;if(z.loop==="yoyo")z.direction=z.direction===1?-1:1,f(z);return z.currentStep=0,z.elapsed>0}function R(z,D,H,J){let M=z.currentStep+1;if(M<z.steps.length){z.currentStep=M;let N=z.steps[M];if(N)for(let U of N.targets){if(U.from!==null)continue;let W=D[U.component];if(!W||typeof W!=="object")continue;let Y=F(W,U.path);U.from=Y??0}return!0}return g(z,H,J)}function I(z,D,H,J){while(!0){let M=z.steps[z.currentStep];if(!M)return;if(M.duration<=0){if(k(M,D,H,J),z.elapsed=0,!R(z,D,H,J))return;continue}if(z.elapsed>=M.duration){k(M,D,H,J);let U=z.elapsed-M.duration;if(z.elapsed=U,!R(z,D,H,J))return;continue}let N=O(z.elapsed/M.duration,0,1);S(M,N,D,H,J);return}}function n(z){let{systemGroup:D="tweens",priority:H=0,phase:J="update"}=z??{};return x({id:"tweens",install(M){M.addSystem("tween-update").setPriority(H).inPhase(J).inGroup(D).addQuery("tweens",{with:["tween"]}).setProcess(({queries:N,dt:U,ecs:W})=>{for(let Y of N.tweens){let X=Y.components.tween,Z=Y.components;if(X.justFinished){X.justFinished=!1;continue}if(X.state==="complete")continue;if(X.state==="pending")v(X,Z),X.state="active";if(!X.steps[X.currentStep])continue;X.elapsed+=U,I(X,Z,Y.id,W)}})}})}export{B as createTweenSequence,n as createTweenPlugin,s as createTweenHelpers,_ as createTween};
|
|
2
|
+
|
|
3
|
+
//# debugId=337A12D65456A62F64756E2164756E21
|
|
4
|
+
//# sourceMappingURL=tween.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/plugins/tween.ts", "../src/utils/easing.ts"],
|
|
4
|
+
"sourcesContent": [
|
|
5
|
+
"/**\n * Tween Plugin for ECSpresso\n *\n * Declarative property animation within the ECS. Tween any numeric component\n * field over time with standard easing functions, sequences, and completion events.\n */\n\nimport { definePlugin, type Plugin, type BasePluginOptions } from 'ecspresso';\nimport type { ComponentsOfWorld, AnyECSpresso } from 'ecspresso';\nimport type { WorldConfigFrom, EmptyConfig } from '../type-utils';\nimport { linear, type EasingFn } from '../utils/easing';\n\n// ==================== Event Types ====================\n\n/**\n * Data structure published when a tween completes.\n * Use this type when defining tween completion events in your EventTypes interface.\n */\nexport interface TweenEventData {\n\t/** The entity ID the tween belongs to */\n\tentityId: number;\n\t/** Number of steps in the tween */\n\tstepCount: number;\n}\n\n\n// ==================== Component Types ====================\n\nexport interface TweenTarget {\n\t/** Component name on the entity */\n\tcomponent: string;\n\t/** Pre-split field path (e.g., ['position', 'x']) */\n\tpath: readonly string[];\n\t/** Starting value. null = resolve from current value on first tick */\n\tfrom: number | null;\n\t/** Target value */\n\tto: number;\n}\n\nexport interface TweenStep {\n\ttargets: TweenTarget[];\n\tduration: number;\n\teasing: EasingFn;\n}\n\nexport interface Tween {\n\tsteps: TweenStep[];\n\tcurrentStep: number;\n\telapsed: number;\n\tloop: LoopMode;\n\ttotalLoops: number;\n\tcompletedLoops: number;\n\tdirection: 1 | -1;\n\tstate: 'pending' | 'active' | 'complete';\n\tonComplete?: (data: TweenEventData) => void;\n\tjustFinished: boolean;\n}\n\nexport type LoopMode = 'once' | 'loop' | 'yoyo';\n\n/**\n * Component types provided by the tween plugin.\n */\nexport interface TweenComponentTypes {\n\ttween: Tween;\n}\n\n// ==================== Plugin Options ====================\n\nexport interface TweenPluginOptions<G extends string = 'tweens'> extends BasePluginOptions<G> {}\n\n// ==================== Helper Functions ====================\n\nexport interface TweenOptions {\n\t/** Explicit starting value (default: captures current value on first tick) */\n\tfrom?: number;\n\t/** Easing function (default: linear) */\n\teasing?: EasingFn;\n\t/** Loop mode (default: 'once') */\n\tloop?: LoopMode;\n\t/** Number of loops. -1 = infinite (default: 1) */\n\tloops?: number;\n\t/** Callback invoked when tween completes */\n\tonComplete?: (data: TweenEventData) => void;\n}\n\n/**\n * Create a single-target tween component.\n *\n * @param component Component name on the entity\n * @param field Field path (dot-separated for nested, e.g. 'position.x')\n * @param to Target value\n * @param duration Duration in seconds\n * @param options Optional configuration\n * @returns Component object suitable for spreading into spawn()\n */\nexport function createTween(\n\tcomponent: string,\n\tfield: string,\n\tto: number,\n\tduration: number,\n\toptions?: TweenOptions,\n): Pick<TweenComponentTypes, 'tween'> {\n\tconst {\n\t\tfrom,\n\t\teasing = linear,\n\t\tloop = 'once',\n\t\tloops = 1,\n\t\tonComplete,\n\t} = options ?? {};\n\n\treturn {\n\t\ttween: {\n\t\t\tsteps: [{\n\t\t\t\ttargets: [{\n\t\t\t\t\tcomponent,\n\t\t\t\t\tpath: field.split('.'),\n\t\t\t\t\tfrom: from ?? null,\n\t\t\t\t\tto,\n\t\t\t\t}],\n\t\t\t\tduration,\n\t\t\t\teasing,\n\t\t\t}],\n\t\t\tcurrentStep: 0,\n\t\t\telapsed: 0,\n\t\t\tloop,\n\t\t\ttotalLoops: loops,\n\t\t\tcompletedLoops: 0,\n\t\t\tdirection: 1,\n\t\t\tstate: 'pending',\n\t\t\tonComplete,\n\t\t\tjustFinished: false,\n\t\t},\n\t};\n}\n\nexport interface TweenSequenceStepInput {\n\ttargets: ReadonlyArray<{\n\t\tcomponent: string;\n\t\tfield: string;\n\t\tto: number;\n\t\tfrom?: number;\n\t}>;\n\tduration: number;\n\teasing?: EasingFn;\n}\n\nexport interface TweenSequenceOptions {\n\t/** Loop mode (default: 'once') */\n\tloop?: LoopMode;\n\t/** Number of loops. -1 = infinite (default: 1) */\n\tloops?: number;\n\t/** Callback invoked when tween completes */\n\tonComplete?: (data: TweenEventData) => void;\n}\n\n/**\n * Create a multi-step tween sequence. Each step can have parallel targets.\n *\n * @param steps Array of step definitions\n * @param options Optional configuration\n * @returns Component object suitable for spreading into spawn()\n */\nexport function createTweenSequence(\n\tsteps: ReadonlyArray<TweenSequenceStepInput>,\n\toptions?: TweenSequenceOptions,\n): Pick<TweenComponentTypes, 'tween'> {\n\tconst {\n\t\tloop = 'once',\n\t\tloops = 1,\n\t\tonComplete,\n\t} = options ?? {};\n\n\treturn {\n\t\ttween: {\n\t\t\tsteps: steps.map((step) => ({\n\t\t\t\ttargets: step.targets.map((target) => ({\n\t\t\t\t\tcomponent: target.component,\n\t\t\t\t\tpath: target.field.split('.'),\n\t\t\t\t\tfrom: target.from ?? null,\n\t\t\t\t\tto: target.to,\n\t\t\t\t})),\n\t\t\t\tduration: step.duration,\n\t\t\t\teasing: step.easing ?? linear,\n\t\t\t})),\n\t\t\tcurrentStep: 0,\n\t\t\telapsed: 0,\n\t\t\tloop,\n\t\t\ttotalLoops: loops,\n\t\t\tcompletedLoops: 0,\n\t\t\tdirection: 1,\n\t\t\tstate: 'pending',\n\t\t\tonComplete,\n\t\t\tjustFinished: false,\n\t\t},\n\t};\n}\n\n// ==================== Kit Types ====================\n\n/**\n * Recursively produce a union of dot-separated paths that resolve to `number`\n * within type T. Depth-limited to 4 levels to prevent TS recursion errors.\n *\n * @example\n * NumericPaths<{ x: number; y: number }> // 'x' | 'y'\n * NumericPaths<{ position: { x: number }; rotation: number }> // 'position.x' | 'rotation'\n */\nexport type NumericPaths<T, Depth extends readonly unknown[] = []> =\n\tDepth['length'] extends 4 ? never :\n\tT extends readonly unknown[] ? never :\n\tT extends Record<string, unknown>\n\t\t? { [K in keyof T & string]:\n\t\t\tNonNullable<T[K]> extends number\n\t\t\t\t? K\n\t\t\t\t: NonNullable<T[K]> extends readonly unknown[]\n\t\t\t\t\t? never\n\t\t\t\t\t: NonNullable<T[K]> extends Record<string, unknown>\n\t\t\t\t\t\t? `${K}.${NumericPaths<NonNullable<T[K]>, [...Depth, unknown]>}`\n\t\t\t\t\t\t: never\n\t\t}[keyof T & string]\n\t\t: never;\n\n/**\n * Discriminated union over component names: each variant constrains `field`\n * to the numeric paths of that component. TS narrows inline object literals\n * by `component` discriminant — zero runtime overhead.\n */\nexport type TypedTweenTargetInput<C extends Record<string, any>> = {\n\t[K in keyof C & string]: {\n\t\tcomponent: K;\n\t\tfield: NumericPaths<C[K]>;\n\t\tto: number;\n\t\tfrom?: number;\n\t}\n}[keyof C & string];\n\nexport interface TypedTweenSequenceStepInput<C extends Record<string, any>> {\n\ttargets: ReadonlyArray<TypedTweenTargetInput<C>>;\n\tduration: number;\n\teasing?: EasingFn;\n}\n\nexport interface TweenHelpers<W extends AnyECSpresso> {\n\tcreateTween: <K extends keyof ComponentsOfWorld<W> & string>(\n\t\tcomponent: K,\n\t\tfield: NumericPaths<ComponentsOfWorld<W>[K]>,\n\t\tto: number,\n\t\tduration: number,\n\t\toptions?: {\n\t\t\tfrom?: number;\n\t\t\teasing?: EasingFn;\n\t\t\tloop?: LoopMode;\n\t\t\tloops?: number;\n\t\t\tonComplete?: (data: TweenEventData) => void;\n\t\t},\n\t) => Pick<TweenComponentTypes, 'tween'>;\n\tcreateTweenSequence: (\n\t\tsteps: ReadonlyArray<TypedTweenSequenceStepInput<ComponentsOfWorld<W>>>,\n\t\toptions?: {\n\t\t\tloop?: LoopMode;\n\t\t\tloops?: number;\n\t\t\tonComplete?: (data: TweenEventData) => void;\n\t\t},\n\t) => Pick<TweenComponentTypes, 'tween'>;\n}\n\nexport function createTweenHelpers<W extends AnyECSpresso>(_world?: W): TweenHelpers<W> {\n\treturn {\n\t\tcreateTween: createTween as TweenHelpers<W>['createTween'],\n\t\tcreateTweenSequence: createTweenSequence as TweenHelpers<W>['createTweenSequence'],\n\t};\n}\n\n// ==================== Field Path Resolution ====================\n\n/**\n * Module-scoped mutable result to avoid per-call allocation in hot path.\n */\nconst _fieldRef: { parent: Record<string, unknown>; key: string } = { parent: {} as Record<string, unknown>, key: '' };\n\n/**\n * Traverse an object by path segments. Returns the parent object and final key\n * for read/write, or null if any segment is missing.\n */\nfunction resolveField(obj: Record<string, unknown>, path: readonly string[]): typeof _fieldRef | null {\n\tconst lastIdx = path.length - 1;\n\tlet current: Record<string, unknown> = obj;\n\n\tfor (let i = 0; i < lastIdx; i++) {\n\t\tconst segment = path[i];\n\t\tif (segment === undefined) return null;\n\t\tconst next = current[segment];\n\t\tif (next === null || next === undefined || typeof next !== 'object') return null;\n\t\tcurrent = next as Record<string, unknown>;\n\t}\n\n\tconst finalKey = path[lastIdx];\n\tif (finalKey === undefined) return null;\n\tif (!(finalKey in current)) return null;\n\n\t_fieldRef.parent = current;\n\t_fieldRef.key = finalKey;\n\treturn _fieldRef;\n}\n\nfunction readField(obj: Record<string, unknown>, path: readonly string[]): number | null {\n\tconst ref = resolveField(obj, path);\n\tif (!ref) return null;\n\tconst val = ref.parent[ref.key];\n\treturn typeof val === 'number' ? val : null;\n}\n\nfunction writeField(obj: Record<string, unknown>, path: readonly string[], value: number): boolean {\n\tconst ref = resolveField(obj, path);\n\tif (!ref) return false;\n\tref.parent[ref.key] = value;\n\treturn true;\n}\n\n// ==================== System Logic ====================\n\nfunction clamp(value: number, min: number, max: number): number {\n\treturn value < min ? min : value > max ? max : value;\n}\n\n/**\n * Resolve all null `from` values by reading current component field values.\n */\nfunction resolveFromValues(\n\ttween: Tween,\n\tentityComponents: Record<string, unknown>,\n): void {\n\tfor (const step of tween.steps) {\n\t\tfor (const target of step.targets) {\n\t\t\tif (target.from !== null) continue;\n\t\t\tconst comp = entityComponents[target.component];\n\t\t\tif (!comp || typeof comp !== 'object') continue;\n\t\t\tconst val = readField(comp as Record<string, unknown>, target.path);\n\t\t\tif (val !== null) {\n\t\t\t\ttarget.from = val;\n\t\t\t} else {\n\t\t\t\ttarget.from = 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/**\n * Apply interpolation for a step's targets at a given progress.\n */\nfunction applyStep(\n\tstep: TweenStep,\n\tprogress: number,\n\tentityComponents: Record<string, unknown>,\n\tentityId: number,\n\tecs: { markChanged: (entityId: number, componentName: any) => void },\n): void {\n\tconst easedT = step.easing(progress);\n\n\tfor (const target of step.targets) {\n\t\tconst comp = entityComponents[target.component];\n\t\tif (!comp || typeof comp !== 'object') continue;\n\t\tconst from = target.from ?? 0;\n\t\tconst value = from + (target.to - from) * easedT;\n\t\tconst written = writeField(comp as Record<string, unknown>, target.path, value);\n\t\tif (written) {\n\t\t\tecs.markChanged(entityId, target.component);\n\t\t}\n\t}\n}\n\n/**\n * Snap all targets in a step to their final values (from or to depending on direction).\n */\nfunction snapStepToEnd(\n\tstep: TweenStep,\n\tentityComponents: Record<string, unknown>,\n\tentityId: number,\n\tecs: { markChanged: (entityId: number, componentName: any) => void },\n): void {\n\tfor (const target of step.targets) {\n\t\tconst comp = entityComponents[target.component];\n\t\tif (!comp || typeof comp !== 'object') continue;\n\t\tconst written = writeField(comp as Record<string, unknown>, target.path, target.to);\n\t\tif (written) {\n\t\t\tecs.markChanged(entityId, target.component);\n\t\t}\n\t}\n}\n\n/**\n * Reverse all from/to values in every step (for yoyo).\n */\nfunction reverseAllTargets(tween: Tween): void {\n\tfor (const step of tween.steps) {\n\t\tfor (const target of step.targets) {\n\t\t\tconst tmp = target.from ?? 0;\n\t\t\ttarget.from = target.to;\n\t\t\ttarget.to = tmp;\n\t\t}\n\t}\n}\n\n// ==================== Tween Processing Helpers ====================\n\ntype TweenEcs = { markChanged: (entityId: number, componentName: any) => void; commands: { removeComponent: (entityId: number, componentName: any) => void } };\n\nfunction completeTween(\n\ttween: Tween,\n\tentityId: number,\n\tecs: { commands: { removeComponent: (entityId: number, componentName: any) => void } },\n): void {\n\ttween.state = 'complete';\n\ttween.justFinished = true;\n\n\ttween.onComplete?.({ entityId, stepCount: tween.steps.length });\n\tecs.commands.removeComponent(entityId, 'tween');\n}\n\nfunction handleTweenEnd(\n\ttween: Tween,\n\tentityId: number,\n\tecs: TweenEcs,\n): boolean {\n\ttween.completedLoops++;\n\n\tif (tween.loop === 'once') {\n\t\tcompleteTween(tween, entityId, ecs);\n\t\treturn false;\n\t}\n\n\t// Check if finite loops exhausted\n\tif (tween.totalLoops > 0 && tween.completedLoops >= tween.totalLoops) {\n\t\tcompleteTween(tween, entityId, ecs);\n\t\treturn false;\n\t}\n\n\t// Loop continues\n\tif (tween.loop === 'yoyo') {\n\t\ttween.direction = tween.direction === 1 ? -1 : 1;\n\t\treverseAllTargets(tween);\n\t}\n\n\ttween.currentStep = 0;\n\n\t// For 'loop' mode, from values stay as-is so the animation replays identically.\n\t// For 'yoyo' mode, reverseAllTargets already swapped from/to.\n\n\treturn tween.elapsed > 0;\n}\n\n/**\n * Advance to next step. Returns true if there's more work to process,\n * false if the tween has completed or looped.\n */\nfunction advanceStep(\n\ttween: Tween,\n\tentityComponents: Record<string, unknown>,\n\tentityId: number,\n\tecs: TweenEcs,\n): boolean {\n\tconst nextStep = tween.currentStep + 1;\n\n\tif (nextStep < tween.steps.length) {\n\t\t// More steps — resolve from values for next step and continue\n\t\ttween.currentStep = nextStep;\n\t\tconst step = tween.steps[nextStep];\n\t\tif (step) {\n\t\t\tfor (const target of step.targets) {\n\t\t\t\tif (target.from !== null) continue;\n\t\t\t\tconst comp = entityComponents[target.component];\n\t\t\t\tif (!comp || typeof comp !== 'object') continue;\n\t\t\t\tconst val = readField(comp as Record<string, unknown>, target.path);\n\t\t\t\ttarget.from = val ?? 0;\n\t\t\t}\n\t\t}\n\t\treturn true;\n\t}\n\n\t// All steps done — handle loop/complete\n\treturn handleTweenEnd(tween, entityId, ecs);\n}\n\nfunction processTweenProgress(\n\ttween: Tween,\n\tentityComponents: Record<string, unknown>,\n\tentityId: number,\n\tecs: TweenEcs,\n): void {\n\t// eslint-disable-next-line no-constant-condition\n\twhile (true) {\n\t\tconst currentStep = tween.steps[tween.currentStep];\n\t\tif (!currentStep) return;\n\n\t\t// Zero-duration steps complete immediately\n\t\tif (currentStep.duration <= 0) {\n\t\t\tsnapStepToEnd(currentStep, entityComponents, entityId, ecs);\n\t\t\ttween.elapsed = 0;\n\n\t\t\tif (!advanceStep(tween, entityComponents, entityId, ecs)) return;\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (tween.elapsed >= currentStep.duration) {\n\t\t\t// Step complete — snap to end and carry overflow\n\t\t\tsnapStepToEnd(currentStep, entityComponents, entityId, ecs);\n\t\t\tconst overflow = tween.elapsed - currentStep.duration;\n\t\t\ttween.elapsed = overflow;\n\n\t\t\tif (!advanceStep(tween, entityComponents, entityId, ecs)) return;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Step in progress — interpolate\n\t\tconst progress = clamp(tween.elapsed / currentStep.duration, 0, 1);\n\t\tapplyStep(currentStep, progress, entityComponents, entityId, ecs);\n\t\treturn;\n\t}\n}\n\n// ==================== Plugin Factory ====================\n\n/**\n * Create a tween plugin for ECSpresso.\n *\n * This plugin provides:\n * - Tween system that processes all tween components each frame\n * - Support for single-field, multi-target, and multi-step sequences\n * - 31 standard easing functions\n * - Loop modes: once, loop, yoyo\n * - `justFinished` flag for one-frame completion detection\n * - `onComplete` callback on completion\n * - Change detection via markChanged\n */\nexport function createTweenPlugin<G extends string = 'tweens'>(\n\toptions?: TweenPluginOptions<G>\n): Plugin<WorldConfigFrom<TweenComponentTypes>, EmptyConfig, 'tween-update', G> {\n\tconst {\n\t\tsystemGroup = 'tweens',\n\t\tpriority = 0,\n\t\tphase = 'update',\n\t} = options ?? {};\n\n\treturn definePlugin<WorldConfigFrom<TweenComponentTypes>, EmptyConfig, 'tween-update', G>({\n\t\tid: 'tweens',\n\t\tinstall(world) {\n\t\t\tworld\n\t\t\t\t.addSystem('tween-update')\n\t\t\t\t.setPriority(priority)\n\t\t\t\t.inPhase(phase)\n\t\t\t\t.inGroup(systemGroup)\n\t\t\t\t.addQuery('tweens', {\n\t\t\t\t\twith: ['tween'],\n\t\t\t\t})\n\t\t\t\t.setProcess(({ queries, dt, ecs }) => {\n\t\t\t\t\tfor (const entity of queries.tweens) {\n\t\t\t\t\t\tconst tween = entity.components.tween as Tween;\n\t\t\t\t\t\tconst entityComponents = entity.components as Record<string, unknown>;\n\n\t\t\t\t\t\t// Reset justFinished flag from previous frame\n\t\t\t\t\t\tif (tween.justFinished) {\n\t\t\t\t\t\t\ttween.justFinished = false;\n\t\t\t\t\t\t\t// Component removal was queued, skip processing\n\t\t\t\t\t\t\tcontinue;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Skip completed tweens\n\t\t\t\t\t\tif (tween.state === 'complete') continue;\n\n\t\t\t\t\t\t// Resolve pending state: capture null from values\n\t\t\t\t\t\tif (tween.state === 'pending') {\n\t\t\t\t\t\t\tresolveFromValues(tween, entityComponents);\n\t\t\t\t\t\t\ttween.state = 'active';\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// Process active tween\n\t\t\t\t\t\tconst currentStep = tween.steps[tween.currentStep];\n\t\t\t\t\t\tif (!currentStep) continue;\n\n\t\t\t\t\t\ttween.elapsed += dt;\n\n\t\t\t\t\t\tprocessTweenProgress(tween, entityComponents, entity.id, ecs);\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t},\n\t});\n}\n",
|
|
6
|
+
"/**\n * Easing Functions\n *\n * 31 standard easing functions for animation. Pure math, no dependencies.\n */\n\nexport type EasingFn = (t: number) => number;\n\nexport function linear(t: number): number {\n\treturn t;\n}\n\n// Quad\nexport function easeInQuad(t: number): number {\n\treturn t * t;\n}\n\nexport function easeOutQuad(t: number): number {\n\treturn t * (2 - t);\n}\n\nexport function easeInOutQuad(t: number): number {\n\treturn t < 0.5\n\t\t? 2 * t * t\n\t\t: -1 + (4 - 2 * t) * t;\n}\n\n// Cubic\nexport function easeInCubic(t: number): number {\n\treturn t * t * t;\n}\n\nexport function easeOutCubic(t: number): number {\n\tconst t1 = t - 1;\n\treturn t1 * t1 * t1 + 1;\n}\n\nexport function easeInOutCubic(t: number): number {\n\treturn t < 0.5\n\t\t? 4 * t * t * t\n\t\t: 1 + (t - 1) * (2 * t - 2) * (2 * t - 2);\n}\n\n// Quart\nexport function easeInQuart(t: number): number {\n\treturn t * t * t * t;\n}\n\nexport function easeOutQuart(t: number): number {\n\tconst t1 = t - 1;\n\treturn 1 - t1 * t1 * t1 * t1;\n}\n\nexport function easeInOutQuart(t: number): number {\n\treturn t < 0.5\n\t\t? 8 * t * t * t * t\n\t\t: 1 - 8 * (t - 1) * (t - 1) * (t - 1) * (t - 1);\n}\n\n// Quint\nexport function easeInQuint(t: number): number {\n\treturn t * t * t * t * t;\n}\n\nexport function easeOutQuint(t: number): number {\n\tconst t1 = t - 1;\n\treturn 1 + t1 * t1 * t1 * t1 * t1;\n}\n\nexport function easeInOutQuint(t: number): number {\n\treturn t < 0.5\n\t\t? 16 * t * t * t * t * t\n\t\t: 1 + 16 * (t - 1) * (t - 1) * (t - 1) * (t - 1) * (t - 1);\n}\n\n// Sine\nexport function easeInSine(t: number): number {\n\treturn 1 - Math.cos((t * Math.PI) / 2);\n}\n\nexport function easeOutSine(t: number): number {\n\treturn Math.sin((t * Math.PI) / 2);\n}\n\nexport function easeInOutSine(t: number): number {\n\treturn -(Math.cos(Math.PI * t) - 1) / 2;\n}\n\n// Expo\nexport function easeInExpo(t: number): number {\n\treturn t === 0 ? 0 : Math.pow(2, 10 * (t - 1));\n}\n\nexport function easeOutExpo(t: number): number {\n\treturn t === 1 ? 1 : 1 - Math.pow(2, -10 * t);\n}\n\nexport function easeInOutExpo(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn t < 0.5\n\t\t? Math.pow(2, 20 * t - 10) / 2\n\t\t: (2 - Math.pow(2, -20 * t + 10)) / 2;\n}\n\n// Circ\nexport function easeInCirc(t: number): number {\n\treturn 1 - Math.sqrt(1 - t * t);\n}\n\nexport function easeOutCirc(t: number): number {\n\tconst t1 = t - 1;\n\treturn Math.sqrt(1 - t1 * t1);\n}\n\nexport function easeInOutCirc(t: number): number {\n\treturn t < 0.5\n\t\t? (1 - Math.sqrt(1 - 4 * t * t)) / 2\n\t\t: (Math.sqrt(1 - (-2 * t + 2) * (-2 * t + 2)) + 1) / 2;\n}\n\n// Back\nconst BACK_C1 = 1.70158;\nconst BACK_C2 = BACK_C1 * 1.525;\nconst BACK_C3 = BACK_C1 + 1;\n\nexport function easeInBack(t: number): number {\n\treturn BACK_C3 * t * t * t - BACK_C1 * t * t;\n}\n\nexport function easeOutBack(t: number): number {\n\tconst t1 = t - 1;\n\treturn 1 + BACK_C3 * t1 * t1 * t1 + BACK_C1 * t1 * t1;\n}\n\nexport function easeInOutBack(t: number): number {\n\treturn t < 0.5\n\t\t? ((2 * t) * (2 * t) * ((BACK_C2 + 1) * 2 * t - BACK_C2)) / 2\n\t\t: ((2 * t - 2) * (2 * t - 2) * ((BACK_C2 + 1) * (t * 2 - 2) + BACK_C2) + 2) / 2;\n}\n\n// Elastic\nconst ELASTIC_C4 = (2 * Math.PI) / 3;\nconst ELASTIC_C5 = (2 * Math.PI) / 4.5;\n\nexport function easeInElastic(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn -Math.pow(2, 10 * t - 10) * Math.sin((10 * t - 10.75) * ELASTIC_C4);\n}\n\nexport function easeOutElastic(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn Math.pow(2, -10 * t) * Math.sin((10 * t - 0.75) * ELASTIC_C4) + 1;\n}\n\nexport function easeInOutElastic(t: number): number {\n\tif (t === 0) return 0;\n\tif (t === 1) return 1;\n\treturn t < 0.5\n\t\t? -(Math.pow(2, 20 * t - 10) * Math.sin((20 * t - 11.125) * ELASTIC_C5)) / 2\n\t\t: (Math.pow(2, -20 * t + 10) * Math.sin((20 * t - 11.125) * ELASTIC_C5)) / 2 + 1;\n}\n\n// Bounce\nexport function easeOutBounce(t: number): number {\n\tconst n1 = 7.5625;\n\tconst d1 = 2.75;\n\n\tif (t < 1 / d1) {\n\t\treturn n1 * t * t;\n\t} else if (t < 2 / d1) {\n\t\tconst t1 = t - 1.5 / d1;\n\t\treturn n1 * t1 * t1 + 0.75;\n\t} else if (t < 2.5 / d1) {\n\t\tconst t1 = t - 2.25 / d1;\n\t\treturn n1 * t1 * t1 + 0.9375;\n\t} else {\n\t\tconst t1 = t - 2.625 / d1;\n\t\treturn n1 * t1 * t1 + 0.984375;\n\t}\n}\n\nexport function easeInBounce(t: number): number {\n\treturn 1 - easeOutBounce(1 - t);\n}\n\nexport function easeInOutBounce(t: number): number {\n\treturn t < 0.5\n\t\t? (1 - easeOutBounce(1 - 2 * t)) / 2\n\t\t: (1 + easeOutBounce(2 * t - 1)) / 2;\n}\n\n/** Runtime lookup of all easing functions by name */\nexport const easings = {\n\tlinear,\n\teaseInQuad,\n\teaseOutQuad,\n\teaseInOutQuad,\n\teaseInCubic,\n\teaseOutCubic,\n\teaseInOutCubic,\n\teaseInQuart,\n\teaseOutQuart,\n\teaseInOutQuart,\n\teaseInQuint,\n\teaseOutQuint,\n\teaseInOutQuint,\n\teaseInSine,\n\teaseOutSine,\n\teaseInOutSine,\n\teaseInExpo,\n\teaseOutExpo,\n\teaseInOutExpo,\n\teaseInCirc,\n\teaseOutCirc,\n\teaseInOutCirc,\n\teaseInBack,\n\teaseOutBack,\n\teaseInOutBack,\n\teaseInElastic,\n\teaseOutElastic,\n\teaseInOutElastic,\n\teaseInBounce,\n\teaseOutBounce,\n\teaseInOutBounce,\n} as const;\n"
|
|
7
|
+
],
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "337A12D65456A62F64756E2164756E21",
|
|
10
|
+
"names": []
|
|
11
|
+
}
|
package/dist/system-builder.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import type {
|
|
1
|
+
import type ECSpresso from "./ecspresso";
|
|
2
|
+
import type { FilteredEntity, QueryDefinition, System, SystemPhase } from "./types";
|
|
3
|
+
import type { WorldConfig, EmptyConfig } from "./type-utils";
|
|
4
4
|
/**
|
|
5
|
-
* Builder class for creating type-safe ECS Systems with proper query inference
|
|
5
|
+
* Builder class for creating type-safe ECS Systems with proper query inference.
|
|
6
|
+
* Systems are automatically registered with their ECSpresso instance when
|
|
7
|
+
* finalized (at the start of initialize() or update()).
|
|
6
8
|
*/
|
|
7
|
-
export declare class SystemBuilder<
|
|
9
|
+
export declare class SystemBuilder<Cfg extends WorldConfig = EmptyConfig, Queries extends Record<string, QueryDefinition<Cfg['components']>> = {}, Label extends string = string, SysGroups extends string = never, ResourceKeys extends keyof Cfg['resources'] = never> {
|
|
8
10
|
private _label;
|
|
9
|
-
private _ecspresso;
|
|
10
|
-
private _bundle;
|
|
11
11
|
private queries;
|
|
12
12
|
private processFunction?;
|
|
13
13
|
private detachFunction?;
|
|
@@ -15,36 +15,20 @@ export declare class SystemBuilder<ComponentTypes extends Record<string, any> =
|
|
|
15
15
|
private eventHandlers?;
|
|
16
16
|
private _priority;
|
|
17
17
|
private _phase;
|
|
18
|
-
private _isRegistered;
|
|
19
18
|
private _groups;
|
|
20
19
|
private _inScreens?;
|
|
21
20
|
private _excludeScreens?;
|
|
22
21
|
private _requiredAssets?;
|
|
23
|
-
|
|
22
|
+
private _runWhenEmpty;
|
|
23
|
+
private _entityEnterHandlers;
|
|
24
|
+
private _resourceKeys?;
|
|
25
|
+
constructor(_label: string);
|
|
24
26
|
get label(): string;
|
|
25
27
|
/**
|
|
26
|
-
*
|
|
28
|
+
* Create a system object with all configured properties.
|
|
29
|
+
* @internal Used by ECSpresso to finalize and register the system
|
|
27
30
|
*/
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Returns the associated ECSpresso instance if one was provided in the constructor
|
|
31
|
-
*/
|
|
32
|
-
get ecspresso(): ECSpresso<ComponentTypes, EventTypes, ResourceTypes, {}, {}> | null;
|
|
33
|
-
/**
|
|
34
|
-
* Auto-register this system with its ECSpresso instance if not already registered
|
|
35
|
-
* @private
|
|
36
|
-
*/
|
|
37
|
-
private _autoRegister;
|
|
38
|
-
/**
|
|
39
|
-
* Create the system object without registering it
|
|
40
|
-
* @private
|
|
41
|
-
*/
|
|
42
|
-
private _buildSystemObject;
|
|
43
|
-
/**
|
|
44
|
-
* Create a system object with all configured properties
|
|
45
|
-
* @private
|
|
46
|
-
*/
|
|
47
|
-
private _createSystemObject;
|
|
31
|
+
_createSystemObject(): System<Cfg, any, any>;
|
|
48
32
|
/**
|
|
49
33
|
* Set the priority of this system. Systems with higher priority values
|
|
50
34
|
* execute before those with lower values. Systems with the same priority
|
|
@@ -67,7 +51,7 @@ export declare class SystemBuilder<ComponentTypes extends Record<string, any> =
|
|
|
67
51
|
* @param groupName The name of the group to add the system to
|
|
68
52
|
* @returns This SystemBuilder instance for method chaining
|
|
69
53
|
*/
|
|
70
|
-
inGroup(groupName:
|
|
54
|
+
inGroup<G extends string>(groupName: G): SystemBuilder<Cfg, Queries, Label, SysGroups | G, ResourceKeys>;
|
|
71
55
|
/**
|
|
72
56
|
* Restrict this system to only run in specified screens.
|
|
73
57
|
* System will be skipped during update() when the current screen
|
|
@@ -75,7 +59,7 @@ export declare class SystemBuilder<ComponentTypes extends Record<string, any> =
|
|
|
75
59
|
* @param screens Array of screen names where this system should run
|
|
76
60
|
* @returns This SystemBuilder instance for method chaining
|
|
77
61
|
*/
|
|
78
|
-
inScreens(screens: ReadonlyArray<string>): this;
|
|
62
|
+
inScreens(screens: ReadonlyArray<keyof Cfg['screens'] & string>): this;
|
|
79
63
|
/**
|
|
80
64
|
* Exclude this system from running in specified screens.
|
|
81
65
|
* System will be skipped during update() when the current screen
|
|
@@ -83,7 +67,7 @@ export declare class SystemBuilder<ComponentTypes extends Record<string, any> =
|
|
|
83
67
|
* @param screens Array of screen names where this system should NOT run
|
|
84
68
|
* @returns This SystemBuilder instance for method chaining
|
|
85
69
|
*/
|
|
86
|
-
excludeScreens(screens: ReadonlyArray<string>): this;
|
|
70
|
+
excludeScreens(screens: ReadonlyArray<keyof Cfg['screens'] & string>): this;
|
|
87
71
|
/**
|
|
88
72
|
* Require specific assets to be loaded for this system to run.
|
|
89
73
|
* System will be skipped during update() if any required asset
|
|
@@ -91,50 +75,64 @@ export declare class SystemBuilder<ComponentTypes extends Record<string, any> =
|
|
|
91
75
|
* @param assets Array of asset keys that must be loaded
|
|
92
76
|
* @returns This SystemBuilder instance for method chaining
|
|
93
77
|
*/
|
|
94
|
-
requiresAssets(assets: ReadonlyArray<string>): this;
|
|
78
|
+
requiresAssets(assets: ReadonlyArray<keyof Cfg['assets'] & string>): this;
|
|
79
|
+
/**
|
|
80
|
+
* Allow this system to run even when all queries return zero entities.
|
|
81
|
+
* By default, systems with queries are skipped when no entities match.
|
|
82
|
+
*/
|
|
83
|
+
runWhenEmpty(): this;
|
|
84
|
+
/**
|
|
85
|
+
* Declare resource dependencies for this system. Resources are resolved
|
|
86
|
+
* once (on first process call) and the same object is reused every frame.
|
|
87
|
+
* The resolved resources are available as ctx.resources in setProcess.
|
|
88
|
+
* @param keys Array of resource keys to resolve
|
|
89
|
+
* @returns This SystemBuilder instance for method chaining
|
|
90
|
+
*/
|
|
91
|
+
withResources<RK extends keyof Cfg['resources'] & string>(keys: readonly RK[]): SystemBuilder<Cfg, Queries, Label, SysGroups, RK>;
|
|
95
92
|
/**
|
|
96
93
|
* Add a query definition to the system
|
|
97
94
|
*/
|
|
98
|
-
addQuery<QueryName extends string, WithComponents extends keyof
|
|
95
|
+
addQuery<QueryName extends string, WithComponents extends keyof Cfg['components'], WithoutComponents extends keyof Cfg['components'] = never, OptionalComponents extends keyof Cfg['components'] = never, NewQueries extends Queries & Record<QueryName, QueryDefinition<Cfg['components'], WithComponents, WithoutComponents, OptionalComponents>> = Queries & Record<QueryName, QueryDefinition<Cfg['components'], WithComponents, WithoutComponents, OptionalComponents>>>(name: QueryName, definition: {
|
|
99
96
|
with: ReadonlyArray<WithComponents>;
|
|
100
97
|
without?: ReadonlyArray<WithoutComponents>;
|
|
101
98
|
changed?: ReadonlyArray<WithComponents>;
|
|
102
99
|
optional?: ReadonlyArray<OptionalComponents>;
|
|
103
|
-
parentHas?: ReadonlyArray<keyof
|
|
104
|
-
}):
|
|
100
|
+
parentHas?: ReadonlyArray<keyof Cfg['components']>;
|
|
101
|
+
}): SystemBuilder<Cfg, NewQueries, Label, SysGroups, ResourceKeys>;
|
|
105
102
|
/**
|
|
106
|
-
* Set the system's process function that runs each update
|
|
103
|
+
* Set the system's process function that runs each update.
|
|
104
|
+
* The callback receives a single context object { queries, dt, ecs, resources? }.
|
|
105
|
+
* The context is pre-allocated per system and reused every frame.
|
|
107
106
|
* @param process Function to process entities matching the system's queries each update
|
|
108
107
|
* @returns This SystemBuilder instance for method chaining
|
|
109
108
|
*/
|
|
110
|
-
setProcess(process: ProcessFunction<
|
|
109
|
+
setProcess(process: ProcessFunction<Cfg, Queries, ResourceKeys>): this;
|
|
111
110
|
/**
|
|
112
|
-
* Register
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
* Complete this system and return the parent container for seamless chaining
|
|
119
|
-
* - For ECSpresso-attached builders: registers the system and returns ECSpresso
|
|
120
|
-
* - For Bundle-attached builders: returns the Bundle
|
|
121
|
-
* This method is typed via the specialized interfaces (SystemBuilderWithEcspresso, SystemBuilderWithBundle)
|
|
111
|
+
* Register a callback that fires once per entity the first time it appears
|
|
112
|
+
* in a query's results. Fires before process. Automatic cleanup when entity
|
|
113
|
+
* leaves the query so re-entry fires the callback again.
|
|
114
|
+
* @param queryName Name of a query previously added via addQuery
|
|
115
|
+
* @param callback Function called with the entity and ecs instance
|
|
116
|
+
* @returns This SystemBuilder instance for method chaining
|
|
122
117
|
*/
|
|
123
|
-
|
|
118
|
+
setOnEntityEnter<QN extends keyof Queries & string>(queryName: QN, callback: (ctx: {
|
|
119
|
+
entity: FilteredEntity<Cfg['components'], Queries[QN] extends QueryDefinition<Cfg['components'], infer W> ? W : never, Queries[QN] extends QueryDefinition<Cfg['components'], any, infer WO> ? WO : never, Queries[QN] extends QueryDefinition<Cfg['components'], any, any, infer O> ? O : never>;
|
|
120
|
+
ecs: ECSpresso<Cfg>;
|
|
121
|
+
}) => void): this;
|
|
124
122
|
/**
|
|
125
123
|
* Set the onDetach lifecycle hook
|
|
126
124
|
* Called when the system is removed from the ECS
|
|
127
125
|
* @param onDetach Function to run when this system is detached from the ECS
|
|
128
126
|
* @returns This SystemBuilder instance for method chaining
|
|
129
127
|
*/
|
|
130
|
-
setOnDetach(onDetach: LifecycleFunction<
|
|
128
|
+
setOnDetach(onDetach: LifecycleFunction<Cfg>): this;
|
|
131
129
|
/**
|
|
132
130
|
* Set the onInitialize lifecycle hook
|
|
133
131
|
* Called when the system is initialized via ECSpresso.initialize() method
|
|
134
132
|
* @param onInitialize Function to run when this system is initialized
|
|
135
133
|
* @returns This SystemBuilder instance for method chaining
|
|
136
134
|
*/
|
|
137
|
-
setOnInitialize(onInitialize: LifecycleFunction<
|
|
135
|
+
setOnInitialize(onInitialize: LifecycleFunction<Cfg>): this;
|
|
138
136
|
/**
|
|
139
137
|
* Set event handlers for the system
|
|
140
138
|
* These handlers will be automatically subscribed when the system is attached
|
|
@@ -142,73 +140,38 @@ export declare class SystemBuilder<ComponentTypes extends Record<string, any> =
|
|
|
142
140
|
* @returns This SystemBuilder instance for method chaining
|
|
143
141
|
*/
|
|
144
142
|
setEventHandlers(handlers: {
|
|
145
|
-
[EventName in keyof
|
|
146
|
-
|
|
147
|
-
|
|
143
|
+
[EventName in keyof Cfg['events']]?: (ctx: {
|
|
144
|
+
data: Cfg['events'][EventName];
|
|
145
|
+
ecs: ECSpresso<Cfg>;
|
|
146
|
+
}) => void;
|
|
148
147
|
}): this;
|
|
149
|
-
/**
|
|
150
|
-
* Build the final system object
|
|
151
|
-
*/
|
|
152
|
-
build(ecspresso?: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>): this;
|
|
153
148
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
* This handles attaching the system and setting up event handlers
|
|
157
|
-
* @internal Used by SystemBuilder and Bundle
|
|
158
|
-
*/
|
|
159
|
-
export declare function registerSystemWithEcspresso<ComponentTypes extends Record<string, any>, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>>(system: System<ComponentTypes, any, any, EventTypes, ResourceTypes>, ecspresso: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>): void;
|
|
160
|
-
type QueryDefinition<ComponentTypes, WithComponents extends keyof ComponentTypes = any, WithoutComponents extends keyof ComponentTypes = any, OptionalComponents extends keyof ComponentTypes = any> = {
|
|
161
|
-
with: ReadonlyArray<WithComponents>;
|
|
162
|
-
without?: ReadonlyArray<WithoutComponents>;
|
|
163
|
-
changed?: ReadonlyArray<WithComponents>;
|
|
164
|
-
optional?: ReadonlyArray<OptionalComponents>;
|
|
165
|
-
parentHas?: ReadonlyArray<keyof ComponentTypes>;
|
|
166
|
-
};
|
|
167
|
-
type QueryResults<ComponentTypes, Queries extends Record<string, QueryDefinition<ComponentTypes>>> = {
|
|
168
|
-
[QueryName in keyof Queries]: QueryName extends string ? FilteredEntity<ComponentTypes, Queries[QueryName] extends QueryDefinition<ComponentTypes, infer W, any, any> ? W : never, Queries[QueryName] extends QueryDefinition<ComponentTypes, any, infer WO, any> ? WO : never, Queries[QueryName] extends QueryDefinition<ComponentTypes, any, any, infer O> ? O : never>[] : never;
|
|
149
|
+
type QueryResults<ComponentTypes extends Record<string, any>, Queries extends Record<string, QueryDefinition<ComponentTypes>>> = {
|
|
150
|
+
[QueryName in keyof Queries]: QueryName extends string ? FilteredEntity<ComponentTypes, Queries[QueryName] extends QueryDefinition<ComponentTypes, infer W> ? W : never, Queries[QueryName] extends QueryDefinition<ComponentTypes, any, infer WO> ? WO : never, Queries[QueryName] extends QueryDefinition<ComponentTypes, any, any, infer O> ? O : never>[] : never;
|
|
169
151
|
};
|
|
170
152
|
/**
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
153
|
+
* Context object passed to system process functions.
|
|
154
|
+
* Pre-allocated per system and reused every frame (zero per-frame allocation).
|
|
155
|
+
* When resources are declared via withResources(), the context includes a
|
|
156
|
+
* `resources` field with the resolved values (cached once on first call).
|
|
175
157
|
*/
|
|
176
|
-
type
|
|
158
|
+
export type ProcessContext<Cfg extends WorldConfig, Queries extends Record<string, QueryDefinition<Cfg['components']>>, ResourceKeys extends keyof Cfg['resources'] = never> = {
|
|
159
|
+
queries: QueryResults<Cfg['components'], Queries>;
|
|
160
|
+
dt: number;
|
|
161
|
+
ecs: ECSpresso<Cfg>;
|
|
162
|
+
} & ([ResourceKeys] extends [never] ? {} : {
|
|
163
|
+
resources: {
|
|
164
|
+
readonly [K in ResourceKeys]: Cfg['resources'][K];
|
|
165
|
+
};
|
|
166
|
+
});
|
|
177
167
|
/**
|
|
178
|
-
*
|
|
179
|
-
*
|
|
168
|
+
* Function signature for system process methods.
|
|
169
|
+
* Receives a single context object with queries, dt, ecs, and optionally resources.
|
|
180
170
|
*/
|
|
181
|
-
type
|
|
171
|
+
type ProcessFunction<Cfg extends WorldConfig, Queries extends Record<string, QueryDefinition<Cfg['components']>>, ResourceKeys extends keyof Cfg['resources'] = never> = (ctx: ProcessContext<Cfg, Queries, ResourceKeys>) => void;
|
|
182
172
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*/
|
|
186
|
-
export declare function createEcspressoSystemBuilder<ComponentTypes extends Record<string, any>, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>>(label: string, ecspresso: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>): SystemBuilderWithEcspresso<ComponentTypes, EventTypes, ResourceTypes>;
|
|
187
|
-
/**
|
|
188
|
-
* Create a SystemBuilder attached to a Bundle
|
|
189
|
-
* Helper function used by Bundle.addSystem
|
|
190
|
-
*/
|
|
191
|
-
export declare function createBundleSystemBuilder<ComponentTypes extends Record<string, any>, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>>(label: string, bundle: Bundle<ComponentTypes, EventTypes, ResourceTypes>): SystemBuilderWithBundle<ComponentTypes, EventTypes, ResourceTypes>;
|
|
192
|
-
/**
|
|
193
|
-
* SystemBuilder with a guaranteed non-null reference to an ECSpresso instance
|
|
194
|
-
*/
|
|
195
|
-
export interface SystemBuilderWithEcspresso<ComponentTypes extends Record<string, any>, EventTypes extends Record<string, any>, ResourceTypes extends Record<string, any>, Queries extends Record<string, QueryDefinition<ComponentTypes>> = {}> extends SystemBuilder<ComponentTypes, EventTypes, ResourceTypes, Queries> {
|
|
196
|
-
readonly ecspresso: ECSpresso<ComponentTypes, EventTypes, ResourceTypes>;
|
|
197
|
-
/**
|
|
198
|
-
* Complete this system and return ECSpresso for seamless chaining
|
|
199
|
-
* Automatically registers the system when called
|
|
200
|
-
*/
|
|
201
|
-
and(): ECSpresso<ComponentTypes, EventTypes, ResourceTypes>;
|
|
202
|
-
}
|
|
203
|
-
/**
|
|
204
|
-
* SystemBuilder with a guaranteed non-null reference to a Bundle
|
|
173
|
+
* Type for system lifecycle functions
|
|
174
|
+
* These can be asynchronous
|
|
205
175
|
*/
|
|
206
|
-
|
|
207
|
-
readonly bundle: Bundle<ComponentTypes, EventTypes, ResourceTypes>;
|
|
208
|
-
/**
|
|
209
|
-
* Complete this system and return the Bundle for chaining
|
|
210
|
-
* Enables fluent API: bundle.addSystem(...).and().addSystem(...)
|
|
211
|
-
*/
|
|
212
|
-
and(): Bundle<ComponentTypes, EventTypes, ResourceTypes>;
|
|
213
|
-
}
|
|
176
|
+
type LifecycleFunction<Cfg extends WorldConfig> = (ecs: ECSpresso<Cfg>) => void | Promise<void>;
|
|
214
177
|
export {};
|