@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.
- package/CHANGELOG.md +23 -0
- package/dist/function.d.ts +9 -1
- package/dist/game.d.ts +39 -13
- 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-api.js +1 -1
- package/dist/gem-web-sdk.server.js +2 -2
- package/dist/index.d.ts +36 -14
- package/dist/sdk.d.ts +49 -14
- package/dist/server-api.d.ts +9 -1
- package/dist/server.d.ts +10 -1
- package/docs/api/api-reference-function.md +162 -2
- package/docs/api/api-reference-game.md +529 -163
- package/docs/api/api-reference-server.md +1 -1
- package/docs/api/reference.json +773 -176
- package/docs/arcade-avatars.md +303 -0
- package/docs/changelog.md +23 -0
- package/docs/getting-started.md +1 -1
- package/docs/manifest.json +15 -10
- package/docs/saving-player-progress.md +16 -10
- package/docs/server-bundles.md +2 -2
- package/docs/what-a-game-must-do.md +53 -46
- package/package.json +1 -1
- package/schema/bridge.v1.json +198 -18
- package/schema/capabilities.public.json +6 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# The browser surface — API reference
|
|
2
2
|
|
|
3
|
-
Generated from `@indiegems/gem-web-sdk/sdk`
|
|
3
|
+
Generated from `@indiegems/gem-web-sdk/sdk` v13.0.0, the version of the package this file ships in. Every symbol below carries an explicit anchor, its `anchor` in `reference.json` beside this file.
|
|
4
4
|
|
|
5
5
|
`@indiegems/gem-web-sdk/sdk` is the one import a game writes, wherever its code runs. `@indiegems/gem-web-sdk/game` exports exactly the symbols on this page and nothing else, for a build that wants only this half.
|
|
6
6
|
|
|
@@ -3669,6 +3669,262 @@ The runtime companion to [PlatformEventName](#platformeventname), for a game tha
|
|
|
3669
3669
|
show what it gets for free, or to check a funnel step it authored against
|
|
3670
3670
|
what the platform actually emits.
|
|
3671
3671
|
|
|
3672
|
+
<a id="avatar3dprice"></a>
|
|
3673
|
+
|
|
3674
|
+
## Avatar3dPrice
|
|
3675
|
+
|
|
3676
|
+
```ts
|
|
3677
|
+
interface Avatar3dPrice
|
|
3678
|
+
```
|
|
3679
|
+
|
|
3680
|
+
One price of a 3D avatar on sale, in one platform currency.
|
|
3681
|
+
|
|
3682
|
+
<a id="avatar3dprice-currencyslug"></a>
|
|
3683
|
+
|
|
3684
|
+
## Avatar3dPrice.currencySlug
|
|
3685
|
+
|
|
3686
|
+
```ts
|
|
3687
|
+
readonly currencySlug: string
|
|
3688
|
+
```
|
|
3689
|
+
|
|
3690
|
+
`GEMS` or `ARCADE_TICKETS` — what to pass as `currencySlug`.
|
|
3691
|
+
|
|
3692
|
+
<a id="avatar3dprice-price"></a>
|
|
3693
|
+
|
|
3694
|
+
## Avatar3dPrice.price
|
|
3695
|
+
|
|
3696
|
+
```ts
|
|
3697
|
+
readonly price: number
|
|
3698
|
+
```
|
|
3699
|
+
|
|
3700
|
+
The price, a whole number of that currency.
|
|
3701
|
+
|
|
3702
|
+
<a id="avatar3doffer"></a>
|
|
3703
|
+
|
|
3704
|
+
## Avatar3dOffer
|
|
3705
|
+
|
|
3706
|
+
```ts
|
|
3707
|
+
interface Avatar3dOffer
|
|
3708
|
+
```
|
|
3709
|
+
|
|
3710
|
+
One of your game's 3D avatars on sale, as the platform prices it.
|
|
3711
|
+
|
|
3712
|
+
<a id="avatar3doffer-itemid"></a>
|
|
3713
|
+
|
|
3714
|
+
## Avatar3dOffer.itemId
|
|
3715
|
+
|
|
3716
|
+
```ts
|
|
3717
|
+
readonly itemId: string
|
|
3718
|
+
```
|
|
3719
|
+
|
|
3720
|
+
The avatar's item id — what [Avatars3dApi.purchase](#avatars3dapi-purchase) takes.
|
|
3721
|
+
|
|
3722
|
+
<a id="avatar3doffer-slug"></a>
|
|
3723
|
+
|
|
3724
|
+
## Avatar3dOffer.slug
|
|
3725
|
+
|
|
3726
|
+
```ts
|
|
3727
|
+
readonly slug: string
|
|
3728
|
+
```
|
|
3729
|
+
|
|
3730
|
+
The slug you uploaded it under. A key, not display text.
|
|
3731
|
+
|
|
3732
|
+
<a id="avatar3doffer-family"></a>
|
|
3733
|
+
|
|
3734
|
+
## Avatar3dOffer.family
|
|
3735
|
+
|
|
3736
|
+
```ts
|
|
3737
|
+
readonly family: Avatar3dFamily
|
|
3738
|
+
```
|
|
3739
|
+
|
|
3740
|
+
The rig family the model is built on.
|
|
3741
|
+
|
|
3742
|
+
<a id="avatar3doffer-displayname"></a>
|
|
3743
|
+
|
|
3744
|
+
## Avatar3dOffer.displayName
|
|
3745
|
+
|
|
3746
|
+
```ts
|
|
3747
|
+
readonly displayName: string
|
|
3748
|
+
```
|
|
3749
|
+
|
|
3750
|
+
The avatar's name, as you uploaded it.
|
|
3751
|
+
|
|
3752
|
+
<a id="avatar3doffer-description"></a>
|
|
3753
|
+
|
|
3754
|
+
## Avatar3dOffer.description
|
|
3755
|
+
|
|
3756
|
+
```ts
|
|
3757
|
+
readonly description?: string
|
|
3758
|
+
```
|
|
3759
|
+
|
|
3760
|
+
The avatar's description, when it has one.
|
|
3761
|
+
|
|
3762
|
+
<a id="avatar3doffer-iconurl"></a>
|
|
3763
|
+
|
|
3764
|
+
## Avatar3dOffer.iconUrl
|
|
3765
|
+
|
|
3766
|
+
```ts
|
|
3767
|
+
readonly iconUrl: string
|
|
3768
|
+
```
|
|
3769
|
+
|
|
3770
|
+
A rendered portrait of the avatar, for a shop tile. Pass it through
|
|
3771
|
+
`platformIconUrl` before putting it in an `<img src>`.
|
|
3772
|
+
|
|
3773
|
+
<a id="avatar3doffer-modelurl"></a>
|
|
3774
|
+
|
|
3775
|
+
## Avatar3dOffer.modelUrl
|
|
3776
|
+
|
|
3777
|
+
```ts
|
|
3778
|
+
readonly modelUrl: string
|
|
3779
|
+
```
|
|
3780
|
+
|
|
3781
|
+
The avatar's model, for a preview. Pass it through `platformIconUrl`
|
|
3782
|
+
before loading it.
|
|
3783
|
+
|
|
3784
|
+
<a id="avatar3doffer-prices"></a>
|
|
3785
|
+
|
|
3786
|
+
## Avatar3dOffer.prices
|
|
3787
|
+
|
|
3788
|
+
```ts
|
|
3789
|
+
readonly prices: readonly Avatar3dPrice[]
|
|
3790
|
+
```
|
|
3791
|
+
|
|
3792
|
+
One or two prices, each in a currency a player can pay with. Show each
|
|
3793
|
+
one the player can choose; pass the chosen one's `currencySlug` to
|
|
3794
|
+
[Avatars3dApi.purchase](#avatars3dapi-purchase).
|
|
3795
|
+
|
|
3796
|
+
<a id="avatars3dapi"></a>
|
|
3797
|
+
|
|
3798
|
+
## Avatars3dApi
|
|
3799
|
+
|
|
3800
|
+
```ts
|
|
3801
|
+
interface Avatars3dApi
|
|
3802
|
+
```
|
|
3803
|
+
|
|
3804
|
+
Arcade 3D avatars — `gem.avatars3d`. What the player wears, the rig families
|
|
3805
|
+
your game accepts, the avatars you sell, and buying one.
|
|
3806
|
+
|
|
3807
|
+
```js
|
|
3808
|
+
function draw() {
|
|
3809
|
+
const worn = gem.avatars3d.worn();
|
|
3810
|
+
if (worn) loadCharacter(platformIconUrl(worn.modelUrl));
|
|
3811
|
+
else useDefaultCharacter();
|
|
3812
|
+
}
|
|
3813
|
+
draw();
|
|
3814
|
+
gem.avatars3d.onChange(draw);
|
|
3815
|
+
```
|
|
3816
|
+
|
|
3817
|
+
The guide is `docs/arcade-avatars.md`: the rig, the config, uploading and
|
|
3818
|
+
selling with gemctl, and loading the model at runtime.
|
|
3819
|
+
|
|
3820
|
+
<a id="avatars3dapi-families"></a>
|
|
3821
|
+
|
|
3822
|
+
## Avatars3dApi.families
|
|
3823
|
+
|
|
3824
|
+
```ts
|
|
3825
|
+
families(): Promise<readonly Avatar3dFamily[]>
|
|
3826
|
+
```
|
|
3827
|
+
|
|
3828
|
+
The rig families your game accepts, in the order your published config
|
|
3829
|
+
declares them (`avatars_3d.families`) — the first is your default.
|
|
3830
|
+
|
|
3831
|
+
Resolves `[]` when your game has no published config on this channel, or
|
|
3832
|
+
its config declares no families. Loads the config through
|
|
3833
|
+
`gem.gameConfig.load()` and shares that one fetch; rejects when the config
|
|
3834
|
+
cannot be loaded, as `load()` does.
|
|
3835
|
+
|
|
3836
|
+
<a id="avatars3dapi-worn"></a>
|
|
3837
|
+
|
|
3838
|
+
## Avatars3dApi.worn
|
|
3839
|
+
|
|
3840
|
+
```ts
|
|
3841
|
+
worn(family?: Avatar3dFamily): WornAvatar3d | undefined
|
|
3842
|
+
```
|
|
3843
|
+
|
|
3844
|
+
The 3D avatar the player wears — in `family` when you name one, otherwise
|
|
3845
|
+
in your game's first declared family that has one. `undefined` when none
|
|
3846
|
+
is worn there, before the player arrives, and outside the Arcade: draw
|
|
3847
|
+
your default look.
|
|
3848
|
+
|
|
3849
|
+
Read live from [Gem.player](#gem-player), so it changes when the player wears
|
|
3850
|
+
another avatar or buys one; [Avatars3dApi.onChange](#avatars3dapi-onchange) tells you when.
|
|
3851
|
+
|
|
3852
|
+
| Parameter | Type |
|
|
3853
|
+
| --- | --- |
|
|
3854
|
+
| `family` | `Avatar3dFamily` |
|
|
3855
|
+
|
|
3856
|
+
<a id="avatars3dapi-offers"></a>
|
|
3857
|
+
|
|
3858
|
+
## Avatars3dApi.offers
|
|
3859
|
+
|
|
3860
|
+
```ts
|
|
3861
|
+
offers(): Promise<readonly Avatar3dOffer[]>
|
|
3862
|
+
```
|
|
3863
|
+
|
|
3864
|
+
Your game's 3D avatars on sale on this channel, with their prices, read
|
|
3865
|
+
from the platform with your game's own token. Render your shop from this,
|
|
3866
|
+
never from prices of your own.
|
|
3867
|
+
|
|
3868
|
+
Resolves `[]` when you have nothing on sale. Rejects with a `GemApiError`
|
|
3869
|
+
when the platform cannot be read — a shop that cannot load says so.
|
|
3870
|
+
|
|
3871
|
+
<a id="avatars3dapi-purchase"></a>
|
|
3872
|
+
|
|
3873
|
+
## Avatars3dApi.purchase
|
|
3874
|
+
|
|
3875
|
+
```ts
|
|
3876
|
+
purchase(request: Avatar3dPurchaseRequest): Promise<PurchaseOutcome>
|
|
3877
|
+
```
|
|
3878
|
+
|
|
3879
|
+
Buy one of your 3D avatars for the player and put it on. The Arcade
|
|
3880
|
+
prices it again, shows its own confirmation sheet, takes the currency and
|
|
3881
|
+
wears the avatar; your game then receives the player again, with the new
|
|
3882
|
+
avatar worn, and [Avatars3dApi.onChange](#avatars3dapi-onchange) runs.
|
|
3883
|
+
|
|
3884
|
+
Call it from the player's own click or key press — the Arcade shows no
|
|
3885
|
+
sheet without one and answers `declined` / `busy`.
|
|
3886
|
+
|
|
3887
|
+
NOTHING REJECTS. Every outcome resolves, exactly as on
|
|
3888
|
+
`gem.purchases.request`:
|
|
3889
|
+
|
|
3890
|
+
- `ok` — bought and worn, and the platform confirmed the player owns it.
|
|
3891
|
+
- `declined` — definitely not bought. `reason` says why:
|
|
3892
|
+
`already_owned` (the player has it — offer to wear it with
|
|
3893
|
+
`gem.openAvatarPicker({ tab: '3d' })`), `cancelled`, `insufficient_funds`,
|
|
3894
|
+
`no_price_in_currency`, `not_purchasable` (not on sale, or not an id from
|
|
3895
|
+
[Avatars3dApi.offers](#avatars3dapi-offers)), `busy`, and the rest of
|
|
3896
|
+
`PurchaseRefusalCode`. Return to your shop silently.
|
|
3897
|
+
- `unavailable` — no reliable answer. Show one neutral message, never
|
|
3898
|
+
retry on your own, and read [Avatars3dApi.worn](#avatars3dapi-worn) again.
|
|
3899
|
+
|
|
3900
|
+
**Outside the Arcade** — opened at the top level of a page with no Arcade
|
|
3901
|
+
around it, or under an Arcade that does not sell 3D avatars yet — it
|
|
3902
|
+
resolves `unavailable` with reason `error` at once and asks nobody.
|
|
3903
|
+
|
|
3904
|
+
| Parameter | Type |
|
|
3905
|
+
| --- | --- |
|
|
3906
|
+
| `request` | `Avatar3dPurchaseRequest` |
|
|
3907
|
+
|
|
3908
|
+
<a id="avatars3dapi-onchange"></a>
|
|
3909
|
+
|
|
3910
|
+
## Avatars3dApi.onChange
|
|
3911
|
+
|
|
3912
|
+
```ts
|
|
3913
|
+
onChange(listener: (worn: readonly WornAvatar3d[]) => void): () => void
|
|
3914
|
+
```
|
|
3915
|
+
|
|
3916
|
+
Call `listener` whenever the avatars the player wears change — they wore
|
|
3917
|
+
another one in the Arcade's picker, bought one, or their avatars arrived
|
|
3918
|
+
for the first time — with every worn avatar, one per family. Not called
|
|
3919
|
+
for the avatars already worn when you subscribe: read
|
|
3920
|
+
[Avatars3dApi.worn](#avatars3dapi-worn) for those.
|
|
3921
|
+
|
|
3922
|
+
| Parameter | Type |
|
|
3923
|
+
| --- | --- |
|
|
3924
|
+
| `listener` | `(worn: readonly WornAvatar3d[]) => void` |
|
|
3925
|
+
|
|
3926
|
+
**Returns.** a function that stops the calls. Calling it twice is harmless.
|
|
3927
|
+
|
|
3672
3928
|
<a id="gemapierror"></a>
|
|
3673
3929
|
|
|
3674
3930
|
## GemApiError
|
|
@@ -4851,50 +5107,11 @@ a key you leave out is unchanged.
|
|
|
4851
5107
|
interface ControlsApi
|
|
4852
5108
|
```
|
|
4853
5109
|
|
|
4854
|
-
`gem.controls` — the player's camera and controls settings:
|
|
4855
|
-
|
|
4856
|
-
turn at the player's sensitivity.
|
|
5110
|
+
`gem.controls` — the player's camera and controls settings: turn look input
|
|
5111
|
+
into a camera turn at the player's sensitivity.
|
|
4857
5112
|
|
|
4858
5113
|
**Guides.** [what-a-game-must-do](../what-a-game-must-do.md)
|
|
4859
5114
|
|
|
4860
|
-
<a id="controlsapi-declare"></a>
|
|
4861
|
-
|
|
4862
|
-
## ControlsApi.declare
|
|
4863
|
-
|
|
4864
|
-
```ts
|
|
4865
|
-
declare(uses: GemControlsDeclaration): boolean
|
|
4866
|
-
```
|
|
4867
|
-
|
|
4868
|
-
Tell the Arcade which of the player's camera and controls settings your
|
|
4869
|
-
game uses, and for each one who decides it: `'player'` — you apply the
|
|
4870
|
-
Arcade's value from `gem.prefs.recommended` (or the player's own choice
|
|
4871
|
-
in your settings, which wins) — or `'game'` — you decide it yourself, and
|
|
4872
|
-
the Arcade's Settings shows that row as "Set by this game". Leave out a
|
|
4873
|
-
setting your game does not use.
|
|
4874
|
-
|
|
4875
|
-
```js
|
|
4876
|
-
gem.controls.declare({
|
|
4877
|
-
lookSensitivity: 'player',
|
|
4878
|
-
gamepadLookSensitivity: 'player',
|
|
4879
|
-
invertLookY: 'player',
|
|
4880
|
-
touchMovement: 'game', // this game always uses a fixed stick
|
|
4881
|
-
});
|
|
4882
|
-
```
|
|
4883
|
-
|
|
4884
|
-
**It is screen state, not a toggle.** Each call is the whole answer and
|
|
4885
|
-
replaces the last. The SDK holds it and sends it again after every
|
|
4886
|
-
reconnect, so call it once at boot, and again only when the answer
|
|
4887
|
-
changes. Any other key or value is dropped.
|
|
4888
|
-
|
|
4889
|
-
| Parameter | Type |
|
|
4890
|
-
| --- | --- |
|
|
4891
|
-
| `uses` | `GemControlsDeclaration` |
|
|
4892
|
-
|
|
4893
|
-
**Returns.** `true` when the Arcade was told now; `false` before the bridge is
|
|
4894
|
-
up (the declaration is held and sent when it connects), outside the
|
|
4895
|
-
Arcade, and under an embedder that does not show these settings. Never
|
|
4896
|
-
throws.
|
|
4897
|
-
|
|
4898
5115
|
<a id="controlsapi-look"></a>
|
|
4899
5116
|
|
|
4900
5117
|
## ControlsApi.look
|
|
@@ -4937,72 +5154,6 @@ readonly LOOK_BASE: GemLookBase
|
|
|
4937
5154
|
|
|
4938
5155
|
The base speeds `look` applies at 1×, in radians. Frozen.
|
|
4939
5156
|
|
|
4940
|
-
<a id="gemcontrolchoice"></a>
|
|
4941
|
-
|
|
4942
|
-
## GemControlChoice
|
|
4943
|
-
|
|
4944
|
-
```ts
|
|
4945
|
-
type GemControlChoice = "player" | "game"
|
|
4946
|
-
```
|
|
4947
|
-
|
|
4948
|
-
Who decides one camera or controls setting: `'player'` — your game applies
|
|
4949
|
-
the Arcade's value, or the player's own in-game choice, which wins — or
|
|
4950
|
-
`'game'` — your game decides it itself, and the Arcade's Settings shows the
|
|
4951
|
-
row as "Set by this game".
|
|
4952
|
-
|
|
4953
|
-
<a id="gemcontrolsdeclaration"></a>
|
|
4954
|
-
|
|
4955
|
-
## GemControlsDeclaration
|
|
4956
|
-
|
|
4957
|
-
```ts
|
|
4958
|
-
interface GemControlsDeclaration
|
|
4959
|
-
```
|
|
4960
|
-
|
|
4961
|
-
What `gem.controls.declare` takes: for each of the player's camera and
|
|
4962
|
-
controls settings your game uses, who decides it. Leave a setting out when
|
|
4963
|
-
your game does not use it at all — the Arcade then says "This game doesn't
|
|
4964
|
-
use this" beside that row.
|
|
4965
|
-
|
|
4966
|
-
<a id="gemcontrolsdeclaration-looksensitivity"></a>
|
|
4967
|
-
|
|
4968
|
-
## GemControlsDeclaration.lookSensitivity
|
|
4969
|
-
|
|
4970
|
-
```ts
|
|
4971
|
-
readonly lookSensitivity?: GemControlChoice
|
|
4972
|
-
```
|
|
4973
|
-
|
|
4974
|
-
Look speed for touch, mouse and pen — `gem.prefs.recommended.lookSensitivity`.
|
|
4975
|
-
|
|
4976
|
-
<a id="gemcontrolsdeclaration-gamepadlooksensitivity"></a>
|
|
4977
|
-
|
|
4978
|
-
## GemControlsDeclaration.gamepadLookSensitivity
|
|
4979
|
-
|
|
4980
|
-
```ts
|
|
4981
|
-
readonly gamepadLookSensitivity?: GemControlChoice
|
|
4982
|
-
```
|
|
4983
|
-
|
|
4984
|
-
Look speed for a gamepad stick — `gem.prefs.recommended.gamepadLookSensitivity`.
|
|
4985
|
-
|
|
4986
|
-
<a id="gemcontrolsdeclaration-invertlooky"></a>
|
|
4987
|
-
|
|
4988
|
-
## GemControlsDeclaration.invertLookY
|
|
4989
|
-
|
|
4990
|
-
```ts
|
|
4991
|
-
readonly invertLookY?: GemControlChoice
|
|
4992
|
-
```
|
|
4993
|
-
|
|
4994
|
-
Inverted up/down look — `gem.prefs.recommended.invertLookY`.
|
|
4995
|
-
|
|
4996
|
-
<a id="gemcontrolsdeclaration-touchmovement"></a>
|
|
4997
|
-
|
|
4998
|
-
## GemControlsDeclaration.touchMovement
|
|
4999
|
-
|
|
5000
|
-
```ts
|
|
5001
|
-
readonly touchMovement?: GemControlChoice
|
|
5002
|
-
```
|
|
5003
|
-
|
|
5004
|
-
The touch movement thumbstick, floating or fixed — `gem.prefs.recommended.touchMovement`.
|
|
5005
|
-
|
|
5006
5157
|
<a id="device_settings_max_bytes"></a>
|
|
5007
5158
|
|
|
5008
5159
|
## DEVICE_SETTINGS_MAX_BYTES
|
|
@@ -5563,41 +5714,17 @@ wallet.
|
|
|
5563
5714
|
**Returns.** whether the message left the frame — `false` before the bridge is
|
|
5564
5715
|
up, for an arcade with no wallet, and past this frame's own limit.
|
|
5565
5716
|
|
|
5566
|
-
<a id="gemframe-declarecontrols"></a>
|
|
5567
|
-
|
|
5568
|
-
## GemFrame.declareControls
|
|
5569
|
-
|
|
5570
|
-
```ts
|
|
5571
|
-
declareControls(uses: GemControlsDeclaration): boolean
|
|
5572
|
-
```
|
|
5573
|
-
|
|
5574
|
-
Tell the arcade which of the player's camera and controls settings your
|
|
5575
|
-
game leaves to the player and which it decides itself — the whole answer
|
|
5576
|
-
each time, the latest one winning.
|
|
5577
|
-
|
|
5578
|
-
Prefer `gem.controls.declare`, which holds the declaration across a
|
|
5579
|
-
reconnect. This one sends only while connected, and only to an arcade
|
|
5580
|
-
that shows these settings.
|
|
5581
|
-
|
|
5582
|
-
| Parameter | Type |
|
|
5583
|
-
| --- | --- |
|
|
5584
|
-
| `uses` | `GemControlsDeclaration` |
|
|
5585
|
-
|
|
5586
|
-
**Returns.** whether the message left the frame — `false` before the bridge is
|
|
5587
|
-
up, for an arcade that does not show these settings, and past this
|
|
5588
|
-
frame's own limit.
|
|
5589
|
-
|
|
5590
5717
|
<a id="gemframe-setmenu"></a>
|
|
5591
5718
|
|
|
5592
5719
|
## GemFrame.setMenu
|
|
5593
5720
|
|
|
5594
5721
|
```ts
|
|
5595
|
-
setMenu(
|
|
5722
|
+
setMenu(items?: readonly { id: string; label: string }[]): boolean
|
|
5596
5723
|
```
|
|
5597
5724
|
|
|
5598
|
-
Tell the arcade what your game adds to its menu:
|
|
5599
|
-
|
|
5600
|
-
|
|
5725
|
+
Tell the arcade what your game adds to its menu: up to four entries of its
|
|
5726
|
+
own — the whole answer each time, the latest one winning. Your settings
|
|
5727
|
+
are not among them: a game opens its settings from its own button.
|
|
5601
5728
|
|
|
5602
5729
|
Prefer `gem.setMenu`, which holds the state across a reconnect and calls
|
|
5603
5730
|
your handlers when the player chooses an entry. This one sends only while
|
|
@@ -5605,7 +5732,6 @@ connected, and only to an arcade that draws a menu.
|
|
|
5605
5732
|
|
|
5606
5733
|
| Parameter | Type |
|
|
5607
5734
|
| --- | --- |
|
|
5608
|
-
| `settings` | `boolean` |
|
|
5609
5735
|
| `items` | `readonly { id: string; label: string }[]` |
|
|
5610
5736
|
|
|
5611
5737
|
**Returns.** whether the message left the frame — `false` before the bridge is
|
|
@@ -5860,6 +5986,24 @@ loading the model is too much. Not the 2D portrait
|
|
|
5860
5986
|
([PlayerIdentity.avatarIconUrl](#playeridentity-avatariconurl)), which is a separate choice. Pass it
|
|
5861
5987
|
through [platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
5862
5988
|
|
|
5989
|
+
<a id="friend-avatars3d"></a>
|
|
5990
|
+
|
|
5991
|
+
## Friend.avatars3d
|
|
5992
|
+
|
|
5993
|
+
```ts
|
|
5994
|
+
readonly avatars3d?: readonly WornAvatar3d[]
|
|
5995
|
+
```
|
|
5996
|
+
|
|
5997
|
+
Every 3D avatar the player wears, one per rig family, in the order the game
|
|
5998
|
+
declared its families. Absent or empty means none is worn — draw your
|
|
5999
|
+
default look.
|
|
6000
|
+
|
|
6001
|
+
[PlayerIdentity.avatar3dId](#playeridentity-avatar3did) and its two URLs are one of these: the
|
|
6002
|
+
avatar of the game's first declared family that has one. A game that
|
|
6003
|
+
accepts a single family can read either; one that accepts several picks by
|
|
6004
|
+
`family` here. Absent too where the platform does not send it yet — read
|
|
6005
|
+
the flat fields then.
|
|
6006
|
+
|
|
5863
6007
|
<a id="friend-badges"></a>
|
|
5864
6008
|
|
|
5865
6009
|
## Friend.badges
|
|
@@ -7721,6 +7865,24 @@ loading the model is too much. Not the 2D portrait
|
|
|
7721
7865
|
([PlayerIdentity.avatarIconUrl](#playeridentity-avatariconurl)), which is a separate choice. Pass it
|
|
7722
7866
|
through [platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
7723
7867
|
|
|
7868
|
+
<a id="gemplayer-avatars3d"></a>
|
|
7869
|
+
|
|
7870
|
+
## GemPlayer.avatars3d
|
|
7871
|
+
|
|
7872
|
+
```ts
|
|
7873
|
+
readonly avatars3d?: readonly WornAvatar3d[]
|
|
7874
|
+
```
|
|
7875
|
+
|
|
7876
|
+
Every 3D avatar the player wears, one per rig family, in the order the game
|
|
7877
|
+
declared its families. Absent or empty means none is worn — draw your
|
|
7878
|
+
default look.
|
|
7879
|
+
|
|
7880
|
+
[PlayerIdentity.avatar3dId](#playeridentity-avatar3did) and its two URLs are one of these: the
|
|
7881
|
+
avatar of the game's first declared family that has one. A game that
|
|
7882
|
+
accepts a single family can read either; one that accepts several picks by
|
|
7883
|
+
`family` here. Absent too where the platform does not send it yet — read
|
|
7884
|
+
the flat fields then.
|
|
7885
|
+
|
|
7724
7886
|
<a id="gemplayer-badges"></a>
|
|
7725
7887
|
|
|
7726
7888
|
## GemPlayer.badges
|
|
@@ -8279,20 +8441,8 @@ and your game is running again by the time this fires.
|
|
|
8279
8441
|
interface GameMenu
|
|
8280
8442
|
```
|
|
8281
8443
|
|
|
8282
|
-
What your game adds to the arcade's menu — see `gem.setMenu`.
|
|
8283
|
-
|
|
8284
|
-
<a id="gamemenu-onsettings"></a>
|
|
8285
|
-
|
|
8286
|
-
## GameMenu.onSettings
|
|
8287
|
-
|
|
8288
|
-
```ts
|
|
8289
|
-
readonly onSettings?: () => void
|
|
8290
|
-
```
|
|
8291
|
-
|
|
8292
|
-
Open your settings screen. Another embedder may show a Settings entry that
|
|
8293
|
-
calls it, but **the Gem Arcade no longer does**: draw a settings button of
|
|
8294
|
-
your own, always (top-right is the recommendation). Passing this is still
|
|
8295
|
-
accepted and harmless.
|
|
8444
|
+
What your game adds to the arcade's menu — see `gem.setMenu`. Your settings
|
|
8445
|
+
are not part of it: they open from your own settings button.
|
|
8296
8446
|
|
|
8297
8447
|
<a id="gamemenu-items"></a>
|
|
8298
8448
|
|
|
@@ -8830,6 +8980,32 @@ client sends.
|
|
|
8830
8980
|
up, under an embedder with no avatar picker, or past six asks a minute.
|
|
8831
8981
|
It never throws and never waits.
|
|
8832
8982
|
|
|
8983
|
+
<a id="gem-avatars3d"></a>
|
|
8984
|
+
|
|
8985
|
+
## Gem.avatars3d
|
|
8986
|
+
|
|
8987
|
+
```ts
|
|
8988
|
+
readonly avatars3d: Avatars3dApi
|
|
8989
|
+
```
|
|
8990
|
+
|
|
8991
|
+
Arcade 3D avatars: what the player wears, the rig families your game
|
|
8992
|
+
accepts, the avatars your game sells, and buying one — which wears it.
|
|
8993
|
+
2D portraits are a separate system and are not here.
|
|
8994
|
+
|
|
8995
|
+
```js
|
|
8996
|
+
const draw = () => {
|
|
8997
|
+
const worn = gem.avatars3d.worn();
|
|
8998
|
+
const url = worn && platformIconUrl(worn.modelUrl);
|
|
8999
|
+
if (url) loadCharacter(url);
|
|
9000
|
+
else useDefaultCharacter();
|
|
9001
|
+
};
|
|
9002
|
+
draw();
|
|
9003
|
+
gem.avatars3d.onChange(draw);
|
|
9004
|
+
```
|
|
9005
|
+
|
|
9006
|
+
See [Arcade avatars](../arcade-avatars.md) for the rig, the config, and
|
|
9007
|
+
uploading and selling avatars with gemctl.
|
|
9008
|
+
|
|
8833
9009
|
<a id="gem-vibration"></a>
|
|
8834
9010
|
|
|
8835
9011
|
## Gem.vibration
|
|
@@ -8874,13 +9050,10 @@ readonly controls: ControlsApi
|
|
|
8874
9050
|
|
|
8875
9051
|
The player's camera and controls settings — look sensitivity, gamepad look
|
|
8876
9052
|
sensitivity, invert look and touch movement, which arrive in
|
|
8877
|
-
`gem.prefs.recommended
|
|
9053
|
+
`gem.prefs.recommended` as the player's Arcade-wide defaults. Turn look
|
|
9054
|
+
input into a camera turn at the player's sensitivity:
|
|
8878
9055
|
|
|
8879
9056
|
```js
|
|
8880
|
-
// 1. Tell the Arcade which ones your game leaves to the player.
|
|
8881
|
-
gem.controls.declare({ lookSensitivity: 'player', invertLookY: 'player' });
|
|
8882
|
-
|
|
8883
|
-
// 2. Turn look input into a camera turn at the player's sensitivity.
|
|
8884
9057
|
const look = gem.controls.look();
|
|
8885
9058
|
// dx, dy: the pointer's move in CSS px since the last event.
|
|
8886
9059
|
const t = look.fromPointer(dx, dy, e.pointerType, camera.pitch);
|
|
@@ -9359,10 +9532,8 @@ gem.setMenu({
|
|
|
9359
9532
|
```
|
|
9360
9533
|
|
|
9361
9534
|
**Your settings are not in it: draw your own settings button, always** —
|
|
9362
|
-
top-right
|
|
9363
|
-
|
|
9364
|
-
shows a Settings entry, so a game whose settings open only from there has
|
|
9365
|
-
none a player can reach.
|
|
9535
|
+
top-right, clear of `gem.viewport.reserved`. The Arcade's menu never opens
|
|
9536
|
+
your settings screen.
|
|
9366
9537
|
|
|
9367
9538
|
**It is screen state, not a toggle.** Each call is the whole answer and
|
|
9368
9539
|
replaces the last, so call it again when the entries change — a new
|
|
@@ -9376,7 +9547,7 @@ should not assume the game was paused by you.
|
|
|
9376
9547
|
|
|
9377
9548
|
| Parameter | Type | Description |
|
|
9378
9549
|
| --- | --- | --- |
|
|
9379
|
-
| `menu` | `GameMenu` | Your
|
|
9550
|
+
| `menu` | `GameMenu` | Your own entries. |
|
|
9380
9551
|
|
|
9381
9552
|
**Returns.** `true` when the arcade was told now; `false` before the bridge is
|
|
9382
9553
|
up (the menu is held and sent when it connects) and under an embedder
|
|
@@ -10368,6 +10539,76 @@ readonly iconUrl: string
|
|
|
10368
10539
|
The badge's thumbnail. Pass it through [platformIconUrl](#platformiconurl) before
|
|
10369
10540
|
putting it in an `<img src>`.
|
|
10370
10541
|
|
|
10542
|
+
<a id="avatar3dfamily"></a>
|
|
10543
|
+
|
|
10544
|
+
## Avatar3dFamily
|
|
10545
|
+
|
|
10546
|
+
```ts
|
|
10547
|
+
type Avatar3dFamily = "small_human" | string & object
|
|
10548
|
+
```
|
|
10549
|
+
|
|
10550
|
+
A rig family — the skeleton contract a 3D avatar is built on, and what a
|
|
10551
|
+
game declares it can animate. `small_human` is the first: a `root` node
|
|
10552
|
+
with a `body` child, which holds `head`, `arm_l`, `arm_r`, `leg_l` and
|
|
10553
|
+
`leg_r`, and a `hand_socket` under `arm_r`.
|
|
10554
|
+
|
|
10555
|
+
Open-ended (`| (string & {})`) so a family the platform adds later does not
|
|
10556
|
+
break a `switch` you compiled against this version: keep a `default` arm and
|
|
10557
|
+
skip a family you do not draw.
|
|
10558
|
+
|
|
10559
|
+
<a id="wornavatar3d"></a>
|
|
10560
|
+
|
|
10561
|
+
## WornAvatar3d
|
|
10562
|
+
|
|
10563
|
+
```ts
|
|
10564
|
+
interface WornAvatar3d
|
|
10565
|
+
```
|
|
10566
|
+
|
|
10567
|
+
One 3D avatar a player wears — at most one per [Avatar3dFamily](#avatar3dfamily).
|
|
10568
|
+
|
|
10569
|
+
<a id="wornavatar3d-family"></a>
|
|
10570
|
+
|
|
10571
|
+
## WornAvatar3d.family
|
|
10572
|
+
|
|
10573
|
+
```ts
|
|
10574
|
+
readonly family: Avatar3dFamily
|
|
10575
|
+
```
|
|
10576
|
+
|
|
10577
|
+
The rig family this avatar is built on.
|
|
10578
|
+
|
|
10579
|
+
<a id="wornavatar3d-id"></a>
|
|
10580
|
+
|
|
10581
|
+
## WornAvatar3d.id
|
|
10582
|
+
|
|
10583
|
+
```ts
|
|
10584
|
+
readonly id: string
|
|
10585
|
+
```
|
|
10586
|
+
|
|
10587
|
+
The avatar's item id — a stable key for the model, never something to
|
|
10588
|
+
show as text.
|
|
10589
|
+
|
|
10590
|
+
<a id="wornavatar3d-modelurl"></a>
|
|
10591
|
+
|
|
10592
|
+
## WornAvatar3d.modelUrl
|
|
10593
|
+
|
|
10594
|
+
```ts
|
|
10595
|
+
readonly modelUrl: string
|
|
10596
|
+
```
|
|
10597
|
+
|
|
10598
|
+
The avatar's model, a binary glTF (`.glb`) on the family's rig. Pass it
|
|
10599
|
+
through [platformIconUrl](#platformiconurl) before loading it.
|
|
10600
|
+
|
|
10601
|
+
<a id="wornavatar3d-iconurl"></a>
|
|
10602
|
+
|
|
10603
|
+
## WornAvatar3d.iconUrl
|
|
10604
|
+
|
|
10605
|
+
```ts
|
|
10606
|
+
readonly iconUrl: string
|
|
10607
|
+
```
|
|
10608
|
+
|
|
10609
|
+
A rendered portrait of the avatar, for a roster or a menu. Pass it through
|
|
10610
|
+
[platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
10611
|
+
|
|
10371
10612
|
<a id="playeridentity"></a>
|
|
10372
10613
|
|
|
10373
10614
|
## PlayerIdentity
|
|
@@ -10511,6 +10752,24 @@ loading the model is too much. Not the 2D portrait
|
|
|
10511
10752
|
([PlayerIdentity.avatarIconUrl](#playeridentity-avatariconurl)), which is a separate choice. Pass it
|
|
10512
10753
|
through [platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
10513
10754
|
|
|
10755
|
+
<a id="playeridentity-avatars3d"></a>
|
|
10756
|
+
|
|
10757
|
+
## PlayerIdentity.avatars3d
|
|
10758
|
+
|
|
10759
|
+
```ts
|
|
10760
|
+
readonly avatars3d?: readonly WornAvatar3d[]
|
|
10761
|
+
```
|
|
10762
|
+
|
|
10763
|
+
Every 3D avatar the player wears, one per rig family, in the order the game
|
|
10764
|
+
declared its families. Absent or empty means none is worn — draw your
|
|
10765
|
+
default look.
|
|
10766
|
+
|
|
10767
|
+
[PlayerIdentity.avatar3dId](#playeridentity-avatar3did) and its two URLs are one of these: the
|
|
10768
|
+
avatar of the game's first declared family that has one. A game that
|
|
10769
|
+
accepts a single family can read either; one that accepts several picks by
|
|
10770
|
+
`family` here. Absent too where the platform does not send it yet — read
|
|
10771
|
+
the flat fields then.
|
|
10772
|
+
|
|
10514
10773
|
<a id="playeridentity-badges"></a>
|
|
10515
10774
|
|
|
10516
10775
|
## PlayerIdentity.badges
|
|
@@ -10550,7 +10809,7 @@ for (const row of opponents) {
|
|
|
10550
10809
|
|
|
10551
10810
|
Reads `player_id`, `display_name`, `discriminator`, `display_tag`,
|
|
10552
10811
|
`avatar_icon_id`, `avatar_icon_url`, `avatar_3d_id`, `avatar_3d_model_url`,
|
|
10553
|
-
`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.
|
|
10812
|
+
`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.
|
|
10554
10813
|
|
|
10555
10814
|
| Parameter | Type |
|
|
10556
10815
|
| --- | --- |
|
|
@@ -11810,12 +12069,44 @@ a Buy button mostly does not write one.
|
|
|
11810
12069
|
| --- | --- |
|
|
11811
12070
|
| `req` | `PurchaseRequest` |
|
|
11812
12071
|
|
|
12072
|
+
<a id="avatar3dpurchaserequest"></a>
|
|
12073
|
+
|
|
12074
|
+
## Avatar3dPurchaseRequest
|
|
12075
|
+
|
|
12076
|
+
```ts
|
|
12077
|
+
interface Avatar3dPurchaseRequest
|
|
12078
|
+
```
|
|
12079
|
+
|
|
12080
|
+
What a game asks for when it buys one of its own 3D avatars for the player
|
|
12081
|
+
with `gem.avatars3d.purchase`.
|
|
12082
|
+
|
|
12083
|
+
<a id="avatar3dpurchaserequest-itemid"></a>
|
|
12084
|
+
|
|
12085
|
+
## Avatar3dPurchaseRequest.itemId
|
|
12086
|
+
|
|
12087
|
+
```ts
|
|
12088
|
+
itemId: string
|
|
12089
|
+
```
|
|
12090
|
+
|
|
12091
|
+
The avatar's item id — `itemId` on an `Avatar3dOffer`.
|
|
12092
|
+
|
|
12093
|
+
<a id="avatar3dpurchaserequest-currencyslug"></a>
|
|
12094
|
+
|
|
12095
|
+
## Avatar3dPurchaseRequest.currencySlug
|
|
12096
|
+
|
|
12097
|
+
```ts
|
|
12098
|
+
currencySlug: string
|
|
12099
|
+
```
|
|
12100
|
+
|
|
12101
|
+
The platform currency to pay in, `GEMS` or `ARCADE_TICKETS`. The offer
|
|
12102
|
+
must carry a price in it.
|
|
12103
|
+
|
|
11813
12104
|
<a id="purchaserefusalcode"></a>
|
|
11814
12105
|
|
|
11815
12106
|
## PurchaseRefusalCode
|
|
11816
12107
|
|
|
11817
12108
|
```ts
|
|
11818
|
-
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"
|
|
12109
|
+
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"
|
|
11819
12110
|
```
|
|
11820
12111
|
|
|
11821
12112
|
Every reason a purchase can be refused, on either purchase path.
|
|
@@ -11841,6 +12132,9 @@ Every reason a purchase can be refused, on either purchase path.
|
|
|
11841
12132
|
- `not_available` — the recipe is outside its availability window, or
|
|
11842
12133
|
inactive; nothing was charged. Hide or disable it until its window opens.
|
|
11843
12134
|
- `recipe_locked` — the recipe's prerequisites are not met.
|
|
12135
|
+
- `already_owned` — the player already owns the 3D avatar
|
|
12136
|
+
`gem.avatars3d.purchase` asked for; nothing was charged. Only that call
|
|
12137
|
+
answers it.
|
|
11844
12138
|
- `recipe_on_cooldown`, `recipe_max_uses_exceeded`, `item_max_stack_exceeded`,
|
|
11845
12139
|
`recipe_costs_unmet`, `quantity_invalid` — the recipe or item refused this
|
|
11846
12140
|
purchase, before any charge.
|
|
@@ -13127,6 +13421,24 @@ loading the model is too much. Not the 2D portrait
|
|
|
13127
13421
|
([PlayerIdentity.avatarIconUrl](#playeridentity-avatariconurl)), which is a separate choice. Pass it
|
|
13128
13422
|
through [platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
13129
13423
|
|
|
13424
|
+
<a id="leaderboardentry-avatars3d"></a>
|
|
13425
|
+
|
|
13426
|
+
## LeaderboardEntry.avatars3d
|
|
13427
|
+
|
|
13428
|
+
```ts
|
|
13429
|
+
readonly avatars3d?: readonly WornAvatar3d[]
|
|
13430
|
+
```
|
|
13431
|
+
|
|
13432
|
+
Every 3D avatar the player wears, one per rig family, in the order the game
|
|
13433
|
+
declared its families. Absent or empty means none is worn — draw your
|
|
13434
|
+
default look.
|
|
13435
|
+
|
|
13436
|
+
[PlayerIdentity.avatar3dId](#playeridentity-avatar3did) and its two URLs are one of these: the
|
|
13437
|
+
avatar of the game's first declared family that has one. A game that
|
|
13438
|
+
accepts a single family can read either; one that accepts several picks by
|
|
13439
|
+
`family` here. Absent too where the platform does not send it yet — read
|
|
13440
|
+
the flat fields then.
|
|
13441
|
+
|
|
13130
13442
|
<a id="leaderboardentry-badges"></a>
|
|
13131
13443
|
|
|
13132
13444
|
## LeaderboardEntry.badges
|
|
@@ -14620,6 +14932,24 @@ loading the model is too much. Not the 2D portrait
|
|
|
14620
14932
|
([PlayerIdentity.avatarIconUrl](#playeridentity-avatariconurl)), which is a separate choice. Pass it
|
|
14621
14933
|
through [platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
14622
14934
|
|
|
14935
|
+
<a id="playerprofile-avatars3d"></a>
|
|
14936
|
+
|
|
14937
|
+
## PlayerProfile.avatars3d
|
|
14938
|
+
|
|
14939
|
+
```ts
|
|
14940
|
+
readonly avatars3d?: readonly WornAvatar3d[]
|
|
14941
|
+
```
|
|
14942
|
+
|
|
14943
|
+
Every 3D avatar the player wears, one per rig family, in the order the game
|
|
14944
|
+
declared its families. Absent or empty means none is worn — draw your
|
|
14945
|
+
default look.
|
|
14946
|
+
|
|
14947
|
+
[PlayerIdentity.avatar3dId](#playeridentity-avatar3did) and its two URLs are one of these: the
|
|
14948
|
+
avatar of the game's first declared family that has one. A game that
|
|
14949
|
+
accepts a single family can read either; one that accepts several picks by
|
|
14950
|
+
`family` here. Absent too where the platform does not send it yet — read
|
|
14951
|
+
the flat fields then.
|
|
14952
|
+
|
|
14623
14953
|
<a id="playerprofile-badges"></a>
|
|
14624
14954
|
|
|
14625
14955
|
## PlayerProfile.badges
|
|
@@ -17206,6 +17536,24 @@ loading the model is too much. Not the 2D portrait
|
|
|
17206
17536
|
([PlayerIdentity.avatarIconUrl](#playeridentity-avatariconurl)), which is a separate choice. Pass it
|
|
17207
17537
|
through [platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
17208
17538
|
|
|
17539
|
+
<a id="roommember-avatars3d"></a>
|
|
17540
|
+
|
|
17541
|
+
## RoomMember.avatars3d
|
|
17542
|
+
|
|
17543
|
+
```ts
|
|
17544
|
+
readonly avatars3d?: readonly WornAvatar3d[]
|
|
17545
|
+
```
|
|
17546
|
+
|
|
17547
|
+
Every 3D avatar the player wears, one per rig family, in the order the game
|
|
17548
|
+
declared its families. Absent or empty means none is worn — draw your
|
|
17549
|
+
default look.
|
|
17550
|
+
|
|
17551
|
+
[PlayerIdentity.avatar3dId](#playeridentity-avatar3did) and its two URLs are one of these: the
|
|
17552
|
+
avatar of the game's first declared family that has one. A game that
|
|
17553
|
+
accepts a single family can read either; one that accepts several picks by
|
|
17554
|
+
`family` here. Absent too where the platform does not send it yet — read
|
|
17555
|
+
the flat fields then.
|
|
17556
|
+
|
|
17209
17557
|
<a id="roommember-badges"></a>
|
|
17210
17558
|
|
|
17211
17559
|
## RoomMember.badges
|
|
@@ -18036,6 +18384,24 @@ loading the model is too much. Not the 2D portrait
|
|
|
18036
18384
|
([PlayerIdentity.avatarIconUrl](#playeridentity-avatariconurl)), which is a separate choice. Pass it
|
|
18037
18385
|
through [platformIconUrl](#platformiconurl) before putting it in an `<img src>`.
|
|
18038
18386
|
|
|
18387
|
+
<a id="showcasefriendentry-avatars3d"></a>
|
|
18388
|
+
|
|
18389
|
+
## ShowcaseFriendEntry.avatars3d
|
|
18390
|
+
|
|
18391
|
+
```ts
|
|
18392
|
+
readonly avatars3d?: readonly WornAvatar3d[]
|
|
18393
|
+
```
|
|
18394
|
+
|
|
18395
|
+
Every 3D avatar the player wears, one per rig family, in the order the game
|
|
18396
|
+
declared its families. Absent or empty means none is worn — draw your
|
|
18397
|
+
default look.
|
|
18398
|
+
|
|
18399
|
+
[PlayerIdentity.avatar3dId](#playeridentity-avatar3did) and its two URLs are one of these: the
|
|
18400
|
+
avatar of the game's first declared family that has one. A game that
|
|
18401
|
+
accepts a single family can read either; one that accepts several picks by
|
|
18402
|
+
`family` here. Absent too where the platform does not send it yet — read
|
|
18403
|
+
the flat fields then.
|
|
18404
|
+
|
|
18039
18405
|
<a id="showcasefriendentry-badges"></a>
|
|
18040
18406
|
|
|
18041
18407
|
## ShowcaseFriendEntry.badges
|