@datamoc/mw_games 0.41.0 → 0.43.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 +105 -0
- package/README.md +62 -1
- package/REFERENCE.md +73 -8
- package/api-surface.json +696 -0
- package/data/microgm-essential.PROVENANCE.md +13 -0
- package/data/microgm-essential.sf2 +0 -0
- package/data/soundfont-catalog.json +8 -0
- package/dist/audio/AudioLoader.d.ts +28 -0
- package/dist/audio/AudioLoader.d.ts.map +1 -0
- package/dist/audio/AudioLoader.js +53 -0
- package/dist/audio/AudioLoader.js.map +1 -0
- package/dist/audio/Channels.d.ts +1 -0
- package/dist/audio/Channels.d.ts.map +1 -1
- package/dist/audio/Channels.js +2 -1
- package/dist/audio/Channels.js.map +1 -1
- package/dist/audio/MidiRender.d.ts +19 -1
- package/dist/audio/MidiRender.d.ts.map +1 -1
- package/dist/audio/MidiRender.js +24 -1
- package/dist/audio/MidiRender.js.map +1 -1
- package/dist/audio/MusicPlayer.d.ts +80 -0
- package/dist/audio/MusicPlayer.d.ts.map +1 -0
- package/dist/audio/MusicPlayer.js +147 -0
- package/dist/audio/MusicPlayer.js.map +1 -0
- package/dist/audio/SampleDecoder.d.ts +16 -2
- package/dist/audio/SampleDecoder.d.ts.map +1 -1
- package/dist/audio/SampleDecoder.js +10 -6
- package/dist/audio/SampleDecoder.js.map +1 -1
- package/dist/audio/Sound.d.ts +2 -0
- package/dist/audio/Sound.d.ts.map +1 -1
- package/dist/audio/Sound.js +3 -0
- package/dist/audio/Sound.js.map +1 -1
- package/dist/audio/SoundFont.d.ts +40 -5
- package/dist/audio/SoundFont.d.ts.map +1 -1
- package/dist/audio/SoundFont.js +52 -14
- package/dist/audio/SoundFont.js.map +1 -1
- package/dist/audio/Synth.d.ts +1 -1
- package/dist/audio/Synth.d.ts.map +1 -1
- package/dist/audio/Synth.js +4 -0
- package/dist/audio/Synth.js.map +1 -1
- package/dist/audio/index.d.ts +8 -4
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +4 -2
- package/dist/audio/index.js.map +1 -1
- package/dist/core/DialogueText.d.ts +25 -0
- package/dist/core/DialogueText.d.ts.map +1 -1
- package/dist/core/DialogueText.js +25 -0
- package/dist/core/DialogueText.js.map +1 -1
- package/dist/core/Motion.d.ts +20 -0
- package/dist/core/Motion.d.ts.map +1 -1
- package/dist/core/Motion.js +20 -0
- package/dist/core/Motion.js.map +1 -1
- package/dist/core/Save.d.ts +31 -0
- package/dist/core/Save.d.ts.map +1 -1
- package/dist/core/Save.js +31 -0
- package/dist/core/Save.js.map +1 -1
- package/dist/core/Twee.d.ts +34 -0
- package/dist/core/Twee.d.ts.map +1 -1
- package/dist/core/Twee.js +34 -0
- package/dist/core/Twee.js.map +1 -1
- package/dist/mw_games.global.js +22 -22
- package/dist/mwl/index.d.ts +2 -0
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +1 -0
- package/dist/mwl/index.js.map +1 -1
- package/dist/mwl/objects.d.ts +31 -0
- package/dist/mwl/objects.d.ts.map +1 -0
- package/dist/mwl/objects.js +83 -0
- package/dist/mwl/objects.js.map +1 -0
- package/dist/mwl/schema.d.ts +1 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/tables.d.ts +2 -0
- package/dist/mwl/tables.d.ts.map +1 -1
- package/dist/mwl/tables.js +13 -0
- package/dist/mwl/tables.js.map +1 -1
- package/dist/physics/XpbdWorld.d.ts +68 -5
- package/dist/physics/XpbdWorld.d.ts.map +1 -1
- package/dist/physics/XpbdWorld.js +135 -6
- package/dist/physics/XpbdWorld.js.map +1 -1
- package/dist/testing/index.d.ts +16 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +21 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/three-d/Collision3D.d.ts +46 -2
- package/dist/three-d/Collision3D.d.ts.map +1 -1
- package/dist/three-d/Collision3D.js +46 -2
- package/dist/three-d/Collision3D.js.map +1 -1
- package/dist/three-d/Grid.d.ts +12 -1
- package/dist/three-d/Grid.d.ts.map +1 -1
- package/dist/three-d/Grid.js +12 -1
- package/dist/three-d/Grid.js.map +1 -1
- package/dist/three-d/Vox.d.ts +13 -1
- package/dist/three-d/Vox.d.ts.map +1 -1
- package/dist/three-d/Vox.js +13 -1
- package/dist/three-d/Vox.js.map +1 -1
- package/dist/two-d/Game.d.ts +21 -1
- package/dist/two-d/Game.d.ts.map +1 -1
- package/dist/two-d/Game.js +73 -6
- package/dist/two-d/Game.js.map +1 -1
- package/dist/two-d/Scene2D.d.ts +24 -0
- package/dist/two-d/Scene2D.d.ts.map +1 -1
- package/dist/two-d/Scene2D.js +30 -0
- package/dist/two-d/Scene2D.js.map +1 -1
- package/dist/two-d/render/SceneTransition.d.ts +272 -0
- package/dist/two-d/render/SceneTransition.d.ts.map +1 -0
- package/dist/two-d/render/SceneTransition.js +431 -0
- package/dist/two-d/render/SceneTransition.js.map +1 -0
- package/dist/two-d/render/index.d.ts +2 -0
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +1 -0
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/two-d/ui/Button.d.ts +23 -0
- package/dist/two-d/ui/Button.d.ts.map +1 -1
- package/dist/two-d/ui/Button.js +37 -0
- package/dist/two-d/ui/Button.js.map +1 -1
- package/dist/two-d/ui/DropdownView.d.ts +49 -3
- package/dist/two-d/ui/DropdownView.d.ts.map +1 -1
- package/dist/two-d/ui/DropdownView.js +150 -9
- package/dist/two-d/ui/DropdownView.js.map +1 -1
- package/dist/two-d/ui/ScrollBox.d.ts +90 -0
- package/dist/two-d/ui/ScrollBox.d.ts.map +1 -1
- package/dist/two-d/ui/ScrollBox.js +206 -4
- package/dist/two-d/ui/ScrollBox.js.map +1 -1
- package/dist/two-d/ui/ScrollText.d.ts +122 -0
- package/dist/two-d/ui/ScrollText.d.ts.map +1 -0
- package/dist/two-d/ui/ScrollText.js +202 -0
- package/dist/two-d/ui/ScrollText.js.map +1 -0
- package/dist/two-d/ui/Tooltip.d.ts +70 -1
- package/dist/two-d/ui/Tooltip.d.ts.map +1 -1
- package/dist/two-d/ui/Tooltip.js +115 -3
- package/dist/two-d/ui/Tooltip.js.map +1 -1
- package/dist/two-d/ui/Window.d.ts +35 -2
- package/dist/two-d/ui/Window.d.ts.map +1 -1
- package/dist/two-d/ui/Window.js +58 -3
- package/dist/two-d/ui/Window.js.map +1 -1
- package/dist/two-d/ui/index.d.ts +7 -5
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +3 -2
- package/dist/two-d/ui/index.js.map +1 -1
- package/dist/two-d/ui/theme.d.ts +18 -0
- package/dist/two-d/ui/theme.d.ts.map +1 -1
- package/dist/two-d/ui/theme.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -3
- package/sbom.cdx.json +462 -243
- package/tools/browser-driver.mjs +3 -3
- package/tools/browser-smoke.mjs +19 -2
- package/tools/build-tablebases.mjs +7 -3
- package/tools/compile-openings.mjs +7 -3
- package/tools/compile-resources.d.mts +8 -0
- package/tools/compile-resources.mjs +62 -14
- package/tools/emit-page.d.mts +9 -0
- package/tools/emit-page.mjs +57 -4
- package/tools/find-chrome.mjs +4 -2
- package/tools/init.mjs +11 -3
- package/tools/play-uci.mjs +9 -5
- package/tools/svg-optimise.d.mts +8 -0
- package/tools/svg-optimise.mjs +45 -0
- package/tools/vite.mjs +10 -1
- package/tools.md +5 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,111 @@ the public API may still change between minor versions.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.43.0] - 2026-10-17
|
|
11
|
+
|
|
12
|
+
Scrolling, tooltips and screen transitions for interface-heavy games, a MIDI music player and
|
|
13
|
+
SoundFont tooling, and the shipped command line tools made to work from an installed package on
|
|
14
|
+
Node 20.19. `@datamoc/pyroeis` moves to 0.9.1, which ends the last moderate audit advisory.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- Scrolling and transitions asked for by a bridge game (Pontifex feedback 45-50):
|
|
19
|
+
- `ScrollBox`: `touch` (drag to scroll, coasting on release, `ScrollDrag`), `handleKey` for line, page and
|
|
20
|
+
ends, a draggable scrollbar thumb and a clickable track, and `autoHeight`. A drag no longer clicks the row
|
|
21
|
+
it ends on.
|
|
22
|
+
- `DropdownView`: `maxVisibleRows` (the list scrolls), an opaque backdrop, `autoWidth`, opening with the chosen
|
|
23
|
+
row in view, `handleKey`, `handleOutsideClick` and `fitBelow`.
|
|
24
|
+
- `ScrollText` (a wrapped label that sizes itself and scrolls), `MarqueeLabel` (a long line sliding inside
|
|
25
|
+
its width, still under reduced motion), `ellipsize` and `truncateLines`.
|
|
26
|
+
- `TooltipHost`: tooltips on a touch long-press and on disabled buttons, from rectangles instead of pointer
|
|
27
|
+
events. `Button` gains `tooltip`, `setIcon` and a second icon for the selected state (`iconSelected`).
|
|
28
|
+
- `Game.switchScene(Next, { transition, duration, easing, pause })`, `Game.transition(swap, options)` and
|
|
29
|
+
`Scene2D.transitionTo`: `tileShrink`, `shrink`, `pop`, `grow`, `slide`, `iris` and `dissolve` over a snapshot of
|
|
30
|
+
the outgoing scene, skipped under reduced motion, with `Game.transitioning` and `Game.active`. `Window.open`
|
|
31
|
+
and `Window.close` take an animation (`pop`, `grow`, `slide`, `fade`). The bridge example switches level
|
|
32
|
+
with `tileShrink`.
|
|
33
|
+
- The dist grew 3.8% with them; the size baseline is moved to match.
|
|
34
|
+
- `limits` (`SoundFontLimits`: `sampleFrames`, `totalFrames`) on `decodeSoundFont`, `loadSoundFont` and
|
|
35
|
+
`webAudioSampleDecoder` raises or lowers what a SoundFont 3 may decode to, instead of the fixed 16 M
|
|
36
|
+
frames per sample and 128 M per font. The sample-rate bounds stay: the format itself stops at 50 kHz.
|
|
37
|
+
- `@example`s for the `3d` functions that need no renderer (`gridPoint3D`, `cellAt`, `heightAt`,
|
|
38
|
+
`buildHeightIndex`, `resolveCapsuleAgainstGrid`, `parseVox`). The generated API reference now covers
|
|
39
|
+
the subpaths the root barrel does not re-export (`3d`, `physics`, `ai/lua`, `mwl/pyroeis`, `testing`,
|
|
40
|
+
`threads`, `headless`, `assets/binary`), so the examples index links 803 of 813 symbols to a page.
|
|
41
|
+
- `npm run build:bridge-tutorial` regenerates the bridge tutorial from `tools/templates` and the example's
|
|
42
|
+
own source. `npm run bins:smoke` packs the project, installs it in an empty folder and runs every `mwg-*`
|
|
43
|
+
bin with `--help`; CI runs it, with the library build, on Node 20.19.0, the floor `engines` claims.
|
|
44
|
+
- `.gitattributes` (`* text=auto eol=lf`) ends the CRLF warnings on Windows checkouts.
|
|
45
|
+
- An optional, reproducible 314 KB SoundFont subset covering six essential presets,
|
|
46
|
+
with provenance and synthetic fallback for the instruments outside it.
|
|
47
|
+
- `AudioBus` loads embedded audio and MIDI by default through `createAudioLoader`, with
|
|
48
|
+
shared decoding, worker MIDI rendering and optional SoundFont settings across lifetimes.
|
|
49
|
+
- `MusicPlayer` controls rendered MIDI or decoded buffers with pause, resume, seek,
|
|
50
|
+
volume and a track-completion signal; `toAudioBuffer` copies rendered stereo samples
|
|
51
|
+
into a Web Audio buffer. The README shows the supplied SoundFont and worker playback path.
|
|
52
|
+
- Page byte budgets and potentially unused asset reports in the standalone build pipeline.
|
|
53
|
+
- XPBD link payloads, explicit rupture, geometric queries and state comparison helpers.
|
|
54
|
+
- Schema-directed `nodesToObjects` conversion and optional literal unions generated from
|
|
55
|
+
MWL table columns. Optional attributes are documented alongside the schema.
|
|
56
|
+
- Generated starter projects include a pre-commit hook for typecheck and tests, an activation
|
|
57
|
+
command and ignores for local agent state.
|
|
58
|
+
- Sawtooth sound synthesis, playback attempt counts and a shared browser test exposure hook.
|
|
59
|
+
|
|
60
|
+
### Fixed
|
|
61
|
+
|
|
62
|
+
- `mwg-uci`, `mwg-openings` and `mwg-tablebases` imported `../src/...`, which the package does not ship, so
|
|
63
|
+
they failed in every installed copy; they load `dist` there, as the other tools already did.
|
|
64
|
+
- The bridge example keeps a saved best only when the new crossing costs less, or costs the same and sags
|
|
65
|
+
less, instead of replacing it by any cheaper one.
|
|
66
|
+
- Fixed waits that failed on a loaded machine: the browser smoke waits for drawn frames instead of 250 ms,
|
|
67
|
+
the driver test polls for its tap instead of sleeping 300 ms, and the WebP noise test is seeded.
|
|
68
|
+
|
|
69
|
+
## [0.42.0] - 2026-10-16
|
|
70
|
+
|
|
71
|
+
The documentation and asset follow-ups to 0.41.0: SVG minification, a policy for the instruments a
|
|
72
|
+
SoundFont lacks, a playable bridge game with its tutorial, an examples index, and function-level
|
|
73
|
+
examples. One behaviour changes: a drum kit a font lacks now falls back to the standard kit instead of
|
|
74
|
+
a melodic preset.
|
|
75
|
+
|
|
76
|
+
### Added
|
|
77
|
+
|
|
78
|
+
- Function-level `@example`s where the examples used to sit on a file or an interface, so an
|
|
79
|
+
editor's hover shows them: `SaveSystem` (with a migration), `parseDialogueLines`,
|
|
80
|
+
`parseTwee` (walking a story), `motionDuration` (reduced motion on a slide and on a
|
|
81
|
+
meaningful bar) and `Theme` (a custom theme, `themeChanged`, the high-contrast swap).
|
|
82
|
+
- `XpbdWorld` documents a bridge: a Warren truss with beams, a `tensionOnly` cable note and a
|
|
83
|
+
breaking load, tested to hold a light cart, break under a heavy one and repeat exactly.
|
|
84
|
+
- `REFERENCE.md` records what subsetting the bundled bank costs (piano and drums 264 KB,
|
|
85
|
+
eight essential programs 421 KB, sixteen 585 KB) and what plays for a program the font lacks.
|
|
86
|
+
- `svgo: true` on `compileResources`, `emitPage` and `mwgPage` (and `--svgo` on `mwg-emit`) minifies
|
|
87
|
+
embedded `.svg` assets through the optional `svgo` package, version 4 or newer, imported only when
|
|
88
|
+
used. Ids and the `viewBox` are left alone, a file is embedded optimised only when that is smaller,
|
|
89
|
+
and one svgo cannot parse fails the build by name. On the repository's own 30 SVGs: 13.6%
|
|
90
|
+
smaller, worst rendered pixel difference 1 in 255. `@datamoc/mw_games/tools/svg-optimise` is the
|
|
91
|
+
standalone half.
|
|
92
|
+
- `missingProgram: 'piano' | 'synth'` on `parseSoundFont`, `loadSoundFont` and
|
|
93
|
+
`renderMidiToBufferAsync`: what plays for a program the font lacks, the font's own piano as
|
|
94
|
+
before or the built-in synthesizer, so a game with a trimmed bank hears the synthesizer's timbre
|
|
95
|
+
for an instrument it did not ship instead of a piano line.
|
|
96
|
+
- `missingSoundFontPrograms` (`audio`) lists the programs a MIDI usage asks for that a SoundFont 2 or
|
|
97
|
+
3 does not carry. The asset compiler runs it: each embedded font's result has a `missing` list and
|
|
98
|
+
`mwg-emit` and `compile-resources` warn about them.
|
|
99
|
+
- `examples/bridge` and the "Build a bridge game" tutorial (`webpage/tutorials/bridge`): a playable
|
|
100
|
+
truss builder on `XpbdWorld` (three levels, a budget, cables, beams coloured by load, breaking),
|
|
101
|
+
a deterministic replay of the player's build through `Recorder`/`Player`, best results through
|
|
102
|
+
`SaveSystem`, reduced motion and the high-contrast theme. The simulation is renderer-free and
|
|
103
|
+
tested, every tutorial snippet is checked to appear verbatim in the example, and the interface
|
|
104
|
+
step points at `examples/mwl-presentation` for HUD and windows described as MWL content.
|
|
105
|
+
- An examples index (`tools/build-example-index.mjs`, written by `npm run webpage:docs` to
|
|
106
|
+
`documentation/examples.html`): for each public module every exported symbol with an `@example`,
|
|
107
|
+
its first lines and a link to its API page, plus the runnable examples that import the module.
|
|
108
|
+
|
|
109
|
+
### Fixed
|
|
110
|
+
|
|
111
|
+
- A request for a drum kit the font lacks falls back to the standard kit, never to a melodic
|
|
112
|
+
preset: it used to take the same program number from bank 0, so a font with a piano and no drums
|
|
113
|
+
played the piano where the kick should be.
|
|
114
|
+
|
|
10
115
|
## [0.41.0] - 2026-10-16
|
|
11
116
|
|
|
12
117
|
`microgm-opt` stays the default SoundFont, and a game that wants another one can now pick it:
|
package/README.md
CHANGED
|
@@ -8,6 +8,10 @@ events, quests, stats and turn order underneath are the same either way.
|
|
|
8
8
|
Licensed under the [MPL-2.0](LICENSE): games built with `mwg` carry no licence obligation
|
|
9
9
|
of their own; improvements to `mwg`'s own files are shared back.
|
|
10
10
|
|
|
11
|
+
The canonical npm package is `@datamoc/mw_games`; use that name for installation, imports,
|
|
12
|
+
tools and bundled data such as `data/microgm-opt.sf2`. `@datamoc/mwg` remains a compatible
|
|
13
|
+
re-export package for existing projects. The recipes here consistently use the canonical name.
|
|
14
|
+
|
|
11
15
|
**[Live examples and API docs](https://datamoc.github.io/mwg/)**: every example below,
|
|
12
16
|
playable in the browser with no download, plus the generated API reference.
|
|
13
17
|
|
|
@@ -15,7 +19,7 @@ playable in the browser with no download, plus the generated API reference.
|
|
|
15
19
|
planning or porting a demanding game with mwg, with links to the [mwg reference](https://github.com/datamoc/mwg/blob/main/REFERENCE.md)
|
|
16
20
|
and the [mwg Pixel Dungeon study](https://datamoc.github.io/mwg-pixel-dungeon/).
|
|
17
21
|
|
|
18
|
-
> **Status: pre-alpha
|
|
22
|
+
> **Status: pre-alpha.** Every module in the shared floor below, plus optional
|
|
19
23
|
> 3D, mobile (Capacitor) and desktop (WebView2) packaging, is built and tested - see
|
|
20
24
|
> [ROADMAP.md](https://github.com/datamoc/mwg/blob/main/ROADMAP.md) for open work, and
|
|
21
25
|
> [CLOSED.md](https://github.com/datamoc/mwg/blob/main/CLOSED.md) for the full, numbered shipped history.
|
|
@@ -184,6 +188,63 @@ private or trustworthy, since the page that rewrites a save can recompute any ch
|
|
|
184
188
|
never put a secret in a save or in settings (an account token, a password, a licence key), and
|
|
185
189
|
treat a save as the player's own editable file, which it is.
|
|
186
190
|
|
|
191
|
+
## Loading assets and input
|
|
192
|
+
|
|
193
|
+
`await load(['vehicles/car.svg'], { resolution: 2 })` rasterizes SVG assets at twice
|
|
194
|
+
their logical resolution, keeping them sharp when a camera zooms in. Import `load` from
|
|
195
|
+
`@datamoc/mw_games/assets`. `loadSvgParts(path, { resolution: 2 })` loads top-level SVG
|
|
196
|
+
groups as textures in their authored stacking order.
|
|
197
|
+
|
|
198
|
+
Input events live under the `Input` namespace: import `{ Input }` from
|
|
199
|
+
`@datamoc/mw_games/core` and subscribe with `Input.onAction.add(...)`. `onAction` is not a
|
|
200
|
+
direct core export. `Input.attachPointer` supplies pointer and drag events for mouse and touch.
|
|
201
|
+
|
|
202
|
+
## Audio
|
|
203
|
+
|
|
204
|
+
Use `AudioBus` for one playback API across ordinary audio and MIDI assets. Its default
|
|
205
|
+
loader reads embedded assets, detects MIDI by its header and renders it on a worker.
|
|
206
|
+
`playBgm`/`playBgs` loop, while `playMe`/`playSe` play once; all take the same
|
|
207
|
+
`{ name, volume, pitch, pan }` fields (volume 0-100, pitch 100 unchanged, pan -100 to 100).
|
|
208
|
+
Pass `load: createAudioLoader(context, { midi: { soundFontBytes, missingProgram: 'synth' } })`
|
|
209
|
+
to use a SoundFont. `Sound` remains useful for pooled HTML audio effects and `MusicPlayer`
|
|
210
|
+
for a seekable jukebox.
|
|
211
|
+
|
|
212
|
+
`Sound` pools short effects. `sound.play(0.7, 1.1)` sets gain and pitch for that play;
|
|
213
|
+
`synthesizeSound({ waveform: 'sawtooth', frequency: 110, duration: 0.2 })` makes a pooled
|
|
214
|
+
effect without an asset file. `Sound.plays` counts started playback attempts for browser tests.
|
|
215
|
+
|
|
216
|
+
For MIDI music, copy the supplied `data/microgm-opt.sf2` from the installed package into
|
|
217
|
+
your assets folder alongside the `.mid` files. `mwgPage({ assets: 'assets' })` automatically
|
|
218
|
+
reduces the embedded SoundFont to the programs those MIDI files use. Load both through
|
|
219
|
+
`loadBinary`, then render once on a worker and play the resulting buffer:
|
|
220
|
+
|
|
221
|
+
For a smaller starting bank, `data/microgm-essential.sf2` supplies piano, guitar, violin,
|
|
222
|
+
trumpet, flute and standard drums in 314 KB. Use it instead of the full bank below, with
|
|
223
|
+
`missingProgram: 'synth'` for the remaining instruments. Both banks retain their WTFPL licence.
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
import { loadBinary, getBinary } from '@datamoc/mw_games/assets';
|
|
227
|
+
import { MusicPlayer, renderMidiToBufferAsync } from '@datamoc/mw_games/audio';
|
|
228
|
+
|
|
229
|
+
const player = new MusicPlayer(new AudioContext(), { volume: 0.7 });
|
|
230
|
+
await loadBinary(['music/theme.mid', 'soundfonts/microgm-opt.sf2']);
|
|
231
|
+
player.load(await renderMidiToBufferAsync(getBinary('music/theme.mid'), {
|
|
232
|
+
soundFontBytes: getBinary('soundfonts/microgm-opt.sf2'),
|
|
233
|
+
missingProgram: 'synth',
|
|
234
|
+
}));
|
|
235
|
+
player.play({ repeat: true });
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Start playback from a player gesture. Call `player.update()` each frame to dispatch
|
|
239
|
+
`onEnded` for a nonrepeating track; that signal lets the game choose its next track.
|
|
240
|
+
`pause()`, `resume()`, `position()` and `seek(seconds)` share the same playhead.
|
|
241
|
+
`toAudioBuffer(context, rendered)` also converts a render for direct `Channel` use.
|
|
242
|
+
`missingProgram: 'synth'` voices instruments absent from the font with the built-in synth.
|
|
243
|
+
|
|
244
|
+
To catch page growth, set `mwgPage({ maxPageBytes: 1_500_000 })`. Its build report lists
|
|
245
|
+
embedded paths not found literally in the generated game script as potentially unused;
|
|
246
|
+
paths assembled at runtime need manual review. `assetFiles` selects exact asset paths to embed.
|
|
247
|
+
|
|
187
248
|
## Capability spec
|
|
188
249
|
|
|
189
250
|
The definition of done for 1.0, drawn from what each of the nine references actually
|
package/REFERENCE.md
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
A one-line-per-export index of `@datamoc/mw_games`'s public API - what exists and where,
|
|
4
4
|
not how to use it. The published Documentation page is this file rendered as HTML; the
|
|
5
5
|
full generated API (every parameter, every doc comment) lives beside it at
|
|
6
|
-
`webpage/documentation/api
|
|
6
|
+
`webpage/documentation/api/`, with an examples index beside it at
|
|
7
|
+
`webpage/documentation/examples.html` listing every `@example` per module (all from
|
|
8
|
+
`npm run webpage:docs`). For *why* the framework
|
|
7
9
|
is shaped this way and what it's for, see `README.md`'s capability spec. For architecture
|
|
8
10
|
and build commands, see `DEVELOPMENT.md`. For the order things shipped in and the reasoning
|
|
9
11
|
behind it, see `CLOSED.md`; for what's still open, see `ROADMAP.md`.
|
|
@@ -528,6 +530,12 @@ batcher/high-shader internals are confined to `ColorTransformBatcher.ts`.
|
|
|
528
530
|
`sequence(steps)` (item 302) chains fade/hold/flash steps end to end as one call, for the
|
|
529
531
|
hold-then-fade-back transition the four individual methods cannot express alone; `update`
|
|
530
532
|
keeps returning false at every step boundary, true only once the whole sequence finishes.
|
|
533
|
+
- `SceneTransition`/`TransitionOptions`/`TransitionRunner` + `tileShrink`, `shrink`, `pop`, `grow`, `slide`, `iris`
|
|
534
|
+
and `dissolve` - screen transitions: `Game.switchScene(Next, { transition, duration, easing })` photographs the
|
|
535
|
+
outgoing scene and animates it away (or grows the new one over it), `Scene2D.transitionTo(swap, options)` does
|
|
536
|
+
the same for a scene changing its own content, `Game.transitioning` tells a scene to ignore input, and
|
|
537
|
+
reduced motion skips it. `tileDelays`, `tileProgress`, `popScale`, `openAnimationFrame` and `resolveEasing` are the
|
|
538
|
+
timing as plain functions.
|
|
531
539
|
- `ActorAnimator` - an idle/move/action animation state machine with one interruption rule.
|
|
532
540
|
- `StatusVisuals` - composes every active status effect's colour additively onto a sprite's
|
|
533
541
|
`colorAdd` (item 301), instead of one status winning by declaration order; each channel clips
|
|
@@ -661,7 +669,9 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
661
669
|
- `BitmapLabel`/`bitmapLabelStyle` - bitmap-font-backed text, for a HUD value redrawn
|
|
662
670
|
every frame where `Label`'s per-string texture re-render would be wasteful.
|
|
663
671
|
- `NinePatch` - a resizable nine-slice panel.
|
|
664
|
-
- `Window` - a themed panel container. `
|
|
672
|
+
- `Window` - a themed panel container. `open({ animation, from })` plays `pop`, `grow`, `slide` or `fade`
|
|
673
|
+
(`WindowAnimation`) and `close({ animation })` plays it backwards before closing, both driven by
|
|
674
|
+
`update(dt)` and skipped under reduced motion. `close()` frees the window and its contents and is
|
|
665
675
|
idempotent; `closed` is the guard for the caller's reference afterwards (input and `place` on a
|
|
666
676
|
closed window are no-ops), and `WindowStack.push` refuses one with a named error. A window's own
|
|
667
677
|
area is not clickable, so a click travels to whatever is under it; `blocker: true` adds the
|
|
@@ -693,7 +703,11 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
693
703
|
- `Spinner`/`SpinnerOptions` + `spinValue` - an up/down numeric stepper; `spinValue` is the rule
|
|
694
704
|
(snap to the step, then clamp at the ends or wrap past them).
|
|
695
705
|
- `DropdownView`/`DropdownViewOptions` - the widget over a `Dropdown` model: a button showing the choice
|
|
696
|
-
that opens a column of `Button`s, the
|
|
706
|
+
that opens a column of `Button`s on an opaque backdrop, the highlighted one marked with
|
|
707
|
+
`Button.setSelected`. `maxVisibleRows` makes a long list scroll (wheel, touch, keys, scrollbar) and it opens with
|
|
708
|
+
the chosen row in view; `autoWidth` fits the longest label; `handleKey` (arrows, Page, Home/End, Enter,
|
|
709
|
+
Escape) and `handleOutsideClick` are what the scene forwards from its own input; `fitBelow` limits the
|
|
710
|
+
rows to what the viewport leaves under the button.
|
|
697
711
|
- `Dropdown`/`DropdownOption`/`DropdownOptions` - a renderer-free option button: a selected entry,
|
|
698
712
|
an open list, a highlight that starts on the selection and skips disabled options.
|
|
699
713
|
- `TextModel`/`TextModelOptions` - the editing state behind a text field, fed by `core.Input`'s
|
|
@@ -725,8 +739,19 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
725
739
|
highlight.
|
|
726
740
|
- `TreeView`/`TreeNode`/`TreeRow`/`TreeViewOptions` - a renderer-free collapsible tree, flattened to
|
|
727
741
|
the rows on screen; collapsing the branch the highlight is inside lands it on the branch.
|
|
728
|
-
- `ScrollBox`/`ScrollBoxOptions` + `scrollOffset` - a clipped viewport with a themed scrollbar;
|
|
729
|
-
`scrollOffset` is the clamping rule the wheel, `scrollBy` and `scrollIntoView` all share.
|
|
742
|
+
- `ScrollBox`/`ScrollBoxOptions`/`ScrollKey` + `scrollOffset` - a clipped viewport with a themed scrollbar;
|
|
743
|
+
`scrollOffset` is the clamping rule the wheel, `scrollBy` and `scrollIntoView` all share. `touch: true` lets a
|
|
744
|
+
finger drag the content (a drag cancels the tap on the row under it) and coast on release, `handleKey`
|
|
745
|
+
scrolls by line, page and to either end, the thumb drags and the track jumps, and `autoHeight` sizes the
|
|
746
|
+
viewport to the content up to `height`. `update(dt)` drives the coasting.
|
|
747
|
+
- `ScrollDrag` - touch scrolling as plain numbers (`start`/`move`/`end`/`step`): how far the content follows a
|
|
748
|
+
finger and how it coasts after release, with no pointer events or clock of its own.
|
|
749
|
+
- `ScrollText`/`ScrollTextOptions` - a wrapped `Label` in a self-sizing `ScrollBox`: as tall as the text up to
|
|
750
|
+
`maxHeight`, scrolling beyond it, re-measured by `setText`.
|
|
751
|
+
- `MarqueeLabel`/`MarqueeLabelOptions` + `marqueeOffset` - one line that stays on one line: a long text slides
|
|
752
|
+
back and forth in a clipped window (and holds still, cut short, under reduced motion).
|
|
753
|
+
- `ellipsize` and `truncateLines` - cut a text to a width or a number of lines, ending in an ellipsis, with the
|
|
754
|
+
caller supplying the measuring function.
|
|
730
755
|
- `flowLayout(items, width, { gap, align })` - left-to-right rows that wrap, returning each item's
|
|
731
756
|
position and the total height: a toolbar that takes two rows on a phone.
|
|
732
757
|
- `Grid`/`GridSpec`/`GridTrack` + `resolveAnchor`/`anchorAlign` with `Anchor`/`AnchorSpec`/
|
|
@@ -772,6 +797,10 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
772
797
|
- `Toast` - a queued, timed pop-up notification (fade in, hold, fade out).
|
|
773
798
|
- `Tooltip` - a hover explanation over a themed `Window`: a frame-driven hover delay, and
|
|
774
799
|
edge-aware placement that flips rather than letting the panel run off screen.
|
|
800
|
+
- `TooltipHost`/`TooltipHostOptions`/`TooltipTarget` - drives one `Tooltip` from rectangles: a press that rests
|
|
801
|
+
for `longPress` seconds shows the text on a touch screen, and a disabled `Button` (which gets no pointer
|
|
802
|
+
events) is still explained from its bounds and its `tooltip`. `attach(pointer)` wires an
|
|
803
|
+
`Input.attachPointer` tracker.
|
|
775
804
|
- `HelpScreen` - a topic-list-plus-body help/controls screen.
|
|
776
805
|
- `StatsScreen` - a player-stats display screen.
|
|
777
806
|
- `LoadingScreen` - a progress-bar loading screen wired to `core.LoadQueue`.
|
|
@@ -822,7 +851,8 @@ The `file://` story: a compiled build resolves paths through a `data:` URI map;
|
|
|
822
851
|
serves them normally. Load once per scene; everything after is synchronous. The compiler side
|
|
823
852
|
of this (`tools/compile-resources.mjs`, `tools/emit-page.mjs`, `tools/single-file.mjs`) offers
|
|
824
853
|
independent build options on top of that one story - multi-file vs. single-file output,
|
|
825
|
-
compression (gzip or brotli, single-file only),
|
|
854
|
+
compression (gzip or brotli, single-file only), WebP image conversion and SVG optimisation
|
|
855
|
+
(`svgo: true`, ids and `viewBox` kept, only when smaller) - documented in
|
|
826
856
|
`DEVELOPMENT.md`'s "Build options" section and `tools.md`'s tool reference; none of them
|
|
827
857
|
change what a game calls to load an asset.
|
|
828
858
|
|
|
@@ -892,11 +922,17 @@ reach the compiled asset map without it.
|
|
|
892
922
|
(`force`, smoothed `ratio`, `peakRatio`) and `onBreak` when a link passes its strength; `ground` (a height or a function of x) holds nodes
|
|
893
923
|
on a surface with `groundFriction`. The engine of
|
|
894
924
|
Pontifex, generalised: bridges, trusses, cranes, anything where the question is which beam fails.
|
|
925
|
+
`XpbdWorld<T>`/`XpbdLink<T>`/`XpbdLinkSpec<T>` carry game-owned link `data` without
|
|
926
|
+
affecting the solver. `breakLink(index)` dispatches rupture once. `linksInBox` includes
|
|
927
|
+
segment crossings; `linksNear` orders nearby unbroken segments by distance. `snapshot()`
|
|
928
|
+
returns positions, velocities, load state and queued forces as detached arrays; `hash()`
|
|
929
|
+
compares their exact numeric bits for runs with the same configuration. Payloads and ground
|
|
930
|
+
callbacks are excluded. The bridge example applies a moving vehicle's weight with `applyForce`.
|
|
895
931
|
- `PhysicsWorld` (`@datamoc/mw_games/physics`) - rigid bodies and constraints in plain TypeScript, no engine
|
|
896
932
|
dependency: `addBox`/`addCircle`/`addConstraint`/`remove`, `update(dt)` from
|
|
897
933
|
`Scene.update`, `onContact`, `alpha` for interpolation. A constraint with `maxStretch` breaks when pulled
|
|
898
934
|
that far past its rest length (`onBreak`, `stretchOf`). Bodies never spin; `angle` is a display offset.
|
|
899
|
-
- `synthesizeTone`/`synthesizeSound`/`playTone`/`Waveform` - a runtime square/triangle/sine/noise
|
|
935
|
+
- `synthesizeTone`/`synthesizeSound`/`playTone`/`Waveform` - a runtime square/triangle/sine/noise/sawtooth
|
|
900
936
|
waveform synth for a procedural tone or SFX, no sample library; `synthesizeSound` returns a
|
|
901
937
|
pooled `Sound` over the tone.
|
|
902
938
|
- `parseMidi`/`scheduleMidi`/`midiLoopStart`/`noteToFrequency`/`MidiPlayer` - a small `.mid` file player
|
|
@@ -932,6 +968,10 @@ reach the compiled asset map without it.
|
|
|
932
968
|
own rate, so the browser never resamples it. A Node tool passes a decoder of its own.
|
|
933
969
|
- `collectSoundFontUsage` - lists the (bank, program) pairs scheduled notes actually
|
|
934
970
|
request across MIDI files, drums as bank 128, for the compile step to keep.
|
|
971
|
+
- `missingSoundFontPrograms` - the pairs of a usage list a font does not carry (the bank 0
|
|
972
|
+
fallback counts as carried, a drum kit needs that exact kit), on a SoundFont 2 or 3. The
|
|
973
|
+
build runs it for you: each embedded font's result lists its `missing` programs and
|
|
974
|
+
`mwg-emit` warns.
|
|
935
975
|
- `mergeSoundFonts` - joins several SoundFont 2 files (or several SoundFont 3 files, the
|
|
936
976
|
Ogg bytes copied untouched) into one, presets in the order given and the first font winning
|
|
937
977
|
a shared (bank, program), which is how single-instrument files (the per-program collection
|
|
@@ -945,12 +985,27 @@ reach the compiled asset map without it.
|
|
|
945
985
|
the programs explicitly, and with no shipped MIDI the font embeds whole because
|
|
946
986
|
the usage is unknowable. The reference bank in `data/microgm-opt.sf2` (2.6 MB,
|
|
947
987
|
WTFPL, shipped in the npm package for a game to copy into its assets) drops to
|
|
948
|
-
tens of kilobytes per game this way
|
|
988
|
+
tens of kilobytes per game this way: measured, piano and drums come to 264 KB, eight
|
|
989
|
+
essential programs (piano, guitar, bass, strings, brass, flute, synth lead, drums) to 421 KB,
|
|
990
|
+
sixteen to 585 KB. A program the font lacks plays the font's own piano (preset 0:0) and,
|
|
991
|
+
only when it has no piano either, the built-in synthesizer; `parseSoundFont(data, { missingProgram:
|
|
992
|
+
'synth' })` (also on `loadSoundFont` and `renderMidiToBufferAsync`) hands those instruments to
|
|
993
|
+
the synthesizer instead of a piano line, and a bank missing the program or a drum kit still falls
|
|
994
|
+
back to bank 0 or the standard kit, never to a melodic preset. It is the default bank. A game that wants another
|
|
949
995
|
picks it from `data/soundfont-catalog.json` and fetches just its instruments with
|
|
950
996
|
`mwg-soundfonts` (see [tools.md](tools.md)). A SoundFont 3 stays compressed all the way to
|
|
951
997
|
the page (FluidR3's piano, guitar and drums: 2.7 MB, against 20 MB decoded to a SoundFont 2),
|
|
952
998
|
and `loadSoundFont` decodes it at load, so prefer it over a SoundFont 2 for anything but the
|
|
953
999
|
smallest banks.
|
|
1000
|
+
- `createAudioLoader`/`AudioLoaderOptions` - a cached loader for embedded audio and MIDI,
|
|
1001
|
+
shared by all `AudioBus` lifetimes. MIDI is detected by header and rendered on a worker;
|
|
1002
|
+
other files use `decodeAudioData`. Options select SoundFont/render settings or a byte source.
|
|
1003
|
+
`AudioBus` uses this loader by default, so `playBgm` and `playSe` accept asset paths with
|
|
1004
|
+
the same volume, pitch and pan fields. BGM/BGS loop; ME/SE play once.
|
|
1005
|
+
- `MusicPlayer`/`MusicPlayerOptions`/`MusicPlayOptions` - one loaded `AudioBuffer` or
|
|
1006
|
+
`RenderedMidi`, with play, pause, resume, seek, position, volume and an `onEnded` signal.
|
|
1007
|
+
Call `update()` each frame to dispatch track completion and choose the next track.
|
|
1008
|
+
`toAudioBuffer(context, rendered)` copies rendered stereo samples into an `AudioBuffer`.
|
|
954
1009
|
- `Channel`/`AudioBus` - buffered WebAudio music: a `Channel` plays one looping track with
|
|
955
1010
|
volume, pitch, pan, a loop region and a seekable playhead, and an `AudioBus` wires two of
|
|
956
1011
|
them into independent BGM/BGS loops, ME jingles that suspend and resume the BGM,
|
|
@@ -1515,6 +1570,8 @@ nothing here reaches a shipped game unless a test imports it.
|
|
|
1515
1570
|
- `memoryStorage` - a `SaveStorage` held in memory, for `SaveSystem`, `Settings`, `Collection`,
|
|
1516
1571
|
`PlayerStats` and the other stored values; the same class `core` falls back to where
|
|
1517
1572
|
`localStorage` is missing.
|
|
1573
|
+
- `exposeForTests(name, object)` - publishes inspectable game state under
|
|
1574
|
+
`window.__mwg_test__[name]` for browser tests; does nothing outside a browser.
|
|
1518
1575
|
- `fakeAudio`/`fakeAudioWithEnded`/`FakePlayable` - a `Playable` for `Sound`/`Music`'s `create`
|
|
1519
1576
|
that counts its plays; the second also has the `onended` slot a backend reporting track ends
|
|
1520
1577
|
has.
|
|
@@ -1734,6 +1791,11 @@ consumes generated data and does not parse `.mwl` source files in the browser.
|
|
|
1734
1791
|
`id=rebels` validates) keys `world.sides` and `world.gold`, marks a map keep (`rebels Kh`), and
|
|
1735
1792
|
is what `unit.side`, `MwlMessage.side` and every `side`/`side_filter` filter hold. `MwlSideRef`
|
|
1736
1793
|
is just that id.
|
|
1794
|
+
- `nodesToObjects`/`MwlObject` - converts compiled nodes to JSON objects using the tag
|
|
1795
|
+
schema: declared numbers and booleans become typed fields, child tags become ordered
|
|
1796
|
+
arrays, and a `cardinality.max` of 1 selects a single object. Missing optional fields stay
|
|
1797
|
+
absent. Check the returned diagnostics for conversion errors and field collisions.
|
|
1798
|
+
Attributes not listed in `MwlTagSchema.required` are optional; use `number`, not `number?`.
|
|
1737
1799
|
- `readAttributes` / `readChildren` - shared typed readers for compiled nodes. They coerce
|
|
1738
1800
|
scalars and lists and return source-located diagnostics instead of silently guessing.
|
|
1739
1801
|
- `MwlNode.attributeLocations` / `valueLocations` - parsed JSON5 nodes retain the exact key and
|
|
@@ -1756,6 +1818,9 @@ consumes generated data and does not parse `.mwl` source files in the browser.
|
|
|
1756
1818
|
absent cell omits the key), and `emitTableTypes` emits one named row interface plus one
|
|
1757
1819
|
typed accessor per table for a game's build script to write and import, so a changed
|
|
1758
1820
|
column type or a removed column fails type-checking at every use.
|
|
1821
|
+
`emitTableTypes(tables, { literalColumns: ['id', 'sprite'] })` also exports sorted,
|
|
1822
|
+
deduplicated readonly value lists and literal unions such as `VehiclesSprite` from the
|
|
1823
|
+
current string-column values. Regenerate them with the catalog to catch stale game ids.
|
|
1759
1824
|
- `MwlValidationOptions.tableReferences`/`MwlTableReference` (item 362) - declarative
|
|
1760
1825
|
cross-table checks, so no game hand-rolls one validator per table: a column declares
|
|
1761
1826
|
`references: { table, column }` or `oneOf: [...]`, and `validateCatalog` checks every cell
|