@fabi-play/sdk 0.0.0-stage → 0.2.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 +76 -0
- package/README.md +86 -2
- package/dist/avatars.d.ts +256 -0
- package/dist/avatars.js +18 -0
- package/dist/chunks/chunk-57WXWAXG.js +2112 -0
- package/dist/fabi-play.d.ts +714 -0
- package/dist/fabi-play.js +1 -0
- package/dist/fabi-play.schema.json +176 -0
- package/dist/fabi-play.umd.js +2612 -0
- package/dist/fabi-play.umd.min.js +1 -0
- package/dist/index.d.ts +801 -0
- package/dist/index.js +490 -0
- package/dist/integrity.json +12 -0
- package/package.json +68 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
Fabi Play Kit License
|
|
2
|
+
Version 1.0 (DRAFT, not yet reviewed by a lawyer)
|
|
3
|
+
|
|
4
|
+
Copyright (c) 2026 Fabi Play. All rights reserved.
|
|
5
|
+
|
|
6
|
+
This license applies to the Fabi Play Kit: the packages @fabi-play/protocol, @fabi-play/motion,
|
|
7
|
+
@fabi-play/avatars, @fabi-play/sdk and @fabi-play/engine, in source and built form, as published on npm, served
|
|
8
|
+
from https://cdn.fabiplay.app or found in this repository, including their code, type declarations, documentation,
|
|
9
|
+
avatar artwork and other assets (together, the "Kit").
|
|
10
|
+
|
|
11
|
+
1. Free of charge, for Fabi Play only
|
|
12
|
+
|
|
13
|
+
Fabi Play grants you a worldwide, non-exclusive, non-transferable, non-sublicensable, royalty-free license to
|
|
14
|
+
use, copy, and modify the Kit, and to distribute it as part of a Permitted Work, solely to make, test, run and
|
|
15
|
+
distribute Permitted Works.
|
|
16
|
+
|
|
17
|
+
A "Permitted Work" is a game, tool, or other software made to run on, be published on, or support the
|
|
18
|
+
development of games for the Fabi Play platform (https://fabiplay.app), including games submitted to Fabi Play
|
|
19
|
+
and tools used to build, test or preview them.
|
|
20
|
+
|
|
21
|
+
2. What is not allowed
|
|
22
|
+
|
|
23
|
+
You may not, without Fabi Play's prior written permission:
|
|
24
|
+
|
|
25
|
+
a. use the Kit, or any part or modified version of it, in software that is not a Permitted Work, including in
|
|
26
|
+
any other game platform, product or service;
|
|
27
|
+
b. distribute the Kit, or a modified version of it, on its own or as a library, SDK, engine or framework for
|
|
28
|
+
others to build with, other than as part of a Permitted Work (installing it from npm or loading it from
|
|
29
|
+
https://cdn.fabiplay.app is not a distribution by you);
|
|
30
|
+
c. sell, rent or sublicense the Kit itself;
|
|
31
|
+
d. remove or alter this license, copyright notices, or the license banners in the built files;
|
|
32
|
+
e. use the avatars, the mascot "Fabi", or the name, logos or other trademarks of Fabi Play, except as the Kit
|
|
33
|
+
does when it runs inside a Permitted Work. This license grants no trademark rights;
|
|
34
|
+
f. use the Kit to circumvent, interfere with or misrepresent the Fabi Play platform, its safety, privacy or
|
|
35
|
+
parental controls, or its rules for games.
|
|
36
|
+
|
|
37
|
+
3. Visible, not open source
|
|
38
|
+
|
|
39
|
+
The Kit's code is published so that developers can read it, debug with it and build Permitted Works. Being
|
|
40
|
+
visible does not make it open source; no rights are granted beyond those stated here.
|
|
41
|
+
|
|
42
|
+
4. Your works
|
|
43
|
+
|
|
44
|
+
You keep all rights in your own code and content. This license does not give Fabi Play any rights in your
|
|
45
|
+
Permitted Works; publishing a game on Fabi Play is governed by Fabi Play's separate developer terms.
|
|
46
|
+
|
|
47
|
+
5. Feedback
|
|
48
|
+
|
|
49
|
+
If you send Fabi Play suggestions or changes for the Kit, Fabi Play may use them without obligation to you.
|
|
50
|
+
|
|
51
|
+
6. Termination
|
|
52
|
+
|
|
53
|
+
This license ends automatically if you break its terms. If you fix the breach within 30 days of becoming aware
|
|
54
|
+
of it, the license is reinstated, unless Fabi Play has already told you in writing that it is terminated. On
|
|
55
|
+
termination you must stop using and distributing the Kit. Permitted Works already distributed to end users in
|
|
56
|
+
compliance with this license may continue to be used by those end users.
|
|
57
|
+
|
|
58
|
+
7. No warranty
|
|
59
|
+
|
|
60
|
+
THE KIT IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
|
61
|
+
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
|
|
62
|
+
|
|
63
|
+
8. Limitation of liability
|
|
64
|
+
|
|
65
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL FABI PLAY OR ITS CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
|
|
66
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
67
|
+
CONNECTION WITH THE KIT OR THE USE OR OTHER DEALINGS IN THE KIT.
|
|
68
|
+
|
|
69
|
+
9. Changes to this license
|
|
70
|
+
|
|
71
|
+
Fabi Play may publish new versions of this license. Each version of the Kit is governed by the license version
|
|
72
|
+
it is distributed with, unless you choose to accept a later version.
|
|
73
|
+
|
|
74
|
+
10. Contact
|
|
75
|
+
|
|
76
|
+
For permissions beyond this license: https://fabiplay.app
|
package/README.md
CHANGED
|
@@ -1,3 +1,87 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @fabi-play/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
What a [Fabi Play](https://fabiplay.app) game uses, from inside its sandboxed frame: the players and their bodies,
|
|
4
|
+
movements by name (jump, squat, clap, shapes, lanes, freeze, hands as pointers), the avatars, the breaks, the scores
|
|
5
|
+
and the end of the game.
|
|
6
|
+
|
|
7
|
+
## npm
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install @fabi-play/sdk
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { connect } from '@fabi-play/sdk'
|
|
15
|
+
|
|
16
|
+
const play = await connect()
|
|
17
|
+
play.on('gesture', ({ player, gesture }) => console.log(player, gesture))
|
|
18
|
+
play.ready()
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
ESM only, types included, no dependencies.
|
|
22
|
+
|
|
23
|
+
## Script tag
|
|
24
|
+
|
|
25
|
+
From the CDN, pinned to an exact version with its Subresource Integrity hash (listed in
|
|
26
|
+
`https://cdn.fabiplay.app/sdk/<version>/integrity.json`, and in `dist/integrity.json` of this package):
|
|
27
|
+
|
|
28
|
+
```html
|
|
29
|
+
<script
|
|
30
|
+
src="https://cdn.fabiplay.app/sdk/0.1.0/fabi-play.umd.min.js"
|
|
31
|
+
integrity="sha384-…"
|
|
32
|
+
crossorigin="anonymous"
|
|
33
|
+
data-keys="jump:Space,left:ArrowLeft,right:ArrowRight"
|
|
34
|
+
></script>
|
|
35
|
+
<script>
|
|
36
|
+
FabiPlay.on('jump', ({ player }) => hop(player))
|
|
37
|
+
</script>
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
| File | What |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| `fabi-play.umd.min.js` | UMD, minified: global `FabiPlay`, or the module under AMD / CommonJS |
|
|
43
|
+
| `fabi-play.umd.js` | the same, readable |
|
|
44
|
+
| `fabi-play.js` | the classic script (IIFE): sets `window.FabiPlay` |
|
|
45
|
+
| `fabi-play.d.ts` | types for script-tag games (`FabiPlay.PlayerFrame`, …) |
|
|
46
|
+
| `fabi-play.schema.json` | the JSON Schema of `fabi-play.json` |
|
|
47
|
+
|
|
48
|
+
## Fabi explains your game (intro)
|
|
49
|
+
|
|
50
|
+
Before calibration, Fabi (the mascot) can explain your game on the TV in a few steps: a speech bubble over an image
|
|
51
|
+
of the game, or a move shown with the children's own avatars, which they can also try. You write only data, in
|
|
52
|
+
`fabi-play.json`:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
"intro": { "steps": [
|
|
56
|
+
{ "say": { "it": "Si corre nel bosco! Ognuno ha la sua pista e raccoglie le stelle.", "en": "Let's run in the woods! Everyone has a track and collects stars." }, "image": "intro/track.jpg" },
|
|
57
|
+
{ "say": { "it": "Tronco? Salta! Provate adesso.", "en": "A log? Jump! Try it now." }, "move": "jump", "practice": true },
|
|
58
|
+
{ "say": { "it": "Ramo basso? Giù!", "en": "Low branch? Down!" }, "move": "squat" }
|
|
59
|
+
] }
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
- **1 to 4 steps.** Fabi Play adds its own last step ("Ready?") and goes on to calibration.
|
|
63
|
+
- **`say`**: one sentence, in Italian and English, up to 90 characters each (an emoji counts as one), with no spaces
|
|
64
|
+
at either end and no control characters. Or one per age range, when the little
|
|
65
|
+
ones need simpler words: `{ "3-5": { "it": "…", "en": "…" }, "9-12": { "it": "…", "en": "…" } }` (at least one
|
|
66
|
+
range; a child of a range you left out gets the closest one you wrote, the younger if two are as close).
|
|
67
|
+
- **Each step has an `image` or a `move`, not both.**
|
|
68
|
+
- `image`: a picture of the game, 16:9, at least 1280×720, `.jpg` or `.png` (no WebP: old TV browsers may not
|
|
69
|
+
decode it; use JPG, quality about 80, for screenshots and PNG only for flat art), a path
|
|
70
|
+
relative to the game's address like `entry`: no scheme (`https://`), no leading `/`, no `.` or `..` segments,
|
|
71
|
+
letters, digits, `_` and `-`, extension in lowercase (`intro/track.jpg`, not `intro/Track.JPG`).
|
|
72
|
+
- `move`: one of `jump`, `squat`, `clap`, `lane-left`, `lane-right`, `arms-up`, `arms-wide`, `arms-v`, `star`,
|
|
73
|
+
drawn with each child's avatar.
|
|
74
|
+
- `practice` (optional, only with a `move`): `true` and the children try it, each gets a tick, and the step moves
|
|
75
|
+
on when all did, or after 8 seconds. Default `false`: Fabi only shows it.
|
|
76
|
+
- **The voice is Fabi Play's.** Fabi speaks each sentence with a clip Fabi Play generates from the text when the
|
|
77
|
+
game is published. Changing a text needs a new clip: until it is made, the TV shows that sentence in the bubble
|
|
78
|
+
only.
|
|
79
|
+
- **No `intro`**: the game goes straight to calibration, as before.
|
|
80
|
+
- **An invalid intro** is reported by the check (the Studio, `parseManifest`, the JSON Schema) and skipped by the
|
|
81
|
+
TV: it never blocks the game.
|
|
82
|
+
|
|
83
|
+
Docs: https://docs.fabiplay.app.
|
|
84
|
+
|
|
85
|
+
## License
|
|
86
|
+
|
|
87
|
+
Fabi Play Kit License: free to use, for making games and tools for Fabi Play only. See [LICENSE](./LICENSE).
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// @fabi-play/sdk/avatars: the avatars' rig @fabi-play/engine draws with. Not a game API.
|
|
2
|
+
// Generated by scripts/types.mjs from the sources: do not edit.
|
|
3
|
+
|
|
4
|
+
type Body = number[];
|
|
5
|
+
/**
|
|
6
|
+
* Age ranges: each has its own games. Only the range is kept, never a birth date: it is all the catalogue and the
|
|
7
|
+
* games need (simpler rules and no losing for the little ones, scores and challenges for the older ones).
|
|
8
|
+
*/
|
|
9
|
+
export type AgeRange = "3-5" | "6-8" | "9-12";
|
|
10
|
+
/**
|
|
11
|
+
* A tiny shape list, the one source of truth for every avatar picture. The rig and the portrait build a list of
|
|
12
|
+
* shapes; two small backends draw it: on a 2D canvas (the TV, the games) and as an SVG string (the phones, the
|
|
13
|
+
* previews). Both read the same numbers, so the canvas and the SVG pictures match.
|
|
14
|
+
*
|
|
15
|
+
* Paths use a small subset of SVG path syntax, absolute commands only (M L Q C Z), so the canvas backend can trace
|
|
16
|
+
* them without Path2D (not available everywhere, e.g. React Native canvases or test mocks). The rig builds them as
|
|
17
|
+
* PathData, number arrays (no strings built and parsed again every frame); fixed paths may stay strings.
|
|
18
|
+
*/
|
|
19
|
+
/** How an avatar shape is painted (CSS colours). */
|
|
20
|
+
export interface Paint {
|
|
21
|
+
/** Fill colour; none without it. */
|
|
22
|
+
fill?: string;
|
|
23
|
+
/** Outline colour; none without it. */
|
|
24
|
+
stroke?: string;
|
|
25
|
+
/** Stroke width, in the units of the shape (scaled by the groups around it). */
|
|
26
|
+
lw?: number;
|
|
27
|
+
/** 0–1, multiplied with the alpha around it. */
|
|
28
|
+
alpha?: number;
|
|
29
|
+
}
|
|
30
|
+
/** One shape of an avatar picture (the rig's output), in canvas units; k says which. */
|
|
31
|
+
export type Shape = (Paint & {
|
|
32
|
+
k: "circle";
|
|
33
|
+
x: number;
|
|
34
|
+
y: number;
|
|
35
|
+
r: number;
|
|
36
|
+
}) | (Paint & {
|
|
37
|
+
k: "ellipse";
|
|
38
|
+
x: number;
|
|
39
|
+
y: number;
|
|
40
|
+
rx: number;
|
|
41
|
+
ry: number;
|
|
42
|
+
rot?: number;
|
|
43
|
+
})
|
|
44
|
+
/** A rounded rectangle by its centre, rotated by rot radians around it. */
|
|
45
|
+
| (Paint & {
|
|
46
|
+
k: "rect";
|
|
47
|
+
x: number;
|
|
48
|
+
y: number;
|
|
49
|
+
w: number;
|
|
50
|
+
h: number;
|
|
51
|
+
r: number;
|
|
52
|
+
rot?: number;
|
|
53
|
+
}) | (Paint & {
|
|
54
|
+
k: "path";
|
|
55
|
+
d: string | PathData;
|
|
56
|
+
})
|
|
57
|
+
/** A polyline [x0, y0, x1, y1, …], stroked with round caps and joins. */
|
|
58
|
+
| (Paint & {
|
|
59
|
+
k: "line";
|
|
60
|
+
pts: number[];
|
|
61
|
+
})
|
|
62
|
+
/** Children drawn translated by (x, y), then rotated by rot radians, then scaled by s. */
|
|
63
|
+
| {
|
|
64
|
+
k: "group";
|
|
65
|
+
x: number;
|
|
66
|
+
y: number;
|
|
67
|
+
rot?: number;
|
|
68
|
+
s?: number;
|
|
69
|
+
alpha?: number;
|
|
70
|
+
items: Shape[];
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* A path as numbers: each command code (PATH.M, …) followed by its coordinates, absolute: M x y, L x y,
|
|
74
|
+
* Q cx cy x y, C c1x c1y c2x c2y x y, Z. Coordinates are rounded to 2 decimals, as the SVG prints them, so canvas and
|
|
75
|
+
* SVG pictures match.
|
|
76
|
+
*/
|
|
77
|
+
export type PathData = readonly number[];
|
|
78
|
+
/** The part of CanvasRenderingContext2D the backend uses (so tests can pass a mock). */
|
|
79
|
+
export type Ctx2D = Pick<CanvasRenderingContext2D, "save" | "restore" | "translate" | "rotate" | "scale" | "beginPath" | "closePath" | "moveTo" | "lineTo" | "quadraticCurveTo" | "bezierCurveTo" | "arc" | "ellipse" | "fill" | "stroke" | "rect" | "clip" | "globalAlpha" | "fillStyle" | "strokeStyle" | "lineWidth" | "lineCap" | "lineJoin">;
|
|
80
|
+
export declare function drawShapes(ctx: Ctx2D, shapes: readonly Shape[]): void;
|
|
81
|
+
export interface Pt {
|
|
82
|
+
x: number;
|
|
83
|
+
y: number;
|
|
84
|
+
}
|
|
85
|
+
/** An avatar's colours (CSS). */
|
|
86
|
+
export interface Palette {
|
|
87
|
+
/** Head and torso. */
|
|
88
|
+
body: string;
|
|
89
|
+
/** Arms and legs. */
|
|
90
|
+
limbs: string;
|
|
91
|
+
/** Belly patch, muzzle, inner face. */
|
|
92
|
+
belly: string;
|
|
93
|
+
/** Details: inner ears, horns, beaks, tail tips, spikes. */
|
|
94
|
+
accent: string;
|
|
95
|
+
/** The outline around every part: dark, so the avatar reads on light and dark backgrounds. */
|
|
96
|
+
outline: string;
|
|
97
|
+
/** Hands; defaults to limbs. */
|
|
98
|
+
hands?: string;
|
|
99
|
+
/** Feet; defaults to limbs. */
|
|
100
|
+
feet?: string;
|
|
101
|
+
/** Solid ears of another colour (panda); by default ears are body-coloured with an accent inside. */
|
|
102
|
+
ears?: string;
|
|
103
|
+
/** The face and head when they differ from the torso (people, the lamb's face); defaults to body. */
|
|
104
|
+
skin?: string;
|
|
105
|
+
/** Hair (hair, curls, locks). */
|
|
106
|
+
hair?: string;
|
|
107
|
+
}
|
|
108
|
+
/** The head's outline. */
|
|
109
|
+
export type HeadShape = "round" | "wide" | "tall" | "square" | "bulb" | "bucket";
|
|
110
|
+
/** The ears (or what is in their place: horns, antennae, a fin). */
|
|
111
|
+
export type EarKind = "none" | "pointy" | "round" | "big-round" | "long" | "side" | "tufts" | "antenna" | "antennae" | "horns" | "unicorn" | "floppy" | "droopy" | "fin" | "human";
|
|
112
|
+
/** How the eyes are drawn. */
|
|
113
|
+
export type EyeKind = "dot" | "big" | "mask" | "screen" | "almond" | "visor" | "calm" | "slit" | "goggle" | "bead";
|
|
114
|
+
/** How the mouth (or beak) is drawn. */
|
|
115
|
+
export type MouthKind = "none" | "smile" | "open" | "cat" | "beak" | "teeth" | "grill" | "tiny" | "smirk" | "bill" | "hook" | "jaws";
|
|
116
|
+
/** The patch around the nose and mouth. */
|
|
117
|
+
export type MuzzleKind = "none" | "oval" | "heart" | "monkey" | "disc" | "cheeks" | "band";
|
|
118
|
+
/** The tail, behind the body. */
|
|
119
|
+
export type TailKind = "none" | "fluffy" | "brush" | "pom" | "long" | "curl" | "tuft" | "striped" | "spiky" | "arrow" | "fish" | "plume";
|
|
120
|
+
/** Things on the head. */
|
|
121
|
+
export type HeadExtra = "mane" | "rainbow-mane" | "stripes" | "spikes" | "spots" | "curl" | "helmet" | "bolts" | "whiskers" | "tuft" | "brows" | "eggshell" | "wool" | "eye-spot" | "crest" | "forehead" | "gills" | "ruff" | "hair" | "curls" | "locks" | "ninja-hood" | "cap" | "safari-hat" | "wizard-hat" | "plume" | "stripe" | "bandana" | "eye-patch" | "dive-mask" | "domino";
|
|
122
|
+
/** Things on the body. */
|
|
123
|
+
export type BodyExtra = "wings" | "backpack" | "sheet" | "tentacles" | "panel" | "badge" | "joints" | "feathers" | "flippers" | "boots" | "scarf" | "spots" | "wool" | "robe" | "cape" | "fins" | "tank" | "straps" | "belt" | "stripes" | "pauldrons" | "emblem" | "pocket" | "racing-stripe" | "star";
|
|
124
|
+
/** An avatar's face. */
|
|
125
|
+
export interface FaceDef {
|
|
126
|
+
/** How the eyes are drawn. */
|
|
127
|
+
eyes: EyeKind;
|
|
128
|
+
/** Eye colour for big/screen/almond/visor eyes. */
|
|
129
|
+
eyeColor?: string;
|
|
130
|
+
/** Eyes higher than usual and bulging over the head (frog). */
|
|
131
|
+
eyesOnTop?: boolean;
|
|
132
|
+
/** Ring around the eyes (owl). */
|
|
133
|
+
eyeRing?: string;
|
|
134
|
+
/** How the mouth is drawn. */
|
|
135
|
+
mouth: MouthKind;
|
|
136
|
+
/** Nose colour; none when missing. */
|
|
137
|
+
nose?: string;
|
|
138
|
+
/** The patch around nose and mouth. */
|
|
139
|
+
muzzle: MuzzleKind;
|
|
140
|
+
/** Blush colour; none when missing. */
|
|
141
|
+
cheeks?: string;
|
|
142
|
+
/** Eyebrows of this colour (people): a little attitude. */
|
|
143
|
+
brows?: string;
|
|
144
|
+
}
|
|
145
|
+
/** The age band an avatar is for, or 'all': offered in every band (Fabi the fox, the app's mascot). */
|
|
146
|
+
export type AvatarBand = AgeRange | "all";
|
|
147
|
+
/** An avatar, as data: one renderer draws any definition. */
|
|
148
|
+
export interface AvatarDef {
|
|
149
|
+
/** Stable kebab-case id: stored on the phone and sent around, never renamed. */
|
|
150
|
+
id: string;
|
|
151
|
+
/** Its name, in English and Italian, for pickers. */
|
|
152
|
+
name: {
|
|
153
|
+
en: string;
|
|
154
|
+
it: string;
|
|
155
|
+
};
|
|
156
|
+
/**
|
|
157
|
+
* The age band the avatar is for: its style and its body proportions (see BAND_BODY in rig.ts). 'all': in every
|
|
158
|
+
* band's list, drawn with the 6–8 proportions.
|
|
159
|
+
*/
|
|
160
|
+
band: AvatarBand;
|
|
161
|
+
/** A special avatar (the mascot): first in every band's list; pickers may mark it. */
|
|
162
|
+
special?: boolean;
|
|
163
|
+
/** Its colours. */
|
|
164
|
+
palette: Palette;
|
|
165
|
+
/** The head's outline. */
|
|
166
|
+
head: HeadShape;
|
|
167
|
+
/** The ears, or what is in their place. */
|
|
168
|
+
ears: EarKind;
|
|
169
|
+
/** The face. */
|
|
170
|
+
face: FaceDef;
|
|
171
|
+
/** The tail. */
|
|
172
|
+
tail: TailKind;
|
|
173
|
+
/** Things on the head (manes, hats, masks). */
|
|
174
|
+
headExtras: readonly HeadExtra[];
|
|
175
|
+
/** Things on the body (wings, capes, belts). */
|
|
176
|
+
bodyExtras: readonly BodyExtra[];
|
|
177
|
+
/** Colours of the rainbow mane, of the stripes, of the spots: per-avatar extra colours. */
|
|
178
|
+
extraColors?: readonly string[];
|
|
179
|
+
}
|
|
180
|
+
/** Where player space (0–1) lands on the canvas. */
|
|
181
|
+
export interface Box {
|
|
182
|
+
/** Left edge, canvas units. */
|
|
183
|
+
x: number;
|
|
184
|
+
/** Top edge, canvas units. */
|
|
185
|
+
y: number;
|
|
186
|
+
/** Width, canvas units. */
|
|
187
|
+
w: number;
|
|
188
|
+
/** Height, canvas units. */
|
|
189
|
+
h: number;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Body proportions by age band, in torso units U (head, limbs, torso) unless said otherwise. The joints are always
|
|
193
|
+
* the child's: limbs run through them and the head sits on the shoulders; only sizes change.
|
|
194
|
+
*
|
|
195
|
+
* 3–5: a big round head, chubby limbs, a round tummy that reaches low (the legs look short): toddlers.
|
|
196
|
+
* 6–8: the original proportions.
|
|
197
|
+
* 9–12: a smaller head, slimmer limbs and hips, a squarer torso: older kids.
|
|
198
|
+
*/
|
|
199
|
+
export interface BandBody {
|
|
200
|
+
/** Head radius. */
|
|
201
|
+
head: number;
|
|
202
|
+
/** How far the head centre sits above the shoulder line, in head radii. */
|
|
203
|
+
neck: number;
|
|
204
|
+
/** Limb thickness. */
|
|
205
|
+
arm: number;
|
|
206
|
+
leg: number;
|
|
207
|
+
/** Hand radius. */
|
|
208
|
+
hand: number;
|
|
209
|
+
/** Feet size, as a factor. */
|
|
210
|
+
foot: number;
|
|
211
|
+
/** The torso: at least this half width at the shoulders and the hips, and this much past the joints. */
|
|
212
|
+
shoulderMin: number;
|
|
213
|
+
shoulderPad: number;
|
|
214
|
+
hipMin: number;
|
|
215
|
+
hipPad: number;
|
|
216
|
+
/** How far the torso reaches below the hips. */
|
|
217
|
+
bottom: number;
|
|
218
|
+
/** The waist's bulge, as a share of the wider of shoulders and hips. */
|
|
219
|
+
bulge: number;
|
|
220
|
+
/** A raised arm is drawn over the head (a big head would hide it). */
|
|
221
|
+
armsOverHead: boolean;
|
|
222
|
+
}
|
|
223
|
+
export declare const BAND_BODY: Record<AgeRange, BandBody>;
|
|
224
|
+
export interface RigOptions {
|
|
225
|
+
mirrored?: boolean;
|
|
226
|
+
/** Portraits leave the tail out: in a head-and-shoulders crop it would only peek in as a stray shape. */
|
|
227
|
+
portrait?: boolean;
|
|
228
|
+
/** Body proportions (see BAND_BODY); 6–8 by default. avatarShapes takes the avatar's own band. */
|
|
229
|
+
band?: AgeRange;
|
|
230
|
+
}
|
|
231
|
+
/** The torso frame: origin between the shoulders, r to the right, d down along the torso; lengths in pixels. */
|
|
232
|
+
export interface Frame {
|
|
233
|
+
S: Pt;
|
|
234
|
+
r: Pt;
|
|
235
|
+
d: Pt;
|
|
236
|
+
U: number;
|
|
237
|
+
/** Shoulders to hips along d. */
|
|
238
|
+
L: number;
|
|
239
|
+
hipsOk: boolean;
|
|
240
|
+
head: Pt;
|
|
241
|
+
headR: number;
|
|
242
|
+
headRot: number;
|
|
243
|
+
angle: number;
|
|
244
|
+
}
|
|
245
|
+
/** The torso frame and head placement, or null when the shoulders are not seen. */
|
|
246
|
+
export declare function frameOf(body: Body, box: Box, opts?: RigOptions): Frame | null;
|
|
247
|
+
/** All the shapes of an avatar for this body, in canvas pixels. Empty when the shoulders are not seen. */
|
|
248
|
+
export declare function avatarShapes(body: Body, def: AvatarDef, box: Box, opts?: RigOptions): Shape[];
|
|
249
|
+
/** The avatar of a child who has not picked one yet: the fox (6–8's first). */
|
|
250
|
+
export declare const DEFAULT_AVATAR: AvatarDef;
|
|
251
|
+
/** The avatar with this id, or undefined. */
|
|
252
|
+
export declare const avatarById: (id: string) => AvatarDef | undefined;
|
|
253
|
+
/** The portrait: head and shoulders of the neutral pose, framed in a size × size square at (0, 0). */
|
|
254
|
+
export declare function iconShapes(def: AvatarDef | string, size: number): Shape[];
|
|
255
|
+
|
|
256
|
+
export {};
|
package/dist/avatars.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BAND_BODY,
|
|
3
|
+
DEFAULT_AVATAR,
|
|
4
|
+
avatarById,
|
|
5
|
+
avatarShapes,
|
|
6
|
+
drawShapes,
|
|
7
|
+
frameOf,
|
|
8
|
+
iconShapes
|
|
9
|
+
} from "./chunks/chunk-57WXWAXG.js";
|
|
10
|
+
export {
|
|
11
|
+
BAND_BODY,
|
|
12
|
+
DEFAULT_AVATAR,
|
|
13
|
+
avatarById,
|
|
14
|
+
avatarShapes,
|
|
15
|
+
drawShapes,
|
|
16
|
+
frameOf,
|
|
17
|
+
iconShapes
|
|
18
|
+
};
|