@datamoc/mw_games 0.31.0 → 0.33.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/CHANGELOG.md +67 -0
- package/api-surface.json +312 -0
- package/dist/assets/index.d.ts +2 -0
- package/dist/assets/index.d.ts.map +1 -1
- package/dist/assets/index.js +1 -0
- package/dist/assets/index.js.map +1 -1
- package/dist/assets/svg.d.ts +87 -0
- package/dist/assets/svg.d.ts.map +1 -0
- package/dist/assets/svg.js +162 -0
- package/dist/assets/svg.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mw_games.global.js +41 -41
- package/dist/mwl/fengari.d.ts +12 -6
- package/dist/mwl/fengari.d.ts.map +1 -1
- package/dist/mwl/fengari.js +56 -70
- package/dist/mwl/fengari.js.map +1 -1
- package/dist/mwl/runtime.d.ts +13 -0
- package/dist/mwl/runtime.d.ts.map +1 -1
- package/dist/mwl/runtime.js +72 -4
- package/dist/mwl/runtime.js.map +1 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/schema.js +4 -1
- package/dist/mwl/schema.js.map +1 -1
- package/dist/roguelike/Cave.d.ts +109 -0
- package/dist/roguelike/Cave.d.ts.map +1 -0
- package/dist/roguelike/Cave.js +192 -0
- package/dist/roguelike/Cave.js.map +1 -0
- package/dist/roguelike/Overworld.d.ts +82 -0
- package/dist/roguelike/Overworld.d.ts.map +1 -0
- package/dist/roguelike/Overworld.js +306 -0
- package/dist/roguelike/Overworld.js.map +1 -0
- package/dist/roguelike/index.d.ts +4 -0
- package/dist/roguelike/index.d.ts.map +1 -1
- package/dist/roguelike/index.js +2 -0
- package/dist/roguelike/index.js.map +1 -1
- package/dist/skeleton/index.d.ts +131 -0
- package/dist/skeleton/index.d.ts.map +1 -0
- package/dist/skeleton/index.js +206 -0
- package/dist/skeleton/index.js.map +1 -0
- package/dist/testing/SceneControls.d.ts +35 -0
- package/dist/testing/SceneControls.d.ts.map +1 -0
- package/dist/testing/SceneControls.js +49 -0
- package/dist/testing/SceneControls.js.map +1 -0
- package/dist/testing/index.d.ts +2 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +1 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/two-d/render/SkeletalSprite.d.ts +118 -0
- package/dist/two-d/render/SkeletalSprite.d.ts.map +1 -0
- package/dist/two-d/render/SkeletalSprite.js +189 -0
- package/dist/two-d/render/SkeletalSprite.js.map +1 -0
- package/dist/two-d/render/index.d.ts +2 -0
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +1 -0
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +7 -2
- package/sbom.cdx.json +222 -222
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/roguelike/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/roguelike/index.ts"],"names":[],"mappings":"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"}
|
package/dist/roguelike/index.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { Level, WALL, FLOOR, rectCenter, rectsOverlap } from './Level.js';
|
|
2
2
|
export { generateDungeon, generateDungeonGraph, findFreeCell, furthestRoom, DUNGEON_KINDS } from './generate.js';
|
|
3
|
+
export { generateCave } from './Cave.js';
|
|
4
|
+
export { generateOverworld } from './Overworld.js';
|
|
3
5
|
export { hallBuilder, eligibleBuilders, pickBuilder } from './RoomBuilders.js';
|
|
4
6
|
export { FeatureLayer } from './Features.js';
|
|
5
7
|
export { rollRoster } from './ContentRoll.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/roguelike/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/roguelike/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1E,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACjH,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAWnD,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAG/E,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAG7C,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAG9C,OAAO,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAG/E,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAG/D,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG3C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,OAAO,EACN,iBAAiB,EACjB,SAAS,EACT,UAAU,EACV,cAAc,EACd,SAAS,EACT,WAAW,EACX,kBAAkB,EAClB,YAAY,GACZ,MAAM,gBAAgB,CAAC;AAExB,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAEhH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAGrD,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAG1C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAGvC,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAErD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AASnD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeletal pose math, renderer-free: a bone tree, keyframed clips, and layered blending.
|
|
3
|
+
*
|
|
4
|
+
* The pose half of a skeletal puppet (roadmap 399), as pure data and arithmetic so it loads
|
|
5
|
+
* headless and tests without a renderer - the same split every other module draws. A bone
|
|
6
|
+
* is a name, a parent name, a length and a local rotation; a pose is one rotation per bone;
|
|
7
|
+
* resolving a pose walks the tree so rotating an arm, a leg or a head is one number on one
|
|
8
|
+
* bone and the rest of the body follows. Clips are keyframed poses over time - plain data a
|
|
9
|
+
* game authors in JSON, never code, and never a borrowed game's animation data - with
|
|
10
|
+
* interpolation, looping, a playback rate and a crossfade between clips. Additive layers
|
|
11
|
+
* (aim on top of walk, hurt on top of idle) mix by priority, so a game composes motion
|
|
12
|
+
* instead of authoring every combination as its own clip.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import * as Skeleton from '@datamoc/mw_games/skeleton';
|
|
17
|
+
*
|
|
18
|
+
* const puppet = Skeleton.create([
|
|
19
|
+
* { name: 'body' },
|
|
20
|
+
* { name: 'arm', parent: 'body', length: 12 },
|
|
21
|
+
* ]);
|
|
22
|
+
* const mixer = Skeleton.createMixer(puppet, { id: 'idle', duration: 2, loop: true, frames: [
|
|
23
|
+
* { time: 0, rotations: {} },
|
|
24
|
+
* { time: 1, rotations: { arm: 90 } },
|
|
25
|
+
* ] });
|
|
26
|
+
* Skeleton.advance(mixer, 1); // the arm swings up over one second
|
|
27
|
+
* const pose = Skeleton.advance(mixer, 1); // the arm swings up over one second
|
|
28
|
+
* const arm = Skeleton.resolve(puppet, pose)[1]; // world x, y and angle per bone
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
export interface Bone {
|
|
32
|
+
/** unique within one skeleton */
|
|
33
|
+
name: string;
|
|
34
|
+
/** the parent bone's name; the roots have none */
|
|
35
|
+
parent?: string;
|
|
36
|
+
/** how far the bone reaches from its base, world units */
|
|
37
|
+
length?: number;
|
|
38
|
+
/** the bone's own offset from its parent's tip, before any pose */
|
|
39
|
+
origin?: {
|
|
40
|
+
x: number;
|
|
41
|
+
y: number;
|
|
42
|
+
};
|
|
43
|
+
/** the bone's rest rotation, degrees */
|
|
44
|
+
rotation?: number;
|
|
45
|
+
}
|
|
46
|
+
export interface SkeletonBones {
|
|
47
|
+
bones: readonly Bone[];
|
|
48
|
+
}
|
|
49
|
+
/** one rotation per bone name, degrees; a bone left out keeps its rest rotation */
|
|
50
|
+
export type Pose = Readonly<Record<string, number>>;
|
|
51
|
+
export interface Keyframe {
|
|
52
|
+
/** seconds into the clip */
|
|
53
|
+
time: number;
|
|
54
|
+
rotations: Pose;
|
|
55
|
+
}
|
|
56
|
+
export interface Clip {
|
|
57
|
+
id: string;
|
|
58
|
+
/** seconds; a looping clip wraps at it, a one-shot holds its last frame */
|
|
59
|
+
duration: number;
|
|
60
|
+
loop?: boolean;
|
|
61
|
+
/** ascending by time; the first needs no time 0 entry, the last needs no duration entry */
|
|
62
|
+
frames: readonly Keyframe[];
|
|
63
|
+
}
|
|
64
|
+
/** a clip in motion: its playhead and rate */
|
|
65
|
+
export interface Playback {
|
|
66
|
+
clip: Clip;
|
|
67
|
+
time: number;
|
|
68
|
+
/** multiplied per advance; negative plays backwards, 0 freezes */
|
|
69
|
+
rate: number;
|
|
70
|
+
}
|
|
71
|
+
export interface Layer {
|
|
72
|
+
playback: Playback;
|
|
73
|
+
/** 0..1, how far this layer's pose displaces the pose beneath it */
|
|
74
|
+
weight: number;
|
|
75
|
+
/** layers apply in ascending priority; same-priority layers keep their given order */
|
|
76
|
+
priority: number;
|
|
77
|
+
}
|
|
78
|
+
export interface Mixer {
|
|
79
|
+
skeleton: SkeletonBones;
|
|
80
|
+
/** the base motion, always present */
|
|
81
|
+
base: Playback;
|
|
82
|
+
/** additive layers over the base */
|
|
83
|
+
layers: Layer[];
|
|
84
|
+
/** while set, the previous base fades out as the new one fades in */
|
|
85
|
+
crossfade?: {
|
|
86
|
+
from: Playback;
|
|
87
|
+
remaining: number;
|
|
88
|
+
duration: number;
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export interface BoneTransform {
|
|
92
|
+
name: string;
|
|
93
|
+
/** the bone's base in world space */
|
|
94
|
+
x: number;
|
|
95
|
+
y: number;
|
|
96
|
+
/** the bone's angle in world space, degrees */
|
|
97
|
+
angle: number;
|
|
98
|
+
/** the bone's tip in world space, `length` along its angle */
|
|
99
|
+
tipX: number;
|
|
100
|
+
tipY: number;
|
|
101
|
+
}
|
|
102
|
+
/** names in dependency order: every bone after its parent, so resolution is one pass */
|
|
103
|
+
export declare function boneOrder(skeleton: SkeletonBones): string[];
|
|
104
|
+
/** create a skeleton, refusing duplicate names and cycles by name */
|
|
105
|
+
export declare function create(bones: readonly Bone[]): SkeletonBones;
|
|
106
|
+
/**
|
|
107
|
+
* A pose through the tree: each bone starts at its parent's tip plus its origin, rotated by
|
|
108
|
+
* its rest rotation plus its pose rotation plus every ancestor's world rotation.
|
|
109
|
+
*/
|
|
110
|
+
export declare function resolve(skeleton: SkeletonBones, pose?: Pose): BoneTransform[];
|
|
111
|
+
/** the pose a clip shows at `time`: its frames interpolated, looped or held */
|
|
112
|
+
export declare function samplePose(clip: Clip, time: number): Record<string, number>;
|
|
113
|
+
/** move a playback's playhead, wrapping a looping clip and clamping a one-shot */
|
|
114
|
+
export declare function advancePlayback(playback: Playback, dt: number): Playback;
|
|
115
|
+
/** a mixer over a skeleton: one base clip, any number of weighted layers */
|
|
116
|
+
export declare function createMixer(skeleton: SkeletonBones, base: Clip, rate?: number): Mixer;
|
|
117
|
+
/** crossfade: the current base fades out over `duration` seconds as the new one fades in */
|
|
118
|
+
export declare function crossfadeTo(mixer: Mixer, clip: Clip, duration?: number, rate?: number): void;
|
|
119
|
+
/** mix poses by layer priority: each layer displaces what is beneath it by its weight */
|
|
120
|
+
export declare function mixPoses(base: Pose, layers: ReadonlyArray<{
|
|
121
|
+
pose: Pose;
|
|
122
|
+
weight: number;
|
|
123
|
+
priority: number;
|
|
124
|
+
}>): Record<string, number>;
|
|
125
|
+
/**
|
|
126
|
+
* One tick: advance every playback, sample the base (crossfaded when a fade is running),
|
|
127
|
+
* layer the additive poses over it, and return the pose. Call `resolve` on the result when
|
|
128
|
+
* the world transforms are what the drawing side needs.
|
|
129
|
+
*/
|
|
130
|
+
export declare function advance(mixer: Mixer, dt: number): Record<string, number>;
|
|
131
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/skeleton/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skeletal pose math, renderer-free: a bone tree, keyframed clips, and layered blending.
|
|
3
|
+
*
|
|
4
|
+
* The pose half of a skeletal puppet (roadmap 399), as pure data and arithmetic so it loads
|
|
5
|
+
* headless and tests without a renderer - the same split every other module draws. A bone
|
|
6
|
+
* is a name, a parent name, a length and a local rotation; a pose is one rotation per bone;
|
|
7
|
+
* resolving a pose walks the tree so rotating an arm, a leg or a head is one number on one
|
|
8
|
+
* bone and the rest of the body follows. Clips are keyframed poses over time - plain data a
|
|
9
|
+
* game authors in JSON, never code, and never a borrowed game's animation data - with
|
|
10
|
+
* interpolation, looping, a playback rate and a crossfade between clips. Additive layers
|
|
11
|
+
* (aim on top of walk, hurt on top of idle) mix by priority, so a game composes motion
|
|
12
|
+
* instead of authoring every combination as its own clip.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import * as Skeleton from '@datamoc/mw_games/skeleton';
|
|
17
|
+
*
|
|
18
|
+
* const puppet = Skeleton.create([
|
|
19
|
+
* { name: 'body' },
|
|
20
|
+
* { name: 'arm', parent: 'body', length: 12 },
|
|
21
|
+
* ]);
|
|
22
|
+
* const mixer = Skeleton.createMixer(puppet, { id: 'idle', duration: 2, loop: true, frames: [
|
|
23
|
+
* { time: 0, rotations: {} },
|
|
24
|
+
* { time: 1, rotations: { arm: 90 } },
|
|
25
|
+
* ] });
|
|
26
|
+
* Skeleton.advance(mixer, 1); // the arm swings up over one second
|
|
27
|
+
* const pose = Skeleton.advance(mixer, 1); // the arm swings up over one second
|
|
28
|
+
* const arm = Skeleton.resolve(puppet, pose)[1]; // world x, y and angle per bone
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
/** names in dependency order: every bone after its parent, so resolution is one pass */
|
|
32
|
+
export function boneOrder(skeleton) {
|
|
33
|
+
const byName = new Map(skeleton.bones.map((bone) => [bone.name, bone]));
|
|
34
|
+
const order = [];
|
|
35
|
+
const placed = new Set();
|
|
36
|
+
const visit = (bone) => {
|
|
37
|
+
if (placed.has(bone.name))
|
|
38
|
+
return;
|
|
39
|
+
if (bone.parent && !byName.has(bone.parent))
|
|
40
|
+
throw new Error(`skeleton bone ${bone.name} names no parent ${bone.parent}`);
|
|
41
|
+
if (bone.parent)
|
|
42
|
+
visit(byName.get(bone.parent));
|
|
43
|
+
if (!placed.has(bone.name)) {
|
|
44
|
+
placed.add(bone.name);
|
|
45
|
+
order.push(bone.name);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
for (const bone of skeleton.bones)
|
|
49
|
+
visit(bone);
|
|
50
|
+
return order;
|
|
51
|
+
}
|
|
52
|
+
/** create a skeleton, refusing duplicate names and cycles by name */
|
|
53
|
+
export function create(bones) {
|
|
54
|
+
const names = new Set();
|
|
55
|
+
for (const bone of bones) {
|
|
56
|
+
if (names.has(bone.name))
|
|
57
|
+
throw new Error(`duplicate skeleton bone ${bone.name}`);
|
|
58
|
+
names.add(bone.name);
|
|
59
|
+
}
|
|
60
|
+
const order = boneOrder({ bones });
|
|
61
|
+
//boneOrder throws on a missing parent, which is also what a cycle surfaces as here
|
|
62
|
+
for (const bone of bones)
|
|
63
|
+
if (bone.parent && order.indexOf(bone.parent) >= order.indexOf(bone.name))
|
|
64
|
+
throw new Error(`skeleton bone ${bone.name} cannot precede its parent ${bone.parent}`);
|
|
65
|
+
return { bones };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A pose through the tree: each bone starts at its parent's tip plus its origin, rotated by
|
|
69
|
+
* its rest rotation plus its pose rotation plus every ancestor's world rotation.
|
|
70
|
+
*/
|
|
71
|
+
export function resolve(skeleton, pose = {}) {
|
|
72
|
+
const byName = new Map(skeleton.bones.map((bone) => [bone.name, bone]));
|
|
73
|
+
const world = new Map();
|
|
74
|
+
for (const name of boneOrder(skeleton)) {
|
|
75
|
+
const bone = byName.get(name);
|
|
76
|
+
const parent = bone.parent ? world.get(bone.parent) : undefined;
|
|
77
|
+
const rotation = (bone.rotation ?? 0) + (pose[name] ?? 0);
|
|
78
|
+
const angle = parent ? parent.angle + rotation : rotation;
|
|
79
|
+
const originX = bone.origin?.x ?? 0;
|
|
80
|
+
const originY = bone.origin?.y ?? 0;
|
|
81
|
+
const x = parent ? parent.tipX : 0;
|
|
82
|
+
const y = parent ? parent.tipY : 0;
|
|
83
|
+
const radians = (angle * Math.PI) / 180;
|
|
84
|
+
world.set(name, {
|
|
85
|
+
name,
|
|
86
|
+
x,
|
|
87
|
+
y,
|
|
88
|
+
angle,
|
|
89
|
+
tipX: x + originX + Math.cos(radians) * (bone.length ?? 0),
|
|
90
|
+
tipY: y + originY + Math.sin(radians) * (bone.length ?? 0),
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return [...world.values()];
|
|
94
|
+
}
|
|
95
|
+
/** the pose a clip shows at `time`: its frames interpolated, looped or held */
|
|
96
|
+
export function samplePose(clip, time) {
|
|
97
|
+
const frames = clip.frames;
|
|
98
|
+
if (frames.length === 0)
|
|
99
|
+
return {};
|
|
100
|
+
let clock = time;
|
|
101
|
+
if (clip.loop && clip.duration > 0) {
|
|
102
|
+
clock = clock % clip.duration;
|
|
103
|
+
if (clock < 0)
|
|
104
|
+
clock += clip.duration;
|
|
105
|
+
}
|
|
106
|
+
else if (clock >= clip.duration) {
|
|
107
|
+
//a one-shot holds its last frame past the end
|
|
108
|
+
return { ...frames[frames.length - 1].rotations };
|
|
109
|
+
}
|
|
110
|
+
else if (clock < 0) {
|
|
111
|
+
return { ...frames[0].rotations };
|
|
112
|
+
}
|
|
113
|
+
let previous = frames[0];
|
|
114
|
+
for (let index = 1; index < frames.length; index++) {
|
|
115
|
+
if (frames[index].time > clock) {
|
|
116
|
+
const span = frames[index].time - previous.time;
|
|
117
|
+
const t = span > 0 ? (clock - previous.time) / span : 0;
|
|
118
|
+
const pose = {};
|
|
119
|
+
const names = new Set([...Object.keys(previous.rotations), ...Object.keys(frames[index].rotations)]);
|
|
120
|
+
for (const name of names) {
|
|
121
|
+
const from = previous.rotations[name] ?? 0;
|
|
122
|
+
const to = frames[index].rotations[name] ?? 0;
|
|
123
|
+
pose[name] = from + (to - from) * t;
|
|
124
|
+
}
|
|
125
|
+
return pose;
|
|
126
|
+
}
|
|
127
|
+
previous = frames[index];
|
|
128
|
+
}
|
|
129
|
+
return { ...previous.rotations };
|
|
130
|
+
}
|
|
131
|
+
/** move a playback's playhead, wrapping a looping clip and clamping a one-shot */
|
|
132
|
+
export function advancePlayback(playback, dt) {
|
|
133
|
+
const clip = playback.clip;
|
|
134
|
+
let time = playback.time + dt * playback.rate;
|
|
135
|
+
if (clip.loop && clip.duration > 0) {
|
|
136
|
+
time = time % clip.duration;
|
|
137
|
+
if (time < 0)
|
|
138
|
+
time += clip.duration;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
time = Math.max(0, Math.min(clip.duration, time));
|
|
142
|
+
}
|
|
143
|
+
return { clip, time, rate: playback.rate };
|
|
144
|
+
}
|
|
145
|
+
/** a mixer over a skeleton: one base clip, any number of weighted layers */
|
|
146
|
+
export function createMixer(skeleton, base, rate = 1) {
|
|
147
|
+
return { skeleton, base: { clip: base, time: 0, rate }, layers: [] };
|
|
148
|
+
}
|
|
149
|
+
/** crossfade: the current base fades out over `duration` seconds as the new one fades in */
|
|
150
|
+
export function crossfadeTo(mixer, clip, duration = 0.25, rate = mixer.base.rate) {
|
|
151
|
+
if (duration <= 0) {
|
|
152
|
+
mixer.base = { clip, time: 0, rate };
|
|
153
|
+
mixer.crossfade = undefined;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
mixer.crossfade = { from: mixer.base, remaining: duration, duration };
|
|
157
|
+
mixer.base = { clip, time: 0, rate };
|
|
158
|
+
}
|
|
159
|
+
/** mix poses by layer priority: each layer displaces what is beneath it by its weight */
|
|
160
|
+
export function mixPoses(base, layers) {
|
|
161
|
+
const mixed = { ...base };
|
|
162
|
+
const sorted = [...layers].sort((a, b) => a.priority - b.priority);
|
|
163
|
+
for (const layer of sorted) {
|
|
164
|
+
const weight = Math.max(0, Math.min(1, layer.weight));
|
|
165
|
+
if (weight === 0)
|
|
166
|
+
continue;
|
|
167
|
+
//a layer displaces only the bones it names: an unnamed bone keeps whatever
|
|
168
|
+
//the layers beneath it decided, so 'aim' never resets the legs to rest
|
|
169
|
+
for (const name of Object.keys(layer.pose)) {
|
|
170
|
+
const from = mixed[name] ?? 0;
|
|
171
|
+
const to = layer.pose[name];
|
|
172
|
+
mixed[name] = from + (to - from) * weight;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return mixed;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* One tick: advance every playback, sample the base (crossfaded when a fade is running),
|
|
179
|
+
* layer the additive poses over it, and return the pose. Call `resolve` on the result when
|
|
180
|
+
* the world transforms are what the drawing side needs.
|
|
181
|
+
*/
|
|
182
|
+
export function advance(mixer, dt) {
|
|
183
|
+
mixer.base = advancePlayback(mixer.base, dt);
|
|
184
|
+
for (const layer of mixer.layers)
|
|
185
|
+
layer.playback = advancePlayback(layer.playback, dt);
|
|
186
|
+
let pose = samplePose(mixer.base.clip, mixer.base.time);
|
|
187
|
+
if (mixer.crossfade) {
|
|
188
|
+
const fade = mixer.crossfade;
|
|
189
|
+
fade.from = advancePlayback(fade.from, dt);
|
|
190
|
+
fade.remaining = Math.max(0, fade.remaining - dt);
|
|
191
|
+
const outgoing = samplePose(fade.from.clip, fade.from.time);
|
|
192
|
+
const t = fade.duration > 0 ? 1 - fade.remaining / fade.duration : 1;
|
|
193
|
+
const blended = {};
|
|
194
|
+
for (const name of new Set([...Object.keys(outgoing), ...Object.keys(pose)]))
|
|
195
|
+
blended[name] = (outgoing[name] ?? 0) * (1 - t) + (pose[name] ?? 0) * t;
|
|
196
|
+
pose = blended;
|
|
197
|
+
if (fade.remaining === 0)
|
|
198
|
+
mixer.crossfade = undefined;
|
|
199
|
+
}
|
|
200
|
+
return mixPoses(pose, mixer.layers.map((layer) => ({
|
|
201
|
+
pose: samplePose(layer.playback.clip, layer.playback.time),
|
|
202
|
+
weight: layer.weight,
|
|
203
|
+
priority: layer.priority,
|
|
204
|
+
})));
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/skeleton/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scene control API for tests (roadmap 408): named steps a game opts into, driven by name.
|
|
3
|
+
*
|
|
4
|
+
* A game's own scene flow - enter a scenario, finish a turn, advance a dialogue - is private
|
|
5
|
+
* to the game, and that is the point of this seam: instead of the framework reaching into a
|
|
6
|
+
* game, the game registers the steps it wants a test or a headless Chrome to be able to
|
|
7
|
+
* drive, by name, on a `SceneControls`. The game decides what a step does; the controls only
|
|
8
|
+
* promise that a registered name runs its step and an unknown name is refused by name.
|
|
9
|
+
*
|
|
10
|
+
* Kept off the root barrel with the rest of `src/testing`: nothing here reaches a shipped
|
|
11
|
+
* game unless a test imports it.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { createSceneControls } from '@datamoc/mw_games/testing';
|
|
16
|
+
*
|
|
17
|
+
* const controls = createSceneControls();
|
|
18
|
+
* const enterScenario = (name: string) => {}; // the game's own private scene flow
|
|
19
|
+
* controls.register('enterScenario', () => enterScenario('the-freelands'));
|
|
20
|
+
* controls.register('finishTurn', () => {});
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export type SceneStep = () => void | Promise<void>;
|
|
24
|
+
export interface SceneControls {
|
|
25
|
+
/** one step by name; a duplicate name is refused rather than overwritten */
|
|
26
|
+
register(step: string, run: SceneStep): void;
|
|
27
|
+
/** whether a step is registered */
|
|
28
|
+
has(step: string): boolean;
|
|
29
|
+
/** every registered name, in registration order */
|
|
30
|
+
steps(): string[];
|
|
31
|
+
/** run one step, refusing an unknown name; awaits a step that returns a promise */
|
|
32
|
+
run(step: string): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export declare function createSceneControls(): SceneControls;
|
|
35
|
+
//# sourceMappingURL=SceneControls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SceneControls.d.ts","sourceRoot":"","sources":["../../src/testing/SceneControls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,MAAM,MAAM,SAAS,GAAG,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnD,MAAM,WAAW,aAAa;IAC7B,4EAA4E;IAC5E,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7C,mCAAmC;IACnC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,mDAAmD;IACnD,KAAK,IAAI,MAAM,EAAE,CAAC;IAClB,mFAAmF;IACnF,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACjC;AAED,wBAAgB,mBAAmB,IAAI,aAAa,CAsBnD"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A scene control API for tests (roadmap 408): named steps a game opts into, driven by name.
|
|
3
|
+
*
|
|
4
|
+
* A game's own scene flow - enter a scenario, finish a turn, advance a dialogue - is private
|
|
5
|
+
* to the game, and that is the point of this seam: instead of the framework reaching into a
|
|
6
|
+
* game, the game registers the steps it wants a test or a headless Chrome to be able to
|
|
7
|
+
* drive, by name, on a `SceneControls`. The game decides what a step does; the controls only
|
|
8
|
+
* promise that a registered name runs its step and an unknown name is refused by name.
|
|
9
|
+
*
|
|
10
|
+
* Kept off the root barrel with the rest of `src/testing`: nothing here reaches a shipped
|
|
11
|
+
* game unless a test imports it.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* import { createSceneControls } from '@datamoc/mw_games/testing';
|
|
16
|
+
*
|
|
17
|
+
* const controls = createSceneControls();
|
|
18
|
+
* const enterScenario = (name: string) => {}; // the game's own private scene flow
|
|
19
|
+
* controls.register('enterScenario', () => enterScenario('the-freelands'));
|
|
20
|
+
* controls.register('finishTurn', () => {});
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export function createSceneControls() {
|
|
24
|
+
const registry = new Map();
|
|
25
|
+
const order = [];
|
|
26
|
+
return {
|
|
27
|
+
register(step, run) {
|
|
28
|
+
if (!step)
|
|
29
|
+
throw new Error('a scene control step needs a name');
|
|
30
|
+
if (registry.has(step))
|
|
31
|
+
throw new Error(`scene control step already registered: ${step}`);
|
|
32
|
+
registry.set(step, run);
|
|
33
|
+
order.push(step);
|
|
34
|
+
},
|
|
35
|
+
has(step) {
|
|
36
|
+
return registry.has(step);
|
|
37
|
+
},
|
|
38
|
+
steps() {
|
|
39
|
+
return [...order];
|
|
40
|
+
},
|
|
41
|
+
async run(step) {
|
|
42
|
+
const run2 = registry.get(step);
|
|
43
|
+
if (!run2)
|
|
44
|
+
throw new Error(`scene control step is not registered: ${step}`);
|
|
45
|
+
await run2();
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=SceneControls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SceneControls.js","sourceRoot":"","sources":["../../src/testing/SceneControls.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAeH,MAAM,UAAU,mBAAmB;IAClC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC9C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,OAAO;QACN,QAAQ,CAAC,IAAI,EAAE,GAAG;YACjB,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;YAChE,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,IAAI,EAAE,CAAC,CAAC;YAC1F,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,CAAC;QACD,GAAG,CAAC,IAAI;YACP,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3B,CAAC;QACD,KAAK;YACJ,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC;QACnB,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,IAAI;YACb,MAAM,IAAI,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,EAAE,CAAC,CAAC;YAC5E,MAAM,IAAI,EAAE,CAAC;QACd,CAAC;KACD,CAAC;AACH,CAAC"}
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -102,4 +102,6 @@ export declare function fakeFetch(respond: (url: string, init?: RequestInit) =>
|
|
|
102
102
|
* would.
|
|
103
103
|
*/
|
|
104
104
|
export declare function fakeGamepad(index: number, buttons?: number[], axes?: number[]): Gamepad;
|
|
105
|
+
export { createSceneControls } from './SceneControls.ts';
|
|
106
|
+
export type { SceneControls, SceneStep } from './SceneControls.ts';
|
|
105
107
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,IAAI,WAAW,CAE3C;AAED,wDAAwD;AACxD,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAexC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAW,YAAW,aAAa;IAC/C,UAAU,SAAK;IACf,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAM;IAC7B,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACjD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAClD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAClD,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAE7D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvB;IAED,KAAK,IAAI,IAAI,CAGZ;IAED,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE9B;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE7B;CACD;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,GACnD,OAAO,UAAU,CAAC,KAAK,GAAG;IAAE,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,CAStD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,EAAO,EAAE,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAM/F"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,aAAa,IAAI,WAAW,CAE3C;AAED,wDAAwD;AACxD,MAAM,WAAW,YAAa,SAAQ,QAAQ;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CAChB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,SAAS,IAAI,YAAY,CAexC;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAEjD;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAW,YAAW,aAAa;IAC/C,UAAU,SAAK;IACf,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,CAAM;IAC7B,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IACjD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAClD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAClD,SAAS,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,CAAQ;IAE7D,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvB;IAED,KAAK,IAAI,IAAI,CAGZ;IAED,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAE9B;IAED,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE7B;CACD;AAED,yCAAyC;AACzC,MAAM,WAAW,aAAa;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,WAAW,CAAC;CACnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,SAAS,CACxB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,GACnD,OAAO,UAAU,CAAC,KAAK,GAAG;IAAE,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,CAStD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,EAAO,EAAE,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAM/F;AAED,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AACzD,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/testing/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAoB,MAAM,iBAAiB,CAAC;AAIlE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,aAAa;IAC5B,OAAO,IAAI,aAAa,EAAE,CAAC;AAC5B,CAAC;AAQD;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,SAAS;IACxB,OAAO;QACN,SAAS,EAAE,CAAC;QACZ,MAAM,EAAE,IAAI;QACZ,MAAM,EAAE,CAAC;QACT,WAAW,EAAE,CAAC;QACd,IAAI,EAAE,KAAK;QACX,IAAI;YACH,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACrB,CAAC;QACD,KAAK;YACJ,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACpB,CAAC;KACD,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IACjC,OAAO,EAAE,GAAG,SAAS,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC1C,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,UAAU;IAAvB;QACC,eAAU,GAAG,CAAC,CAAC;QACN,SAAI,GAAa,EAAE,CAAC;QAC7B,WAAM,GAAsC,IAAI,CAAC;QACjD,YAAO,GAAsC,IAAI,CAAC;QAClD,YAAO,GAAsC,IAAI,CAAC;QAClD,cAAS,GAA+C,IAAI,CAAC;IAkB9D,CAAC;IAhBA,IAAI,CAAC,IAAY;QAChB,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACtB,CAAC;IAED,KAAK;QACJ,IAAI,CAAC,UAAU,GAAG,CAAC,CAAC;QACpB,IAAI,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC;IAC3B,CAAC;IAED,OAAO,CAAC,OAAgB;QACvB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,UAAU,CAAC,IAAY;QACtB,IAAI,CAAC,SAAS,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5B,CAAC;CACD;AAQD;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CACxB,OAAqD;IAErD,MAAM,KAAK,GAAoB,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,KAAK,EAAE,KAAwB,EAAE,IAAkB,EAAE,EAAE;QACpE,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;QAC1B,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;QAC1B,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxC,OAAO,MAAM,YAAY,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;IACpG,CAAC,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,KAAgC,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC;AACnE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,OAAO,GAAa,EAAE,EAAE,IAAI,GAAa,EAAE;IACrF,OAAO;QACN,KAAK;QACL,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,GAAG,GAAG,EAAE,OAAO,EAAE,KAAK,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QACxF,IAAI;KACkB,CAAC;AACzB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/testing/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Container } from 'pixi.js';
|
|
2
|
+
import { type Mixer, type Pose, type SkeletonBones } from '../../skeleton/index.ts';
|
|
3
|
+
import { TintedSprite } from './TintedSprite.ts';
|
|
4
|
+
import type { Texture2D } from './Types2D.ts';
|
|
5
|
+
/**
|
|
6
|
+
* Bones driving drawn parts (roadmap 400), the drawing half of the `skeleton` module.
|
|
7
|
+
*
|
|
8
|
+
* A `SkeletalSprite` binds sprites or texture regions to bones by name; `update(dt)` advances
|
|
9
|
+
* the mixer (or applies a pose a game resolved itself) and places each part at its bone's
|
|
10
|
+
* world transform, so a character sheet, an SVG puppet split by groups or a mixed rig
|
|
11
|
+
* animate through the same bone tree. Walk, swing and look cycles are clips; the binding is
|
|
12
|
+
* the only rendering decision this side makes.
|
|
13
|
+
*
|
|
14
|
+
* An `ArticulatedChain` is the one articulated shape a keyframe cannot express: a long body
|
|
15
|
+
* (a dragon's neck and tail, a serpent's spine) drawn as a chain of segments that follows
|
|
16
|
+
* its head procedurally. Each segment eases toward its parent's previous position, with
|
|
17
|
+
* per-segment rotation limits so the body bends like a body and not like a rope of beads.
|
|
18
|
+
* This is deliberately not a clip - it is a follower rule, not authored motion - and
|
|
19
|
+
* composes with one: a clip drives the head and limbs, the chain does the rest of the body.
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```ts
|
|
23
|
+
* import * as Skeleton from '@datamoc/mw_games/skeleton';
|
|
24
|
+
* import { Texture, TextureSource } from 'pixi.js';
|
|
25
|
+
* import { SkeletalSprite } from '@datamoc/mw_games/two-d/render';
|
|
26
|
+
*
|
|
27
|
+
* const puppet = Skeleton.create([{ name: 'arm', length: 12 }]);
|
|
28
|
+
* const mixer = Skeleton.createMixer(puppet, {
|
|
29
|
+
* id: 'idle', duration: 1, loop: true,
|
|
30
|
+
* frames: [{ time: 0, rotations: { arm: 0 } }, { time: 1, rotations: { arm: 90 } }],
|
|
31
|
+
* });
|
|
32
|
+
* const rig = new SkeletalSprite(puppet, mixer);
|
|
33
|
+
* const armTexture = new Texture({ source: new TextureSource({ width: 8, height: 8 }) });
|
|
34
|
+
* rig.bind('arm', armTexture).anchor.set(0, 0.5);
|
|
35
|
+
* rig.update(1 / 60); // each part sits on its bone, every frame
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare class SkeletalSprite extends Container {
|
|
39
|
+
private readonly skeleton;
|
|
40
|
+
private readonly parts;
|
|
41
|
+
private readonly mixer;
|
|
42
|
+
constructor(skeleton: SkeletonBones, mixer?: Mixer);
|
|
43
|
+
/** bind a drawn part to a bone by name; the sprite's own anchor decides how it hangs */
|
|
44
|
+
bind(bone: string, texture: Texture2D): TintedSprite;
|
|
45
|
+
/** the sprite bound to a bone, or undefined for an unbound one */
|
|
46
|
+
part(bone: string): TintedSprite | undefined;
|
|
47
|
+
/** apply a pose directly; `update` does this itself when a mixer was given */
|
|
48
|
+
apply(pose: Pose): void;
|
|
49
|
+
/** advance the mixer and place every part; needs a mixer, refuses without one */
|
|
50
|
+
update(_dt: number): void;
|
|
51
|
+
}
|
|
52
|
+
export interface ChainOptions {
|
|
53
|
+
/** how many segments the body has */
|
|
54
|
+
segments: number;
|
|
55
|
+
/** how far each segment sits from the one before it */
|
|
56
|
+
length: number;
|
|
57
|
+
/** 0..1, how fast a segment eases toward its parent's previous position; defaults to 1 */
|
|
58
|
+
stiffness?: number;
|
|
59
|
+
/** degrees, the sharpest bend two consecutive segments may make; defaults to 90 */
|
|
60
|
+
bendLimit?: number;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A serpent's spine as pure positions: the head is pulled by the game, every segment eases
|
|
64
|
+
* toward where its parent just was, and no two segments may bend past `bendLimit`. The
|
|
65
|
+
* positions are the contract; drawing them is a `ChainRig`.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* A serpent's spine as pure positions.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* import { ArticulatedChain } from '@datamoc/mw_games/two-d/render';
|
|
73
|
+
*
|
|
74
|
+
* const spine = new ArticulatedChain({ segments: 8, length: 12, bendLimit: 45 });
|
|
75
|
+
* spine.pull(100, 0);
|
|
76
|
+
* spine.update(1); // the body starts following the head
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
export declare class ArticulatedChain {
|
|
80
|
+
readonly points: Array<{
|
|
81
|
+
x: number;
|
|
82
|
+
y: number;
|
|
83
|
+
}>;
|
|
84
|
+
private readonly previous;
|
|
85
|
+
private readonly length;
|
|
86
|
+
private readonly stiffness;
|
|
87
|
+
private readonly bendLimit;
|
|
88
|
+
constructor(options: ChainOptions);
|
|
89
|
+
/** move the head; the body follows on the next `update` */
|
|
90
|
+
pull(x: number, y: number): void;
|
|
91
|
+
/** one tick of the follower rule: ease toward the parent's previous position, then bend-limit */
|
|
92
|
+
update(_dt: number): void;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The drawing half of a chain: one sprite per segment, placed on `update`. The chain is the
|
|
96
|
+
* motion; this only positions what the game handed over.
|
|
97
|
+
*/
|
|
98
|
+
/**
|
|
99
|
+
* The drawing half of a chain: one sprite per segment.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```ts
|
|
103
|
+
* import { Texture, TextureSource } from 'pixi.js';
|
|
104
|
+
* import { ArticulatedChain, ChainRig } from '@datamoc/mw_games/two-d/render';
|
|
105
|
+
*
|
|
106
|
+
* const skin = new Texture({ source: new TextureSource({ width: 8, height: 8 }) });
|
|
107
|
+
* const spine = new ArticulatedChain({ segments: 3, length: 10 });
|
|
108
|
+
* const rig = new ChainRig(spine, [skin, skin, skin]); // one Texture per segment
|
|
109
|
+
* rig.update(1); // segments sit on the chain's points
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
112
|
+
export declare class ChainRig extends Container {
|
|
113
|
+
private readonly chain;
|
|
114
|
+
private readonly segments;
|
|
115
|
+
constructor(chain: ArticulatedChain, textures: readonly Texture2D[]);
|
|
116
|
+
update(dt: number): void;
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=SkeletalSprite.d.ts.map
|