@indiegems/gem-web-sdk 12.0.0 → 13.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.
@@ -2,7 +2,7 @@
2
2
  "schema_version": 2,
3
3
  "source": {
4
4
  "repo": "IndieGems/gem-web-sdk",
5
- "version": "12.0.0"
5
+ "version": "13.0.0"
6
6
  },
7
7
  "entries": [
8
8
  {
@@ -45,6 +45,10 @@
45
45
  "key": "purchases",
46
46
  "title": "Selling for platform currency"
47
47
  },
48
+ {
49
+ "key": "avatars3d",
50
+ "title": "Arcade 3D avatars"
51
+ },
48
52
  {
49
53
  "key": "deviceSettings",
50
54
  "title": "Settings kept on this device"
@@ -4484,6 +4488,315 @@
4484
4488
  "defaultValue": null,
4485
4489
  "guides": []
4486
4490
  },
4491
+ {
4492
+ "id": "Avatar3dPrice",
4493
+ "name": "Avatar3dPrice",
4494
+ "anchor": "avatar3dprice",
4495
+ "group": "avatars3d",
4496
+ "signature": "interface Avatar3dPrice",
4497
+ "documented": true,
4498
+ "summary": "One price of a 3D avatar on sale, in one platform currency.",
4499
+ "deprecated": null,
4500
+ "params": [],
4501
+ "returns": null,
4502
+ "throws": null,
4503
+ "example": null,
4504
+ "defaultValue": null,
4505
+ "guides": []
4506
+ },
4507
+ {
4508
+ "id": "Avatar3dPrice.currencySlug",
4509
+ "name": "currencySlug",
4510
+ "anchor": "avatar3dprice-currencyslug",
4511
+ "group": "avatars3d",
4512
+ "signature": "readonly currencySlug: string",
4513
+ "documented": true,
4514
+ "summary": "`GEMS` or `ARCADE_TICKETS` — what to pass as `currencySlug`.",
4515
+ "deprecated": null,
4516
+ "params": [],
4517
+ "returns": null,
4518
+ "throws": null,
4519
+ "example": null,
4520
+ "defaultValue": null,
4521
+ "guides": []
4522
+ },
4523
+ {
4524
+ "id": "Avatar3dPrice.price",
4525
+ "name": "price",
4526
+ "anchor": "avatar3dprice-price",
4527
+ "group": "avatars3d",
4528
+ "signature": "readonly price: number",
4529
+ "documented": true,
4530
+ "summary": "The price, a whole number of that currency.",
4531
+ "deprecated": null,
4532
+ "params": [],
4533
+ "returns": null,
4534
+ "throws": null,
4535
+ "example": null,
4536
+ "defaultValue": null,
4537
+ "guides": []
4538
+ },
4539
+ {
4540
+ "id": "Avatar3dOffer",
4541
+ "name": "Avatar3dOffer",
4542
+ "anchor": "avatar3doffer",
4543
+ "group": "avatars3d",
4544
+ "signature": "interface Avatar3dOffer",
4545
+ "documented": true,
4546
+ "summary": "One of your game's 3D avatars on sale, as the platform prices it.",
4547
+ "deprecated": null,
4548
+ "params": [],
4549
+ "returns": null,
4550
+ "throws": null,
4551
+ "example": null,
4552
+ "defaultValue": null,
4553
+ "guides": []
4554
+ },
4555
+ {
4556
+ "id": "Avatar3dOffer.itemId",
4557
+ "name": "itemId",
4558
+ "anchor": "avatar3doffer-itemid",
4559
+ "group": "avatars3d",
4560
+ "signature": "readonly itemId: string",
4561
+ "documented": true,
4562
+ "summary": "The avatar's item id — what [Avatars3dApi.purchase](api-reference-game.md#avatars3dapi-purchase) takes.",
4563
+ "deprecated": null,
4564
+ "params": [],
4565
+ "returns": null,
4566
+ "throws": null,
4567
+ "example": null,
4568
+ "defaultValue": null,
4569
+ "guides": []
4570
+ },
4571
+ {
4572
+ "id": "Avatar3dOffer.slug",
4573
+ "name": "slug",
4574
+ "anchor": "avatar3doffer-slug",
4575
+ "group": "avatars3d",
4576
+ "signature": "readonly slug: string",
4577
+ "documented": true,
4578
+ "summary": "The slug you uploaded it under. A key, not display text.",
4579
+ "deprecated": null,
4580
+ "params": [],
4581
+ "returns": null,
4582
+ "throws": null,
4583
+ "example": null,
4584
+ "defaultValue": null,
4585
+ "guides": []
4586
+ },
4587
+ {
4588
+ "id": "Avatar3dOffer.family",
4589
+ "name": "family",
4590
+ "anchor": "avatar3doffer-family",
4591
+ "group": "avatars3d",
4592
+ "signature": "readonly family: Avatar3dFamily",
4593
+ "documented": true,
4594
+ "summary": "The rig family the model is built on.",
4595
+ "deprecated": null,
4596
+ "params": [],
4597
+ "returns": null,
4598
+ "throws": null,
4599
+ "example": null,
4600
+ "defaultValue": null,
4601
+ "guides": []
4602
+ },
4603
+ {
4604
+ "id": "Avatar3dOffer.displayName",
4605
+ "name": "displayName",
4606
+ "anchor": "avatar3doffer-displayname",
4607
+ "group": "avatars3d",
4608
+ "signature": "readonly displayName: string",
4609
+ "documented": true,
4610
+ "summary": "The avatar's name, as you uploaded it.",
4611
+ "deprecated": null,
4612
+ "params": [],
4613
+ "returns": null,
4614
+ "throws": null,
4615
+ "example": null,
4616
+ "defaultValue": null,
4617
+ "guides": []
4618
+ },
4619
+ {
4620
+ "id": "Avatar3dOffer.description",
4621
+ "name": "description",
4622
+ "anchor": "avatar3doffer-description",
4623
+ "group": "avatars3d",
4624
+ "signature": "readonly description?: string",
4625
+ "documented": true,
4626
+ "summary": "The avatar's description, when it has one.",
4627
+ "deprecated": null,
4628
+ "params": [],
4629
+ "returns": null,
4630
+ "throws": null,
4631
+ "example": null,
4632
+ "defaultValue": null,
4633
+ "guides": []
4634
+ },
4635
+ {
4636
+ "id": "Avatar3dOffer.iconUrl",
4637
+ "name": "iconUrl",
4638
+ "anchor": "avatar3doffer-iconurl",
4639
+ "group": "avatars3d",
4640
+ "signature": "readonly iconUrl: string",
4641
+ "documented": true,
4642
+ "summary": "A rendered portrait of the avatar, for a shop tile. Pass it through\n`platformIconUrl` before putting it in an `<img src>`.",
4643
+ "deprecated": null,
4644
+ "params": [],
4645
+ "returns": null,
4646
+ "throws": null,
4647
+ "example": null,
4648
+ "defaultValue": null,
4649
+ "guides": []
4650
+ },
4651
+ {
4652
+ "id": "Avatar3dOffer.modelUrl",
4653
+ "name": "modelUrl",
4654
+ "anchor": "avatar3doffer-modelurl",
4655
+ "group": "avatars3d",
4656
+ "signature": "readonly modelUrl: string",
4657
+ "documented": true,
4658
+ "summary": "The avatar's model, for a preview. Pass it through `platformIconUrl`\nbefore loading it.",
4659
+ "deprecated": null,
4660
+ "params": [],
4661
+ "returns": null,
4662
+ "throws": null,
4663
+ "example": null,
4664
+ "defaultValue": null,
4665
+ "guides": []
4666
+ },
4667
+ {
4668
+ "id": "Avatar3dOffer.prices",
4669
+ "name": "prices",
4670
+ "anchor": "avatar3doffer-prices",
4671
+ "group": "avatars3d",
4672
+ "signature": "readonly prices: readonly Avatar3dPrice[]",
4673
+ "documented": true,
4674
+ "summary": "One or two prices, each in a currency a player can pay with. Show each\none the player can choose; pass the chosen one's `currencySlug` to\n[Avatars3dApi.purchase](api-reference-game.md#avatars3dapi-purchase).",
4675
+ "deprecated": null,
4676
+ "params": [],
4677
+ "returns": null,
4678
+ "throws": null,
4679
+ "example": null,
4680
+ "defaultValue": null,
4681
+ "guides": []
4682
+ },
4683
+ {
4684
+ "id": "Avatars3dApi",
4685
+ "name": "Avatars3dApi",
4686
+ "anchor": "avatars3dapi",
4687
+ "group": "avatars3d",
4688
+ "signature": "interface Avatars3dApi",
4689
+ "documented": true,
4690
+ "summary": "Arcade 3D avatars — `gem.avatars3d`. What the player wears, the rig families\nyour game accepts, the avatars you sell, and buying one.\n\n```js\nfunction draw() {\n const worn = gem.avatars3d.worn();\n if (worn) loadCharacter(platformIconUrl(worn.modelUrl));\n else useDefaultCharacter();\n}\ndraw();\ngem.avatars3d.onChange(draw);\n```\n\nThe guide is `docs/arcade-avatars.md`: the rig, the config, uploading and\nselling with gemctl, and loading the model at runtime.",
4691
+ "deprecated": null,
4692
+ "params": [],
4693
+ "returns": null,
4694
+ "throws": null,
4695
+ "example": null,
4696
+ "defaultValue": null,
4697
+ "guides": []
4698
+ },
4699
+ {
4700
+ "id": "Avatars3dApi.families",
4701
+ "name": "families",
4702
+ "anchor": "avatars3dapi-families",
4703
+ "group": "avatars3d",
4704
+ "signature": "families(): Promise<readonly Avatar3dFamily[]>",
4705
+ "documented": true,
4706
+ "summary": "The rig families your game accepts, in the order your published config\ndeclares them (`avatars_3d.families`) — the first is your default.\n\nResolves `[]` when your game has no published config on this channel, or\nits config declares no families. Loads the config through\n`gem.gameConfig.load()` and shares that one fetch; rejects when the config\ncannot be loaded, as `load()` does.",
4707
+ "deprecated": null,
4708
+ "params": [],
4709
+ "returns": null,
4710
+ "throws": null,
4711
+ "example": null,
4712
+ "defaultValue": null,
4713
+ "guides": []
4714
+ },
4715
+ {
4716
+ "id": "Avatars3dApi.worn",
4717
+ "name": "worn",
4718
+ "anchor": "avatars3dapi-worn",
4719
+ "group": "avatars3d",
4720
+ "signature": "worn(family?: Avatar3dFamily): WornAvatar3d | undefined",
4721
+ "documented": true,
4722
+ "summary": "The 3D avatar the player wears — in `family` when you name one, otherwise\nin your game's first declared family that has one. `undefined` when none\nis worn there, before the player arrives, and outside the Arcade: draw\nyour default look.\n\nRead live from [Gem.player](api-reference-game.md#gem-player), so it changes when the player wears\nanother avatar or buys one; [Avatars3dApi.onChange](api-reference-game.md#avatars3dapi-onchange) tells you when.",
4723
+ "deprecated": null,
4724
+ "params": [
4725
+ {
4726
+ "name": "family",
4727
+ "type": "Avatar3dFamily",
4728
+ "optional": true,
4729
+ "summary": null
4730
+ }
4731
+ ],
4732
+ "returns": null,
4733
+ "throws": null,
4734
+ "example": null,
4735
+ "defaultValue": null,
4736
+ "guides": []
4737
+ },
4738
+ {
4739
+ "id": "Avatars3dApi.offers",
4740
+ "name": "offers",
4741
+ "anchor": "avatars3dapi-offers",
4742
+ "group": "avatars3d",
4743
+ "signature": "offers(): Promise<readonly Avatar3dOffer[]>",
4744
+ "documented": true,
4745
+ "summary": "Your game's 3D avatars on sale on this channel, with their prices, read\nfrom the platform with your game's own token. Render your shop from this,\nnever from prices of your own.\n\nResolves `[]` when you have nothing on sale. Rejects with a `GemApiError`\nwhen the platform cannot be read — a shop that cannot load says so.",
4746
+ "deprecated": null,
4747
+ "params": [],
4748
+ "returns": null,
4749
+ "throws": null,
4750
+ "example": null,
4751
+ "defaultValue": null,
4752
+ "guides": []
4753
+ },
4754
+ {
4755
+ "id": "Avatars3dApi.purchase",
4756
+ "name": "purchase",
4757
+ "anchor": "avatars3dapi-purchase",
4758
+ "group": "avatars3d",
4759
+ "signature": "purchase(request: Avatar3dPurchaseRequest): Promise<PurchaseOutcome>",
4760
+ "documented": true,
4761
+ "summary": "Buy one of your 3D avatars for the player and put it on. The Arcade\nprices it again, shows its own confirmation sheet, takes the currency and\nwears the avatar; your game then receives the player again, with the new\navatar worn, and [Avatars3dApi.onChange](api-reference-game.md#avatars3dapi-onchange) runs.\n\nCall it from the player's own click or key press — the Arcade shows no\nsheet without one and answers `declined` / `busy`.\n\nNOTHING REJECTS. Every outcome resolves, exactly as on\n`gem.purchases.request`:\n\n- `ok` — bought and worn, and the platform confirmed the player owns it.\n- `declined` — definitely not bought. `reason` says why:\n `already_owned` (the player has it — offer to wear it with\n `gem.openAvatarPicker({ tab: '3d' })`), `cancelled`, `insufficient_funds`,\n `no_price_in_currency`, `not_purchasable` (not on sale, or not an id from\n [Avatars3dApi.offers](api-reference-game.md#avatars3dapi-offers)), `busy`, and the rest of\n `PurchaseRefusalCode`. Return to your shop silently.\n- `unavailable` — no reliable answer. Show one neutral message, never\n retry on your own, and read [Avatars3dApi.worn](api-reference-game.md#avatars3dapi-worn) again.\n\n**Outside the Arcade** — opened at the top level of a page with no Arcade\naround it, or under an Arcade that does not sell 3D avatars yet — it\nresolves `unavailable` with reason `error` at once and asks nobody.",
4762
+ "deprecated": null,
4763
+ "params": [
4764
+ {
4765
+ "name": "request",
4766
+ "type": "Avatar3dPurchaseRequest",
4767
+ "optional": false,
4768
+ "summary": null
4769
+ }
4770
+ ],
4771
+ "returns": null,
4772
+ "throws": null,
4773
+ "example": null,
4774
+ "defaultValue": null,
4775
+ "guides": []
4776
+ },
4777
+ {
4778
+ "id": "Avatars3dApi.onChange",
4779
+ "name": "onChange",
4780
+ "anchor": "avatars3dapi-onchange",
4781
+ "group": "avatars3d",
4782
+ "signature": "onChange(listener: (worn: readonly WornAvatar3d[]) => void): () => void",
4783
+ "documented": true,
4784
+ "summary": "Call `listener` whenever the avatars the player wears change — they wore\nanother one in the Arcade's picker, bought one, or their avatars arrived\nfor the first time — with every worn avatar, one per family. Not called\nfor the avatars already worn when you subscribe: read\n[Avatars3dApi.worn](api-reference-game.md#avatars3dapi-worn) for those.",
4785
+ "deprecated": null,
4786
+ "params": [
4787
+ {
4788
+ "name": "listener",
4789
+ "type": "(worn: readonly WornAvatar3d[]) => void",
4790
+ "optional": false,
4791
+ "summary": null
4792
+ }
4793
+ ],
4794
+ "returns": "a function that stops the calls. Calling it twice is harmless.",
4795
+ "throws": null,
4796
+ "example": null,
4797
+ "defaultValue": null,
4798
+ "guides": []
4799
+ },
4487
4800
  {
4488
4801
  "id": "GemApiError",
4489
4802
  "name": "GemApiError",
@@ -5783,7 +6096,7 @@
5783
6096
  "group": "controls",
5784
6097
  "signature": "interface ControlsApi",
5785
6098
  "documented": true,
5786
- "summary": "`gem.controls` — the player's camera and controls settings: tell the Arcade\nwhich ones your game leaves to the player, and turn look input into a camera\nturn at the player's sensitivity.",
6099
+ "summary": "`gem.controls` — the player's camera and controls settings: turn look input\ninto a camera turn at the player's sensitivity.",
5787
6100
  "deprecated": null,
5788
6101
  "params": [],
5789
6102
  "returns": null,
@@ -5797,29 +6110,6 @@
5797
6110
  }
5798
6111
  ]
5799
6112
  },
5800
- {
5801
- "id": "ControlsApi.declare",
5802
- "name": "declare",
5803
- "anchor": "controlsapi-declare",
5804
- "group": "controls",
5805
- "signature": "declare(uses: GemControlsDeclaration): boolean",
5806
- "documented": true,
5807
- "summary": "Tell the Arcade which of the player's camera and controls settings your\ngame uses, and for each one who decides it: `'player'` — you apply the\nArcade's value from `gem.prefs.recommended` (or the player's own choice\nin your settings, which wins) — or `'game'` — you decide it yourself, and\nthe Arcade's Settings shows that row as \"Set by this game\". Leave out a\nsetting your game does not use.\n\n```js\ngem.controls.declare({\n lookSensitivity: 'player',\n gamepadLookSensitivity: 'player',\n invertLookY: 'player',\n touchMovement: 'game', // this game always uses a fixed stick\n});\n```\n\n**It is screen state, not a toggle.** Each call is the whole answer and\nreplaces the last. The SDK holds it and sends it again after every\nreconnect, so call it once at boot, and again only when the answer\nchanges. Any other key or value is dropped.",
5808
- "deprecated": null,
5809
- "params": [
5810
- {
5811
- "name": "uses",
5812
- "type": "GemControlsDeclaration",
5813
- "optional": false,
5814
- "summary": null
5815
- }
5816
- ],
5817
- "returns": "`true` when the Arcade was told now; `false` before the bridge is\n up (the declaration is held and sent when it connects), outside the\n Arcade, and under an embedder that does not show these settings. Never\n throws.",
5818
- "throws": null,
5819
- "example": null,
5820
- "defaultValue": null,
5821
- "guides": []
5822
- },
5823
6113
  {
5824
6114
  "id": "ControlsApi.look",
5825
6115
  "name": "look",
@@ -5859,102 +6149,6 @@
5859
6149
  "defaultValue": null,
5860
6150
  "guides": []
5861
6151
  },
5862
- {
5863
- "id": "GemControlChoice",
5864
- "name": "GemControlChoice",
5865
- "anchor": "gemcontrolchoice",
5866
- "group": "controls",
5867
- "signature": "type GemControlChoice = \"player\" | \"game\"",
5868
- "documented": true,
5869
- "summary": "Who decides one camera or controls setting: `'player'` — your game applies\nthe Arcade's value, or the player's own in-game choice, which wins — or\n`'game'` — your game decides it itself, and the Arcade's Settings shows the\nrow as \"Set by this game\".",
5870
- "deprecated": null,
5871
- "params": [],
5872
- "returns": null,
5873
- "throws": null,
5874
- "example": null,
5875
- "defaultValue": null,
5876
- "guides": []
5877
- },
5878
- {
5879
- "id": "GemControlsDeclaration",
5880
- "name": "GemControlsDeclaration",
5881
- "anchor": "gemcontrolsdeclaration",
5882
- "group": "controls",
5883
- "signature": "interface GemControlsDeclaration",
5884
- "documented": true,
5885
- "summary": "What `gem.controls.declare` takes: for each of the player's camera and\ncontrols settings your game uses, who decides it. Leave a setting out when\nyour game does not use it at all — the Arcade then says \"This game doesn't\nuse this\" beside that row.",
5886
- "deprecated": null,
5887
- "params": [],
5888
- "returns": null,
5889
- "throws": null,
5890
- "example": null,
5891
- "defaultValue": null,
5892
- "guides": []
5893
- },
5894
- {
5895
- "id": "GemControlsDeclaration.lookSensitivity",
5896
- "name": "lookSensitivity",
5897
- "anchor": "gemcontrolsdeclaration-looksensitivity",
5898
- "group": "controls",
5899
- "signature": "readonly lookSensitivity?: GemControlChoice",
5900
- "documented": true,
5901
- "summary": "Look speed for touch, mouse and pen — `gem.prefs.recommended.lookSensitivity`.",
5902
- "deprecated": null,
5903
- "params": [],
5904
- "returns": null,
5905
- "throws": null,
5906
- "example": null,
5907
- "defaultValue": null,
5908
- "guides": []
5909
- },
5910
- {
5911
- "id": "GemControlsDeclaration.gamepadLookSensitivity",
5912
- "name": "gamepadLookSensitivity",
5913
- "anchor": "gemcontrolsdeclaration-gamepadlooksensitivity",
5914
- "group": "controls",
5915
- "signature": "readonly gamepadLookSensitivity?: GemControlChoice",
5916
- "documented": true,
5917
- "summary": "Look speed for a gamepad stick — `gem.prefs.recommended.gamepadLookSensitivity`.",
5918
- "deprecated": null,
5919
- "params": [],
5920
- "returns": null,
5921
- "throws": null,
5922
- "example": null,
5923
- "defaultValue": null,
5924
- "guides": []
5925
- },
5926
- {
5927
- "id": "GemControlsDeclaration.invertLookY",
5928
- "name": "invertLookY",
5929
- "anchor": "gemcontrolsdeclaration-invertlooky",
5930
- "group": "controls",
5931
- "signature": "readonly invertLookY?: GemControlChoice",
5932
- "documented": true,
5933
- "summary": "Inverted up/down look — `gem.prefs.recommended.invertLookY`.",
5934
- "deprecated": null,
5935
- "params": [],
5936
- "returns": null,
5937
- "throws": null,
5938
- "example": null,
5939
- "defaultValue": null,
5940
- "guides": []
5941
- },
5942
- {
5943
- "id": "GemControlsDeclaration.touchMovement",
5944
- "name": "touchMovement",
5945
- "anchor": "gemcontrolsdeclaration-touchmovement",
5946
- "group": "controls",
5947
- "signature": "readonly touchMovement?: GemControlChoice",
5948
- "documented": true,
5949
- "summary": "The touch movement thumbstick, floating or fixed — `gem.prefs.recommended.touchMovement`.",
5950
- "deprecated": null,
5951
- "params": [],
5952
- "returns": null,
5953
- "throws": null,
5954
- "example": null,
5955
- "defaultValue": null,
5956
- "guides": []
5957
- },
5958
6152
  {
5959
6153
  "id": "DEVICE_SETTINGS_MAX_BYTES",
5960
6154
  "name": "DEVICE_SETTINGS_MAX_BYTES",
@@ -6552,45 +6746,16 @@
6552
6746
  "defaultValue": null,
6553
6747
  "guides": []
6554
6748
  },
6555
- {
6556
- "id": "GemFrame.declareControls",
6557
- "name": "declareControls",
6558
- "anchor": "gemframe-declarecontrols",
6559
- "group": "frame",
6560
- "signature": "declareControls(uses: GemControlsDeclaration): boolean",
6561
- "documented": true,
6562
- "summary": "Tell the arcade which of the player's camera and controls settings your\ngame leaves to the player and which it decides itself — the whole answer\neach time, the latest one winning.\n\nPrefer `gem.controls.declare`, which holds the declaration across a\nreconnect. This one sends only while connected, and only to an arcade\nthat shows these settings.",
6563
- "deprecated": null,
6564
- "params": [
6565
- {
6566
- "name": "uses",
6567
- "type": "GemControlsDeclaration",
6568
- "optional": false,
6569
- "summary": null
6570
- }
6571
- ],
6572
- "returns": "whether the message left the frame — `false` before the bridge is\n up, for an arcade that does not show these settings, and past this\n frame's own limit.",
6573
- "throws": null,
6574
- "example": null,
6575
- "defaultValue": null,
6576
- "guides": []
6577
- },
6578
6749
  {
6579
6750
  "id": "GemFrame.setMenu",
6580
6751
  "name": "setMenu",
6581
6752
  "anchor": "gemframe-setmenu",
6582
6753
  "group": "frame",
6583
- "signature": "setMenu(settings: boolean, items?: readonly { id: string; label: string }[]): boolean",
6754
+ "signature": "setMenu(items?: readonly { id: string; label: string }[]): boolean",
6584
6755
  "documented": true,
6585
- "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.",
6756
+ "summary": "Tell the arcade what your game adds to its menu: up to four entries of its\nown — the whole answer each time, the latest one winning. Your settings\nare not among them: a game opens its settings from its own button.\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.",
6586
6757
  "deprecated": null,
6587
6758
  "params": [
6588
- {
6589
- "name": "settings",
6590
- "type": "boolean",
6591
- "optional": false,
6592
- "summary": null
6593
- },
6594
6759
  {
6595
6760
  "name": "items",
6596
6761
  "type": "readonly { id: string; label: string }[]",
@@ -6879,6 +7044,22 @@
6879
7044
  "defaultValue": null,
6880
7045
  "guides": []
6881
7046
  },
7047
+ {
7048
+ "id": "Friend.avatars3d",
7049
+ "name": "avatars3d",
7050
+ "anchor": "friend-avatars3d",
7051
+ "group": "friends",
7052
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
7053
+ "documented": true,
7054
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
7055
+ "deprecated": null,
7056
+ "params": [],
7057
+ "returns": null,
7058
+ "throws": null,
7059
+ "example": null,
7060
+ "defaultValue": null,
7061
+ "guides": []
7062
+ },
6882
7063
  {
6883
7064
  "id": "Friend.badges",
6884
7065
  "name": "badges",
@@ -8697,6 +8878,22 @@
8697
8878
  "defaultValue": null,
8698
8879
  "guides": []
8699
8880
  },
8881
+ {
8882
+ "id": "GemPlayer.avatars3d",
8883
+ "name": "avatars3d",
8884
+ "anchor": "gemplayer-avatars3d",
8885
+ "group": "player",
8886
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
8887
+ "documented": true,
8888
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
8889
+ "deprecated": null,
8890
+ "params": [],
8891
+ "returns": null,
8892
+ "throws": null,
8893
+ "example": null,
8894
+ "defaultValue": null,
8895
+ "guides": []
8896
+ },
8700
8897
  {
8701
8898
  "id": "GemPlayer.badges",
8702
8899
  "name": "badges",
@@ -9396,23 +9593,7 @@
9396
9593
  "group": "frame",
9397
9594
  "signature": "interface GameMenu",
9398
9595
  "documented": true,
9399
- "summary": "What your game adds to the arcade's menu — see `gem.setMenu`.",
9400
- "deprecated": null,
9401
- "params": [],
9402
- "returns": null,
9403
- "throws": null,
9404
- "example": null,
9405
- "defaultValue": null,
9406
- "guides": []
9407
- },
9408
- {
9409
- "id": "GameMenu.onSettings",
9410
- "name": "onSettings",
9411
- "anchor": "gamemenu-onsettings",
9412
- "group": "frame",
9413
- "signature": "readonly onSettings?: () => void",
9414
- "documented": true,
9415
- "summary": "Open your settings screen. Another embedder may show a Settings entry that\ncalls it, but **the Gem Arcade no longer does**: draw a settings button of\nyour own, always (top-right is the recommendation). Passing this is still\naccepted and harmless.",
9596
+ "summary": "What your game adds to the arcade's menu — see `gem.setMenu`. Your settings\nare not part of it: they open from your own settings button.",
9416
9597
  "deprecated": null,
9417
9598
  "params": [],
9418
9599
  "returns": null,
@@ -9877,6 +10058,22 @@
9877
10058
  "defaultValue": null,
9878
10059
  "guides": []
9879
10060
  },
10061
+ {
10062
+ "id": "Gem.avatars3d",
10063
+ "name": "avatars3d",
10064
+ "anchor": "gem-avatars3d",
10065
+ "group": "start",
10066
+ "signature": "readonly avatars3d: Avatars3dApi",
10067
+ "documented": true,
10068
+ "summary": "Arcade 3D avatars: what the player wears, the rig families your game\naccepts, the avatars your game sells, and buying one — which wears it.\n2D portraits are a separate system and are not here.\n\n```js\nconst draw = () => {\n const worn = gem.avatars3d.worn();\n const url = worn && platformIconUrl(worn.modelUrl);\n if (url) loadCharacter(url);\n else useDefaultCharacter();\n};\ndraw();\ngem.avatars3d.onChange(draw);\n```\n\nSee [Arcade avatars](../arcade-avatars.md) for the rig, the config, and\nuploading and selling avatars with gemctl.",
10069
+ "deprecated": null,
10070
+ "params": [],
10071
+ "returns": null,
10072
+ "throws": null,
10073
+ "example": null,
10074
+ "defaultValue": null,
10075
+ "guides": []
10076
+ },
9880
10077
  {
9881
10078
  "id": "Gem.vibration",
9882
10079
  "name": "vibration",
@@ -9905,7 +10102,7 @@
9905
10102
  "group": "start",
9906
10103
  "signature": "readonly controls: ControlsApi",
9907
10104
  "documented": true,
9908
- "summary": "The player's camera and controls settings — look sensitivity, gamepad look\nsensitivity, invert look and touch movement, which arrive in\n`gem.prefs.recommended`. Two things to do with them:\n\n```js\n// 1. Tell the Arcade which ones your game leaves to the player.\ngem.controls.declare({ lookSensitivity: 'player', invertLookY: 'player' });\n\n// 2. Turn look input into a camera turn at the player's sensitivity.\nconst look = gem.controls.look();\n// dx, dy: the pointer's move in CSS px since the last event.\nconst t = look.fromPointer(dx, dy, e.pointerType, camera.pitch);\ncamera.yaw += t.yaw;\ncamera.pitch += t.pitch;\n```\n\nSee the guide's \"Camera and look controls\" section.",
10105
+ "summary": "The player's camera and controls settings — look sensitivity, gamepad look\nsensitivity, invert look and touch movement, which arrive in\n`gem.prefs.recommended` as the player's Arcade-wide defaults. Turn look\ninput into a camera turn at the player's sensitivity:\n\n```js\nconst look = gem.controls.look();\n// dx, dy: the pointer's move in CSS px since the last event.\nconst t = look.fromPointer(dx, dy, e.pointerType, camera.pitch);\ncamera.yaw += t.yaw;\ncamera.pitch += t.pitch;\n```\n\nSee the guide's \"Camera and look controls\" section.",
9909
10106
  "deprecated": null,
9910
10107
  "params": [],
9911
10108
  "returns": null,
@@ -10267,14 +10464,14 @@
10267
10464
  "group": "start",
10268
10465
  "signature": "setMenu(menu: GameMenu): boolean",
10269
10466
  "documented": true,
10270
- "summary": "Put up to four entries of your own in the arcade's menu. The arcade keeps\none menu button over your game: it is how a player leaves, mutes, goes\nfullscreen, reports a problem and changes their Arcade-wide settings.\n\n```js\ngem.setMenu({\n items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],\n});\n```\n\n**Your settings are not in it: draw your own settings button, always** —\ntop-right is the recommendation. `onSettings` is still accepted, and\nanother embedder may show an entry for it, but the Gem Arcade no longer\nshows a Settings entry, so a game whose settings open only from there has\nnone a player can reach.\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.",
10467
+ "summary": "Put up to four entries of your own in the arcade's menu. The arcade keeps\none menu button over your game: it is how a player leaves, mutes, goes\nfullscreen, reports a problem and changes their Arcade-wide settings.\n\n```js\ngem.setMenu({\n items: [{ id: 'how-to-play', label: t('How to play'), onSelect: () => help.open() }],\n});\n```\n\n**Your settings are not in it: draw your own settings button, always** —\ntop-right, clear of `gem.viewport.reserved`. The Arcade's menu never opens\nyour settings screen.\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.",
10271
10468
  "deprecated": null,
10272
10469
  "params": [
10273
10470
  {
10274
10471
  "name": "menu",
10275
10472
  "type": "GameMenu",
10276
10473
  "optional": false,
10277
- "summary": "Your settings handler and your own entries."
10474
+ "summary": "Your own entries."
10278
10475
  }
10279
10476
  ],
10280
10477
  "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.",
@@ -11417,6 +11614,102 @@
11417
11614
  "defaultValue": null,
11418
11615
  "guides": []
11419
11616
  },
11617
+ {
11618
+ "id": "Avatar3dFamily",
11619
+ "name": "Avatar3dFamily",
11620
+ "anchor": "avatar3dfamily",
11621
+ "group": "avatars3d",
11622
+ "signature": "type Avatar3dFamily = \"small_human\" | string & object",
11623
+ "documented": true,
11624
+ "summary": "A rig family — the skeleton contract a 3D avatar is built on, and what a\ngame declares it can animate. `small_human` is the first: a `root` node\nwith a `body` child, which holds `head`, `arm_l`, `arm_r`, `leg_l` and\n`leg_r`, and a `hand_socket` under `arm_r`.\n\nOpen-ended (`| (string & {})`) so a family the platform adds later does not\nbreak a `switch` you compiled against this version: keep a `default` arm and\nskip a family you do not draw.",
11625
+ "deprecated": null,
11626
+ "params": [],
11627
+ "returns": null,
11628
+ "throws": null,
11629
+ "example": null,
11630
+ "defaultValue": null,
11631
+ "guides": []
11632
+ },
11633
+ {
11634
+ "id": "WornAvatar3d",
11635
+ "name": "WornAvatar3d",
11636
+ "anchor": "wornavatar3d",
11637
+ "group": "avatars3d",
11638
+ "signature": "interface WornAvatar3d",
11639
+ "documented": true,
11640
+ "summary": "One 3D avatar a player wears — at most one per [Avatar3dFamily](api-reference-game.md#avatar3dfamily).",
11641
+ "deprecated": null,
11642
+ "params": [],
11643
+ "returns": null,
11644
+ "throws": null,
11645
+ "example": null,
11646
+ "defaultValue": null,
11647
+ "guides": []
11648
+ },
11649
+ {
11650
+ "id": "WornAvatar3d.family",
11651
+ "name": "family",
11652
+ "anchor": "wornavatar3d-family",
11653
+ "group": "avatars3d",
11654
+ "signature": "readonly family: Avatar3dFamily",
11655
+ "documented": true,
11656
+ "summary": "The rig family this avatar is built on.",
11657
+ "deprecated": null,
11658
+ "params": [],
11659
+ "returns": null,
11660
+ "throws": null,
11661
+ "example": null,
11662
+ "defaultValue": null,
11663
+ "guides": []
11664
+ },
11665
+ {
11666
+ "id": "WornAvatar3d.id",
11667
+ "name": "id",
11668
+ "anchor": "wornavatar3d-id",
11669
+ "group": "avatars3d",
11670
+ "signature": "readonly id: string",
11671
+ "documented": true,
11672
+ "summary": "The avatar's item id — a stable key for the model, never something to\nshow as text.",
11673
+ "deprecated": null,
11674
+ "params": [],
11675
+ "returns": null,
11676
+ "throws": null,
11677
+ "example": null,
11678
+ "defaultValue": null,
11679
+ "guides": []
11680
+ },
11681
+ {
11682
+ "id": "WornAvatar3d.modelUrl",
11683
+ "name": "modelUrl",
11684
+ "anchor": "wornavatar3d-modelurl",
11685
+ "group": "avatars3d",
11686
+ "signature": "readonly modelUrl: string",
11687
+ "documented": true,
11688
+ "summary": "The avatar's model, a binary glTF (`.glb`) on the family's rig. Pass it\nthrough [platformIconUrl](api-reference-game.md#platformiconurl) before loading it.",
11689
+ "deprecated": null,
11690
+ "params": [],
11691
+ "returns": null,
11692
+ "throws": null,
11693
+ "example": null,
11694
+ "defaultValue": null,
11695
+ "guides": []
11696
+ },
11697
+ {
11698
+ "id": "WornAvatar3d.iconUrl",
11699
+ "name": "iconUrl",
11700
+ "anchor": "wornavatar3d-iconurl",
11701
+ "group": "avatars3d",
11702
+ "signature": "readonly iconUrl: string",
11703
+ "documented": true,
11704
+ "summary": "A rendered portrait of the avatar, for a roster or a menu. Pass it through\n[platformIconUrl](api-reference-game.md#platformiconurl) before putting it in an `<img src>`.",
11705
+ "deprecated": null,
11706
+ "params": [],
11707
+ "returns": null,
11708
+ "throws": null,
11709
+ "example": null,
11710
+ "defaultValue": null,
11711
+ "guides": []
11712
+ },
11420
11713
  {
11421
11714
  "id": "PlayerIdentity",
11422
11715
  "name": "PlayerIdentity",
@@ -11577,6 +11870,22 @@
11577
11870
  "defaultValue": null,
11578
11871
  "guides": []
11579
11872
  },
11873
+ {
11874
+ "id": "PlayerIdentity.avatars3d",
11875
+ "name": "avatars3d",
11876
+ "anchor": "playeridentity-avatars3d",
11877
+ "group": "player",
11878
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
11879
+ "documented": true,
11880
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
11881
+ "deprecated": null,
11882
+ "params": [],
11883
+ "returns": null,
11884
+ "throws": null,
11885
+ "example": null,
11886
+ "defaultValue": null,
11887
+ "guides": []
11888
+ },
11580
11889
  {
11581
11890
  "id": "PlayerIdentity.badges",
11582
11891
  "name": "badges",
@@ -11600,7 +11909,7 @@
11600
11909
  "group": "player",
11601
11910
  "signature": "function mapIdentity(row: unknown): PlayerIdentity",
11602
11911
  "documented": true,
11603
- "summary": "Map one row's identity fields, as the platform's API sends them, to\n[PlayerIdentity](api-reference-game.md#playeridentity).\n\nEvery surface this SDK names maps its rows through this already. You need it\nonly for a row you fetched yourself through the raw client — an async\nopponent, for instance:\n\n```js\nimport { mapIdentity, text } from '@indiegems/gem-web-sdk/sdk';\n\nconst { opponents } = await gem.client.get('/v1/async-opponents?opponent_type=ghost-lap');\nfor (const row of opponents) {\n const who = mapIdentity(row);\n label(who.displayName ? text.toPlain(who.displayName) : 'Anonymous player');\n}\n```\n\nReads `player_id`, `display_name`, `discriminator`, `display_tag`,\n`avatar_icon_id`, `avatar_icon_url`, `avatar_3d_id`, `avatar_3d_model_url`,\n`avatar_3d_icon_url` and `badges`, and nothing else from the row. Anything missing, empty or of the wrong type is absent in the result.",
11912
+ "summary": "Map one row's identity fields, as the platform's API sends them, to\n[PlayerIdentity](api-reference-game.md#playeridentity).\n\nEvery surface this SDK names maps its rows through this already. You need it\nonly for a row you fetched yourself through the raw client — an async\nopponent, for instance:\n\n```js\nimport { mapIdentity, text } from '@indiegems/gem-web-sdk/sdk';\n\nconst { opponents } = await gem.client.get('/v1/async-opponents?opponent_type=ghost-lap');\nfor (const row of opponents) {\n const who = mapIdentity(row);\n label(who.displayName ? text.toPlain(who.displayName) : 'Anonymous player');\n}\n```\n\nReads `player_id`, `display_name`, `discriminator`, `display_tag`,\n`avatar_icon_id`, `avatar_icon_url`, `avatar_3d_id`, `avatar_3d_model_url`,\n`avatar_3d_icon_url`, `avatars_3d` and `badges`, and nothing else from the row. Anything missing, empty or of the wrong type is absent in the result.",
11604
11913
  "deprecated": null,
11605
11914
  "params": [
11606
11915
  {
@@ -12920,14 +13229,62 @@
12920
13229
  "defaultValue": null,
12921
13230
  "guides": []
12922
13231
  },
13232
+ {
13233
+ "id": "Avatar3dPurchaseRequest",
13234
+ "name": "Avatar3dPurchaseRequest",
13235
+ "anchor": "avatar3dpurchaserequest",
13236
+ "group": "avatars3d",
13237
+ "signature": "interface Avatar3dPurchaseRequest",
13238
+ "documented": true,
13239
+ "summary": "What a game asks for when it buys one of its own 3D avatars for the player\nwith `gem.avatars3d.purchase`.",
13240
+ "deprecated": null,
13241
+ "params": [],
13242
+ "returns": null,
13243
+ "throws": null,
13244
+ "example": null,
13245
+ "defaultValue": null,
13246
+ "guides": []
13247
+ },
13248
+ {
13249
+ "id": "Avatar3dPurchaseRequest.itemId",
13250
+ "name": "itemId",
13251
+ "anchor": "avatar3dpurchaserequest-itemid",
13252
+ "group": "avatars3d",
13253
+ "signature": "itemId: string",
13254
+ "documented": true,
13255
+ "summary": "The avatar's item id — `itemId` on an `Avatar3dOffer`.",
13256
+ "deprecated": null,
13257
+ "params": [],
13258
+ "returns": null,
13259
+ "throws": null,
13260
+ "example": null,
13261
+ "defaultValue": null,
13262
+ "guides": []
13263
+ },
13264
+ {
13265
+ "id": "Avatar3dPurchaseRequest.currencySlug",
13266
+ "name": "currencySlug",
13267
+ "anchor": "avatar3dpurchaserequest-currencyslug",
13268
+ "group": "avatars3d",
13269
+ "signature": "currencySlug: string",
13270
+ "documented": true,
13271
+ "summary": "The platform currency to pay in, `GEMS` or `ARCADE_TICKETS`. The offer\nmust carry a price in it.",
13272
+ "deprecated": null,
13273
+ "params": [],
13274
+ "returns": null,
13275
+ "throws": null,
13276
+ "example": null,
13277
+ "defaultValue": null,
13278
+ "guides": []
13279
+ },
12923
13280
  {
12924
13281
  "id": "PurchaseRefusalCode",
12925
13282
  "name": "PurchaseRefusalCode",
12926
13283
  "anchor": "purchaserefusalcode",
12927
13284
  "group": "purchases",
12928
- "signature": "type PurchaseRefusalCode = \"unknown_currency\" | \"currency_not_in_game_payable\" | \"not_purchasable\" | \"no_price_in_currency\" | \"busy\" | \"cancelled\" | \"insufficient_funds\" | \"price_mismatch\" | \"config_version_stale\" | \"platform_currency_unavailable\" | \"not_available\" | \"recipe_locked\" | \"recipe_on_cooldown\" | \"recipe_max_uses_exceeded\" | \"item_max_stack_exceeded\" | \"recipe_costs_unmet\" | \"quantity_invalid\" | \"error\"",
13285
+ "signature": "type PurchaseRefusalCode = \"unknown_currency\" | \"currency_not_in_game_payable\" | \"not_purchasable\" | \"no_price_in_currency\" | \"busy\" | \"cancelled\" | \"insufficient_funds\" | \"price_mismatch\" | \"config_version_stale\" | \"platform_currency_unavailable\" | \"not_available\" | \"recipe_locked\" | \"already_owned\" | \"recipe_on_cooldown\" | \"recipe_max_uses_exceeded\" | \"item_max_stack_exceeded\" | \"recipe_costs_unmet\" | \"quantity_invalid\" | \"error\"",
12929
13286
  "documented": true,
12930
- "summary": "Every reason a purchase can be refused, on either purchase path.\n\n- `unknown_currency` — the slug names no active platform currency. There are\n no aliases: `TICKETS` is the retired name for Gems and is refused, never\n resolved. Write `GEMS` or `ARCADE_TICKETS`.\n- `currency_not_in_game_payable` — the currency exists but cannot pay for game\n items.\n- `not_purchasable` — the target is not in the purchase catalog, or cannot be\n bought (not published on this channel, inactive, or its config does not\n convert).\n- `no_price_in_currency` — the target has prices, but none in that currency.\n- `busy` — another purchase is in flight, a sheet is already open, the\n platform is backing off after a cancel, or the ask came without a player's\n click or key press.\n- `cancelled` — the player dismissed the platform sheet.\n- `insufficient_funds` — the player cannot afford it.\n- `price_mismatch`, `config_version_stale` — the price or config changed\n between reading the catalog and buying; nothing was charged. Re-read.\n- `platform_currency_unavailable` — the currency is not available to this\n purchase right now.\n- `not_available` — the recipe is outside its availability window, or\n inactive; nothing was charged. Hide or disable it until its window opens.\n- `recipe_locked` — the recipe's prerequisites are not met.\n- `recipe_on_cooldown`, `recipe_max_uses_exceeded`, `item_max_stack_exceeded`,\n `recipe_costs_unmet`, `quantity_invalid` — the recipe or item refused this\n purchase, before any charge.\n- `error` — anything else, including a reason this SDK version does not know\n and an answer that never arrived.\n\nFOR YOUR LOGS AND YOUR SHOP'S STATE, NEVER FOR A PAYMENT SCREEN. Grey out a\nlocked recipe; fix a slug. Never draw a top-up, \"buy more\" or payment UI of\nyour own — only the arcade may, and a game screen that looks like one is an\nimitation of platform payment chrome.",
13287
+ "summary": "Every reason a purchase can be refused, on either purchase path.\n\n- `unknown_currency` — the slug names no active platform currency. There are\n no aliases: `TICKETS` is the retired name for Gems and is refused, never\n resolved. Write `GEMS` or `ARCADE_TICKETS`.\n- `currency_not_in_game_payable` — the currency exists but cannot pay for game\n items.\n- `not_purchasable` — the target is not in the purchase catalog, or cannot be\n bought (not published on this channel, inactive, or its config does not\n convert).\n- `no_price_in_currency` — the target has prices, but none in that currency.\n- `busy` — another purchase is in flight, a sheet is already open, the\n platform is backing off after a cancel, or the ask came without a player's\n click or key press.\n- `cancelled` — the player dismissed the platform sheet.\n- `insufficient_funds` — the player cannot afford it.\n- `price_mismatch`, `config_version_stale` — the price or config changed\n between reading the catalog and buying; nothing was charged. Re-read.\n- `platform_currency_unavailable` — the currency is not available to this\n purchase right now.\n- `not_available` — the recipe is outside its availability window, or\n inactive; nothing was charged. Hide or disable it until its window opens.\n- `recipe_locked` — the recipe's prerequisites are not met.\n- `already_owned` — the player already owns the 3D avatar\n `gem.avatars3d.purchase` asked for; nothing was charged. Only that call\n answers it.\n- `recipe_on_cooldown`, `recipe_max_uses_exceeded`, `item_max_stack_exceeded`,\n `recipe_costs_unmet`, `quantity_invalid` — the recipe or item refused this\n purchase, before any charge.\n- `error` — anything else, including a reason this SDK version does not know\n and an answer that never arrived.\n\nFOR YOUR LOGS AND YOUR SHOP'S STATE, NEVER FOR A PAYMENT SCREEN. Grey out a\nlocked recipe; fix a slug. Never draw a top-up, \"buy more\" or payment UI of\nyour own — only the arcade may, and a game screen that looks like one is an\nimitation of platform payment chrome.",
12931
13288
  "deprecated": null,
12932
13289
  "params": [],
12933
13290
  "returns": null,
@@ -14413,6 +14770,22 @@
14413
14770
  "defaultValue": null,
14414
14771
  "guides": []
14415
14772
  },
14773
+ {
14774
+ "id": "LeaderboardEntry.avatars3d",
14775
+ "name": "avatars3d",
14776
+ "anchor": "leaderboardentry-avatars3d",
14777
+ "group": "leaderboards",
14778
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
14779
+ "documented": true,
14780
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
14781
+ "deprecated": null,
14782
+ "params": [],
14783
+ "returns": null,
14784
+ "throws": null,
14785
+ "example": null,
14786
+ "defaultValue": null,
14787
+ "guides": []
14788
+ },
14416
14789
  {
14417
14790
  "id": "LeaderboardEntry.badges",
14418
14791
  "name": "badges",
@@ -16224,6 +16597,22 @@
16224
16597
  "defaultValue": null,
16225
16598
  "guides": []
16226
16599
  },
16600
+ {
16601
+ "id": "PlayerProfile.avatars3d",
16602
+ "name": "avatars3d",
16603
+ "anchor": "playerprofile-avatars3d",
16604
+ "group": "profiles",
16605
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
16606
+ "documented": true,
16607
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
16608
+ "deprecated": null,
16609
+ "params": [],
16610
+ "returns": null,
16611
+ "throws": null,
16612
+ "example": null,
16613
+ "defaultValue": null,
16614
+ "guides": []
16615
+ },
16227
16616
  {
16228
16617
  "id": "PlayerProfile.badges",
16229
16618
  "name": "badges",
@@ -19285,6 +19674,22 @@
19285
19674
  "defaultValue": null,
19286
19675
  "guides": []
19287
19676
  },
19677
+ {
19678
+ "id": "RoomMember.avatars3d",
19679
+ "name": "avatars3d",
19680
+ "anchor": "roommember-avatars3d",
19681
+ "group": "rooms",
19682
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
19683
+ "documented": true,
19684
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
19685
+ "deprecated": null,
19686
+ "params": [],
19687
+ "returns": null,
19688
+ "throws": null,
19689
+ "example": null,
19690
+ "defaultValue": null,
19691
+ "guides": []
19692
+ },
19288
19693
  {
19289
19694
  "id": "RoomMember.badges",
19290
19695
  "name": "badges",
@@ -20431,6 +20836,22 @@
20431
20836
  "defaultValue": null,
20432
20837
  "guides": []
20433
20838
  },
20839
+ {
20840
+ "id": "ShowcaseFriendEntry.avatars3d",
20841
+ "name": "avatars3d",
20842
+ "anchor": "showcasefriendentry-avatars3d",
20843
+ "group": "showcase",
20844
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
20845
+ "documented": true,
20846
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-game.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
20847
+ "deprecated": null,
20848
+ "params": [],
20849
+ "returns": null,
20850
+ "throws": null,
20851
+ "example": null,
20852
+ "defaultValue": null,
20853
+ "guides": []
20854
+ },
20434
20855
  {
20435
20856
  "id": "ShowcaseFriendEntry.badges",
20436
20857
  "name": "badges",
@@ -30711,6 +31132,22 @@
30711
31132
  "defaultValue": null,
30712
31133
  "guides": []
30713
31134
  },
31135
+ {
31136
+ "id": "Friend.avatars3d",
31137
+ "name": "avatars3d",
31138
+ "anchor": "friend-avatars3d",
31139
+ "group": "friends",
31140
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
31141
+ "documented": true,
31142
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-function.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
31143
+ "deprecated": null,
31144
+ "params": [],
31145
+ "returns": null,
31146
+ "throws": null,
31147
+ "example": null,
31148
+ "defaultValue": null,
31149
+ "guides": []
31150
+ },
30714
31151
  {
30715
31152
  "id": "Friend.badges",
30716
31153
  "name": "badges",
@@ -31627,6 +32064,102 @@
31627
32064
  "defaultValue": null,
31628
32065
  "guides": []
31629
32066
  },
32067
+ {
32068
+ "id": "Avatar3dFamily",
32069
+ "name": "Avatar3dFamily",
32070
+ "anchor": "avatar3dfamily",
32071
+ "group": "identity",
32072
+ "signature": "type Avatar3dFamily = \"small_human\" | string & object",
32073
+ "documented": true,
32074
+ "summary": "A rig family — the skeleton contract a 3D avatar is built on, and what a\ngame declares it can animate. `small_human` is the first: a `root` node\nwith a `body` child, which holds `head`, `arm_l`, `arm_r`, `leg_l` and\n`leg_r`, and a `hand_socket` under `arm_r`.\n\nOpen-ended (`| (string & {})`) so a family the platform adds later does not\nbreak a `switch` you compiled against this version: keep a `default` arm and\nskip a family you do not draw.",
32075
+ "deprecated": null,
32076
+ "params": [],
32077
+ "returns": null,
32078
+ "throws": null,
32079
+ "example": null,
32080
+ "defaultValue": null,
32081
+ "guides": []
32082
+ },
32083
+ {
32084
+ "id": "WornAvatar3d",
32085
+ "name": "WornAvatar3d",
32086
+ "anchor": "wornavatar3d",
32087
+ "group": "identity",
32088
+ "signature": "interface WornAvatar3d",
32089
+ "documented": true,
32090
+ "summary": "One 3D avatar a player wears — at most one per [Avatar3dFamily](api-reference-function.md#avatar3dfamily).",
32091
+ "deprecated": null,
32092
+ "params": [],
32093
+ "returns": null,
32094
+ "throws": null,
32095
+ "example": null,
32096
+ "defaultValue": null,
32097
+ "guides": []
32098
+ },
32099
+ {
32100
+ "id": "WornAvatar3d.family",
32101
+ "name": "family",
32102
+ "anchor": "wornavatar3d-family",
32103
+ "group": "identity",
32104
+ "signature": "readonly family: Avatar3dFamily",
32105
+ "documented": true,
32106
+ "summary": "The rig family this avatar is built on.",
32107
+ "deprecated": null,
32108
+ "params": [],
32109
+ "returns": null,
32110
+ "throws": null,
32111
+ "example": null,
32112
+ "defaultValue": null,
32113
+ "guides": []
32114
+ },
32115
+ {
32116
+ "id": "WornAvatar3d.id",
32117
+ "name": "id",
32118
+ "anchor": "wornavatar3d-id",
32119
+ "group": "identity",
32120
+ "signature": "readonly id: string",
32121
+ "documented": true,
32122
+ "summary": "The avatar's item id — a stable key for the model, never something to\nshow as text.",
32123
+ "deprecated": null,
32124
+ "params": [],
32125
+ "returns": null,
32126
+ "throws": null,
32127
+ "example": null,
32128
+ "defaultValue": null,
32129
+ "guides": []
32130
+ },
32131
+ {
32132
+ "id": "WornAvatar3d.modelUrl",
32133
+ "name": "modelUrl",
32134
+ "anchor": "wornavatar3d-modelurl",
32135
+ "group": "identity",
32136
+ "signature": "readonly modelUrl: string",
32137
+ "documented": true,
32138
+ "summary": "The avatar's model, a binary glTF (`.glb`) on the family's rig. Pass it\nthrough [platformIconUrl](api-reference-function.md#platformiconurl) before loading it.",
32139
+ "deprecated": null,
32140
+ "params": [],
32141
+ "returns": null,
32142
+ "throws": null,
32143
+ "example": null,
32144
+ "defaultValue": null,
32145
+ "guides": []
32146
+ },
32147
+ {
32148
+ "id": "WornAvatar3d.iconUrl",
32149
+ "name": "iconUrl",
32150
+ "anchor": "wornavatar3d-iconurl",
32151
+ "group": "identity",
32152
+ "signature": "readonly iconUrl: string",
32153
+ "documented": true,
32154
+ "summary": "A rendered portrait of the avatar, for a roster or a menu. Pass it through\n[platformIconUrl](api-reference-function.md#platformiconurl) before putting it in an `<img src>`.",
32155
+ "deprecated": null,
32156
+ "params": [],
32157
+ "returns": null,
32158
+ "throws": null,
32159
+ "example": null,
32160
+ "defaultValue": null,
32161
+ "guides": []
32162
+ },
31630
32163
  {
31631
32164
  "id": "PlayerIdentity",
31632
32165
  "name": "PlayerIdentity",
@@ -31787,6 +32320,22 @@
31787
32320
  "defaultValue": null,
31788
32321
  "guides": []
31789
32322
  },
32323
+ {
32324
+ "id": "PlayerIdentity.avatars3d",
32325
+ "name": "avatars3d",
32326
+ "anchor": "playeridentity-avatars3d",
32327
+ "group": "identity",
32328
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
32329
+ "documented": true,
32330
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-function.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
32331
+ "deprecated": null,
32332
+ "params": [],
32333
+ "returns": null,
32334
+ "throws": null,
32335
+ "example": null,
32336
+ "defaultValue": null,
32337
+ "guides": []
32338
+ },
31790
32339
  {
31791
32340
  "id": "PlayerIdentity.badges",
31792
32341
  "name": "badges",
@@ -31810,7 +32359,7 @@
31810
32359
  "group": "identity",
31811
32360
  "signature": "function mapIdentity(row: unknown): PlayerIdentity",
31812
32361
  "documented": true,
31813
- "summary": "Map one row's identity fields, as the platform's API sends them, to\n[PlayerIdentity](api-reference-function.md#playeridentity).\n\nEvery surface this SDK names maps its rows through this already. You need it\nonly for a row you fetched yourself through the raw client — an async\nopponent, for instance:\n\n```js\nimport { mapIdentity, text } from '@indiegems/gem-web-sdk/sdk';\n\nconst { opponents } = await gem.client.get('/v1/async-opponents?opponent_type=ghost-lap');\nfor (const row of opponents) {\n const who = mapIdentity(row);\n label(who.displayName ? text.toPlain(who.displayName) : 'Anonymous player');\n}\n```\n\nReads `player_id`, `display_name`, `discriminator`, `display_tag`,\n`avatar_icon_id`, `avatar_icon_url`, `avatar_3d_id`, `avatar_3d_model_url`,\n`avatar_3d_icon_url` and `badges`, and nothing else from the row. Anything missing, empty or of the wrong type is absent in the result.",
32362
+ "summary": "Map one row's identity fields, as the platform's API sends them, to\n[PlayerIdentity](api-reference-function.md#playeridentity).\n\nEvery surface this SDK names maps its rows through this already. You need it\nonly for a row you fetched yourself through the raw client — an async\nopponent, for instance:\n\n```js\nimport { mapIdentity, text } from '@indiegems/gem-web-sdk/sdk';\n\nconst { opponents } = await gem.client.get('/v1/async-opponents?opponent_type=ghost-lap');\nfor (const row of opponents) {\n const who = mapIdentity(row);\n label(who.displayName ? text.toPlain(who.displayName) : 'Anonymous player');\n}\n```\n\nReads `player_id`, `display_name`, `discriminator`, `display_tag`,\n`avatar_icon_id`, `avatar_icon_url`, `avatar_3d_id`, `avatar_3d_model_url`,\n`avatar_3d_icon_url`, `avatars_3d` and `badges`, and nothing else from the row. Anything missing, empty or of the wrong type is absent in the result.",
31814
32363
  "deprecated": null,
31815
32364
  "params": [
31816
32365
  {
@@ -32137,6 +32686,22 @@
32137
32686
  "defaultValue": null,
32138
32687
  "guides": []
32139
32688
  },
32689
+ {
32690
+ "id": "LeaderboardEntry.avatars3d",
32691
+ "name": "avatars3d",
32692
+ "anchor": "leaderboardentry-avatars3d",
32693
+ "group": "leaderboards",
32694
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
32695
+ "documented": true,
32696
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-function.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
32697
+ "deprecated": null,
32698
+ "params": [],
32699
+ "returns": null,
32700
+ "throws": null,
32701
+ "example": null,
32702
+ "defaultValue": null,
32703
+ "guides": []
32704
+ },
32140
32705
  {
32141
32706
  "id": "LeaderboardEntry.badges",
32142
32707
  "name": "badges",
@@ -33939,6 +34504,22 @@
33939
34504
  "defaultValue": null,
33940
34505
  "guides": []
33941
34506
  },
34507
+ {
34508
+ "id": "PlayerProfile.avatars3d",
34509
+ "name": "avatars3d",
34510
+ "anchor": "playerprofile-avatars3d",
34511
+ "group": "profiles",
34512
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
34513
+ "documented": true,
34514
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-function.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
34515
+ "deprecated": null,
34516
+ "params": [],
34517
+ "returns": null,
34518
+ "throws": null,
34519
+ "example": null,
34520
+ "defaultValue": null,
34521
+ "guides": []
34522
+ },
33942
34523
  {
33943
34524
  "id": "PlayerProfile.badges",
33944
34525
  "name": "badges",
@@ -34398,6 +34979,22 @@
34398
34979
  "defaultValue": null,
34399
34980
  "guides": []
34400
34981
  },
34982
+ {
34983
+ "id": "ShowcaseFriendEntry.avatars3d",
34984
+ "name": "avatars3d",
34985
+ "anchor": "showcasefriendentry-avatars3d",
34986
+ "group": "showcase",
34987
+ "signature": "readonly avatars3d?: readonly WornAvatar3d[]",
34988
+ "documented": true,
34989
+ "summary": "Every 3D avatar the player wears, one per rig family, in the order the game\ndeclared its families. Absent or empty means none is worn — draw your\ndefault look.\n\n[PlayerIdentity.avatar3dId](api-reference-function.md#playeridentity-avatar3did) and its two URLs are one of these: the\navatar of the game's first declared family that has one. A game that\naccepts a single family can read either; one that accepts several picks by\n`family` here. Absent too where the platform does not send it yet — read\nthe flat fields then.",
34990
+ "deprecated": null,
34991
+ "params": [],
34992
+ "returns": null,
34993
+ "throws": null,
34994
+ "example": null,
34995
+ "defaultValue": null,
34996
+ "guides": []
34997
+ },
34401
34998
  {
34402
34999
  "id": "ShowcaseFriendEntry.badges",
34403
35000
  "name": "badges",