aispritejs 0.1.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/LICENSE +21 -0
- package/README.md +211 -0
- package/README_ZHTW.md +209 -0
- package/dist/atlas/index.cjs +518 -0
- package/dist/atlas/index.cjs.map +1 -0
- package/dist/atlas/index.d.cts +52 -0
- package/dist/atlas/index.d.ts +52 -0
- package/dist/atlas/index.js +514 -0
- package/dist/atlas/index.js.map +1 -0
- package/dist/index.cjs +451 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +58 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.js +445 -0
- package/dist/index.js.map +1 -0
- package/dist/pixi/index.cjs +520 -0
- package/dist/pixi/index.cjs.map +1 -0
- package/dist/pixi/index.d.cts +75 -0
- package/dist/pixi/index.d.ts +75 -0
- package/dist/pixi/index.js +517 -0
- package/dist/pixi/index.js.map +1 -0
- package/dist/types-DKMFvfx9.d.cts +251 -0
- package/dist/types-DKMFvfx9.d.ts +251 -0
- package/llms-full.txt +449 -0
- package/llms.txt +40 -0
- package/package.json +103 -0
- package/schemas/aispritejs-graph.schema.json +126 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Sprite, Texture, Spritesheet } from 'pixi.js';
|
|
2
|
+
import { b as SpriteGraph } from '../types-DKMFvfx9.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Thrown by {@link createPixiSpriteAnimator} when the supplied textures are
|
|
6
|
+
* missing one or more frame keys the graph's animations reference. Fail-fast at
|
|
7
|
+
* construction, so `update()` never has to guard.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
declare class MissingTextureError extends Error {
|
|
12
|
+
readonly keys: readonly string[];
|
|
13
|
+
constructor(keys: readonly string[]);
|
|
14
|
+
}
|
|
15
|
+
/** Frame-key → texture lookup. A PixiJS `Spritesheet` exposes one as `.textures`. */
|
|
16
|
+
type TextureMap = Record<string, Texture>;
|
|
17
|
+
/**
|
|
18
|
+
* Options for {@link createPixiSpriteAnimator}.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
interface PixiSpriteAnimatorOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Apply each frame's atlas anchor (`texture.defaultAnchor`) to the sprite when
|
|
25
|
+
* the frame changes — preserving non-centre / foot pivots. Default `true`.
|
|
26
|
+
* Set `false` to manage the anchor yourself.
|
|
27
|
+
*/
|
|
28
|
+
readonly applyAnchor?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A PixiJS-bound animator: the core machine plus a sprite whose texture tracks
|
|
32
|
+
* the active frame.
|
|
33
|
+
*
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
interface PixiSpriteAnimator {
|
|
37
|
+
/** The bound sprite, updated in place. */
|
|
38
|
+
readonly sprite: Sprite;
|
|
39
|
+
/** Run the core machine for `deltaMs`, then sync the sprite's texture. */
|
|
40
|
+
update(deltaMs: number): void;
|
|
41
|
+
/** Set a Number / Boolean input on the core machine. */
|
|
42
|
+
setInput(name: string, value: number | boolean): void;
|
|
43
|
+
/** Fire a Trigger on the core machine. */
|
|
44
|
+
fireTrigger(name: string): void;
|
|
45
|
+
/** Reset the core machine and re-sync the sprite. */
|
|
46
|
+
reset(): void;
|
|
47
|
+
/** Dispose the core machine. Idempotent. Does not destroy the sprite. */
|
|
48
|
+
dispose(): void;
|
|
49
|
+
/** Current state name. */
|
|
50
|
+
readonly activeState: string;
|
|
51
|
+
/** Current frame key. */
|
|
52
|
+
readonly activeFrameKey: string;
|
|
53
|
+
/** `true` once disposed. */
|
|
54
|
+
readonly disposed: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Bind an input-driven {@link SpriteGraph} to a PixiJS `Sprite`.
|
|
58
|
+
*
|
|
59
|
+
* @param sprite - a plain `Sprite` to drive; its `texture` (and, by default,
|
|
60
|
+
* `anchor`) are updated in place. The adapter owns frame selection, so if a
|
|
61
|
+
* *playing* `AnimatedSprite` is passed (it extends `Sprite`), its internal
|
|
62
|
+
* playback is stopped to stop it fighting the adapter for the texture.
|
|
63
|
+
* @param graph - the input-driven graph (same shape the core consumes).
|
|
64
|
+
* @param textures - a `Spritesheet` or a frame-key → `Texture` map covering
|
|
65
|
+
* every frame the graph references.
|
|
66
|
+
* @param options - see {@link PixiSpriteAnimatorOptions}.
|
|
67
|
+
* @returns a {@link PixiSpriteAnimator}.
|
|
68
|
+
* @throws {@link MissingTextureError} if a referenced frame key has no texture.
|
|
69
|
+
* @throws {@link InvalidGraphError} if the graph is invalid.
|
|
70
|
+
*
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
declare function createPixiSpriteAnimator(sprite: Sprite, graph: SpriteGraph, textures: Spritesheet | TextureMap, options?: PixiSpriteAnimatorOptions): PixiSpriteAnimator;
|
|
74
|
+
|
|
75
|
+
export { MissingTextureError, type PixiSpriteAnimator, type PixiSpriteAnimatorOptions, type TextureMap, createPixiSpriteAnimator };
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Sprite, Texture, Spritesheet } from 'pixi.js';
|
|
2
|
+
import { b as SpriteGraph } from '../types-DKMFvfx9.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Thrown by {@link createPixiSpriteAnimator} when the supplied textures are
|
|
6
|
+
* missing one or more frame keys the graph's animations reference. Fail-fast at
|
|
7
|
+
* construction, so `update()` never has to guard.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
declare class MissingTextureError extends Error {
|
|
12
|
+
readonly keys: readonly string[];
|
|
13
|
+
constructor(keys: readonly string[]);
|
|
14
|
+
}
|
|
15
|
+
/** Frame-key → texture lookup. A PixiJS `Spritesheet` exposes one as `.textures`. */
|
|
16
|
+
type TextureMap = Record<string, Texture>;
|
|
17
|
+
/**
|
|
18
|
+
* Options for {@link createPixiSpriteAnimator}.
|
|
19
|
+
*
|
|
20
|
+
* @public
|
|
21
|
+
*/
|
|
22
|
+
interface PixiSpriteAnimatorOptions {
|
|
23
|
+
/**
|
|
24
|
+
* Apply each frame's atlas anchor (`texture.defaultAnchor`) to the sprite when
|
|
25
|
+
* the frame changes — preserving non-centre / foot pivots. Default `true`.
|
|
26
|
+
* Set `false` to manage the anchor yourself.
|
|
27
|
+
*/
|
|
28
|
+
readonly applyAnchor?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* A PixiJS-bound animator: the core machine plus a sprite whose texture tracks
|
|
32
|
+
* the active frame.
|
|
33
|
+
*
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
interface PixiSpriteAnimator {
|
|
37
|
+
/** The bound sprite, updated in place. */
|
|
38
|
+
readonly sprite: Sprite;
|
|
39
|
+
/** Run the core machine for `deltaMs`, then sync the sprite's texture. */
|
|
40
|
+
update(deltaMs: number): void;
|
|
41
|
+
/** Set a Number / Boolean input on the core machine. */
|
|
42
|
+
setInput(name: string, value: number | boolean): void;
|
|
43
|
+
/** Fire a Trigger on the core machine. */
|
|
44
|
+
fireTrigger(name: string): void;
|
|
45
|
+
/** Reset the core machine and re-sync the sprite. */
|
|
46
|
+
reset(): void;
|
|
47
|
+
/** Dispose the core machine. Idempotent. Does not destroy the sprite. */
|
|
48
|
+
dispose(): void;
|
|
49
|
+
/** Current state name. */
|
|
50
|
+
readonly activeState: string;
|
|
51
|
+
/** Current frame key. */
|
|
52
|
+
readonly activeFrameKey: string;
|
|
53
|
+
/** `true` once disposed. */
|
|
54
|
+
readonly disposed: boolean;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Bind an input-driven {@link SpriteGraph} to a PixiJS `Sprite`.
|
|
58
|
+
*
|
|
59
|
+
* @param sprite - a plain `Sprite` to drive; its `texture` (and, by default,
|
|
60
|
+
* `anchor`) are updated in place. The adapter owns frame selection, so if a
|
|
61
|
+
* *playing* `AnimatedSprite` is passed (it extends `Sprite`), its internal
|
|
62
|
+
* playback is stopped to stop it fighting the adapter for the texture.
|
|
63
|
+
* @param graph - the input-driven graph (same shape the core consumes).
|
|
64
|
+
* @param textures - a `Spritesheet` or a frame-key → `Texture` map covering
|
|
65
|
+
* every frame the graph references.
|
|
66
|
+
* @param options - see {@link PixiSpriteAnimatorOptions}.
|
|
67
|
+
* @returns a {@link PixiSpriteAnimator}.
|
|
68
|
+
* @throws {@link MissingTextureError} if a referenced frame key has no texture.
|
|
69
|
+
* @throws {@link InvalidGraphError} if the graph is invalid.
|
|
70
|
+
*
|
|
71
|
+
* @public
|
|
72
|
+
*/
|
|
73
|
+
declare function createPixiSpriteAnimator(sprite: Sprite, graph: SpriteGraph, textures: Spritesheet | TextureMap, options?: PixiSpriteAnimatorOptions): PixiSpriteAnimator;
|
|
74
|
+
|
|
75
|
+
export { MissingTextureError, type PixiSpriteAnimator, type PixiSpriteAnimatorOptions, type TextureMap, createPixiSpriteAnimator };
|
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
// src/sprite/errors.ts
|
|
2
|
+
var SpriteAnimatorDisposedError = class extends Error {
|
|
3
|
+
constructor() {
|
|
4
|
+
super("aispritejs: animator has been disposed; this operation is not allowed");
|
|
5
|
+
this.name = "SpriteAnimatorDisposedError";
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
var InvalidGraphError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(`aispritejs: invalid graph \u2014 ${message}`);
|
|
11
|
+
this.name = "InvalidGraphError";
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var UnknownInputError = class extends Error {
|
|
15
|
+
input;
|
|
16
|
+
constructor(input) {
|
|
17
|
+
super(`aispritejs: unknown input "${input}"; declare it in the graph's inputs block`);
|
|
18
|
+
this.name = "UnknownInputError";
|
|
19
|
+
this.input = input;
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
var InputTypeError = class extends Error {
|
|
23
|
+
input;
|
|
24
|
+
constructor(input, message) {
|
|
25
|
+
super(`aispritejs: input "${input}" \u2014 ${message}`);
|
|
26
|
+
this.name = "InputTypeError";
|
|
27
|
+
this.input = input;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
// src/sprite/compile.ts
|
|
32
|
+
var DEFAULT_FRAME_DURATION = 100;
|
|
33
|
+
function compileGraph(graph) {
|
|
34
|
+
const stateEntries = Object.entries(graph.states);
|
|
35
|
+
if (stateEntries.length === 0) {
|
|
36
|
+
throw new InvalidGraphError("states must declare at least one state");
|
|
37
|
+
}
|
|
38
|
+
for (const [name, def] of Object.entries(graph.inputs)) {
|
|
39
|
+
if (def.type !== "number" && def.type !== "boolean" && def.type !== "trigger") {
|
|
40
|
+
throw new InvalidGraphError(
|
|
41
|
+
`input "${name}" has unknown type "${def.type}"`
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const defaultDuration = graph.defaultFrameDuration ?? DEFAULT_FRAME_DURATION;
|
|
46
|
+
if (!(defaultDuration > 0)) {
|
|
47
|
+
throw new InvalidGraphError(`defaultFrameDuration must be > 0, got ${defaultDuration}`);
|
|
48
|
+
}
|
|
49
|
+
if (graph.frames) {
|
|
50
|
+
for (const [key, timing] of Object.entries(graph.frames)) {
|
|
51
|
+
if (timing.duration !== void 0 && !(timing.duration > 0)) {
|
|
52
|
+
throw new InvalidGraphError(`frame "${key}" duration must be > 0, got ${timing.duration}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const initial = graph.initial ?? stateEntries[0][0];
|
|
57
|
+
if (!(initial in graph.states)) {
|
|
58
|
+
throw new InvalidGraphError(`initial state "${initial}" is not declared`);
|
|
59
|
+
}
|
|
60
|
+
const states = /* @__PURE__ */ new Map();
|
|
61
|
+
for (const [name, st] of stateEntries) {
|
|
62
|
+
const frameKeys = graph.animations[st.animation];
|
|
63
|
+
if (frameKeys === void 0) {
|
|
64
|
+
throw new InvalidGraphError(`state "${name}" references unknown animation "${st.animation}"`);
|
|
65
|
+
}
|
|
66
|
+
if (frameKeys.length === 0) {
|
|
67
|
+
throw new InvalidGraphError(`animation "${st.animation}" (state "${name}") has no frames`);
|
|
68
|
+
}
|
|
69
|
+
const speed = st.speed ?? 1;
|
|
70
|
+
if (!(speed > 0)) {
|
|
71
|
+
throw new InvalidGraphError(`state "${name}" speed must be > 0, got ${speed}`);
|
|
72
|
+
}
|
|
73
|
+
const loop = st.loop === true;
|
|
74
|
+
if (loop && st.onEnd !== void 0) {
|
|
75
|
+
throw new InvalidGraphError(
|
|
76
|
+
`state "${name}" loops, so onEnd "${st.onEnd}" would never fire; set loop:false or drop onEnd`
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (st.onEnd !== void 0 && !(st.onEnd in graph.states)) {
|
|
80
|
+
throw new InvalidGraphError(`state "${name}" onEnd target "${st.onEnd}" is not declared`);
|
|
81
|
+
}
|
|
82
|
+
const cumulative = [];
|
|
83
|
+
let running = 0;
|
|
84
|
+
for (const key of frameKeys) {
|
|
85
|
+
running += graph.frames?.[key]?.duration ?? defaultDuration;
|
|
86
|
+
cumulative.push(running);
|
|
87
|
+
}
|
|
88
|
+
states.set(name, {
|
|
89
|
+
name,
|
|
90
|
+
animation: st.animation,
|
|
91
|
+
loop,
|
|
92
|
+
speed,
|
|
93
|
+
onEnd: st.onEnd,
|
|
94
|
+
frameKeys,
|
|
95
|
+
cumulative,
|
|
96
|
+
total: running
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
const compiled = [];
|
|
100
|
+
graph.transitions.forEach((t, order) => {
|
|
101
|
+
if (t.from !== "*" && !(t.from in graph.states)) {
|
|
102
|
+
throw new InvalidGraphError(`transition #${order} from "${t.from}" is not a declared state`);
|
|
103
|
+
}
|
|
104
|
+
if (!(t.to in graph.states)) {
|
|
105
|
+
throw new InvalidGraphError(`transition #${order} to "${t.to}" is not a declared state`);
|
|
106
|
+
}
|
|
107
|
+
const conditions = [];
|
|
108
|
+
const triggers = [];
|
|
109
|
+
for (const c of t.when ?? []) {
|
|
110
|
+
const def = graph.inputs[c.input];
|
|
111
|
+
if (def === void 0) {
|
|
112
|
+
throw new InvalidGraphError(
|
|
113
|
+
`transition #${order} condition references unknown input "${c.input}"`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
compileCondition(order, c.input, c.op, c.value, def.type, conditions, triggers);
|
|
117
|
+
}
|
|
118
|
+
compiled.push({
|
|
119
|
+
from: t.from,
|
|
120
|
+
to: t.to,
|
|
121
|
+
priority: t.priority ?? 0,
|
|
122
|
+
order,
|
|
123
|
+
conditions,
|
|
124
|
+
triggers
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
const candidatesByState = /* @__PURE__ */ new Map();
|
|
128
|
+
for (const [name] of stateEntries) {
|
|
129
|
+
const list = compiled.filter((t) => t.from === name || t.from === "*");
|
|
130
|
+
list.sort((a, b) => b.priority - a.priority || a.order - b.order);
|
|
131
|
+
candidatesByState.set(name, list);
|
|
132
|
+
}
|
|
133
|
+
return { initial, states, candidatesByState };
|
|
134
|
+
}
|
|
135
|
+
function compileCondition(order, input, op, value, kind, out, triggers) {
|
|
136
|
+
switch (op) {
|
|
137
|
+
case "Trigger": {
|
|
138
|
+
if (kind !== "trigger") {
|
|
139
|
+
throw new InvalidGraphError(
|
|
140
|
+
`transition #${order}: op Trigger requires a trigger input, but "${input}" is ${kind}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
if (value !== void 0) {
|
|
144
|
+
throw new InvalidGraphError(
|
|
145
|
+
`transition #${order}: Trigger condition on "${input}" must not carry a value`
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
out.push((s) => s.isPending(input));
|
|
149
|
+
triggers.push(input);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
case "GreaterThan":
|
|
153
|
+
case "LessThan": {
|
|
154
|
+
if (kind !== "number") {
|
|
155
|
+
throw new InvalidGraphError(
|
|
156
|
+
`transition #${order}: op ${op} requires a number input, but "${input}" is ${kind}`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (typeof value !== "number") {
|
|
160
|
+
throw new InvalidGraphError(
|
|
161
|
+
`transition #${order}: op ${op} on "${input}" needs a numeric value`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
const v = value;
|
|
165
|
+
out.push(
|
|
166
|
+
op === "GreaterThan" ? (s) => s.readNumber(input) > v : (s) => s.readNumber(input) < v
|
|
167
|
+
);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
case "Equals":
|
|
171
|
+
case "NotEquals": {
|
|
172
|
+
if (kind === "trigger") {
|
|
173
|
+
throw new InvalidGraphError(
|
|
174
|
+
`transition #${order}: op ${op} cannot apply to trigger input "${input}"`
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
if (kind === "number") {
|
|
178
|
+
if (typeof value !== "number") {
|
|
179
|
+
throw new InvalidGraphError(
|
|
180
|
+
`transition #${order}: op ${op} on number input "${input}" needs a numeric value`
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
const v2 = value;
|
|
184
|
+
out.push(
|
|
185
|
+
op === "Equals" ? (s) => s.readNumber(input) === v2 : (s) => s.readNumber(input) !== v2
|
|
186
|
+
);
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (typeof value !== "boolean") {
|
|
190
|
+
throw new InvalidGraphError(
|
|
191
|
+
`transition #${order}: op ${op} on boolean input "${input}" needs a boolean value`
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const v = value;
|
|
195
|
+
out.push(
|
|
196
|
+
op === "Equals" ? (s) => s.readBoolean(input) === v : (s) => s.readBoolean(input) !== v
|
|
197
|
+
);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
default:
|
|
201
|
+
throw new InvalidGraphError(`transition #${order}: unknown operator "${op}"`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// src/sprite/emitter.ts
|
|
206
|
+
function createSignal() {
|
|
207
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
208
|
+
function on(handler, options) {
|
|
209
|
+
if (options?.signal?.aborted) return () => {
|
|
210
|
+
};
|
|
211
|
+
let detachAbort;
|
|
212
|
+
const cleanup = () => {
|
|
213
|
+
listeners.delete(wrapped);
|
|
214
|
+
if (detachAbort) {
|
|
215
|
+
detachAbort();
|
|
216
|
+
detachAbort = void 0;
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
let wrapped = handler;
|
|
220
|
+
if (options?.once) {
|
|
221
|
+
wrapped = (payload) => {
|
|
222
|
+
cleanup();
|
|
223
|
+
handler(payload);
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
listeners.add(wrapped);
|
|
227
|
+
const sig = options?.signal;
|
|
228
|
+
if (sig) {
|
|
229
|
+
const onAbort = () => cleanup();
|
|
230
|
+
sig.addEventListener("abort", onAbort, { once: true });
|
|
231
|
+
detachAbort = () => sig.removeEventListener("abort", onAbort);
|
|
232
|
+
}
|
|
233
|
+
return cleanup;
|
|
234
|
+
}
|
|
235
|
+
function emit(payload) {
|
|
236
|
+
if (listeners.size === 0) return;
|
|
237
|
+
for (const fn of [...listeners]) fn(payload);
|
|
238
|
+
}
|
|
239
|
+
function clear() {
|
|
240
|
+
listeners.clear();
|
|
241
|
+
}
|
|
242
|
+
return { on, emit, clear };
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/sprite/inputs.ts
|
|
246
|
+
function createInputStore(inputs) {
|
|
247
|
+
const kinds = /* @__PURE__ */ new Map();
|
|
248
|
+
const numberDefaults = /* @__PURE__ */ new Map();
|
|
249
|
+
const booleanDefaults = /* @__PURE__ */ new Map();
|
|
250
|
+
const numbers = /* @__PURE__ */ new Map();
|
|
251
|
+
const booleans = /* @__PURE__ */ new Map();
|
|
252
|
+
const triggers = /* @__PURE__ */ new Map();
|
|
253
|
+
for (const [name, def] of Object.entries(inputs)) {
|
|
254
|
+
kinds.set(name, def.type);
|
|
255
|
+
if (def.type === "number") {
|
|
256
|
+
const d = def.default ?? 0;
|
|
257
|
+
numberDefaults.set(name, d);
|
|
258
|
+
numbers.set(name, d);
|
|
259
|
+
} else if (def.type === "boolean") {
|
|
260
|
+
const d = def.default ?? false;
|
|
261
|
+
booleanDefaults.set(name, d);
|
|
262
|
+
booleans.set(name, d);
|
|
263
|
+
} else {
|
|
264
|
+
triggers.set(name, false);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function setInput(name, value) {
|
|
268
|
+
const kind = kinds.get(name);
|
|
269
|
+
if (kind === void 0) throw new UnknownInputError(name);
|
|
270
|
+
if (kind === "trigger") {
|
|
271
|
+
throw new InputTypeError(name, "is a Trigger; use fireTrigger()");
|
|
272
|
+
}
|
|
273
|
+
if (kind === "number") {
|
|
274
|
+
if (typeof value !== "number") {
|
|
275
|
+
throw new InputTypeError(name, `expects a number, received ${typeof value}`);
|
|
276
|
+
}
|
|
277
|
+
if (Number.isNaN(value)) {
|
|
278
|
+
throw new InputTypeError(name, "cannot be set to NaN");
|
|
279
|
+
}
|
|
280
|
+
numbers.set(name, value);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
if (typeof value !== "boolean") {
|
|
284
|
+
throw new InputTypeError(name, `expects a boolean, received ${typeof value}`);
|
|
285
|
+
}
|
|
286
|
+
booleans.set(name, value);
|
|
287
|
+
}
|
|
288
|
+
function fireTrigger(name) {
|
|
289
|
+
const kind = kinds.get(name);
|
|
290
|
+
if (kind === void 0) throw new UnknownInputError(name);
|
|
291
|
+
if (kind !== "trigger") {
|
|
292
|
+
throw new InputTypeError(name, "is not a Trigger; use setInput()");
|
|
293
|
+
}
|
|
294
|
+
triggers.set(name, true);
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
setInput,
|
|
298
|
+
fireTrigger,
|
|
299
|
+
// `!` is sound: every declared input is seeded at construction and the
|
|
300
|
+
// compiler validates that conditions only read declared inputs of the
|
|
301
|
+
// matching kind, so these reads always hit a present entry.
|
|
302
|
+
readNumber: (name) => numbers.get(name),
|
|
303
|
+
readBoolean: (name) => booleans.get(name),
|
|
304
|
+
isPending: (name) => triggers.get(name) === true,
|
|
305
|
+
consume: (name) => {
|
|
306
|
+
triggers.set(name, false);
|
|
307
|
+
},
|
|
308
|
+
reset: () => {
|
|
309
|
+
for (const [name, d] of numberDefaults) numbers.set(name, d);
|
|
310
|
+
for (const [name, d] of booleanDefaults) booleans.set(name, d);
|
|
311
|
+
for (const name of triggers.keys()) triggers.set(name, false);
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// src/sprite/machine.ts
|
|
317
|
+
var NO_TRIGGERS = [];
|
|
318
|
+
function createSpriteAnimator(graph) {
|
|
319
|
+
const compiled = compileGraph(graph);
|
|
320
|
+
const store = createInputStore(graph.inputs);
|
|
321
|
+
const stateChange = createSignal();
|
|
322
|
+
const complete = createSignal();
|
|
323
|
+
let current = mustState(compiled.initial);
|
|
324
|
+
let elapsed = 0;
|
|
325
|
+
let completed = false;
|
|
326
|
+
let activeFrameIndex = 0;
|
|
327
|
+
let activeFrameKey = current.frameKeys[0];
|
|
328
|
+
let disposed = false;
|
|
329
|
+
function mustState(name) {
|
|
330
|
+
return compiled.states.get(name);
|
|
331
|
+
}
|
|
332
|
+
function enter(to, consume) {
|
|
333
|
+
const from = current.name;
|
|
334
|
+
for (let i = 0; i < consume.length; i++) {
|
|
335
|
+
store.consume(consume[i]);
|
|
336
|
+
}
|
|
337
|
+
current = mustState(to);
|
|
338
|
+
elapsed = 0;
|
|
339
|
+
completed = false;
|
|
340
|
+
activeFrameIndex = 0;
|
|
341
|
+
activeFrameKey = current.frameKeys[0];
|
|
342
|
+
if (to !== from) stateChange.emit({ to, from });
|
|
343
|
+
}
|
|
344
|
+
function resolve() {
|
|
345
|
+
const list = compiled.candidatesByState.get(current.name);
|
|
346
|
+
for (let i = 0; i < list.length; i++) {
|
|
347
|
+
const t = list[i];
|
|
348
|
+
let ok = true;
|
|
349
|
+
const conds = t.conditions;
|
|
350
|
+
for (let j = 0; j < conds.length; j++) {
|
|
351
|
+
if (!conds[j](store)) {
|
|
352
|
+
ok = false;
|
|
353
|
+
break;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
if (!ok) continue;
|
|
357
|
+
if (t.to === current.name && t.triggers.length === 0) continue;
|
|
358
|
+
return { to: t.to, triggers: t.triggers };
|
|
359
|
+
}
|
|
360
|
+
return void 0;
|
|
361
|
+
}
|
|
362
|
+
function update(deltaMs) {
|
|
363
|
+
if (disposed) throw new SpriteAnimatorDisposedError();
|
|
364
|
+
elapsed += (deltaMs > 0 ? deltaMs : 0) * current.speed;
|
|
365
|
+
const chosen = resolve();
|
|
366
|
+
if (chosen) {
|
|
367
|
+
enter(chosen.to, chosen.triggers);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
const cs = current;
|
|
371
|
+
const n = cs.frameKeys.length;
|
|
372
|
+
let ended = false;
|
|
373
|
+
if (!cs.loop && elapsed >= cs.total) {
|
|
374
|
+
activeFrameIndex = n - 1;
|
|
375
|
+
ended = true;
|
|
376
|
+
} else {
|
|
377
|
+
const t = cs.loop ? elapsed % cs.total : elapsed;
|
|
378
|
+
let i = 0;
|
|
379
|
+
while (i < n - 1 && t >= cs.cumulative[i]) i++;
|
|
380
|
+
activeFrameIndex = i;
|
|
381
|
+
}
|
|
382
|
+
activeFrameKey = cs.frameKeys[activeFrameIndex];
|
|
383
|
+
if (ended && !completed) {
|
|
384
|
+
completed = true;
|
|
385
|
+
complete.emit(cs.name);
|
|
386
|
+
if (cs.onEnd !== void 0) enter(cs.onEnd, NO_TRIGGERS);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
function reset() {
|
|
390
|
+
if (disposed) throw new SpriteAnimatorDisposedError();
|
|
391
|
+
const from = current.name;
|
|
392
|
+
store.reset();
|
|
393
|
+
current = mustState(compiled.initial);
|
|
394
|
+
elapsed = 0;
|
|
395
|
+
completed = false;
|
|
396
|
+
activeFrameIndex = 0;
|
|
397
|
+
activeFrameKey = current.frameKeys[0];
|
|
398
|
+
if (from !== compiled.initial) stateChange.emit({ to: compiled.initial, from });
|
|
399
|
+
}
|
|
400
|
+
function dispose() {
|
|
401
|
+
if (disposed) return;
|
|
402
|
+
disposed = true;
|
|
403
|
+
stateChange.clear();
|
|
404
|
+
complete.clear();
|
|
405
|
+
}
|
|
406
|
+
return {
|
|
407
|
+
setInput(name, value) {
|
|
408
|
+
if (disposed) throw new SpriteAnimatorDisposedError();
|
|
409
|
+
store.setInput(name, value);
|
|
410
|
+
},
|
|
411
|
+
fireTrigger(name) {
|
|
412
|
+
if (disposed) throw new SpriteAnimatorDisposedError();
|
|
413
|
+
store.fireTrigger(name);
|
|
414
|
+
},
|
|
415
|
+
update,
|
|
416
|
+
reset,
|
|
417
|
+
dispose,
|
|
418
|
+
onStateChange(handler, options) {
|
|
419
|
+
if (disposed) return () => {
|
|
420
|
+
};
|
|
421
|
+
return stateChange.on((p) => handler(p.to, p.from), options);
|
|
422
|
+
},
|
|
423
|
+
onComplete(handler, options) {
|
|
424
|
+
if (disposed) return () => {
|
|
425
|
+
};
|
|
426
|
+
return complete.on(handler, options);
|
|
427
|
+
},
|
|
428
|
+
get activeState() {
|
|
429
|
+
return current.name;
|
|
430
|
+
},
|
|
431
|
+
get activeFrameKey() {
|
|
432
|
+
return activeFrameKey;
|
|
433
|
+
},
|
|
434
|
+
get activeFrameIndex() {
|
|
435
|
+
return activeFrameIndex;
|
|
436
|
+
},
|
|
437
|
+
get disposed() {
|
|
438
|
+
return disposed;
|
|
439
|
+
}
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// src/pixi/animator.ts
|
|
444
|
+
var MissingTextureError = class extends Error {
|
|
445
|
+
keys;
|
|
446
|
+
constructor(keys) {
|
|
447
|
+
super(`aispritejs/pixi: no texture for frame key(s): ${keys.join(", ")}`);
|
|
448
|
+
this.name = "MissingTextureError";
|
|
449
|
+
this.keys = keys;
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
function toTextureMap(src) {
|
|
453
|
+
const maybe = src;
|
|
454
|
+
if (maybe.textures && typeof maybe.textures === "object") {
|
|
455
|
+
return maybe.textures;
|
|
456
|
+
}
|
|
457
|
+
return src;
|
|
458
|
+
}
|
|
459
|
+
function createPixiSpriteAnimator(sprite, graph, textures, options) {
|
|
460
|
+
const map = toTextureMap(textures);
|
|
461
|
+
const applyAnchor = options?.applyAnchor !== false;
|
|
462
|
+
const playable = sprite;
|
|
463
|
+
if (typeof playable.stop === "function") playable.stop();
|
|
464
|
+
const missing = /* @__PURE__ */ new Set();
|
|
465
|
+
for (const frameKeys of Object.values(graph.animations)) {
|
|
466
|
+
for (const key of frameKeys) {
|
|
467
|
+
if (!(key in map)) missing.add(key);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (missing.size > 0) throw new MissingTextureError([...missing]);
|
|
471
|
+
const core = createSpriteAnimator(graph);
|
|
472
|
+
let boundKey = "";
|
|
473
|
+
function sync() {
|
|
474
|
+
const key = core.activeFrameKey;
|
|
475
|
+
if (key === boundKey) return;
|
|
476
|
+
boundKey = key;
|
|
477
|
+
const tex = map[key];
|
|
478
|
+
sprite.texture = tex;
|
|
479
|
+
if (applyAnchor && tex.defaultAnchor) {
|
|
480
|
+
sprite.anchor.set(tex.defaultAnchor.x, tex.defaultAnchor.y);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
sync();
|
|
484
|
+
return {
|
|
485
|
+
sprite,
|
|
486
|
+
update(deltaMs) {
|
|
487
|
+
core.update(deltaMs);
|
|
488
|
+
sync();
|
|
489
|
+
},
|
|
490
|
+
setInput(name, value) {
|
|
491
|
+
core.setInput(name, value);
|
|
492
|
+
},
|
|
493
|
+
fireTrigger(name) {
|
|
494
|
+
core.fireTrigger(name);
|
|
495
|
+
},
|
|
496
|
+
reset() {
|
|
497
|
+
core.reset();
|
|
498
|
+
sync();
|
|
499
|
+
},
|
|
500
|
+
dispose() {
|
|
501
|
+
core.dispose();
|
|
502
|
+
},
|
|
503
|
+
get activeState() {
|
|
504
|
+
return core.activeState;
|
|
505
|
+
},
|
|
506
|
+
get activeFrameKey() {
|
|
507
|
+
return core.activeFrameKey;
|
|
508
|
+
},
|
|
509
|
+
get disposed() {
|
|
510
|
+
return core.disposed;
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export { MissingTextureError, createPixiSpriteAnimator };
|
|
516
|
+
//# sourceMappingURL=index.js.map
|
|
517
|
+
//# sourceMappingURL=index.js.map
|