@indiegems/gem-web-sdk 16.3.1 → 18.0.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 +40 -0
- package/dist/conformance.d.ts +1 -1
- package/dist/game.d.ts +24 -3
- package/dist/gem-web-sdk.bridge.js +2 -2
- package/dist/gem-web-sdk.conformance.js +3 -3
- package/dist/gem-web-sdk.esm.js +3 -3
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +3 -3
- package/dist/gem-web-sdk.min.js +3 -3
- package/dist/gem-web-sdk.sdk.js +3 -3
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +41 -8
- package/dist/sdk.d.ts +24 -3
- package/docs/api/api-reference-function.md +1 -1
- package/docs/api/api-reference-game.md +148 -3
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +135 -2
- package/docs/arcade-and-store.md +70 -10
- package/docs/arcade-economy.md +6 -5
- package/docs/changelog.md +40 -0
- package/docs/getting-started.md +1 -1
- package/docs/inventory-and-catalogs.md +3 -2
- package/docs/manifest.json +14 -14
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +29 -0
- package/package.json +1 -1
- package/schema/bridge.v1.json +166 -0
- package/schema/capabilities.public.json +13 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The function surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/function`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/function` v18.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/function` exports exactly the symbols on this page and nothing else.
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The browser surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v18.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
|
@@ -4605,6 +4605,19 @@ readonly available: boolean
|
|
|
4605
4605
|
Whether the Arcade Store answers here, synchronously. Draw your own "Get
|
|
4606
4606
|
Gems" button only when it is true.
|
|
4607
4607
|
|
|
4608
|
+
<a id="storeapi-showofferavailable"></a>
|
|
4609
|
+
|
|
4610
|
+
## StoreApi.showOfferAvailable
|
|
4611
|
+
|
|
4612
|
+
```ts
|
|
4613
|
+
readonly showOfferAvailable: boolean
|
|
4614
|
+
```
|
|
4615
|
+
|
|
4616
|
+
Whether [StoreApi.showOffer](#storeapi-showoffer) answers here, synchronously. An older
|
|
4617
|
+
Arcade can sell through `purchase` and still not draw this sheet, so read
|
|
4618
|
+
this, not [StoreApi.available](#storeapi-available), before you draw a button that calls
|
|
4619
|
+
`showOffer`. `false` until the bridge is up.
|
|
4620
|
+
|
|
4608
4621
|
<a id="storeapi-offers"></a>
|
|
4609
4622
|
|
|
4610
4623
|
## StoreApi.offers
|
|
@@ -4659,6 +4672,43 @@ payment takes as long as it takes.
|
|
|
4659
4672
|
|
|
4660
4673
|
**Returns.** Always resolves; never rejects.
|
|
4661
4674
|
|
|
4675
|
+
<a id="storeapi-showoffer"></a>
|
|
4676
|
+
|
|
4677
|
+
## StoreApi.showOffer
|
|
4678
|
+
|
|
4679
|
+
```ts
|
|
4680
|
+
showOffer(skuId: string): Promise<ArcadeOutcome>
|
|
4681
|
+
```
|
|
4682
|
+
|
|
4683
|
+
Ask the arcade to show one SKU from [StoreApi.offers](#storeapi-offers) in its own
|
|
4684
|
+
sheet, over your paused and hidden game: its art, what the player gets, a
|
|
4685
|
+
sale if one is on, and, for a pass the player already holds, how long it
|
|
4686
|
+
has left. The sheet's Buy opens the same checkout as
|
|
4687
|
+
[StoreApi.purchase](#storeapi-purchase). **Call it from the player's press handler.**
|
|
4688
|
+
|
|
4689
|
+
Use it for an offer you would have to hardcode a pitch for: VIP and other
|
|
4690
|
+
passes, and bundles. Your button only needs to say what it opens. Draw a
|
|
4691
|
+
Gem pack yourself and use `purchase` instead. Check
|
|
4692
|
+
[StoreApi.showOfferAvailable](#storeapi-showofferavailable) first, and that the SKU is in `offers`.
|
|
4693
|
+
|
|
4694
|
+
It answers exactly as `purchase` does, with one addition: `declined` /
|
|
4695
|
+
`cancelled` also covers the player leaving the sheet without pressing Buy,
|
|
4696
|
+
and you are not told which of the two they closed. That starts the same
|
|
4697
|
+
back-off, so do not ask again unprompted. `unavailable` / `unsupported`
|
|
4698
|
+
means this Arcade does not draw the sheet, and nothing was sent.
|
|
4699
|
+
`unavailable` / `error` means **the outcome is unknown**: re-read balances
|
|
4700
|
+
and inventory through `gem.me`, and keep listening with
|
|
4701
|
+
[StoreApi.onSettled](#storeapi-onsettled).
|
|
4702
|
+
|
|
4703
|
+
NO TIMEOUT: it waits for as long as the arcade is connected, because a
|
|
4704
|
+
player may read a sheet and pay for as long as they like.
|
|
4705
|
+
|
|
4706
|
+
| Parameter | Type |
|
|
4707
|
+
| --- | --- |
|
|
4708
|
+
| `skuId` | `string` |
|
|
4709
|
+
|
|
4710
|
+
**Returns.** Always resolves; never rejects.
|
|
4711
|
+
|
|
4662
4712
|
<a id="storeapi-onsettled"></a>
|
|
4663
4713
|
|
|
4664
4714
|
## StoreApi.onSettled
|
|
@@ -6919,6 +6969,37 @@ A corner of your frame, for `gem.setGemCorner`: where you would like the
|
|
|
6919
6969
|
arcade's Gem button to sit. `'top-left'` is where it sits unless you ask.
|
|
6920
6970
|
Physical corners: `'top-left'` is the top-left in every language.
|
|
6921
6971
|
|
|
6972
|
+
<a id="menustyle"></a>
|
|
6973
|
+
|
|
6974
|
+
## MenuStyle
|
|
6975
|
+
|
|
6976
|
+
```ts
|
|
6977
|
+
interface MenuStyle
|
|
6978
|
+
```
|
|
6979
|
+
|
|
6980
|
+
Two colours for the arcade's own screens over your game, for
|
|
6981
|
+
`gem.setMenuStyle`: each exactly `#rrggbb`. The arcade works out the rest.
|
|
6982
|
+
|
|
6983
|
+
<a id="menustyle-ground"></a>
|
|
6984
|
+
|
|
6985
|
+
## MenuStyle.ground
|
|
6986
|
+
|
|
6987
|
+
```ts
|
|
6988
|
+
readonly ground: string
|
|
6989
|
+
```
|
|
6990
|
+
|
|
6991
|
+
The colour the arcade's surfaces are built on, such as `'#1b2a4a'`.
|
|
6992
|
+
|
|
6993
|
+
<a id="menustyle-accent"></a>
|
|
6994
|
+
|
|
6995
|
+
## MenuStyle.accent
|
|
6996
|
+
|
|
6997
|
+
```ts
|
|
6998
|
+
readonly accent: string
|
|
6999
|
+
```
|
|
7000
|
+
|
|
7001
|
+
The one highlight, at least 3:1 against `ground`, such as `'#ffb300'`.
|
|
7002
|
+
|
|
6922
7003
|
<a id="framestate"></a>
|
|
6923
7004
|
|
|
6924
7005
|
## FrameState
|
|
@@ -7296,6 +7377,29 @@ its control.
|
|
|
7296
7377
|
up, for an arcade that cannot move its control, for a value that is not
|
|
7297
7378
|
one of the four corners, and past this frame's own limit.
|
|
7298
7379
|
|
|
7380
|
+
<a id="gemframe-setmenustyle"></a>
|
|
7381
|
+
|
|
7382
|
+
## GemFrame.setMenuStyle
|
|
7383
|
+
|
|
7384
|
+
```ts
|
|
7385
|
+
setMenuStyle(style: MenuStyle | null): boolean
|
|
7386
|
+
```
|
|
7387
|
+
|
|
7388
|
+
Give the arcade's own screens over your game two colours to wear, or
|
|
7389
|
+
`null` to hand them back to the player's style: the whole answer each
|
|
7390
|
+
time, the latest one winning.
|
|
7391
|
+
|
|
7392
|
+
Prefer `gem.setMenuStyle`, which checks the colours and holds them across
|
|
7393
|
+
a reconnect. This one sends only while connected, and only to an arcade
|
|
7394
|
+
that can wear them.
|
|
7395
|
+
|
|
7396
|
+
| Parameter | Type |
|
|
7397
|
+
| --- | --- |
|
|
7398
|
+
| `style` | `MenuStyle \| null` |
|
|
7399
|
+
|
|
7400
|
+
**Returns.** whether the message left the frame: `false` before the bridge is
|
|
7401
|
+
up, for an arcade that cannot wear them, and past this frame's own limit.
|
|
7402
|
+
|
|
7299
7403
|
<a id="gemframe-requestshare"></a>
|
|
7300
7404
|
|
|
7301
7405
|
## GemFrame.requestShare
|
|
@@ -11216,8 +11320,10 @@ sheet. To spend Gems or Coins on your game's own items, use
|
|
|
11216
11320
|
[Gem.purchases](#gem-purchases).
|
|
11217
11321
|
|
|
11218
11322
|
List what the store sells here with `offers()`, show each `priceDisplay`
|
|
11219
|
-
exactly as given, and call `purchase(skuId)` from the player's press.
|
|
11220
|
-
|
|
11323
|
+
exactly as given, and call `purchase(skuId)` from the player's press. For a
|
|
11324
|
+
pass, a bundle or anything else whose pitch you would have to hardcode, call
|
|
11325
|
+
`showOffer(skuId)` instead, and the arcade draws the offer. After `ok`,
|
|
11326
|
+
re-read balances through [Gem.me](#gem-me). Nothing rejects.
|
|
11221
11327
|
|
|
11222
11328
|
**Guides.** [arcade-and-store](../arcade-and-store.md)
|
|
11223
11329
|
|
|
@@ -11967,6 +12073,45 @@ with a warning on the console, and changes nothing.
|
|
|
11967
12073
|
up (the corner is held and sent when it connects), under an embedder that
|
|
11968
12074
|
cannot move its control, and for a value that is not a corner.
|
|
11969
12075
|
|
|
12076
|
+
<a id="gem-setmenustyle"></a>
|
|
12077
|
+
|
|
12078
|
+
## Gem.setMenuStyle
|
|
12079
|
+
|
|
12080
|
+
```ts
|
|
12081
|
+
setMenuStyle(style: MenuStyle | null): boolean
|
|
12082
|
+
```
|
|
12083
|
+
|
|
12084
|
+
Give the arcade's own screens over your game your colours, so they match
|
|
12085
|
+
it: on the Gem Arcade, the burger button, the Gem menu and its pages, and
|
|
12086
|
+
every Arcade pop-up over the running game.
|
|
12087
|
+
|
|
12088
|
+
```js
|
|
12089
|
+
gem.setMenuStyle({ ground: '#1b2a4a', accent: '#ffb300' });
|
|
12090
|
+
gem.setMenuStyle(null); // back to the player's own style
|
|
12091
|
+
```
|
|
12092
|
+
|
|
12093
|
+
**Colours only.** `ground` is what the arcade's surfaces are built on and
|
|
12094
|
+
`accent` its one highlight; the arcade works out the text, rims, button
|
|
12095
|
+
shapes and fonts, so its screens stay readable on any ground.
|
|
12096
|
+
|
|
12097
|
+
**The Arcade refuses colours it cannot use.** Each must be exactly
|
|
12098
|
+
`#rrggbb`, and `accent` must reach 3:1 contrast against `ground`. Anything
|
|
12099
|
+
else is ignored, with a warning on the console giving the measured ratio:
|
|
12100
|
+
the last colours stand, nothing is sent, and this returns `false`.
|
|
12101
|
+
|
|
12102
|
+
**Your colours win over the player's chosen style while your game runs**,
|
|
12103
|
+
and the player's style comes back when it ends. It is screen state, not a
|
|
12104
|
+
toggle: each call is the whole answer, so you may change it between levels.
|
|
12105
|
+
Call it at boot, so your colours arrive with the first screen.
|
|
12106
|
+
|
|
12107
|
+
| Parameter | Type | Description |
|
|
12108
|
+
| --- | --- | --- |
|
|
12109
|
+
| `style` | `MenuStyle \| null` | The two colours, or `null` to hand back to the player's style. |
|
|
12110
|
+
|
|
12111
|
+
**Returns.** `true` when the arcade was told now; `false` before the bridge is
|
|
12112
|
+
up (the colours are held and sent when it connects), under an embedder
|
|
12113
|
+
that cannot wear them, and for colours it would refuse.
|
|
12114
|
+
|
|
11970
12115
|
<a id="gem-reportframe"></a>
|
|
11971
12116
|
|
|
11972
12117
|
## Gem.reportFrame
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The server surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v18.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/server` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
package/docs/api/reference.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schema_version": 2,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "
|
|
5
|
+
"version": "18.0.0"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -5537,6 +5537,22 @@
|
|
|
5537
5537
|
"defaultValue": null,
|
|
5538
5538
|
"guides": []
|
|
5539
5539
|
},
|
|
5540
|
+
{
|
|
5541
|
+
"id": "StoreApi.showOfferAvailable",
|
|
5542
|
+
"name": "showOfferAvailable",
|
|
5543
|
+
"anchor": "storeapi-showofferavailable",
|
|
5544
|
+
"group": "store",
|
|
5545
|
+
"signature": "readonly showOfferAvailable: boolean",
|
|
5546
|
+
"documented": true,
|
|
5547
|
+
"summary": "Whether [StoreApi.showOffer](api-reference-game.md#storeapi-showoffer) answers here, synchronously. An older\nArcade can sell through `purchase` and still not draw this sheet, so read\nthis, not [StoreApi.available](api-reference-game.md#storeapi-available), before you draw a button that calls\n`showOffer`. `false` until the bridge is up.",
|
|
5548
|
+
"deprecated": null,
|
|
5549
|
+
"params": [],
|
|
5550
|
+
"returns": null,
|
|
5551
|
+
"throws": null,
|
|
5552
|
+
"example": null,
|
|
5553
|
+
"defaultValue": null,
|
|
5554
|
+
"guides": []
|
|
5555
|
+
},
|
|
5540
5556
|
{
|
|
5541
5557
|
"id": "StoreApi.offers",
|
|
5542
5558
|
"name": "offers",
|
|
@@ -5576,6 +5592,29 @@
|
|
|
5576
5592
|
"defaultValue": null,
|
|
5577
5593
|
"guides": []
|
|
5578
5594
|
},
|
|
5595
|
+
{
|
|
5596
|
+
"id": "StoreApi.showOffer",
|
|
5597
|
+
"name": "showOffer",
|
|
5598
|
+
"anchor": "storeapi-showoffer",
|
|
5599
|
+
"group": "store",
|
|
5600
|
+
"signature": "showOffer(skuId: string): Promise<ArcadeOutcome>",
|
|
5601
|
+
"documented": true,
|
|
5602
|
+
"summary": "Ask the arcade to show one SKU from [StoreApi.offers](api-reference-game.md#storeapi-offers) in its own\nsheet, over your paused and hidden game: its art, what the player gets, a\nsale if one is on, and, for a pass the player already holds, how long it\nhas left. The sheet's Buy opens the same checkout as\n[StoreApi.purchase](api-reference-game.md#storeapi-purchase). **Call it from the player's press handler.**\n\nUse it for an offer you would have to hardcode a pitch for: VIP and other\npasses, and bundles. Your button only needs to say what it opens. Draw a\nGem pack yourself and use `purchase` instead. Check\n[StoreApi.showOfferAvailable](api-reference-game.md#storeapi-showofferavailable) first, and that the SKU is in `offers`.\n\nIt answers exactly as `purchase` does, with one addition: `declined` /\n`cancelled` also covers the player leaving the sheet without pressing Buy,\nand you are not told which of the two they closed. That starts the same\nback-off, so do not ask again unprompted. `unavailable` / `unsupported`\nmeans this Arcade does not draw the sheet, and nothing was sent.\n`unavailable` / `error` means **the outcome is unknown**: re-read balances\nand inventory through `gem.me`, and keep listening with\n[StoreApi.onSettled](api-reference-game.md#storeapi-onsettled).\n\nNO TIMEOUT: it waits for as long as the arcade is connected, because a\nplayer may read a sheet and pay for as long as they like.",
|
|
5603
|
+
"deprecated": null,
|
|
5604
|
+
"params": [
|
|
5605
|
+
{
|
|
5606
|
+
"name": "skuId",
|
|
5607
|
+
"type": "string",
|
|
5608
|
+
"optional": false,
|
|
5609
|
+
"summary": null
|
|
5610
|
+
}
|
|
5611
|
+
],
|
|
5612
|
+
"returns": "Always resolves; never rejects.",
|
|
5613
|
+
"throws": null,
|
|
5614
|
+
"example": null,
|
|
5615
|
+
"defaultValue": null,
|
|
5616
|
+
"guides": []
|
|
5617
|
+
},
|
|
5579
5618
|
{
|
|
5580
5619
|
"id": "StoreApi.onSettled",
|
|
5581
5620
|
"name": "onSettled",
|
|
@@ -8265,6 +8304,54 @@
|
|
|
8265
8304
|
"defaultValue": null,
|
|
8266
8305
|
"guides": []
|
|
8267
8306
|
},
|
|
8307
|
+
{
|
|
8308
|
+
"id": "MenuStyle",
|
|
8309
|
+
"name": "MenuStyle",
|
|
8310
|
+
"anchor": "menustyle",
|
|
8311
|
+
"group": "frame",
|
|
8312
|
+
"signature": "interface MenuStyle",
|
|
8313
|
+
"documented": true,
|
|
8314
|
+
"summary": "Two colours for the arcade's own screens over your game, for\n`gem.setMenuStyle`: each exactly `#rrggbb`. The arcade works out the rest.",
|
|
8315
|
+
"deprecated": null,
|
|
8316
|
+
"params": [],
|
|
8317
|
+
"returns": null,
|
|
8318
|
+
"throws": null,
|
|
8319
|
+
"example": null,
|
|
8320
|
+
"defaultValue": null,
|
|
8321
|
+
"guides": []
|
|
8322
|
+
},
|
|
8323
|
+
{
|
|
8324
|
+
"id": "MenuStyle.ground",
|
|
8325
|
+
"name": "ground",
|
|
8326
|
+
"anchor": "menustyle-ground",
|
|
8327
|
+
"group": "frame",
|
|
8328
|
+
"signature": "readonly ground: string",
|
|
8329
|
+
"documented": true,
|
|
8330
|
+
"summary": "The colour the arcade's surfaces are built on, such as `'#1b2a4a'`.",
|
|
8331
|
+
"deprecated": null,
|
|
8332
|
+
"params": [],
|
|
8333
|
+
"returns": null,
|
|
8334
|
+
"throws": null,
|
|
8335
|
+
"example": null,
|
|
8336
|
+
"defaultValue": null,
|
|
8337
|
+
"guides": []
|
|
8338
|
+
},
|
|
8339
|
+
{
|
|
8340
|
+
"id": "MenuStyle.accent",
|
|
8341
|
+
"name": "accent",
|
|
8342
|
+
"anchor": "menustyle-accent",
|
|
8343
|
+
"group": "frame",
|
|
8344
|
+
"signature": "readonly accent: string",
|
|
8345
|
+
"documented": true,
|
|
8346
|
+
"summary": "The one highlight, at least 3:1 against `ground`, such as `'#ffb300'`.",
|
|
8347
|
+
"deprecated": null,
|
|
8348
|
+
"params": [],
|
|
8349
|
+
"returns": null,
|
|
8350
|
+
"throws": null,
|
|
8351
|
+
"example": null,
|
|
8352
|
+
"defaultValue": null,
|
|
8353
|
+
"guides": []
|
|
8354
|
+
},
|
|
8268
8355
|
{
|
|
8269
8356
|
"id": "FrameState",
|
|
8270
8357
|
"name": "FrameState",
|
|
@@ -8645,6 +8732,29 @@
|
|
|
8645
8732
|
"defaultValue": null,
|
|
8646
8733
|
"guides": []
|
|
8647
8734
|
},
|
|
8735
|
+
{
|
|
8736
|
+
"id": "GemFrame.setMenuStyle",
|
|
8737
|
+
"name": "setMenuStyle",
|
|
8738
|
+
"anchor": "gemframe-setmenustyle",
|
|
8739
|
+
"group": "frame",
|
|
8740
|
+
"signature": "setMenuStyle(style: MenuStyle | null): boolean",
|
|
8741
|
+
"documented": true,
|
|
8742
|
+
"summary": "Give the arcade's own screens over your game two colours to wear, or\n`null` to hand them back to the player's style: the whole answer each\ntime, the latest one winning.\n\nPrefer `gem.setMenuStyle`, which checks the colours and holds them across\na reconnect. This one sends only while connected, and only to an arcade\nthat can wear them.",
|
|
8743
|
+
"deprecated": null,
|
|
8744
|
+
"params": [
|
|
8745
|
+
{
|
|
8746
|
+
"name": "style",
|
|
8747
|
+
"type": "MenuStyle | null",
|
|
8748
|
+
"optional": false,
|
|
8749
|
+
"summary": null
|
|
8750
|
+
}
|
|
8751
|
+
],
|
|
8752
|
+
"returns": "whether the message left the frame: `false` before the bridge is\n up, for an arcade that cannot wear them, and past this frame's own limit.",
|
|
8753
|
+
"throws": null,
|
|
8754
|
+
"example": null,
|
|
8755
|
+
"defaultValue": null,
|
|
8756
|
+
"guides": []
|
|
8757
|
+
},
|
|
8648
8758
|
{
|
|
8649
8759
|
"id": "GemFrame.requestShare",
|
|
8650
8760
|
"name": "requestShare",
|
|
@@ -12790,7 +12900,7 @@
|
|
|
12790
12900
|
"group": "start",
|
|
12791
12901
|
"signature": "readonly store: StoreApi",
|
|
12792
12902
|
"documented": true,
|
|
12793
|
-
"summary": "**Real money.** The Arcade Store's own Gem packs, Boosts, passes and\nbundles, sold for the player's money through the arcade's own purchase\nsheet. To spend Gems or Coins on your game's own items, use\n[Gem.purchases](api-reference-game.md#gem-purchases).\n\nList what the store sells here with `offers()`, show each `priceDisplay`\nexactly as given, and call `purchase(skuId)` from the player's press.
|
|
12903
|
+
"summary": "**Real money.** The Arcade Store's own Gem packs, Boosts, passes and\nbundles, sold for the player's money through the arcade's own purchase\nsheet. To spend Gems or Coins on your game's own items, use\n[Gem.purchases](api-reference-game.md#gem-purchases).\n\nList what the store sells here with `offers()`, show each `priceDisplay`\nexactly as given, and call `purchase(skuId)` from the player's press. For a\npass, a bundle or anything else whose pitch you would have to hardcode, call\n`showOffer(skuId)` instead, and the arcade draws the offer. After `ok`,\nre-read balances through [Gem.me](api-reference-game.md#gem-me). Nothing rejects.",
|
|
12794
12904
|
"deprecated": null,
|
|
12795
12905
|
"params": [],
|
|
12796
12906
|
"returns": null,
|
|
@@ -13325,6 +13435,29 @@
|
|
|
13325
13435
|
"defaultValue": null,
|
|
13326
13436
|
"guides": []
|
|
13327
13437
|
},
|
|
13438
|
+
{
|
|
13439
|
+
"id": "Gem.setMenuStyle",
|
|
13440
|
+
"name": "setMenuStyle",
|
|
13441
|
+
"anchor": "gem-setmenustyle",
|
|
13442
|
+
"group": "start",
|
|
13443
|
+
"signature": "setMenuStyle(style: MenuStyle | null): boolean",
|
|
13444
|
+
"documented": true,
|
|
13445
|
+
"summary": "Give the arcade's own screens over your game your colours, so they match\nit: on the Gem Arcade, the burger button, the Gem menu and its pages, and\nevery Arcade pop-up over the running game.\n\n```js\ngem.setMenuStyle({ ground: '#1b2a4a', accent: '#ffb300' });\ngem.setMenuStyle(null); // back to the player's own style\n```\n\n**Colours only.** `ground` is what the arcade's surfaces are built on and\n`accent` its one highlight; the arcade works out the text, rims, button\nshapes and fonts, so its screens stay readable on any ground.\n\n**The Arcade refuses colours it cannot use.** Each must be exactly\n`#rrggbb`, and `accent` must reach 3:1 contrast against `ground`. Anything\nelse is ignored, with a warning on the console giving the measured ratio:\nthe last colours stand, nothing is sent, and this returns `false`.\n\n**Your colours win over the player's chosen style while your game runs**,\nand the player's style comes back when it ends. It is screen state, not a\ntoggle: each call is the whole answer, so you may change it between levels.\nCall it at boot, so your colours arrive with the first screen.",
|
|
13446
|
+
"deprecated": null,
|
|
13447
|
+
"params": [
|
|
13448
|
+
{
|
|
13449
|
+
"name": "style",
|
|
13450
|
+
"type": "MenuStyle | null",
|
|
13451
|
+
"optional": false,
|
|
13452
|
+
"summary": "The two colours, or `null` to hand back to the player's style."
|
|
13453
|
+
}
|
|
13454
|
+
],
|
|
13455
|
+
"returns": "`true` when the arcade was told now; `false` before the bridge is\n up (the colours are held and sent when it connects), under an embedder\n that cannot wear them, and for colours it would refuse.",
|
|
13456
|
+
"throws": null,
|
|
13457
|
+
"example": null,
|
|
13458
|
+
"defaultValue": null,
|
|
13459
|
+
"guides": []
|
|
13460
|
+
},
|
|
13328
13461
|
{
|
|
13329
13462
|
"id": "Gem.reportFrame",
|
|
13330
13463
|
"name": "reportFrame",
|
package/docs/arcade-and-store.md
CHANGED
|
@@ -18,6 +18,12 @@ async function onBuyPressed(offer) {
|
|
|
18
18
|
// Read through gem.me; nothing here carries an amount. `error` is "outcome unknown" (§9).
|
|
19
19
|
if (outcome === 'ok' || reason === 'error') await refreshBalances();
|
|
20
20
|
}
|
|
21
|
+
|
|
22
|
+
// From the press on your own "Get VIP" button: the Arcade draws the offer.
|
|
23
|
+
async function onVipPressed() {
|
|
24
|
+
const { outcome, reason } = await gem.store.showOffer(VIP_SKU);
|
|
25
|
+
if (outcome === 'ok' || reason === 'error') await refreshBalances();
|
|
26
|
+
}
|
|
21
27
|
```
|
|
22
28
|
|
|
23
29
|
**Your game asks; the Arcade decides.** Nothing your game sends carries a URL, a price or a card
|
|
@@ -33,7 +39,8 @@ itself. You get an outcome.
|
|
|
33
39
|
- [7. Similar games, as cards](#7-similar-games-as-cards)
|
|
34
40
|
- [8. The Arcade Store's offers](#8-the-arcade-stores-offers)
|
|
35
41
|
- [9. Buying from the Arcade Store](#9-buying-from-the-arcade-store)
|
|
36
|
-
- [10.
|
|
42
|
+
- [10. Letting the Arcade show one offer](#10-letting-the-arcade-show-one-offer)
|
|
43
|
+
- [11. Outcomes and reasons](#11-outcomes-and-reasons)
|
|
37
44
|
|
|
38
45
|
## 1. Two surfaces, and which one is real money
|
|
39
46
|
|
|
@@ -41,7 +48,7 @@ itself. You get an outcome.
|
|
|
41
48
|
| ------------ | ------------------------------------------------ | --------------------------------------------------- |
|
|
42
49
|
| What it does | app-store listings, another game, similar games | the Arcade Store's own SKUs |
|
|
43
50
|
| Money | none | **real money**, through the Arcade Store's checkout |
|
|
44
|
-
| Who draws it | the Arcade (a new tab, a QR code, the next game) |
|
|
51
|
+
| Who draws it | the Arcade (a new tab, a QR code, the next game) | your card or the Arcade's sheet, then the checkout |
|
|
45
52
|
|
|
46
53
|
`gem.store` is not `gem.purchases`. **`gem.purchases` spends Gems or Coins on your game's own
|
|
47
54
|
items** ([`inventory-and-catalogs.md`](inventory-and-catalogs.md)). **`gem.store` sells the Arcade
|
|
@@ -60,7 +67,7 @@ three and never grows:
|
|
|
60
67
|
|
|
61
68
|
`reason` is a short code that says why. The set is open: a new Arcade build may send a reason your
|
|
62
69
|
game has never seen, so branch on `outcome` and treat `reason` as a hint. The codes in use today are
|
|
63
|
-
in [§
|
|
70
|
+
in [§11](#11-outcomes-and-reasons).
|
|
64
71
|
|
|
65
72
|
## 3. What is available here
|
|
66
73
|
|
|
@@ -71,17 +78,19 @@ An Arcade that does not offer a feature answers every call for it at once with `
|
|
|
71
78
|
if (gem.arcade.available.openGame) showMoreGamesSection();
|
|
72
79
|
if (gem.arcade.available.appLinks) showGetTheAppButton();
|
|
73
80
|
if (gem.store.available) showGetGemsButton();
|
|
81
|
+
if (gem.store.showOfferAvailable) showGetVipButton();
|
|
74
82
|
```
|
|
75
83
|
|
|
76
|
-
`gem.arcade.available` is `{ appLinks, openGame }`, and `gem.store.available`
|
|
77
|
-
|
|
84
|
+
`gem.arcade.available` is `{ appLinks, openGame }`, and `gem.store.available` and
|
|
85
|
+
`gem.store.showOfferAvailable` are one boolean each. All of them are synchronous and `false` until
|
|
86
|
+
the bridge is up. A feature can be off for reasons you
|
|
78
87
|
cannot see from inside the frame: a TV, an in-app browser that cannot take payment, an app build
|
|
79
88
|
where it is withheld. Do not guess; read it.
|
|
80
89
|
|
|
81
90
|
## 4. Call the acting ones from a press
|
|
82
91
|
|
|
83
|
-
|
|
84
|
-
`gem.store.purchase`. **Call each one from the player's own press handler**, in the same turn as the
|
|
92
|
+
Four calls put something in front of the player: `gem.arcade.openApp`, `gem.arcade.openGame`,
|
|
93
|
+
`gem.store.purchase` and `gem.store.showOffer`. **Call each one from the player's own press handler**, in the same turn as the
|
|
85
94
|
press. The Arcade checks that a person pressed, and refuses an ask nobody pressed for with
|
|
86
95
|
`declined` / `busy`, showing nothing.
|
|
87
96
|
|
|
@@ -273,7 +282,57 @@ arrives, so your own celebration is visible.
|
|
|
273
282
|
if (outcome === 'unavailable' && reason === 'error') await refreshBalances(); // not "nothing bought"
|
|
274
283
|
```
|
|
275
284
|
|
|
276
|
-
## 10.
|
|
285
|
+
## 10. Letting the Arcade show one offer
|
|
286
|
+
|
|
287
|
+
```js
|
|
288
|
+
// Decide on the screen you draw the button on.
|
|
289
|
+
const { offers } = await gem.store.offers();
|
|
290
|
+
const vip = offers.find((offer) => offer.skuId === VIP_SKU);
|
|
291
|
+
if (gem.store.showOfferAvailable && vip) showGetVipButton();
|
|
292
|
+
else hideGetVipButton();
|
|
293
|
+
|
|
294
|
+
// From the press on that button.
|
|
295
|
+
const { outcome, reason } = await gem.store.showOffer(VIP_SKU);
|
|
296
|
+
if (outcome === 'ok' || reason === 'error') await refreshBalances();
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
`showOffer(skuId)` asks the Arcade to draw one SKU from `offers()` in its own sheet, over your paused
|
|
300
|
+
and hidden game. The Arcade draws the art, what the player gets, a sale if one is running, and, for a
|
|
301
|
+
pass the player already holds, how long it has left. The sheet's Buy opens the same checkout
|
|
302
|
+
`purchase` opens.
|
|
303
|
+
|
|
304
|
+
Pick by who can say what the offer is:
|
|
305
|
+
|
|
306
|
+
| | **You draw it: `offers()` + `purchase()`** | **The Arcade draws it: `showOffer()`** |
|
|
307
|
+
| --------------- | --------------------------------------------------------- | ------------------------------------------------------------ |
|
|
308
|
+
| Use it for | Gem packs: a name, an amount and a price say it all | VIP and other passes, and bundles |
|
|
309
|
+
| Your button | says what it buys and shows `priceDisplay` | says what it opens, such as "Get VIP" |
|
|
310
|
+
| The press opens | the checkout, straight away | the Arcade's sheet, then the checkout if the player presses Buy |
|
|
311
|
+
| Perks and sales | not available to you | always current, in the player's language |
|
|
312
|
+
| A holder sees | the same Buy button | what they already have |
|
|
313
|
+
|
|
314
|
+
- **Never write an offer's perks into your game.** They change without a new build of your game, and
|
|
315
|
+
yours would go stale. Your button only needs the offer's name.
|
|
316
|
+
- **Do not show a price on the button.** The sheet shows the right one for this player, which a
|
|
317
|
+
sale or a pass can change, and `priceDisplay` does not say either of those. If you want a price on
|
|
318
|
+
screen, draw the offer yourself and use `purchase`.
|
|
319
|
+
- **You cannot tell whether the player holds a pass.** The Arcade keeps that to itself, and the
|
|
320
|
+
sheet tells the player. So the button stays up for a holder, and that is fine: the sheet is what
|
|
321
|
+
they see, and they choose from there.
|
|
322
|
+
- **Hide the button when the SKU is not in `offers()`.** A SKU that is not on sale here answers
|
|
323
|
+
`unavailable` / `not_found`, and the player would have pressed a button that does nothing.
|
|
324
|
+
- **Read `showOfferAvailable`, not `available`.** An older Arcade can sell through `purchase` and
|
|
325
|
+
still not draw this sheet. It answers `unavailable` / `unsupported` and sends nothing. Hide the
|
|
326
|
+
button there.
|
|
327
|
+
|
|
328
|
+
Every rule of `purchase` holds ([§9](#9-buying-from-the-arcade-store)), and the answers are the
|
|
329
|
+
same, with one addition. `declined` / `cancelled` also covers the player leaving the sheet without
|
|
330
|
+
pressing Buy, and you are not told which of the two they closed. That starts the same back-off as a
|
|
331
|
+
closed checkout, shared with `purchase`: the next ask of either kind is `declined` / `busy` for a
|
|
332
|
+
while. The press shows nothing during a back-off, so give the player something else to do on that
|
|
333
|
+
screen.
|
|
334
|
+
|
|
335
|
+
## 11. Outcomes and reasons
|
|
277
336
|
|
|
278
337
|
| Call | `ok` | `declined` | `unavailable` |
|
|
279
338
|
| ---------------- | ------------------------- | ------------------- | ------------------------------------------------------------------------------------------------------------- |
|
|
@@ -283,6 +342,7 @@ arrives, so your own celebration is visible.
|
|
|
283
342
|
| `similarGames` | cards, possibly none | | `unsupported`, `error` |
|
|
284
343
|
| `store.offers` | offers, possibly none | | `sign_in_required`, `store_off`, `unsupported`, `error` |
|
|
285
344
|
| `store.purchase` | paid, or `pending` | `cancelled`, `busy` | `sign_in_required`, `store_off`, `not_found`, the provider's reason, `unsupported`, `error` (outcome unknown) |
|
|
345
|
+
| `store.showOffer` | paid, or `pending` | `cancelled`, `busy` | `sign_in_required`, `store_off`, `not_found`, the provider's reason, `unsupported`, `error` (outcome unknown) |
|
|
286
346
|
|
|
287
347
|
Every reason is a short code of lower-case letters and underscores. The Arcade turns a payment
|
|
288
348
|
provider's own code into that shape before it reaches you.
|
|
@@ -296,5 +356,5 @@ Four reasons come from the SDK itself rather than the Arcade:
|
|
|
296
356
|
- `error`: no reliable answer arrived. The ask could not be sent, a read or an open waited out its
|
|
297
357
|
timeout (30 seconds for a read, two minutes for `openApp` and `openGame`), the bridge closed
|
|
298
358
|
before the answer came, or the Arcade said it had no reliable answer. For a read, try again
|
|
299
|
-
later. For `store.purchase` it means **the outcome is unknown**, never
|
|
300
|
-
([§9](#9-buying-from-the-arcade-store)).
|
|
359
|
+
later. For `store.purchase` and `store.showOffer` it means **the outcome is unknown**, never
|
|
360
|
+
"nothing was bought" ([§9](#9-buying-from-the-arcade-store)).
|
package/docs/arcade-economy.md
CHANGED
|
@@ -204,9 +204,9 @@ player got.
|
|
|
204
204
|
Nothing sells Coins or achievement points, alone or in a bundle. The arcade's own store sells
|
|
205
205
|
Gems, and the Gems a player bought there spend in your game exactly as the ones they earned do: your
|
|
206
206
|
game cannot tell the two apart and prices nothing differently for either. Your game never sells
|
|
207
|
-
either itself. It may put the arcade store's own Gem packs and
|
|
208
|
-
button of its own, through `gem.store`, and the arcade prices them, opens its own checkout
|
|
209
|
-
takes the money ([arcade-and-store.md](arcade-and-store.md)). The real-money product that changes
|
|
207
|
+
either itself. It may put the arcade store's own Gem packs, Boosts and passes in front of a player
|
|
208
|
+
from a button of its own, through `gem.store`, and the arcade prices them, opens its own checkout
|
|
209
|
+
and takes the money ([arcade-and-store.md](arcade-and-store.md)). The real-money product that changes
|
|
210
210
|
what your game pays is a **Boost**, which a player always buys in the arcade's own checkout:
|
|
211
211
|
|
|
212
212
|
- **It multiplies what Gems and Coins grants pay, and the ceilings with them.** A player on a 2×
|
|
@@ -246,8 +246,9 @@ animates every payout into the right total. Gems and Coins follow the same rules
|
|
|
246
246
|
- **No top-up or payment screen of your own.** Never draw a "buy Coins" offer, a checkout, a
|
|
247
247
|
wallet top-up, or anything shaped like the arcade's purchase sheet: a game screen that looks like
|
|
248
248
|
platform payment chrome is an imitation of it, and players cannot tell the two apart. Your shop's
|
|
249
|
-
own Buy button is yours, and so is a "Get Gems" button that calls `gem.store.purchase
|
|
250
|
-
player's press: the checkout
|
|
249
|
+
own Buy button is yours, and so is a "Get Gems" button that calls `gem.store.purchase`, or a "Get
|
|
250
|
+
VIP" button that calls `gem.store.showOffer`, from the player's press: the checkout each one
|
|
251
|
+
leads to is the arcade store's own
|
|
251
252
|
([arcade-and-store.md](arcade-and-store.md)). When a purchase is refused for
|
|
252
253
|
`insufficient_funds`, say so in your shop's own words ("Not enough Coins") and stop there: a
|
|
253
254
|
refusal never opens the store by itself.
|
package/docs/changelog.md
CHANGED
|
@@ -16,6 +16,46 @@ Its entries are grouped under up to five headings:
|
|
|
16
16
|
- **Rebuild**: a type changed. Recompile against the new version; code that compiles today keeps compiling unless the entry says otherwise.
|
|
17
17
|
- **New capability**: something a game can now do. Nothing changes unless you use it.
|
|
18
18
|
|
|
19
|
+
## 18.0.0
|
|
20
|
+
|
|
21
|
+
Upgrading from 17.0.0. Impact: required.
|
|
22
|
+
|
|
23
|
+
### Rebuild
|
|
24
|
+
|
|
25
|
+
- The set of capability names a game can query has a new member, menuStyle. Recompiling is enough, but if your code handles every capability name exhaustively, add a case for the new one.
|
|
26
|
+
- The frame message type has a new member for menu style, and the list of messages a game frame sends to the Arcade now includes gem.menuStyle. Recompiling is enough, but code that handles every message exhaustively needs a case for the new message.
|
|
27
|
+
|
|
28
|
+
### New capability
|
|
29
|
+
|
|
30
|
+
- A game can now hand the Arcade its own colours for the Arcade menu by calling Gem.setMenuStyle with a MenuStyle value. Check for the menuStyle capability first, since an older Arcade will not act on it. The same call is also available on the frame object.
|
|
31
|
+
|
|
32
|
+
## 17.0.0
|
|
33
|
+
|
|
34
|
+
*Written from the API facts alone; no summary was generated for this release.*
|
|
35
|
+
|
|
36
|
+
Upgrading from 16.3.1. Impact: required.
|
|
37
|
+
|
|
38
|
+
### Must change
|
|
39
|
+
|
|
40
|
+
- PARENT_CONFORMANCE_VERSION changed its signature.
|
|
41
|
+
- FRAME_TO_PARENT changed its signature.
|
|
42
|
+
- FrameMessage changed its signature.
|
|
43
|
+
- GemCapability changed its signature.
|
|
44
|
+
- PARENT_TO_FRAME changed its signature.
|
|
45
|
+
- ParentMessage changed its signature.
|
|
46
|
+
|
|
47
|
+
### New capability
|
|
48
|
+
|
|
49
|
+
- StoreApi.showOffer was added.
|
|
50
|
+
- StoreApi.showOfferAvailable was added.
|
|
51
|
+
- GemHost.answerStoreOfferSheet was added.
|
|
52
|
+
- GemRequestStoreOfferSheet was added.
|
|
53
|
+
- GemStoreOfferSheetResult was added.
|
|
54
|
+
- HostCallbacks.onRequestStoreOfferSheet was added.
|
|
55
|
+
- HostOptions.onRequestStoreOfferSheet was added.
|
|
56
|
+
- validateGemRequestStoreOfferSheet was added.
|
|
57
|
+
- validateGemStoreOfferSheetResult was added.
|
|
58
|
+
|
|
19
59
|
## 16.3.1
|
|
20
60
|
|
|
21
61
|
Upgrading from 16.3.0. Impact: none.
|
package/docs/getting-started.md
CHANGED
|
@@ -827,8 +827,9 @@ inventory, never on a reason, and do not retry because of one.
|
|
|
827
827
|
⚠️ **Never draw a top-up or payment screen of your own** — no checkout, no wallet top-up, nothing
|
|
828
828
|
that looks like the arcade's purchase sheet or wallet. A game screen shaped like platform payment
|
|
829
829
|
chrome is an imitation of it. Your Buy button is yours, and taking a player's money is the
|
|
830
|
-
arcade's alone: a "Get Gems" button of yours calls `gem.store.purchase` from the player's press,
|
|
831
|
-
and the arcade store's own checkout does the rest
|
|
830
|
+
arcade's alone: a "Get Gems" button of yours calls `gem.store.purchase` from the player's press, a
|
|
831
|
+
"Get VIP" button calls `gem.store.showOffer`, and the arcade store's own checkout does the rest
|
|
832
|
+
([arcade-and-store.md](arcade-and-store.md)). An
|
|
832
833
|
`insufficient_funds` refusal never opens it by itself.
|
|
833
834
|
|
|
834
835
|
### What Coins and Gems may buy: the spend scope
|