@indiegems/gem-web-sdk 7.8.1 → 7.9.1
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 +69 -0
- package/dist/game.d.ts +22 -1
- package/dist/gem-web-sdk.bridge.js +1 -1
- package/dist/gem-web-sdk.esm.js +2 -2
- package/dist/gem-web-sdk.function.js +2 -2
- package/dist/gem-web-sdk.game.js +2 -2
- package/dist/gem-web-sdk.min.js +2 -2
- package/dist/gem-web-sdk.sdk.js +2 -2
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +32 -4
- package/dist/sdk.d.ts +22 -1
- package/docs/achievements.md +99 -3
- package/docs/api/api-reference-game.md +164 -11
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +177 -4
- package/docs/changelog.md +69 -0
- package/docs/daily-challenges.md +9 -8
- package/docs/game-economy-tutorial.md +3 -2
- package/docs/getting-started.md +8 -4
- package/docs/manifest.json +14 -14
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +180 -69
- package/package.json +1 -1
- package/schema/bridge.v1.json +104 -2
- package/schema/capabilities.public.json +7 -0
package/docs/achievements.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
An **achievement** is a platform-wide recognition a player earns in your game: a name, a
|
|
4
4
|
description and some **achievement points**. The arcade lists your achievements on your store page, shows each player what they have unlocked, and
|
|
5
|
-
adds their points to their **Achievement Score**, a total that spans every game they play (§
|
|
5
|
+
adds their points to their **Achievement Score**, a total that spans every game they play (§6).
|
|
6
6
|
|
|
7
7
|
There is no achievements API to call. An achievement sits on a **progression threshold your game
|
|
8
8
|
already has**, and it unlocks when your server, or a serverless function the player called, moves
|
|
@@ -194,7 +194,8 @@ first write, list them from your own game config.
|
|
|
194
194
|
|
|
195
195
|
The client caches what it reads, and an unlock happens on your server, so the client does not see
|
|
196
196
|
it by itself. When your server tells the game a player unlocked something, call
|
|
197
|
-
`gem.me.inventory.refresh()` before reading the progression again.
|
|
197
|
+
`gem.me.inventory.refresh()` before reading the progression again. Celebrating that unlock is your
|
|
198
|
+
game's job: [§5](#5-celebrating-an-unlock-is-your-games-job) says how the game learns it happened.
|
|
198
199
|
|
|
199
200
|
**This read is for display.** Never send it to your server as proof that a player holds something;
|
|
200
201
|
a modified client can report anything. Your server reads the standing itself.
|
|
@@ -203,7 +204,102 @@ a modified client can report anything. Your server reads the standing itself.
|
|
|
203
204
|
threshold — it is in your config. The arcade hides its name and description from players until they
|
|
204
205
|
unlock it; if your game shows its own achievement list, do the same.
|
|
205
206
|
|
|
206
|
-
## 5.
|
|
207
|
+
## 5. Celebrating an unlock is your game's job
|
|
208
|
+
|
|
209
|
+
By default the arcade draws **no** card when a player unlocks one of your game's achievements — not
|
|
210
|
+
during play, and not when they leave the game. Your game celebrates the unlock, in its own frame, in
|
|
211
|
+
its own art and sound. The arcade's record is the same either way: the unlock is recorded, its
|
|
212
|
+
points count toward the Achievement Score (§6), and the arcade's achievement lists show it. Only the
|
|
213
|
+
card over your game depends on what you declare.
|
|
214
|
+
|
|
215
|
+
### If your game has no celebration of its own
|
|
216
|
+
|
|
217
|
+
Declare the arcade's card on your store page instead, and the arcade draws it for every unlock of
|
|
218
|
+
one of your game's achievements:
|
|
219
|
+
|
|
220
|
+
```yaml
|
|
221
|
+
unlock_celebration: arcade
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
- **Values are `game` and `arcade`.** `game` is the default, and leaving the line out means `game`.
|
|
225
|
+
Any other value is refused when the store page is uploaded.
|
|
226
|
+
- **It is one line, on any store page you already have.** It is optional in every store-page
|
|
227
|
+
`schema_version`, so adding it needs no `schema_version` change.
|
|
228
|
+
- **Only the store page on the `release` channel is read**, and the setting takes effect when that
|
|
229
|
+
store page is promoted to `release` — the same channel achievements unlock on (§2). A store page
|
|
230
|
+
on another channel, and an experiment variant, are accepted but never read.
|
|
231
|
+
- **It governs the card for your game's achievements only**, and it is display-only: it changes no
|
|
232
|
+
unlock, no points and no reward.
|
|
233
|
+
- **Not both.** A game that draws its own celebration and declares `arcade` shows the player two,
|
|
234
|
+
one over the other.
|
|
235
|
+
|
|
236
|
+
### Platform milestones are always the arcade's
|
|
237
|
+
|
|
238
|
+
Platform milestones — the tiers of the player's Achievement Score, which belong to no game — always
|
|
239
|
+
get the arcade's card, whatever your store page declares. That includes a milestone an unlock in
|
|
240
|
+
your game reaches, and one reached in the same moment as an unlock the arcade does not show. Your
|
|
241
|
+
game neither celebrates nor suppresses a milestone. A reward one of your achievements grants gets no
|
|
242
|
+
arcade card of its own while your store page says `game`.
|
|
243
|
+
|
|
244
|
+
### Knowing an unlock happened
|
|
245
|
+
|
|
246
|
+
There is no unlock message from the arcade to your game. Your game learns of an unlock one of two
|
|
247
|
+
ways.
|
|
248
|
+
|
|
249
|
+
**On your server — the reliable path.** The order your server gets back says what it unlocked
|
|
250
|
+
(§3). For each `unlocked` receipt, tell your game client over your game's own channel — the response
|
|
251
|
+
to the request the client made, or a match message — and celebrate it there:
|
|
252
|
+
|
|
253
|
+
```js
|
|
254
|
+
const unlocked = achievementReceipts(order)
|
|
255
|
+
.filter((r) => r.outcome === 'unlocked')
|
|
256
|
+
.map((r) => ({ id: r.achievementId, points: r.points }));
|
|
257
|
+
// Send `unlocked` back in your own response; the client celebrates each one.
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
**In the game — the compare.** Read the progression, and compare which achievements carry an
|
|
261
|
+
`unlockedAt` with what the game held at its last read: one that is newly present was just unlocked.
|
|
262
|
+
Read once when the game starts, before any write — that first read only records what the player
|
|
263
|
+
already holds, and celebrates nothing. Then run the compare after **every** response from your
|
|
264
|
+
game's own server that may have written the progression, not only after one that reported an
|
|
265
|
+
unlock, because the case it exists for is the report that never arrived:
|
|
266
|
+
|
|
267
|
+
```js
|
|
268
|
+
let held = null; // ids the game has seen unlocked; null until the first good read
|
|
269
|
+
|
|
270
|
+
async function checkUnlocks() {
|
|
271
|
+
gem.me.inventory.refresh(); // drops the cache; it returns nothing to wait for
|
|
272
|
+
const p = await gem.me.progressions.get('boards_cleared');
|
|
273
|
+
if (p?.achievementsUnavailable) return; // this read says nothing: keep the last snapshot
|
|
274
|
+
const now = (p?.achievements ?? []).filter((a) => a.unlockedAt !== undefined && !a.revoked);
|
|
275
|
+
if (!held) { held = new Set(now.map((a) => a.id)); return; } // the seed celebrates nothing
|
|
276
|
+
for (const a of now) if (!held.has(a.id)) { held.add(a.id); celebrate(a); }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// After each response from your server. A failed read is caught by the next check.
|
|
280
|
+
checkUnlocks().catch(() => {});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Keep one snapshot per progression that carries achievements. Only add to the snapshot, never replace
|
|
284
|
+
it, so a read that finishes late cannot bring back an unlock already celebrated. When
|
|
285
|
+
`achievementsUnavailable` is true the read says nothing about which achievements are unlocked, so it
|
|
286
|
+
is never a change. A start-up read that is unavailable does not seed: the first good read seeds, so
|
|
287
|
+
an unlock in that gap is not celebrated. The unlock is still recorded.
|
|
288
|
+
|
|
289
|
+
**When it can miss.** A receipt is a report, not a trigger (§3): a retried or replayed order reports
|
|
290
|
+
nothing, so a celebration keyed only on an `unlocked` receipt is missed whenever the first response
|
|
291
|
+
never arrived. The client compare catches that case. A missed celebration loses nothing else — the
|
|
292
|
+
unlock, its points and its rewards are already recorded.
|
|
293
|
+
|
|
294
|
+
### What the celebration shows
|
|
295
|
+
|
|
296
|
+
- **The unlock is yours; a platform-currency payout is not.** Any Gems or Tickets the same moment
|
|
297
|
+
pays still go through `gem.cueReward`, with no Gems or Tickets flight, counter or count-up of your
|
|
298
|
+
own — [arcade-economy.md](arcade-economy.md) has that display rule.
|
|
299
|
+
- **The achievement's own `points` are yours to show.** The player's Achievement Score is not: your
|
|
300
|
+
game never computes or restates it (§6).
|
|
301
|
+
|
|
302
|
+
## 6. Points are prestige: the Achievement Score
|
|
207
303
|
|
|
208
304
|
Achievement points are a player's permanent standing on the arcade. They are **never spent,
|
|
209
305
|
bought, boosted, decayed or expired**, and they are **removed only for fraud or moderation**.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The browser surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk` v7.
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v7.9.1. Every symbol below links to its page on the Gem Arcade developer site.
|
|
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
|
|
|
@@ -4886,6 +4886,30 @@ wallet.
|
|
|
4886
4886
|
**Returns.** whether the message left the frame — `false` before the bridge is
|
|
4887
4887
|
up, for an arcade with no wallet, and past this frame's own limit.
|
|
4888
4888
|
|
|
4889
|
+
## GemFrame.setMenu
|
|
4890
|
+
|
|
4891
|
+
[GemFrame.setMenu](https://gemarcade.com/developer/api/game#gemframe-setmenu)
|
|
4892
|
+
|
|
4893
|
+
```ts
|
|
4894
|
+
setMenu(settings: boolean, items?: readonly { id: string; label: string }[]): boolean
|
|
4895
|
+
```
|
|
4896
|
+
|
|
4897
|
+
Tell the arcade what your game adds to its menu: whether it has a settings
|
|
4898
|
+
screen, and up to four entries of its own — the whole answer each time, the
|
|
4899
|
+
latest one winning.
|
|
4900
|
+
|
|
4901
|
+
Prefer `gem.setMenu`, which holds the state across a reconnect and calls
|
|
4902
|
+
your handlers when the player chooses an entry. This one sends only while
|
|
4903
|
+
connected, and only to an arcade that draws a menu.
|
|
4904
|
+
|
|
4905
|
+
| Parameter | Type |
|
|
4906
|
+
| --- | --- |
|
|
4907
|
+
| `settings` | `boolean` |
|
|
4908
|
+
| `items` | `readonly { id: string; label: string }[]` |
|
|
4909
|
+
|
|
4910
|
+
**Returns.** whether the message left the frame — `false` before the bridge is
|
|
4911
|
+
up, for an arcade with no menu, and past this frame's own limit.
|
|
4912
|
+
|
|
4889
4913
|
## GemFrame.requestShare
|
|
4890
4914
|
|
|
4891
4915
|
[GemFrame.requestShare](https://gemarcade.com/developer/api/game#gemframe-requestshare)
|
|
@@ -6769,8 +6793,8 @@ The unsafe margin on each edge of your frame.
|
|
|
6769
6793
|
readonly reserved: readonly GemReservedArea[]
|
|
6770
6794
|
```
|
|
6771
6795
|
|
|
6772
|
-
Rectangles of your frame the arcade's own controls cover — its
|
|
6773
|
-
the top-
|
|
6796
|
+
Rectangles of your frame the arcade's own controls cover — its menu button
|
|
6797
|
+
in the top-right corner, and the wallets in the middle of the top row while they are shown (see
|
|
6774
6798
|
`setWalletVisible`). Keep essential UI and touch targets out of them. Empty
|
|
6775
6799
|
when the arcade covers nothing.
|
|
6776
6800
|
|
|
@@ -6853,6 +6877,83 @@ matched exactly, and the literal `'score'` for the achievement score. Only
|
|
|
6853
6877
|
for a screen about one currency, such as a ticket store; leave it out and
|
|
6854
6878
|
every wallet shows, which is always safe.
|
|
6855
6879
|
|
|
6880
|
+
## GameMenuItem
|
|
6881
|
+
|
|
6882
|
+
[GameMenuItem](https://gemarcade.com/developer/api/game#gamemenuitem)
|
|
6883
|
+
|
|
6884
|
+
```ts
|
|
6885
|
+
interface GameMenuItem
|
|
6886
|
+
```
|
|
6887
|
+
|
|
6888
|
+
One entry of your own in the arcade's menu.
|
|
6889
|
+
|
|
6890
|
+
## GameMenuItem.id
|
|
6891
|
+
|
|
6892
|
+
[GameMenuItem.id](https://gemarcade.com/developer/api/game#gamemenuitem-id)
|
|
6893
|
+
|
|
6894
|
+
```ts
|
|
6895
|
+
readonly id: string
|
|
6896
|
+
```
|
|
6897
|
+
|
|
6898
|
+
Your own name for the entry, 1 to 32 characters, unique among your items.
|
|
6899
|
+
An entry with an empty or longer id is left out.
|
|
6900
|
+
|
|
6901
|
+
## GameMenuItem.label
|
|
6902
|
+
|
|
6903
|
+
[GameMenuItem.label](https://gemarcade.com/developer/api/game#gamemenuitem-label)
|
|
6904
|
+
|
|
6905
|
+
```ts
|
|
6906
|
+
readonly label: string
|
|
6907
|
+
```
|
|
6908
|
+
|
|
6909
|
+
What the player reads, **already in the player's language**
|
|
6910
|
+
(`gem.prefs.locale`) — the arcade does not translate it. Keep it short:
|
|
6911
|
+
anything past 40 characters is cut, and the arcade may shorten it further
|
|
6912
|
+
to fit.
|
|
6913
|
+
|
|
6914
|
+
## GameMenuItem.onSelect
|
|
6915
|
+
|
|
6916
|
+
[GameMenuItem.onSelect](https://gemarcade.com/developer/api/game#gamemenuitem-onselect)
|
|
6917
|
+
|
|
6918
|
+
```ts
|
|
6919
|
+
readonly onSelect: () => void
|
|
6920
|
+
```
|
|
6921
|
+
|
|
6922
|
+
Called when the player chooses this entry. The arcade has closed its menu
|
|
6923
|
+
and your game is running again by the time this fires.
|
|
6924
|
+
|
|
6925
|
+
## GameMenu
|
|
6926
|
+
|
|
6927
|
+
[GameMenu](https://gemarcade.com/developer/api/game#gamemenu)
|
|
6928
|
+
|
|
6929
|
+
```ts
|
|
6930
|
+
interface GameMenu
|
|
6931
|
+
```
|
|
6932
|
+
|
|
6933
|
+
What your game adds to the arcade's menu — see `gem.setMenu`.
|
|
6934
|
+
|
|
6935
|
+
## GameMenu.onSettings
|
|
6936
|
+
|
|
6937
|
+
[GameMenu.onSettings](https://gemarcade.com/developer/api/game#gamemenu-onsettings)
|
|
6938
|
+
|
|
6939
|
+
```ts
|
|
6940
|
+
readonly onSettings?: () => void
|
|
6941
|
+
```
|
|
6942
|
+
|
|
6943
|
+
Open your settings screen. Give this and the arcade's menu gains a
|
|
6944
|
+
Settings entry that calls it; leave it out and there is no such entry.
|
|
6945
|
+
|
|
6946
|
+
## GameMenu.items
|
|
6947
|
+
|
|
6948
|
+
[GameMenu.items](https://gemarcade.com/developer/api/game#gamemenu-items)
|
|
6949
|
+
|
|
6950
|
+
```ts
|
|
6951
|
+
readonly items?: readonly GameMenuItem[]
|
|
6952
|
+
```
|
|
6953
|
+
|
|
6954
|
+
Up to four entries of your own, shown in this order below the arcade's.
|
|
6955
|
+
A fifth and later are left out.
|
|
6956
|
+
|
|
6856
6957
|
## GemTerminationNotice
|
|
6857
6958
|
|
|
6858
6959
|
[GemTerminationNotice](https://gemarcade.com/developer/api/game#gemterminationnotice)
|
|
@@ -7566,12 +7667,13 @@ reportMuted(muted: boolean): void
|
|
|
7566
7667
|
```
|
|
7567
7668
|
|
|
7568
7669
|
Tell the arcade the player muted or unmuted **inside your game**, so the
|
|
7569
|
-
arcade's own mute
|
|
7670
|
+
arcade's own mute entry shows the same thing.
|
|
7570
7671
|
|
|
7571
|
-
**Every game needs its own mute control
|
|
7572
|
-
|
|
7672
|
+
**Every game needs its own mute control**, in its settings: the arcade's
|
|
7673
|
+
menu has a mute entry, and under an embedder with no menu yours is the
|
|
7674
|
+
player's only one. Call it once at start-up with the
|
|
7573
7675
|
setting you saved, and again every time the player changes it — and pair it
|
|
7574
|
-
with `onPrefs`, because a press of the arcade's
|
|
7676
|
+
with `onPrefs`, because a press of the arcade's entry arrives there as
|
|
7575
7677
|
`prefs.muted`, and your own control should follow it. With both halves the
|
|
7576
7678
|
player has one mute instead of two that disagree.
|
|
7577
7679
|
|
|
@@ -7653,8 +7755,13 @@ setWalletVisible(visible: boolean, options?: WalletVisibleOptions): boolean
|
|
|
7653
7755
|
```
|
|
7654
7756
|
|
|
7655
7757
|
Show or hide the player's wallets — Gems, Tickets, the achievement score —
|
|
7656
|
-
that
|
|
7657
|
-
|
|
7758
|
+
that an embedder draws over your game.
|
|
7759
|
+
|
|
7760
|
+
**The Gem Arcade no longer acts on this** (since 2026-10-04): it shows
|
|
7761
|
+
every wallet the whole time, during play too, and this call — still
|
|
7762
|
+
accepted, still returning `true` there — hides nothing. Lay every screen
|
|
7763
|
+
out below the arcade's row (`gem.viewport.reserved`). Another embedder
|
|
7764
|
+
may still honour it, where it works as described below.
|
|
7658
7765
|
|
|
7659
7766
|
```js
|
|
7660
7767
|
level.onStart = () => gem.setWalletVisible(false);
|
|
@@ -7666,8 +7773,8 @@ ticketStore.onOpen = () => gem.setWalletVisible(true, { only: ['ARCADE_TICKETS']
|
|
|
7666
7773
|
**It is screen state, not a toggle.** Each call is the whole answer and
|
|
7667
7774
|
replaces the last. While hidden, the arcade still pops a wallet up for a
|
|
7668
7775
|
moment when something happens to it — a reward you cued, a spend, a
|
|
7669
|
-
landing — and your state never cancels that. Shown wallets cover
|
|
7670
|
-
your frame's top
|
|
7776
|
+
landing — and your state never cancels that. Shown wallets cover the
|
|
7777
|
+
middle of your frame's top row, each arriving as a rectangle in
|
|
7671
7778
|
`gem.viewport.reserved`; hiding them gives that space back. A wallet the
|
|
7672
7779
|
arcade pops up for a moment floats over your game and moves nothing.
|
|
7673
7780
|
|
|
@@ -7684,6 +7791,52 @@ for one: the arcade takes every figure from the platform. If this returns
|
|
|
7684
7791
|
up (the state is held and sent when it connects) and under an embedder
|
|
7685
7792
|
that draws no wallet.
|
|
7686
7793
|
|
|
7794
|
+
## Gem.setMenu
|
|
7795
|
+
|
|
7796
|
+
[Gem.setMenu](https://gemarcade.com/developer/api/game#gem-setmenu)
|
|
7797
|
+
|
|
7798
|
+
```ts
|
|
7799
|
+
setMenu(menu: GameMenu): boolean
|
|
7800
|
+
```
|
|
7801
|
+
|
|
7802
|
+
Put your settings, and up to four entries of your own, in the arcade's
|
|
7803
|
+
menu. The arcade keeps one menu button over your game, in the top-right
|
|
7804
|
+
corner: it is how a player leaves, mutes, goes fullscreen and reports a
|
|
7805
|
+
problem — and, once you call this, how they open **your** settings.
|
|
7806
|
+
|
|
7807
|
+
```js
|
|
7808
|
+
const inArcadeMenu = gem.setMenu({
|
|
7809
|
+
onSettings: () => settingsScreen.open(),
|
|
7810
|
+
items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],
|
|
7811
|
+
});
|
|
7812
|
+
ownSettingsButton.hidden = inArcadeMenu;
|
|
7813
|
+
```
|
|
7814
|
+
|
|
7815
|
+
**Do not draw a settings button of your own while this returns `true`** —
|
|
7816
|
+
the arcade's menu is the one place a player looks, in every game. Draw
|
|
7817
|
+
your own only when it returns `false` once the bridge has settled
|
|
7818
|
+
(`await gem.awaitBridge()`): an embedder with no menu, or no embedder at
|
|
7819
|
+
all. The top-left corner is yours, for a back or home button that moves
|
|
7820
|
+
around inside your game.
|
|
7821
|
+
|
|
7822
|
+
**It is screen state, not a toggle.** Each call is the whole answer and
|
|
7823
|
+
replaces the last, so call it again when the entries change — a new
|
|
7824
|
+
language, a screen with different options. `gem.setMenu({})` takes
|
|
7825
|
+
everything of yours back out. The arcade's own entries are not yours to
|
|
7826
|
+
change, and Return to the Arcade is always among them.
|
|
7827
|
+
|
|
7828
|
+
**The arcade pauses your game while its menu is open** (`onPause`), and
|
|
7829
|
+
resumes it before a handler of yours runs. A handler opens a screen; it
|
|
7830
|
+
should not assume the game was paused by you.
|
|
7831
|
+
|
|
7832
|
+
| Parameter | Type | Description |
|
|
7833
|
+
| --- | --- | --- |
|
|
7834
|
+
| `menu` | `GameMenu` | Your settings handler and your own entries. |
|
|
7835
|
+
|
|
7836
|
+
**Returns.** `true` when the arcade was told now; `false` before the bridge is
|
|
7837
|
+
up (the menu is held and sent when it connects) and under an embedder
|
|
7838
|
+
that draws no menu.
|
|
7839
|
+
|
|
7687
7840
|
## Gem.exit
|
|
7688
7841
|
|
|
7689
7842
|
[Gem.exit](https://gemarcade.com/developer/api/game#gem-exit)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The server surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk` v7.
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v7.9.1. Every symbol below links to its page on the Gem Arcade developer site.
|
|
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": 1,
|
|
3
3
|
"source": {
|
|
4
4
|
"repo": "IndieGems/gem-web-sdk",
|
|
5
|
-
"version": "7.
|
|
5
|
+
"version": "7.9.1"
|
|
6
6
|
},
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -6130,6 +6130,36 @@
|
|
|
6130
6130
|
"defaultValue": null,
|
|
6131
6131
|
"guides": []
|
|
6132
6132
|
},
|
|
6133
|
+
{
|
|
6134
|
+
"id": "GemFrame.setMenu",
|
|
6135
|
+
"name": "setMenu",
|
|
6136
|
+
"anchor": "gemframe-setmenu",
|
|
6137
|
+
"group": "frame",
|
|
6138
|
+
"url": "https://gemarcade.com/developer/api/game#gemframe-setmenu",
|
|
6139
|
+
"signature": "setMenu(settings: boolean, items?: readonly { id: string; label: string }[]): boolean",
|
|
6140
|
+
"documented": true,
|
|
6141
|
+
"summary": "Tell the arcade what your game adds to its menu: whether it has a settings\nscreen, and up to four entries of its own — the whole answer each time, the\nlatest one winning.\n\nPrefer `gem.setMenu`, which holds the state across a reconnect and calls\nyour handlers when the player chooses an entry. This one sends only while\nconnected, and only to an arcade that draws a menu.",
|
|
6142
|
+
"deprecated": null,
|
|
6143
|
+
"params": [
|
|
6144
|
+
{
|
|
6145
|
+
"name": "settings",
|
|
6146
|
+
"type": "boolean",
|
|
6147
|
+
"optional": false,
|
|
6148
|
+
"summary": null
|
|
6149
|
+
},
|
|
6150
|
+
{
|
|
6151
|
+
"name": "items",
|
|
6152
|
+
"type": "readonly { id: string; label: string }[]",
|
|
6153
|
+
"optional": true,
|
|
6154
|
+
"summary": null
|
|
6155
|
+
}
|
|
6156
|
+
],
|
|
6157
|
+
"returns": "whether the message left the frame — `false` before the bridge is\n up, for an arcade with no menu, and past this frame's own limit.",
|
|
6158
|
+
"throws": null,
|
|
6159
|
+
"example": null,
|
|
6160
|
+
"defaultValue": null,
|
|
6161
|
+
"guides": []
|
|
6162
|
+
},
|
|
6133
6163
|
{
|
|
6134
6164
|
"id": "GemFrame.requestShare",
|
|
6135
6165
|
"name": "requestShare",
|
|
@@ -8188,7 +8218,7 @@
|
|
|
8188
8218
|
"url": "https://gemarcade.com/developer/api/game#gemviewport-reserved",
|
|
8189
8219
|
"signature": "readonly reserved: readonly GemReservedArea[]",
|
|
8190
8220
|
"documented": true,
|
|
8191
|
-
"summary": "Rectangles of your frame the arcade's own controls cover — its
|
|
8221
|
+
"summary": "Rectangles of your frame the arcade's own controls cover — its menu button\nin the top-right corner, and the wallets in the middle of the top row while they are shown (see\n`setWalletVisible`). Keep essential UI and touch targets out of them. Empty\nwhen the arcade covers nothing.\n\nA stylesheet reads the per-corner custom properties the SDK stamps from\nthese instead — `--gem-reserved-tl-height` and friends.",
|
|
8192
8222
|
"deprecated": null,
|
|
8193
8223
|
"params": [],
|
|
8194
8224
|
"returns": null,
|
|
@@ -8316,6 +8346,125 @@
|
|
|
8316
8346
|
"defaultValue": null,
|
|
8317
8347
|
"guides": []
|
|
8318
8348
|
},
|
|
8349
|
+
{
|
|
8350
|
+
"id": "GameMenuItem",
|
|
8351
|
+
"name": "GameMenuItem",
|
|
8352
|
+
"anchor": "gamemenuitem",
|
|
8353
|
+
"group": "frame",
|
|
8354
|
+
"url": "https://gemarcade.com/developer/api/game#gamemenuitem",
|
|
8355
|
+
"signature": "interface GameMenuItem",
|
|
8356
|
+
"documented": true,
|
|
8357
|
+
"summary": "One entry of your own in the arcade's menu.",
|
|
8358
|
+
"deprecated": null,
|
|
8359
|
+
"params": [],
|
|
8360
|
+
"returns": null,
|
|
8361
|
+
"throws": null,
|
|
8362
|
+
"example": null,
|
|
8363
|
+
"defaultValue": null,
|
|
8364
|
+
"guides": []
|
|
8365
|
+
},
|
|
8366
|
+
{
|
|
8367
|
+
"id": "GameMenuItem.id",
|
|
8368
|
+
"name": "id",
|
|
8369
|
+
"anchor": "gamemenuitem-id",
|
|
8370
|
+
"group": "frame",
|
|
8371
|
+
"url": "https://gemarcade.com/developer/api/game#gamemenuitem-id",
|
|
8372
|
+
"signature": "readonly id: string",
|
|
8373
|
+
"documented": true,
|
|
8374
|
+
"summary": "Your own name for the entry, 1 to 32 characters, unique among your items.\nAn entry with an empty or longer id is left out.",
|
|
8375
|
+
"deprecated": null,
|
|
8376
|
+
"params": [],
|
|
8377
|
+
"returns": null,
|
|
8378
|
+
"throws": null,
|
|
8379
|
+
"example": null,
|
|
8380
|
+
"defaultValue": null,
|
|
8381
|
+
"guides": []
|
|
8382
|
+
},
|
|
8383
|
+
{
|
|
8384
|
+
"id": "GameMenuItem.label",
|
|
8385
|
+
"name": "label",
|
|
8386
|
+
"anchor": "gamemenuitem-label",
|
|
8387
|
+
"group": "frame",
|
|
8388
|
+
"url": "https://gemarcade.com/developer/api/game#gamemenuitem-label",
|
|
8389
|
+
"signature": "readonly label: string",
|
|
8390
|
+
"documented": true,
|
|
8391
|
+
"summary": "What the player reads, **already in the player's language**\n(`gem.prefs.locale`) — the arcade does not translate it. Keep it short:\nanything past 40 characters is cut, and the arcade may shorten it further\nto fit.",
|
|
8392
|
+
"deprecated": null,
|
|
8393
|
+
"params": [],
|
|
8394
|
+
"returns": null,
|
|
8395
|
+
"throws": null,
|
|
8396
|
+
"example": null,
|
|
8397
|
+
"defaultValue": null,
|
|
8398
|
+
"guides": []
|
|
8399
|
+
},
|
|
8400
|
+
{
|
|
8401
|
+
"id": "GameMenuItem.onSelect",
|
|
8402
|
+
"name": "onSelect",
|
|
8403
|
+
"anchor": "gamemenuitem-onselect",
|
|
8404
|
+
"group": "frame",
|
|
8405
|
+
"url": "https://gemarcade.com/developer/api/game#gamemenuitem-onselect",
|
|
8406
|
+
"signature": "readonly onSelect: () => void",
|
|
8407
|
+
"documented": true,
|
|
8408
|
+
"summary": "Called when the player chooses this entry. The arcade has closed its menu\nand your game is running again by the time this fires.",
|
|
8409
|
+
"deprecated": null,
|
|
8410
|
+
"params": [],
|
|
8411
|
+
"returns": null,
|
|
8412
|
+
"throws": null,
|
|
8413
|
+
"example": null,
|
|
8414
|
+
"defaultValue": null,
|
|
8415
|
+
"guides": []
|
|
8416
|
+
},
|
|
8417
|
+
{
|
|
8418
|
+
"id": "GameMenu",
|
|
8419
|
+
"name": "GameMenu",
|
|
8420
|
+
"anchor": "gamemenu",
|
|
8421
|
+
"group": "frame",
|
|
8422
|
+
"url": "https://gemarcade.com/developer/api/game#gamemenu",
|
|
8423
|
+
"signature": "interface GameMenu",
|
|
8424
|
+
"documented": true,
|
|
8425
|
+
"summary": "What your game adds to the arcade's menu — see `gem.setMenu`.",
|
|
8426
|
+
"deprecated": null,
|
|
8427
|
+
"params": [],
|
|
8428
|
+
"returns": null,
|
|
8429
|
+
"throws": null,
|
|
8430
|
+
"example": null,
|
|
8431
|
+
"defaultValue": null,
|
|
8432
|
+
"guides": []
|
|
8433
|
+
},
|
|
8434
|
+
{
|
|
8435
|
+
"id": "GameMenu.onSettings",
|
|
8436
|
+
"name": "onSettings",
|
|
8437
|
+
"anchor": "gamemenu-onsettings",
|
|
8438
|
+
"group": "frame",
|
|
8439
|
+
"url": "https://gemarcade.com/developer/api/game#gamemenu-onsettings",
|
|
8440
|
+
"signature": "readonly onSettings?: () => void",
|
|
8441
|
+
"documented": true,
|
|
8442
|
+
"summary": "Open your settings screen. Give this and the arcade's menu gains a\nSettings entry that calls it; leave it out and there is no such entry.",
|
|
8443
|
+
"deprecated": null,
|
|
8444
|
+
"params": [],
|
|
8445
|
+
"returns": null,
|
|
8446
|
+
"throws": null,
|
|
8447
|
+
"example": null,
|
|
8448
|
+
"defaultValue": null,
|
|
8449
|
+
"guides": []
|
|
8450
|
+
},
|
|
8451
|
+
{
|
|
8452
|
+
"id": "GameMenu.items",
|
|
8453
|
+
"name": "items",
|
|
8454
|
+
"anchor": "gamemenu-items",
|
|
8455
|
+
"group": "frame",
|
|
8456
|
+
"url": "https://gemarcade.com/developer/api/game#gamemenu-items",
|
|
8457
|
+
"signature": "readonly items?: readonly GameMenuItem[]",
|
|
8458
|
+
"documented": true,
|
|
8459
|
+
"summary": "Up to four entries of your own, shown in this order below the arcade's.\nA fifth and later are left out.",
|
|
8460
|
+
"deprecated": null,
|
|
8461
|
+
"params": [],
|
|
8462
|
+
"returns": null,
|
|
8463
|
+
"throws": null,
|
|
8464
|
+
"example": null,
|
|
8465
|
+
"defaultValue": null,
|
|
8466
|
+
"guides": []
|
|
8467
|
+
},
|
|
8319
8468
|
{
|
|
8320
8469
|
"id": "GemTerminationNotice",
|
|
8321
8470
|
"name": "GemTerminationNotice",
|
|
@@ -9001,7 +9150,7 @@
|
|
|
9001
9150
|
"url": "https://gemarcade.com/developer/api/game#gem-reportmuted",
|
|
9002
9151
|
"signature": "reportMuted(muted: boolean): void",
|
|
9003
9152
|
"documented": true,
|
|
9004
|
-
"summary": "Tell the arcade the player muted or unmuted **inside your game**, so the\narcade's own mute
|
|
9153
|
+
"summary": "Tell the arcade the player muted or unmuted **inside your game**, so the\narcade's own mute entry shows the same thing.\n\n**Every game needs its own mute control**, in its settings: the arcade's\nmenu has a mute entry, and under an embedder with no menu yours is the\nplayer's only one. Call it once at start-up with the\nsetting you saved, and again every time the player changes it — and pair it\nwith `onPrefs`, because a press of the arcade's entry arrives there as\n`prefs.muted`, and your own control should follow it. With both halves the\nplayer has one mute instead of two that disagree.\n\n```js\nconst gem = createGem({\n // ...\n onPrefs: (prefs) => sound.setMuted(prefs.muted),\n});\ngem.reportMuted(sound.muted); // at start-up, with the saved setting\nmuteButton.onclick = () => {\n sound.setMuted(!sound.muted);\n gem.reportMuted(sound.muted); // and on every change\n};\n```\n\nSafe to call at any time. Before the bridge is up the value is held and sent\nwhen it connects; an arcade that does not mirror a game's mute is simply not\ntold; and calling it from `onPrefs` is fine — a report that matches what the\narcade just said is not sent.",
|
|
9005
9154
|
"deprecated": null,
|
|
9006
9155
|
"params": [
|
|
9007
9156
|
{
|
|
@@ -9049,7 +9198,7 @@
|
|
|
9049
9198
|
"url": "https://gemarcade.com/developer/api/game#gem-setwalletvisible",
|
|
9050
9199
|
"signature": "setWalletVisible(visible: boolean, options?: WalletVisibleOptions): boolean",
|
|
9051
9200
|
"documented": true,
|
|
9052
|
-
"summary": "Show or hide the player's wallets — Gems, Tickets, the achievement score —\nthat
|
|
9201
|
+
"summary": "Show or hide the player's wallets — Gems, Tickets, the achievement score —\nthat an embedder draws over your game.\n\n**The Gem Arcade no longer acts on this** (since 2026-10-04): it shows\nevery wallet the whole time, during play too, and this call — still\naccepted, still returning `true` there — hides nothing. Lay every screen\nout below the arcade's row (`gem.viewport.reserved`). Another embedder\nmay still honour it, where it works as described below.\n\n```js\nlevel.onStart = () => gem.setWalletVisible(false);\nmenu.onOpen = () => gem.setWalletVisible(true);\n// A screen that sells in one currency may narrow it:\nticketStore.onOpen = () => gem.setWalletVisible(true, { only: ['ARCADE_TICKETS'] });\n```\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last. While hidden, the arcade still pops a wallet up for a\nmoment when something happens to it — a reward you cued, a spend, a\nlanding — and your state never cancels that. Shown wallets cover the\nmiddle of your frame's top row, each arriving as a rectangle in\n`gem.viewport.reserved`; hiding them gives that space back. A wallet the\narcade pops up for a moment floats over your game and moves nothing.\n\n**Never draw a platform balance yourself**, and never show a pending state\nfor one: the arcade takes every figure from the platform. If this returns\n`false`, do nothing in its place.",
|
|
9053
9202
|
"deprecated": null,
|
|
9054
9203
|
"params": [
|
|
9055
9204
|
{
|
|
@@ -9071,6 +9220,30 @@
|
|
|
9071
9220
|
"defaultValue": null,
|
|
9072
9221
|
"guides": []
|
|
9073
9222
|
},
|
|
9223
|
+
{
|
|
9224
|
+
"id": "Gem.setMenu",
|
|
9225
|
+
"name": "setMenu",
|
|
9226
|
+
"anchor": "gem-setmenu",
|
|
9227
|
+
"group": "start",
|
|
9228
|
+
"url": "https://gemarcade.com/developer/api/game#gem-setmenu",
|
|
9229
|
+
"signature": "setMenu(menu: GameMenu): boolean",
|
|
9230
|
+
"documented": true,
|
|
9231
|
+
"summary": "Put your settings, and up to four entries of your own, in the arcade's\nmenu. The arcade keeps one menu button over your game, in the top-right\ncorner: it is how a player leaves, mutes, goes fullscreen and reports a\nproblem — and, once you call this, how they open **your** settings.\n\n```js\nconst inArcadeMenu = gem.setMenu({\n onSettings: () => settingsScreen.open(),\n items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],\n});\nownSettingsButton.hidden = inArcadeMenu;\n```\n\n**Do not draw a settings button of your own while this returns `true`** —\nthe arcade's menu is the one place a player looks, in every game. Draw\nyour own only when it returns `false` once the bridge has settled\n(`await gem.awaitBridge()`): an embedder with no menu, or no embedder at\nall. The top-left corner is yours, for a back or home button that moves\naround inside your game.\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last, so call it again when the entries change — a new\nlanguage, a screen with different options. `gem.setMenu({})` takes\neverything of yours back out. The arcade's own entries are not yours to\nchange, and Return to the Arcade is always among them.\n\n**The arcade pauses your game while its menu is open** (`onPause`), and\nresumes it before a handler of yours runs. A handler opens a screen; it\nshould not assume the game was paused by you.",
|
|
9232
|
+
"deprecated": null,
|
|
9233
|
+
"params": [
|
|
9234
|
+
{
|
|
9235
|
+
"name": "menu",
|
|
9236
|
+
"type": "GameMenu",
|
|
9237
|
+
"optional": false,
|
|
9238
|
+
"summary": "Your settings handler and your own entries."
|
|
9239
|
+
}
|
|
9240
|
+
],
|
|
9241
|
+
"returns": "`true` when the arcade was told now; `false` before the bridge is\n up (the menu is held and sent when it connects) and under an embedder\n that draws no menu.",
|
|
9242
|
+
"throws": null,
|
|
9243
|
+
"example": null,
|
|
9244
|
+
"defaultValue": null,
|
|
9245
|
+
"guides": []
|
|
9246
|
+
},
|
|
9074
9247
|
{
|
|
9075
9248
|
"id": "Gem.exit",
|
|
9076
9249
|
"name": "exit",
|