@fabi-play/sdk 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +79 -0
- package/dist/avatars.js +1 -1
- package/dist/chunks/{chunk-57WXWAXG.js → chunk-GFNOIOSX.js} +17 -3
- package/dist/fabi-play.d.ts +160 -21
- package/dist/fabi-play.js +1 -1
- package/dist/fabi-play.schema.json +21 -0
- package/dist/fabi-play.umd.js +92 -14
- package/dist/fabi-play.umd.min.js +1 -1
- package/dist/index.d.ts +164 -21
- package/dist/index.js +79 -11
- package/dist/integrity.json +6 -6
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -80,6 +80,85 @@ of the game, or a move shown with the children's own avatars, which they can als
|
|
|
80
80
|
- **An invalid intro** is reported by the check (the Studio, `parseManifest`, the JSON Schema) and skipped by the
|
|
81
81
|
TV: it never blocks the game.
|
|
82
82
|
|
|
83
|
+
## Where the children are: the view and framing
|
|
84
|
+
|
|
85
|
+
`play.view` is where the camera image lies on your page, so each child's avatar and hands are where the child stands.
|
|
86
|
+
Draw anything that belongs to a child at `play.toScreen(point)` (a wrist, the head, the avatar's box), never at your
|
|
87
|
+
own mapping of player space.
|
|
88
|
+
|
|
89
|
+
The view is not fixed. By default the screen frames the children: when they stand far from the camera it zooms in on
|
|
90
|
+
them, up to 1.8×, so every child's avatar is about the same size and reaches the same things, near or far. It decides
|
|
91
|
+
the zoom at calibration and changes it only in breaks (pause, level, turn), never during play; the image may then
|
|
92
|
+
reach past every edge of the page. Listen for it:
|
|
93
|
+
|
|
94
|
+
```js
|
|
95
|
+
play.onBackdrop((backdrop, view) => layoutAround(view))
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
A game drawn on exact spots of the live video can keep the whole image with `"framing": "fixed"` in `fabi-play.json`.
|
|
99
|
+
|
|
100
|
+
## Playing with the TV remote
|
|
101
|
+
|
|
102
|
+
On TVs (Samsung, LG, Android TV, Fire TV) Fabi Play can be played without a phone: with the TV remote. A game
|
|
103
|
+
opts in with `inputs` in `fabi-play.json`:
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
"inputs": ["body", "remote"]
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- **`inputs`** is optional; absent means `["body"]`. It always includes `"body"` (Fabi Play is a movement
|
|
110
|
+
platform), lists each input once, and knows only `body` and `remote`: anything else is an error in the check
|
|
111
|
+
(the Studio, `parseManifest`, the JSON Schema).
|
|
112
|
+
- **With one child, most games need no code.** The screen turns the remote's keys into the gestures the body makes, and animates a
|
|
113
|
+
puppet body in the frames (as the simulator does), so `gesture` events, `drawAvatar` and the keyboard adapter
|
|
114
|
+
(`data-keys`, `FabiPlay.keys()`) work as with a camera:
|
|
115
|
+
|
|
116
|
+
| Key | Gesture |
|
|
117
|
+
| --- | --- |
|
|
118
|
+
| ↑ or OK | `jump` (then `land`) |
|
|
119
|
+
| ↓ held | `squat`, then `stand` on release |
|
|
120
|
+
| ← / → | `lane-left` / `lane-right` (stepping back to the middle is `lane-center`, as with the body) |
|
|
121
|
+
| OK twice quickly | `clap` |
|
|
122
|
+
| Back | Fabi Play's pause (a `pause` message), never the game's |
|
|
123
|
+
|
|
124
|
+
Nothing else is made: no hands aiming, no shapes, no freeze. The game never gets the keys themselves.
|
|
125
|
+
- **The Magic Remote (LG) points too.** It reaches the game as ordinary pointer and mouse events: hover, and a
|
|
126
|
+
click with OK. `play.pointer` is `true` when the remote points (only with `"remote"`). A game that lists
|
|
127
|
+
`"remote"` must work with the arrow keys alone and, where it has things to pick (bubbles, answers, buttons), let a
|
|
128
|
+
pointer click them as well: pointer events, large hit areas, a hover highlight. Fabi Play's own menus, pickers
|
|
129
|
+
and turn screen are clickable too.
|
|
130
|
+
- **One remote is one child; several children take turns, a round each, if the game follows the contract.**
|
|
131
|
+
Turns are not free: the game must follow the steps below, and say so in `fabi-play.json` with
|
|
132
|
+
`"turns": true` (only next to `"remote"` in `inputs`): `"inputs": ["body", "remote"], "turns": true`. A game
|
|
133
|
+
with `"turns": true` follows the turn contract. Only then does the screen offer several children with the
|
|
134
|
+
remote; without it, remote mode is one child only.
|
|
135
|
+
|
|
136
|
+
The game sees one child at a time. `play.players` is the child playing now (exactly one id, a frozen array: read
|
|
137
|
+
it again after a turn), and `play.turns` is every child who plays, in order (`[1, 2, 3]`; one child, or the body:
|
|
138
|
+
the same as `play.players`). The contract:
|
|
139
|
+
1. Play a round (a race, a level) with the child in `play.players`.
|
|
140
|
+
2. When that child's round ends, whatever happened in it (a crash or a fall ends a round too), and turns remain,
|
|
141
|
+
call `play.break('turn')`. The screen always answers `break-start` (its turn screen is the break, and may
|
|
142
|
+
show an ad), then a `turn` message (`play.on('turn', ({ players }) => …)`, or `FabiPlay.on('turn', …)`),
|
|
143
|
+
after which `play.players` is the next child, then `break-done`. Start that child's round.
|
|
144
|
+
3. After the last child's round, call `play.end()` with every child's scores:
|
|
145
|
+
`play.end([{ player: 1, … }, { player: 2, … }])`.
|
|
146
|
+
|
|
147
|
+
Only `break('turn')` swaps children: `break('level')` and `break('gameover')` never do, so a round never changes
|
|
148
|
+
hands midway. The SDK takes a `turn` only inside a `'turn'` break, and only to the next child in `play.turns`; it
|
|
149
|
+
drops any other. Player ids keep their meaning (slots, avatars): keep each child's score by id. At a turn the next
|
|
150
|
+
child's puppet starts recentred, in the middle lane, with no lane gesture (the keyboard adapter resets its lane
|
|
151
|
+
too). With the simple API, `score(n)`
|
|
152
|
+
without a player goes to the child playing now, and `end(n)` gives the child playing now `n` and each child
|
|
153
|
+
before them their last `score()` (or 0). A body session never gets a `turn`: its players stay for the whole
|
|
154
|
+
game.
|
|
155
|
+
- **Adapt with `play.input`** (`'body'`, the default and what older screens mean, or `'remote'`; also in the
|
|
156
|
+
`start` event with `pointer`, and live as `FabiPlay.input`, `FabiPlay.hasPointer`, `FabiPlay.players`,
|
|
157
|
+
`FabiPlay.turns`): slow the game down a little, show a selection the arrows move (and a pointer
|
|
158
|
+
clicks) instead of a hand cursor, drop what the remote can't do.
|
|
159
|
+
- **Games aimed with the hands** (`movements: ["hands"]`, the `pointer()` adapter) should not list `"remote"`
|
|
160
|
+
unless they offer a remote-friendly selection: a child with a remote has no hand cursor.
|
|
161
|
+
|
|
83
162
|
Docs: https://docs.fabiplay.app.
|
|
84
163
|
|
|
85
164
|
## License
|
package/dist/avatars.js
CHANGED
|
@@ -57,6 +57,14 @@ function parseGameStats(v) {
|
|
|
57
57
|
if (DEVICE_TIERS.includes(v.tier)) out.tier = v.tier;
|
|
58
58
|
return out;
|
|
59
59
|
}
|
|
60
|
+
var isPlayerId = (id) => Number.isInteger(id) && id >= 1 && id <= MAX_PLAYERS;
|
|
61
|
+
function parseTurnPlayers(v) {
|
|
62
|
+
return Array.isArray(v) && v.length === 1 && isPlayerId(v[0]) ? [v[0]] : void 0;
|
|
63
|
+
}
|
|
64
|
+
function parseTurnOrder(v) {
|
|
65
|
+
if (!Array.isArray(v) || v.length < 1 || v.length > MAX_PLAYERS || !v.every(isPlayerId) || new Set(v).size !== v.length) return void 0;
|
|
66
|
+
return [...v];
|
|
67
|
+
}
|
|
60
68
|
var isObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
61
69
|
var isNumber = (v) => typeof v === "number" && Number.isFinite(v);
|
|
62
70
|
var POSE_HEADER = 12;
|
|
@@ -2004,9 +2012,11 @@ function smoothBody(prev, next, amount = 0.5) {
|
|
|
2004
2012
|
const wasSeen = pv >= 0.5;
|
|
2005
2013
|
const isSeen = nv >= 0.5;
|
|
2006
2014
|
if (wasSeen && nv >= 0.3) out[i + 2] = Math.max(nv, 0.5);
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2015
|
+
const step = Math.hypot(next[i] - prev[i], next[i + 1] - prev[i + 1]);
|
|
2016
|
+
if (wasSeen && isSeen && step < 0.15) {
|
|
2017
|
+
const k = amount * Math.min(1, STILL_SHARE + (1 - STILL_SHARE) * step / STILL_FULL);
|
|
2018
|
+
out[i] = prev[i] + (next[i] - prev[i]) * k;
|
|
2019
|
+
out[i + 1] = prev[i + 1] + (next[i + 1] - prev[i + 1]) * k;
|
|
2010
2020
|
} else if (wasSeen && !isSeen && out[i + 2] >= 0.5) {
|
|
2011
2021
|
out[i] = prev[i];
|
|
2012
2022
|
out[i + 1] = prev[i + 1];
|
|
@@ -2015,6 +2025,8 @@ function smoothBody(prev, next, amount = 0.5) {
|
|
|
2015
2025
|
smoothFace(prev, next, out, amount * FACE_SMOOTHING);
|
|
2016
2026
|
return out;
|
|
2017
2027
|
}
|
|
2028
|
+
var STILL_SHARE = 0.1;
|
|
2029
|
+
var STILL_FULL = 0.03;
|
|
2018
2030
|
var FACE_SMOOTHING = 0.35;
|
|
2019
2031
|
var FACE_POINTS = 11;
|
|
2020
2032
|
function smoothFace(prev, next, out, amount) {
|
|
@@ -2098,6 +2110,8 @@ export {
|
|
|
2098
2110
|
toScreen,
|
|
2099
2111
|
parseDeviceHint,
|
|
2100
2112
|
parseGameStats,
|
|
2113
|
+
parseTurnPlayers,
|
|
2114
|
+
parseTurnOrder,
|
|
2101
2115
|
drawShapes,
|
|
2102
2116
|
BAND_BODY,
|
|
2103
2117
|
frameOf,
|
package/dist/fabi-play.d.ts
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
/** The Fabi Play SDK's types, for script-tag games: FabiPlay.PlayerFrame, FabiPlay.Point, FabiPlay.Gesture, … */
|
|
9
9
|
declare namespace FabiPlay {
|
|
10
|
-
/** Anything with Play's on(): a Play, or a stand-in in tests. */
|
|
11
|
-
export type On = Pick<Play, "on"
|
|
10
|
+
/** Anything with Play's on() (and, from a Play, who plays and how): a Play, or a stand-in in tests. */
|
|
11
|
+
export type On = Pick<Play, "on"> & Partial<Pick<Play, "input" | "players">>;
|
|
12
12
|
/** What the child does, as the keyboard adapter names it. */
|
|
13
13
|
export type Action = "jump" | "squat" | "left" | "right" | "clap" | "arms-up";
|
|
14
14
|
/** Action → key code (KeyboardEvent.code: Space, Enter, ArrowUp, KeyW, Digit1…). */
|
|
@@ -29,7 +29,11 @@ declare namespace FabiPlay {
|
|
|
29
29
|
}
|
|
30
30
|
/** Options of keyboard(). */
|
|
31
31
|
export interface KeyboardOptions {
|
|
32
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Whose movements (default 1); another player can get other keys (e.g. WASD). Not given and played with the
|
|
34
|
+
* remote: the child playing now, following the turns (at a turn the lane is the middle one again, as the screen
|
|
35
|
+
* recentres the puppet).
|
|
36
|
+
*/
|
|
33
37
|
player?: number;
|
|
34
38
|
/** Where the keys go: by default the page (domKeys()). */
|
|
35
39
|
target?: KeyTarget;
|
|
@@ -82,10 +86,15 @@ declare namespace FabiPlay {
|
|
|
82
86
|
/**
|
|
83
87
|
* Why the game stops, when it asks for a break:
|
|
84
88
|
* - level: a level is over (the best moment);
|
|
85
|
-
* - gameover: the game is over and a new one may start
|
|
86
|
-
*
|
|
89
|
+
* - gameover: the game is over and a new one may start;
|
|
90
|
+
* - turn: with input 'remote' and turns remaining, the child playing now finished their round (whatever happened in
|
|
91
|
+
* it: a crash or a fall ends a round too). The screen always answers with break-start (its turn screen is the
|
|
92
|
+
* break, and may host an ad), then turn (the next child), then break-done. Hosts before turns answer it as an
|
|
93
|
+
* unknown reason (break-done at once), but they never send turns either.
|
|
94
|
+
* Only 'turn' ever swaps players; 'level' and 'gameover' never do. Never at the game's own pause menu: a child who
|
|
95
|
+
* stops for a moment must not get an ad for it.
|
|
87
96
|
*/
|
|
88
|
-
export type BreakReason = "level" | "gameover";
|
|
97
|
+
export type BreakReason = "level" | "gameover" | "turn";
|
|
89
98
|
/**
|
|
90
99
|
* The movements the host recognises, per player:
|
|
91
100
|
* - `jump`, then `land` when the feet are back on the ground;
|
|
@@ -140,7 +149,9 @@ declare namespace FabiPlay {
|
|
|
140
149
|
* image is cropped to fill the screen), with any backdrop, so avatars and hands are where the children stand (a
|
|
141
150
|
* "virtual mirror"). The screen decides it: with 'video', where the video is drawn (cover or contain); with 'none',
|
|
142
151
|
* the whole image contained at 92% of the viewport's height, bottom-aligned. Games don't compute it: they just use
|
|
143
|
-
* it.
|
|
152
|
+
* it. It is not fixed: with automatic framing (the default, see GameManifest.framing) the screen zooms in on children
|
|
153
|
+
* standing far away, up to 1.8×, so the image may reach past every edge; it changes only at calibration and in breaks
|
|
154
|
+
* (pause, level, turn), never during play, and arrives in a backdrop message. Body points are in player space (0–1 of the camera image, already mirrored like the video), so a point p is on
|
|
144
155
|
* screen at (x + p.x·w, y + p.y·h): see toScreen.
|
|
145
156
|
*/
|
|
146
157
|
export interface BackdropView {
|
|
@@ -183,6 +194,22 @@ declare namespace FabiPlay {
|
|
|
183
194
|
/** view: where the camera image is, with any backdrop (see BackdropView); absent if the screen doesn't know. */
|
|
184
195
|
/** stats: a debug host (the simulator) asks the game to say how often it draws (stats messages); absent on the TV. */
|
|
185
196
|
/** device: what the screen knows about the device (a starting tier, the display's rate); absent if it knows nothing. */
|
|
197
|
+
/**
|
|
198
|
+
* input: how this session is played (see GameInput); absent = 'body'. Only games whose manifest lists 'remote' ever
|
|
199
|
+
* get 'remote'. With 'remote', players is exactly one id: the child playing now (see turn).
|
|
200
|
+
*/
|
|
201
|
+
/**
|
|
202
|
+
* turns: only with input 'remote', and only to games whose manifest says "turns": true: every child who
|
|
203
|
+
* will play this game, one id each, in turn order, players[0] first (see parseTurnOrder); absent = players (one
|
|
204
|
+
* child, no turns). A round per child: at the end of each child's round, while turns remain, the game calls
|
|
205
|
+
* break('turn'); the screen answers break-start, turn (the next child), break-done. After the last child's round
|
|
206
|
+
* the game calls end() with every child's scores. An order that is not valid, or doesn't start with players[0],
|
|
207
|
+
* makes the session one child only.
|
|
208
|
+
*/
|
|
209
|
+
/**
|
|
210
|
+
* pointer: only with input 'remote', true when the TV's remote also points (LG's Magic Remote): it reaches the game
|
|
211
|
+
* as ordinary pointer and mouse events (hover, click with OK). Absent = false.
|
|
212
|
+
*/
|
|
186
213
|
{
|
|
187
214
|
type: "init";
|
|
188
215
|
v: number;
|
|
@@ -195,8 +222,11 @@ declare namespace FabiPlay {
|
|
|
195
222
|
view?: BackdropView;
|
|
196
223
|
stats?: boolean;
|
|
197
224
|
device?: DeviceHint;
|
|
225
|
+
input?: GameInput;
|
|
226
|
+
turns?: number[];
|
|
227
|
+
pointer?: boolean;
|
|
198
228
|
}
|
|
199
|
-
/** The backdrop or the view changed mid-game (video switched on or off, the camera image's fit changed). */
|
|
229
|
+
/** The backdrop or the view changed mid-game (video switched on or off, the camera image's fit or framing changed). */
|
|
200
230
|
| {
|
|
201
231
|
type: "backdrop";
|
|
202
232
|
backdrop: Backdrop;
|
|
@@ -235,6 +265,19 @@ declare namespace FabiPlay {
|
|
|
235
265
|
| {
|
|
236
266
|
type: "break-done";
|
|
237
267
|
shown: boolean;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Turns (input 'remote' only): from now on this child plays, in place of the one before. One remote is one child,
|
|
271
|
+
* so with several children the screen plays one at a time: the same game goes on with the next child in init's
|
|
272
|
+
* turns (players [2] after [1], …), exactly one id. Sent only inside the break of a break('turn') the game asked
|
|
273
|
+
* for (after break-start, before break-done); never at other breaks, so a round never changes hands midway. The
|
|
274
|
+
* new child's puppet starts recentred, in the middle lane (lane 0), with no lane gesture. Ids keep their meaning
|
|
275
|
+
* (slots, avatars): keep each player's score by id across turns, and end with the scores of every child who
|
|
276
|
+
* played. Never sent with input 'body', where init's players stay for the whole game.
|
|
277
|
+
*/
|
|
278
|
+
| {
|
|
279
|
+
type: "turn";
|
|
280
|
+
players: number[];
|
|
238
281
|
};
|
|
239
282
|
/** Why a game is paused (see HostMessage pause). */
|
|
240
283
|
export type PauseReason = "user" | "distance";
|
|
@@ -330,7 +373,39 @@ declare namespace FabiPlay {
|
|
|
330
373
|
backdrop?: GameBackdrop[];
|
|
331
374
|
/** Optional: Fabi explains the game on the TV before calibration (see GameIntro). Absent: straight to calibration. */
|
|
332
375
|
intro?: GameIntro;
|
|
376
|
+
/** Optional: how it can be played; absent = ['body']. Must include 'body' (Fabi Play is a movement platform); add 'remote' to be offered without a phone. */
|
|
377
|
+
inputs?: GameInput[];
|
|
378
|
+
/**
|
|
379
|
+
* Optional, only with 'remote' in inputs: true when the game follows the turns contract, so the screen may let
|
|
380
|
+
* several children play with the remote, a round each (init.turns): play a round with the child in players; when
|
|
381
|
+
* it ends (a crash too) and turns remain, ask break('turn') (the screen answers break-start, turn, break-done);
|
|
382
|
+
* after the last round, end with every child's scores. Absent or false: remote mode is one child only.
|
|
383
|
+
*/
|
|
384
|
+
turns?: boolean;
|
|
385
|
+
/**
|
|
386
|
+
* Optional: 'auto' (the default) lets the screen frame the children: when they stand far from the camera it zooms
|
|
387
|
+
* in on them (up to 1.8×), so every child's avatar is about the same size and reaches the same things, near or
|
|
388
|
+
* far; the view changes only at calibration and in breaks. 'fixed' keeps the whole camera image, as before (for
|
|
389
|
+
* a game drawn on exact spots of the live video).
|
|
390
|
+
*/
|
|
391
|
+
framing?: GameFraming;
|
|
333
392
|
}
|
|
393
|
+
/** How the screen frames the camera image for a game: see GameManifest.framing. */
|
|
394
|
+
export type GameFraming = "auto" | "fixed";
|
|
395
|
+
/**
|
|
396
|
+
* How a game can be played: with the body (camera on the phone), or with the TV remote (keys become gestures).
|
|
397
|
+
*
|
|
398
|
+
* 'remote' is for TV apps without a phone (Samsung, LG, Android TV, Fire TV): one remote is one child, and with
|
|
399
|
+
* several children the screen runs turns, only for games whose manifest says "turns": true (see GameManifest.turns,
|
|
400
|
+
* BreakReason turn, HostMessage turn). The screen turns keys into the gestures the body makes (↑ or OK jump;
|
|
401
|
+
* ↓ held squat, stand on release; ← → lanes; OK twice quickly clap; Back is the platform's pause, never the game's)
|
|
402
|
+
* and animates a puppet body in the frames, so with one child most games need no code. On LG the Magic
|
|
403
|
+
* Remote also points: it reaches the game as ordinary pointer and mouse events (init.pointer says when). A game that
|
|
404
|
+
* lists 'remote' must play with those keys alone and, where it has things to pick (bubbles, answers, buttons), let a
|
|
405
|
+
* pointer click them too (pointer events, large hit areas, a hover highlight). Games aimed with the hands should not
|
|
406
|
+
* list it unless they offer a selection the keys can drive.
|
|
407
|
+
*/
|
|
408
|
+
export type GameInput = "body" | "remote";
|
|
334
409
|
/** The backdrops a game may declare in its manifest (its own background is always supported). */
|
|
335
410
|
export type GameBackdrop = Exclude<Backdrop, "none">;
|
|
336
411
|
/** The moves an intro step can show; the platform draws them with each child's avatar. Same names the SDK detects. */
|
|
@@ -394,12 +469,12 @@ declare namespace FabiPlay {
|
|
|
394
469
|
export type DrawAvatar = (ctx: CanvasRenderingContext2D, who: PlayerFrame | Body, player?: number | string, box?: AvatarBox | null, options?: DrawAvatarOptions) => Body | null;
|
|
395
470
|
/** A frame's body smoothed (and interpolated) for its player, or a player id's last one; null if none. */
|
|
396
471
|
export type SmoothedBody = (who: PlayerFrame | number, options?: Pick<DrawAvatarOptions, "smooth" | "interpolate">) => Body | null;
|
|
397
|
-
/** What the 'start' event says: who plays and how. */
|
|
472
|
+
/** What the 'start' event says: who plays and how, as it was at the start (late listeners get the same; see Easy for now). */
|
|
398
473
|
export interface StartInfo {
|
|
399
474
|
/** The players' age range: simpler rules for 3-5, scores and challenges for 9-12. */
|
|
400
475
|
ageRange: AgeRange;
|
|
401
|
-
/** The ids of the players (1, 2, …). */
|
|
402
|
-
players: number[];
|
|
476
|
+
/** The ids of the players (1, 2, …) at the start, frozen; with input 'remote', the first child's turn (see the turn event). */
|
|
477
|
+
players: readonly number[];
|
|
403
478
|
/** The language to show text in. */
|
|
404
479
|
locale: Locale;
|
|
405
480
|
/** 'tv': on a TV, children standing back; 'solo': a tablet in front of one child. */
|
|
@@ -410,6 +485,12 @@ declare namespace FabiPlay {
|
|
|
410
485
|
view: BackdropView | null;
|
|
411
486
|
/** What the screen knows about the device (a starting tier, the display's rate), or null: see Play.device. */
|
|
412
487
|
device: DeviceHint | null;
|
|
488
|
+
/** Every child in turn order, frozen (with input 'remote', one round each; see Play.turns); otherwise players. */
|
|
489
|
+
turns: readonly number[];
|
|
490
|
+
/** 'body' (the default) or 'remote': played with the TV remote, keys turned into gestures (see Play.input). */
|
|
491
|
+
input: GameInput;
|
|
492
|
+
/** With input 'remote', whether the remote also points (LG's Magic Remote; see Play.pointer). */
|
|
493
|
+
pointer: boolean;
|
|
413
494
|
}
|
|
414
495
|
/**
|
|
415
496
|
* The events of FabiPlay.on, by name, with what each listener gets. Every gesture (jump, land, squat, stand, clap,
|
|
@@ -461,6 +542,13 @@ declare namespace FabiPlay {
|
|
|
461
542
|
backdrop: Backdrop;
|
|
462
543
|
view: BackdropView | null;
|
|
463
544
|
};
|
|
545
|
+
/**
|
|
546
|
+
* Remote mode only, inside the break of break('turn'): the next child's turn; players (one id) is who plays from
|
|
547
|
+
* now on. Keep each player's score by id.
|
|
548
|
+
*/
|
|
549
|
+
turn: {
|
|
550
|
+
players: number[];
|
|
551
|
+
};
|
|
464
552
|
};
|
|
465
553
|
/** An event name of FabiPlay.on. */
|
|
466
554
|
export type EasyEvent = keyof EasyEvents;
|
|
@@ -477,7 +565,7 @@ declare namespace FabiPlay {
|
|
|
477
565
|
export interface Easy {
|
|
478
566
|
/**
|
|
479
567
|
* Listens to an event: a gesture by its name ('jump', 'clap', 'lane-left', …), 'start', 'hands', 'shape',
|
|
480
|
-
* 'players', 'pause', 'resume', 'break-start', 'break-end', 'backdrop' (see EasyEvents). Returns a function that
|
|
568
|
+
* 'players', 'pause', 'resume', 'break-start', 'break-end', 'backdrop', 'turn' (see EasyEvents). Returns a function that
|
|
481
569
|
* stops listening.
|
|
482
570
|
*
|
|
483
571
|
* @example
|
|
@@ -486,11 +574,14 @@ declare namespace FabiPlay {
|
|
|
486
574
|
on<K extends EasyEvent>(event: K, listener: (data: EasyEvents[K]) => void): () => void;
|
|
487
575
|
/** Say the game is loaded; done for you on page load unless the script tag says data-ready="manual". */
|
|
488
576
|
ready(): void;
|
|
489
|
-
/** The score so far, for the screen to show; player 1 unless said. */
|
|
577
|
+
/** The score so far, for the screen to show; player 1 unless said (with input 'remote', the child playing now). */
|
|
490
578
|
score(score: number, player?: number): void;
|
|
491
579
|
/** The game stopped by itself ('level' or 'gameover', never its pause menu): Fabi Play may show an ad. Resolves when it can go on. */
|
|
492
580
|
break(reason?: BreakReason): Promise<boolean>;
|
|
493
|
-
/**
|
|
581
|
+
/**
|
|
582
|
+
* The end: a score (player 1's; with input 'remote', the child playing now's, and for each child who played
|
|
583
|
+
* before them in turns their last score() or 0), or one per player. Stars from 0 to 3, 2 if not given.
|
|
584
|
+
*/
|
|
494
585
|
end(score: number | {
|
|
495
586
|
player: number;
|
|
496
587
|
score: number;
|
|
@@ -509,6 +600,14 @@ declare namespace FabiPlay {
|
|
|
509
600
|
* clicks by staying still on a spot. The script tag's data-pointer does the same.
|
|
510
601
|
*/
|
|
511
602
|
pointer(options?: Omit<PointerOptions, "target">): void;
|
|
603
|
+
/** The players playing now (frozen; see Play.players); empty before start. */
|
|
604
|
+
readonly players: readonly number[];
|
|
605
|
+
/** Every child in turn order (frozen; see Play.turns); empty before start. */
|
|
606
|
+
readonly turns: readonly number[];
|
|
607
|
+
/** How this session is played (see Play.input); 'body' before start. */
|
|
608
|
+
readonly input: GameInput;
|
|
609
|
+
/** With input 'remote', whether the remote also points (Play.pointer; pointer() is the hand adapter); false before start. */
|
|
610
|
+
readonly hasPointer: boolean;
|
|
512
611
|
/** Where the camera image is, with any backdrop; null if the host didn't say, and before start. */
|
|
513
612
|
readonly view: BackdropView | null;
|
|
514
613
|
/** What the screen knows about the device (see Play.device); null if it said nothing, and before start. */
|
|
@@ -568,6 +667,14 @@ declare namespace FabiPlay {
|
|
|
568
667
|
backdrop: Extract<HostMessage, {
|
|
569
668
|
type: "backdrop";
|
|
570
669
|
}>;
|
|
670
|
+
/**
|
|
671
|
+
* Remote mode only (input 'remote'), inside the break of a break('turn'): the next child's turn, the same game goes
|
|
672
|
+
* on with this one player (see Play.players). Keep each player's score by id. The SDK passes on only a turn to the
|
|
673
|
+
* next child in Play.turns, while a 'turn' break is under way; it drops any other.
|
|
674
|
+
*/
|
|
675
|
+
turn: Extract<HostMessage, {
|
|
676
|
+
type: "turn";
|
|
677
|
+
}>;
|
|
571
678
|
};
|
|
572
679
|
/**
|
|
573
680
|
* The game's side of Fabi Play, from connect(): who plays, their movements, and how the game tells it is ready,
|
|
@@ -583,8 +690,39 @@ declare namespace FabiPlay {
|
|
|
583
690
|
readonly locale: Locale;
|
|
584
691
|
/** 'tv': on a TV or Chromecast, children standing back; 'solo': a tablet in front of one child. */
|
|
585
692
|
readonly mode: "tv" | "solo";
|
|
586
|
-
/**
|
|
587
|
-
|
|
693
|
+
/**
|
|
694
|
+
* The ids of the players playing now (1, 2, …), as frames and gestures name them; a frozen array, read it again
|
|
695
|
+
* after a turn. With input 'body' the same for the whole game. With input 'remote' exactly one child, and it
|
|
696
|
+
* changes only inside the break of a break('turn') (the 'turn' event, between break-start and break-done). Keep
|
|
697
|
+
* each player's score by id across turns, and end with the scores of everyone who played.
|
|
698
|
+
*/
|
|
699
|
+
readonly players: readonly number[];
|
|
700
|
+
/**
|
|
701
|
+
* Every child who plays this game, in turn order (frozen): with input 'remote' and several children, one round
|
|
702
|
+
* each, players being the one playing now (turns[0] at the start). Only for games whose manifest says
|
|
703
|
+
* "turns": true (with "remote" in inputs); otherwise remote mode is one child. The contract:
|
|
704
|
+
* 1. play a round with the child in players;
|
|
705
|
+
* 2. at the end of it (whatever happened: a crash ends a round too), if turns remain, call break('turn'): the
|
|
706
|
+
* screen always answers break-start (its turn screen), 'turn' (the next child), then break-done;
|
|
707
|
+
* 3. after the last child's round, call end() with every child's scores.
|
|
708
|
+
* 'level' and 'gameover' breaks never swap players. With 'body', one child, or hosts that don't say: players.
|
|
709
|
+
*/
|
|
710
|
+
readonly turns: readonly number[];
|
|
711
|
+
/**
|
|
712
|
+
* How this session is played: 'body' (the camera on the phone; the default, and what older hosts mean) or
|
|
713
|
+
* 'remote' (a TV without a phone, only for games whose manifest lists it in inputs): the screen turns the remote's
|
|
714
|
+
* keys into the same gestures (↑ or OK jump, ↓ held squat then stand, ← → lanes, OK twice clap) and moves a puppet
|
|
715
|
+
* body in the frames, one child at a time. Adapt if it helps: slow down, show a selection the arrows move instead
|
|
716
|
+
* of a hand cursor.
|
|
717
|
+
*/
|
|
718
|
+
readonly input: GameInput;
|
|
719
|
+
/**
|
|
720
|
+
* Only with input 'remote': the remote also points (LG's Magic Remote), reaching the game as ordinary pointer and
|
|
721
|
+
* mouse events (hover, click with OK). Then let what the game has to pick be clicked too (large hit areas, a hover
|
|
722
|
+
* highlight). false otherwise, and from hosts that don't say. Not the pointer() adapter, which makes a child's hand
|
|
723
|
+
* the mouse.
|
|
724
|
+
*/
|
|
725
|
+
readonly pointer: boolean;
|
|
588
726
|
/** The screen's sound is off: start the game muted. */
|
|
589
727
|
readonly muted: boolean;
|
|
590
728
|
/** The players' age range: simpler rules for 3-5, scores and challenges for 9-12. */
|
|
@@ -646,8 +784,8 @@ declare namespace FabiPlay {
|
|
|
646
784
|
/** Called when backdrop or view changes; returns a function that stops listening. */
|
|
647
785
|
onBackdrop(listener: (backdrop: Backdrop, view: BackdropView | null) => void): () => void;
|
|
648
786
|
/**
|
|
649
|
-
* Listens to the host: 'frame', 'gesture', 'pause', 'resume', 'break-start', 'break-done', 'backdrop'
|
|
650
|
-
* Events). Returns a function that stops listening.
|
|
787
|
+
* Listens to the host: 'frame', 'gesture', 'pause', 'resume', 'break-start', 'break-done', 'backdrop', 'turn'
|
|
788
|
+
* (see Events). Returns a function that stops listening.
|
|
651
789
|
*
|
|
652
790
|
* @example
|
|
653
791
|
* const off = play.on('frame', (f) => (players = f.players))
|
|
@@ -658,9 +796,10 @@ declare namespace FabiPlay {
|
|
|
658
796
|
/** A player's score so far, for the screen to show. */
|
|
659
797
|
score(player: number, score: number): void;
|
|
660
798
|
/**
|
|
661
|
-
* The game has stopped by itself: a level is over
|
|
662
|
-
*
|
|
663
|
-
*
|
|
799
|
+
* The game has stopped by itself: a level is over, the game is over, or (with the remote, see turns) a child's
|
|
800
|
+
* round is over and the next child's turn is due: 'turn' (never at its pause menu). Fabi Play may show an ad
|
|
801
|
+
* there, if its rules allow (break-start, then break-done; with 'turn', the turn message in between); resolves
|
|
802
|
+
* when the game can go on, true if an ad was shown. Never call it in the middle of play.
|
|
664
803
|
*/
|
|
665
804
|
break(reason?: BreakReason): Promise<boolean>;
|
|
666
805
|
/**
|