@hopple/sdk 1.0.0-next.1 → 1.0.0-next.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +116 -21
- package/index.d.ts +221 -104
- package/index.js +1 -1
- package/manifest.d.ts +97 -1
- package/manifest.js +59 -4
- package/manifest.md +60 -17
- package/package.json +5 -2
- package/config.schema.json +0 -954
package/manifest.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
//#region ../../../../../../../tmp/hopple-sdk-types-
|
|
1
|
+
//#region ../../../../../../../tmp/hopple-sdk-types-ky983p/manifest/src/manifest.d.ts
|
|
2
2
|
/**
|
|
3
3
|
* The API a game is written against: `M.m`, a major and the least minor it
|
|
4
4
|
* needs, or `next`, the pre-releases of the next stable release.
|
|
@@ -217,6 +217,26 @@ type ScoreBoard = {
|
|
|
217
217
|
direction: ScoreDirection;
|
|
218
218
|
/** See `ScoreFormat`. Absent means `integer`. */
|
|
219
219
|
format?: ScoreFormat;
|
|
220
|
+
/**
|
|
221
|
+
* Which board, of the ones this id has named. Absent is `1`.
|
|
222
|
+
*
|
|
223
|
+
* **Raising it starts the board again from nobody.** The runs under the
|
|
224
|
+
* version before are kept and no longer counted, shown or ranked, so a board
|
|
225
|
+
* whose scores stopped being comparable — new levels, a new rule — begins
|
|
226
|
+
* again under the id the game already submits to.
|
|
227
|
+
*
|
|
228
|
+
* **Only a change to what a stored score means needs it.** The title changes
|
|
229
|
+
* freely, and so does the format between `integer` and `decimal`. Once the
|
|
230
|
+
* board holds scores, turning it around (`direction`) or writing it in
|
|
231
|
+
* another unit (to or from `duration-ms`) is refused at the same version.
|
|
232
|
+
* Going back to a version declared before brings its runs back, so it is
|
|
233
|
+
* held to what that version was.
|
|
234
|
+
*
|
|
235
|
+
* @asType integer
|
|
236
|
+
* @minimum 1
|
|
237
|
+
* @maximum 1000
|
|
238
|
+
*/
|
|
239
|
+
version?: number;
|
|
220
240
|
};
|
|
221
241
|
/**
|
|
222
242
|
* The boards this game keeps a score on.
|
|
@@ -380,6 +400,28 @@ type Achievement = {
|
|
|
380
400
|
secret?: boolean;
|
|
381
401
|
/** Present makes this measured rather than flagged. See `AchievementProgress`. */
|
|
382
402
|
progress?: AchievementProgress;
|
|
403
|
+
/**
|
|
404
|
+
* Which achievement, of the ones this id has named. Absent is `1`.
|
|
405
|
+
*
|
|
406
|
+
* **Raising it starts the achievement again for everybody.** What players
|
|
407
|
+
* reported and unlocked under the version before is kept and no longer
|
|
408
|
+
* counted, so it is earned afresh under the id the game already reports to.
|
|
409
|
+
*
|
|
410
|
+
* **Only a change to what a stored value means needs it.** The words change
|
|
411
|
+
* freely — title, detail, icon, secret — and so does the target, and a tier
|
|
412
|
+
* added after the last one: progress is kept as the number reported, which
|
|
413
|
+
* stays true when the goal moves, and a player who unlocked it keeps it. At
|
|
414
|
+
* the same version, a published achievement may not turn from a flag into a
|
|
415
|
+
* measure or back, turn its direction around, write its value in another
|
|
416
|
+
* unit (to or from `duration-ms`), or move or drop a tier it already has.
|
|
417
|
+
* Going back to a version declared before brings back what was earned under
|
|
418
|
+
* it, so it is held to what that version was.
|
|
419
|
+
*
|
|
420
|
+
* @asType integer
|
|
421
|
+
* @minimum 1
|
|
422
|
+
* @maximum 1000
|
|
423
|
+
*/
|
|
424
|
+
version?: number;
|
|
383
425
|
};
|
|
384
426
|
/**
|
|
385
427
|
* The achievements this game keeps for a player.
|
|
@@ -671,6 +713,20 @@ type ControlSlot = {
|
|
|
671
713
|
as?: InputStick | Record<string, InputBinding>;
|
|
672
714
|
/** `thumbstick` only. See `ControlAxis`. Absent, the stick moves both ways. */
|
|
673
715
|
axis?: ControlAxis;
|
|
716
|
+
/**
|
|
717
|
+
* How big this control is next to the layout's others: `1.25` for a jump
|
|
718
|
+
* button a thumb lands on without looking, `0.8` for one pressed now and then.
|
|
719
|
+
* Absent, 1.
|
|
720
|
+
*
|
|
721
|
+
* A size in the layout rather than in the game's stylesheet, because whether
|
|
722
|
+
* the layout fits on a small screen is worked out from it before the
|
|
723
|
+
* controls are drawn. A game's stylesheet changes how they look, never how
|
|
724
|
+
* much of the screen they answer in.
|
|
725
|
+
*
|
|
726
|
+
* @minimum 0.75
|
|
727
|
+
* @maximum 1.5
|
|
728
|
+
*/
|
|
729
|
+
scale?: number;
|
|
674
730
|
};
|
|
675
731
|
/**
|
|
676
732
|
* The one direction an on-screen thumbstick moves in: `x` across, `y` up and
|
|
@@ -821,6 +877,23 @@ type PreviewsDeclaration = {
|
|
|
821
877
|
*/
|
|
822
878
|
network?: PreviewRevision;
|
|
823
879
|
};
|
|
880
|
+
/**
|
|
881
|
+
* What kind of game this is, as one word from a list every game shares.
|
|
882
|
+
*
|
|
883
|
+
* A closed list, because a client files games under these and has to name each
|
|
884
|
+
* one in front of a player: left to free text, one kind of game becomes three
|
|
885
|
+
* spellings of it with a game each. The list only grows, since a published
|
|
886
|
+
* manifest has to stay valid. Whoever reads one may be older than the game, and
|
|
887
|
+
* ignores a word it does not know.
|
|
888
|
+
*
|
|
889
|
+
* `local-multiplayer` and `online-multiplayer` say how a game is played rather
|
|
890
|
+
* than what it is, and only count where the manifest bears them out: see
|
|
891
|
+
* `declaredGenres`.
|
|
892
|
+
*
|
|
893
|
+
* @group Manifest
|
|
894
|
+
* @category Useful
|
|
895
|
+
*/
|
|
896
|
+
type GameGenre = 'racing' | 'shooter' | 'platformer' | 'puzzle' | 'arcade' | 'physics' | 'roguelike' | 'strategy' | 'building' | 'idle' | 'sports' | 'horror' | 'stealth' | 'fighting' | 'casual' | 'simulation' | 'adventure' | 'rhythm' | 'rpg' | 'local-multiplayer' | 'online-multiplayer';
|
|
824
897
|
/**
|
|
825
898
|
* Publishing refuses a key the manifest's API does not know; a host reading a
|
|
826
899
|
* published one ignores it (see `validateManifest`).
|
|
@@ -860,8 +933,19 @@ type GameManifest = {
|
|
|
860
933
|
* @errorMessage must be a handle: lowercase letters, digits and hyphens
|
|
861
934
|
*/
|
|
862
935
|
owner?: string;
|
|
936
|
+
/**
|
|
937
|
+
* `false` for a game that is never to be uploaded: an example, a test, a
|
|
938
|
+
* game kept to one machine. `hopple dev` and `hopple check` still run it;
|
|
939
|
+
* `hopple upload` and `hopple publish` refuse it, and so does a server it
|
|
940
|
+
* is sent to another way. Absent, it may be published.
|
|
941
|
+
*
|
|
942
|
+
* @category Useful
|
|
943
|
+
*/
|
|
944
|
+
publishable?: boolean;
|
|
863
945
|
/**
|
|
864
946
|
* The game's own version, `MAJOR.MINOR.PATCH`. Ours to read, never to parse for meaning.
|
|
947
|
+
* People see it beside the build the server numbers each upload with, and
|
|
948
|
+
* each published version needs one no other published version of the game has.
|
|
865
949
|
*
|
|
866
950
|
* @category Important
|
|
867
951
|
* @pattern ^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$
|
|
@@ -884,6 +968,18 @@ type GameManifest = {
|
|
|
884
968
|
* @errorMessage must not be blank
|
|
885
969
|
*/
|
|
886
970
|
description?: string;
|
|
971
|
+
/**
|
|
972
|
+
* What kind of game it is, the main one first. Read by whoever files it in a
|
|
973
|
+
* catalogue, which never runs it. At most `GENRE_MAX`, not counting
|
|
974
|
+
* `local-multiplayer` and `online-multiplayer`: a game that is every kind
|
|
975
|
+
* tells a player nothing. The publisher's claim, taken at its word, except
|
|
976
|
+
* for those two. See `GameGenre`.
|
|
977
|
+
*
|
|
978
|
+
* @category Useful
|
|
979
|
+
* @minItems 1
|
|
980
|
+
* @maxItems 5
|
|
981
|
+
*/
|
|
982
|
+
genres?: GameGenre[];
|
|
887
983
|
/**
|
|
888
984
|
* A picture of the game, shown wherever a link to it is pasted — a chat, a
|
|
889
985
|
* post, a message.
|
package/manifest.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! @hopple/sdk 1.0.0-next.
|
|
1
|
+
/*! @hopple/sdk 1.0.0-next.5 | Copyright (c) 2026 The Hopple authors | MIT License */
|
|
2
2
|
//#region ../manifest/src/manifest.schema.ts
|
|
3
3
|
const GameManifest = {
|
|
4
4
|
"type": "object",
|
|
@@ -16,6 +16,7 @@ const GameManifest = {
|
|
|
16
16
|
"maxLength": 64,
|
|
17
17
|
"errorMessage": "must be a handle: lowercase letters, digits and hyphens"
|
|
18
18
|
},
|
|
19
|
+
"publishable": { "type": "boolean" },
|
|
19
20
|
"version": {
|
|
20
21
|
"type": "string",
|
|
21
22
|
"pattern": "^\\d+\\.\\d+\\.\\d+(?:-[0-9A-Za-z.-]+)?$",
|
|
@@ -33,6 +34,12 @@ const GameManifest = {
|
|
|
33
34
|
"maxLength": 500,
|
|
34
35
|
"errorMessage": "must not be blank"
|
|
35
36
|
},
|
|
37
|
+
"genres": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": { "$ref": "#/definitions/GameGenre" },
|
|
40
|
+
"minItems": 1,
|
|
41
|
+
"maxItems": 5
|
|
42
|
+
},
|
|
36
43
|
"cover": {
|
|
37
44
|
"type": "string",
|
|
38
45
|
"pattern": "^(?!/)(?!.*\\.\\.)[A-Za-z0-9._/-]+\\.(?:png|jpe?g|webp)$",
|
|
@@ -65,6 +72,32 @@ const ApiVersion = {
|
|
|
65
72
|
"pattern": "^(?:(?:0|[1-9][0-9]*)\\.(?:0|[1-9][0-9]*)|next)$",
|
|
66
73
|
"errorMessage": "must be the API the game is written against, like \"1.0\", or \"next\""
|
|
67
74
|
};
|
|
75
|
+
const GameGenre = {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"enum": [
|
|
78
|
+
"racing",
|
|
79
|
+
"shooter",
|
|
80
|
+
"platformer",
|
|
81
|
+
"puzzle",
|
|
82
|
+
"arcade",
|
|
83
|
+
"physics",
|
|
84
|
+
"roguelike",
|
|
85
|
+
"strategy",
|
|
86
|
+
"building",
|
|
87
|
+
"idle",
|
|
88
|
+
"sports",
|
|
89
|
+
"horror",
|
|
90
|
+
"stealth",
|
|
91
|
+
"fighting",
|
|
92
|
+
"casual",
|
|
93
|
+
"simulation",
|
|
94
|
+
"adventure",
|
|
95
|
+
"rhythm",
|
|
96
|
+
"rpg",
|
|
97
|
+
"local-multiplayer",
|
|
98
|
+
"online-multiplayer"
|
|
99
|
+
]
|
|
100
|
+
};
|
|
68
101
|
const Orientation = {
|
|
69
102
|
"type": "string",
|
|
70
103
|
"enum": [
|
|
@@ -153,7 +186,12 @@ const ScoreBoard = {
|
|
|
153
186
|
"errorMessage": "must not be blank"
|
|
154
187
|
},
|
|
155
188
|
"direction": { "$ref": "#/definitions/ScoreDirection" },
|
|
156
|
-
"format": { "$ref": "#/definitions/ScoreFormat" }
|
|
189
|
+
"format": { "$ref": "#/definitions/ScoreFormat" },
|
|
190
|
+
"version": {
|
|
191
|
+
"type": "integer",
|
|
192
|
+
"minimum": 1,
|
|
193
|
+
"maximum": 1e3
|
|
194
|
+
}
|
|
157
195
|
},
|
|
158
196
|
"required": ["title", "direction"],
|
|
159
197
|
"additionalProperties": false
|
|
@@ -208,7 +246,12 @@ const Achievement = {
|
|
|
208
246
|
"errorMessage": "must not be blank"
|
|
209
247
|
},
|
|
210
248
|
"secret": { "type": "boolean" },
|
|
211
|
-
"progress": { "$ref": "#/definitions/AchievementProgress" }
|
|
249
|
+
"progress": { "$ref": "#/definitions/AchievementProgress" },
|
|
250
|
+
"version": {
|
|
251
|
+
"type": "integer",
|
|
252
|
+
"minimum": 1,
|
|
253
|
+
"maximum": 1e3
|
|
254
|
+
}
|
|
212
255
|
},
|
|
213
256
|
"required": ["title"],
|
|
214
257
|
"additionalProperties": false
|
|
@@ -372,7 +415,12 @@ const ControlSlot = {
|
|
|
372
415
|
"type": "object",
|
|
373
416
|
"additionalProperties": { "$ref": "#/definitions/InputBinding" }
|
|
374
417
|
}] },
|
|
375
|
-
"axis": { "$ref": "#/definitions/ControlAxis" }
|
|
418
|
+
"axis": { "$ref": "#/definitions/ControlAxis" },
|
|
419
|
+
"scale": {
|
|
420
|
+
"type": "number",
|
|
421
|
+
"minimum": .75,
|
|
422
|
+
"maximum": 1.5
|
|
423
|
+
}
|
|
376
424
|
},
|
|
377
425
|
"required": ["type"],
|
|
378
426
|
"additionalProperties": false,
|
|
@@ -412,6 +460,7 @@ const InputStick = {
|
|
|
412
460
|
const definitions = {
|
|
413
461
|
GameManifest,
|
|
414
462
|
ApiVersion,
|
|
463
|
+
GameGenre,
|
|
415
464
|
Orientation,
|
|
416
465
|
LayoutDeclaration,
|
|
417
466
|
InsetMode,
|
|
@@ -573,6 +622,7 @@ const definitions = {
|
|
|
573
622
|
};
|
|
574
623
|
AchievementProgress.properties.target.maximum;
|
|
575
624
|
GameManifest.required;
|
|
625
|
+
GameGenre.enum;
|
|
576
626
|
/**
|
|
577
627
|
* The zones a layout places controls in, in the order they are drawn.
|
|
578
628
|
*
|
|
@@ -750,6 +800,9 @@ function walkObject(node, value, path, fail, lenient) {
|
|
|
750
800
|
}
|
|
751
801
|
}
|
|
752
802
|
function checkReferences(manifest, fail) {
|
|
803
|
+
const genres = manifest.genres ?? [];
|
|
804
|
+
for (const genre of new Set(genres.filter((one, index) => genres.indexOf(one) !== index))) fail("genres", `names "${genre}" twice`);
|
|
805
|
+
if (genres.filter((genre) => !MULTIPLAYER_GENRES.includes(genre)).length > 3) fail("genres", `must hold at most 3 genres, beside local-multiplayer and online-multiplayer`);
|
|
753
806
|
const players = manifest.input?.player;
|
|
754
807
|
const own = new Set(Object.keys(players?.actions ?? {}));
|
|
755
808
|
const actions = /* @__PURE__ */ new Set([...Object.keys(manifest.input?.actions ?? {}), ...own]);
|
|
@@ -787,6 +840,8 @@ function checkReferences(manifest, fail) {
|
|
|
787
840
|
}
|
|
788
841
|
}
|
|
789
842
|
}
|
|
843
|
+
/** The genres that say how a game is played together rather than what it is. */
|
|
844
|
+
const MULTIPLAYER_GENRES = ["local-multiplayer", "online-multiplayer"];
|
|
790
845
|
/**
|
|
791
846
|
* Whether `a` beats `b` on a board sorted this way. Ties are not improvements.
|
|
792
847
|
*
|
package/manifest.md
CHANGED
|
@@ -35,6 +35,7 @@ leave `api` and `previews` out: the CLI fills `api` from the installed
|
|
|
35
35
|
| `version` | `MAJOR.MINOR.PATCH`, optional `-prerelease` | The game's own version. Shown to the game as `sdk.info.game.version`. |
|
|
36
36
|
| `title` | at most 60 characters | What players see. |
|
|
37
37
|
| `description` | optional; at most 500 characters | A sentence or two shown beside the title. |
|
|
38
|
+
| `genres` | optional; see [`genres`](#genres) | What kind of game it is, the main one first. |
|
|
38
39
|
| `orientation` | `portrait`, `landscape` or `any` | The shape the game is designed for. Hosts lay the game out to match. |
|
|
39
40
|
| `audio` | `null`, or an object | Whether the game makes sound, and its volume channels. See [`audio`](#audio). |
|
|
40
41
|
|
|
@@ -54,15 +55,17 @@ same function, so they agree on whether a manifest is valid.
|
|
|
54
55
|
|
|
55
56
|
### In an editor
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
as `config.schema.json`, for completion, hover docs and errors as
|
|
59
|
-
JSON config can point at it:
|
|
58
|
+
The `hopple` CLI ships the hopple config's JSON Schema, the manifest's
|
|
59
|
+
included, as `config.schema.json`, for completion, hover docs and errors as
|
|
60
|
+
you type. A JSON config in a project with `hopple` installed can point at it:
|
|
60
61
|
|
|
61
62
|
```json
|
|
62
|
-
{ "$schema": "./node_modules
|
|
63
|
+
{ "$schema": "./node_modules/hopple/config.schema.json", "page": "dist/index.html", "manifest": { ... } }
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
A TypeScript config gets the same checks from its type,
|
|
66
|
+
A TypeScript config gets the same checks from its type,
|
|
67
|
+
`import type { HoppleConfig } from 'hopple'`, whose manifest is the type of
|
|
68
|
+
the installed `@hopple/sdk`.
|
|
66
69
|
|
|
67
70
|
A few rules relate one part of a manifest to another, which JSON Schema can't
|
|
68
71
|
express: buttons must name declared actions, a control's `as` must name its
|
|
@@ -141,6 +144,7 @@ A game without this key can still use `sdk.save`.
|
|
|
141
144
|
| `title` | yes | What players see. At most 40 characters. |
|
|
142
145
|
| `direction` | yes | `desc`: higher is better. `asc`: lower is better (times, strokes). |
|
|
143
146
|
| `format` | no | `integer` (default), `decimal`, or `duration-ms` (so `224310` shows as `3:44.31`). |
|
|
147
|
+
| `version` | no | 1 by default. Raising it starts the board again; see [Changing the manifest in a new version](#changing-the-manifest-in-a-new-version). |
|
|
144
148
|
|
|
145
149
|
`boards` is a map even for one board, so a game can add a second one later
|
|
146
150
|
without changing the meaning of the first. At most 16 boards.
|
|
@@ -179,6 +183,9 @@ There are two kinds:
|
|
|
179
183
|
`secret: true` hides the title and detail from players until it is earned.
|
|
180
184
|
The manifest is public, so this only stops spoilers.
|
|
181
185
|
|
|
186
|
+
`version` is 1 by default. Raising it starts the achievement again for
|
|
187
|
+
everybody; see [Changing the manifest in a new version](#changing-the-manifest-in-a-new-version).
|
|
188
|
+
|
|
182
189
|
Limits: 128 achievements, titles up to 40 characters, details up to 120, icons
|
|
183
190
|
up to 8 characters, 8 tiers.
|
|
184
191
|
|
|
@@ -219,7 +226,8 @@ up to 8 characters, 8 tiers.
|
|
|
219
226
|
`buttons` (naming actions) arranged as a `row`, `column`, `diamond` or
|
|
220
227
|
`cluster`. An unknown shape is drawn as a column. A stick held to an axis is
|
|
221
228
|
drawn as a track and always reads 0 on the other axis; a real pad's stick
|
|
222
|
-
still reports both.
|
|
229
|
+
still reports both. Any control may have a `scale` from 0.75 to 1.5, its
|
|
230
|
+
size next to the layout's others (1 when left out).
|
|
223
231
|
|
|
224
232
|
Binding names:
|
|
225
233
|
|
|
@@ -295,6 +303,31 @@ Each channel gets its own labelled volume slider in the host's sound controls.
|
|
|
295
303
|
`title` is required: it is the slider's label. `master` is reserved, since
|
|
296
304
|
every host already has a master volume. At most 8 channels.
|
|
297
305
|
|
|
306
|
+
### genres
|
|
307
|
+
|
|
308
|
+
```json
|
|
309
|
+
"genres": ["puzzle", "shooter", "online-multiplayer"]
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
What kind of game this is, the main one first: at least one, and at most 3
|
|
313
|
+
beside the two multiplayer words, none twice. A game with none declared is
|
|
314
|
+
filed by what its title and description say, and most often less well.
|
|
315
|
+
|
|
316
|
+
The words are `racing`, `shooter`, `platformer`, `puzzle`, `arcade`, `physics`,
|
|
317
|
+
`roguelike`, `strategy`, `building`, `idle`, `sports`, `horror`, `stealth`,
|
|
318
|
+
`fighting`, `casual`, `simulation`, `adventure`, `rhythm` and `rpg`, and two
|
|
319
|
+
that say how it is played together:
|
|
320
|
+
|
|
321
|
+
- **`local-multiplayer`**: several people on one device. Needs `players.local`
|
|
322
|
+
above 1.
|
|
323
|
+
- **`online-multiplayer`**: over the network. Needs `previews.network`.
|
|
324
|
+
|
|
325
|
+
Either of those without what it needs is not refused: the game is published,
|
|
326
|
+
that word is ignored, and `hopple check`, `hopple upload` and `hopple publish`
|
|
327
|
+
warn about it. They warn the other way round too, for a game that declares
|
|
328
|
+
`genres` and leaves out a way of playing together its manifest shows, since it
|
|
329
|
+
then is not listed as one. Clients group the two under one shelf.
|
|
330
|
+
|
|
298
331
|
### previews
|
|
299
332
|
|
|
300
333
|
```json
|
|
@@ -327,17 +360,27 @@ approval and links, and an upload declaring it is refused.
|
|
|
327
360
|
|
|
328
361
|
## Changing the manifest in a new version
|
|
329
362
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
363
|
+
Every board and every achievement has a `version`, 1 when left out. The
|
|
364
|
+
server refuses a new version of the game whose manifest would change what data
|
|
365
|
+
players already have means, and the way out is always to raise that board's or
|
|
366
|
+
achievement's `version`: it starts again from nobody under the same id, and
|
|
367
|
+
what was stored under the version before is kept but no longer counted.
|
|
368
|
+
|
|
369
|
+
- **Boards and achievements can be added and dropped freely.** What players
|
|
370
|
+
stored under one that is dropped is kept, and comes back if it is declared
|
|
371
|
+
again.
|
|
372
|
+
- **A board's title can change, and so can its format between `integer` and
|
|
373
|
+
`decimal`.** Once it has scores, flipping its `direction` or moving its
|
|
374
|
+
format to or from `duration-ms` needs a new `version`.
|
|
375
|
+
- **An achievement's title, detail, icon and secret flag can change, and so can
|
|
376
|
+
its target; tiers can be added after the last one.** Progress is kept as the
|
|
377
|
+
number reported, so a player at 12 of 12 is at 12 of 25 when the target is
|
|
378
|
+
raised, and keeps the unlock; when it is lowered past them, they have it.
|
|
379
|
+
Turning a flag into a measured achievement or back, flipping `direction`,
|
|
380
|
+
moving its format to or from `duration-ms`, or moving or dropping a tier it
|
|
381
|
+
already has needs a new `version`, whether or not anybody holds it yet.
|
|
382
|
+
- **A version declared again is held to what it was**, since what was stored
|
|
383
|
+
under it comes back.
|
|
341
384
|
|
|
342
385
|
Input action ids and audio channel ids may change freely. A player's remapped
|
|
343
386
|
keys or volume settings for an id that no longer exists are simply dropped.
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hopple/sdk",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.5",
|
|
4
4
|
"description": "The Hopple SDK: what a game imports to talk to the host that runs it, forwarded to the runtime its page loads.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://hopple.app",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/pamarit/hopple.git"
|
|
10
|
+
},
|
|
7
11
|
"keywords": [
|
|
8
12
|
"hopple",
|
|
9
13
|
"game",
|
|
@@ -25,7 +29,6 @@
|
|
|
25
29
|
"types": "./manifest.d.ts",
|
|
26
30
|
"default": "./manifest.js"
|
|
27
31
|
},
|
|
28
|
-
"./config.schema.json": "./config.schema.json",
|
|
29
32
|
"./package.json": "./package.json"
|
|
30
33
|
},
|
|
31
34
|
"main": "./index.js",
|