@datamoc/mw_games 0.42.0 → 0.44.2
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 +126 -0
- package/README.md +84 -1
- package/REFERENCE.md +134 -6
- package/api-surface.json +1814 -116
- package/data/microgm-essential.PROVENANCE.md +13 -0
- package/data/microgm-essential.sf2 +0 -0
- package/data/soundfont-catalog.json +8 -0
- package/dist/actors/Resource.d.ts.map +1 -1
- package/dist/actors/Resource.js +2 -3
- package/dist/actors/Resource.js.map +1 -1
- package/dist/audio/AdaptiveMusic.d.ts +85 -0
- package/dist/audio/AdaptiveMusic.d.ts.map +1 -0
- package/dist/audio/AdaptiveMusic.js +230 -0
- package/dist/audio/AdaptiveMusic.js.map +1 -0
- package/dist/audio/AudioLoader.d.ts +33 -0
- package/dist/audio/AudioLoader.d.ts.map +1 -0
- package/dist/audio/AudioLoader.js +58 -0
- package/dist/audio/AudioLoader.js.map +1 -0
- package/dist/audio/AudioViews.d.ts +70 -0
- package/dist/audio/AudioViews.d.ts.map +1 -0
- package/dist/audio/AudioViews.js +119 -0
- package/dist/audio/AudioViews.js.map +1 -0
- package/dist/audio/BusBudget.d.ts +89 -0
- package/dist/audio/BusBudget.d.ts.map +1 -0
- package/dist/audio/BusBudget.js +120 -0
- package/dist/audio/BusBudget.js.map +1 -0
- package/dist/audio/Channels.d.ts +66 -1
- package/dist/audio/Channels.d.ts.map +1 -1
- package/dist/audio/Channels.js +119 -31
- package/dist/audio/Channels.js.map +1 -1
- package/dist/audio/CoreHost.d.ts +114 -0
- package/dist/audio/CoreHost.d.ts.map +1 -0
- package/dist/audio/CoreHost.js +335 -0
- package/dist/audio/CoreHost.js.map +1 -0
- package/dist/audio/DecoderGuard.d.ts +139 -0
- package/dist/audio/DecoderGuard.d.ts.map +1 -0
- package/dist/audio/DecoderGuard.js +208 -0
- package/dist/audio/DecoderGuard.js.map +1 -0
- package/dist/audio/Fft.d.ts +3 -0
- package/dist/audio/Fft.d.ts.map +1 -0
- package/dist/audio/Fft.js +45 -0
- package/dist/audio/Fft.js.map +1 -0
- package/dist/audio/LoopTags.d.ts +18 -0
- package/dist/audio/LoopTags.d.ts.map +1 -1
- package/dist/audio/LoopTags.js +20 -0
- package/dist/audio/LoopTags.js.map +1 -1
- package/dist/audio/Midi.d.ts +35 -0
- package/dist/audio/Midi.d.ts.map +1 -1
- package/dist/audio/Midi.js +71 -1
- package/dist/audio/Midi.js.map +1 -1
- package/dist/audio/MidiRender.d.ts +47 -0
- package/dist/audio/MidiRender.d.ts.map +1 -1
- package/dist/audio/MidiRender.js +100 -2
- package/dist/audio/MidiRender.js.map +1 -1
- package/dist/audio/MidiSynth.d.ts +147 -0
- package/dist/audio/MidiSynth.d.ts.map +1 -0
- package/dist/audio/MidiSynth.js +205 -0
- package/dist/audio/MidiSynth.js.map +1 -0
- 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/Playable.d.ts +7 -0
- package/dist/audio/Playable.d.ts.map +1 -1
- package/dist/audio/Playable.js.map +1 -1
- package/dist/audio/Positional.d.ts +12 -4
- package/dist/audio/Positional.d.ts.map +1 -1
- package/dist/audio/Positional.js +5 -2
- package/dist/audio/Positional.js.map +1 -1
- package/dist/audio/Resample.d.ts +61 -0
- package/dist/audio/Resample.d.ts.map +1 -0
- package/dist/audio/Resample.js +97 -0
- package/dist/audio/Resample.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/Settings.d.ts +99 -0
- package/dist/audio/Settings.d.ts.map +1 -0
- package/dist/audio/Settings.js +134 -0
- package/dist/audio/Settings.js.map +1 -0
- package/dist/audio/Sfx.d.ts +148 -0
- package/dist/audio/Sfx.d.ts.map +1 -0
- package/dist/audio/Sfx.js +213 -0
- package/dist/audio/Sfx.js.map +1 -0
- 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 +5 -3
- package/dist/audio/SoundFont.d.ts.map +1 -1
- package/dist/audio/SoundFont.js +10 -8
- package/dist/audio/SoundFont.js.map +1 -1
- package/dist/audio/SpectralDistance.d.ts +34 -0
- package/dist/audio/SpectralDistance.d.ts.map +1 -0
- package/dist/audio/SpectralDistance.js +64 -0
- package/dist/audio/SpectralDistance.js.map +1 -0
- package/dist/audio/StreamChannel.d.ts +150 -0
- package/dist/audio/StreamChannel.d.ts.map +1 -0
- package/dist/audio/StreamChannel.js +390 -0
- package/dist/audio/StreamChannel.js.map +1 -0
- package/dist/audio/StreamPlayable.d.ts +73 -0
- package/dist/audio/StreamPlayable.d.ts.map +1 -0
- package/dist/audio/StreamPlayable.js +130 -0
- package/dist/audio/StreamPlayable.js.map +1 -0
- 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/SyxBank.d.ts +73 -0
- package/dist/audio/SyxBank.d.ts.map +1 -0
- package/dist/audio/SyxBank.js +130 -0
- package/dist/audio/SyxBank.js.map +1 -0
- package/dist/audio/Underrun.d.ts +25 -0
- package/dist/audio/Underrun.d.ts.map +1 -0
- package/dist/audio/Underrun.js +18 -0
- package/dist/audio/Underrun.js.map +1 -0
- package/dist/audio/Wav.d.ts +56 -0
- package/dist/audio/Wav.d.ts.map +1 -0
- package/dist/audio/Wav.js +164 -0
- package/dist/audio/Wav.js.map +1 -0
- package/dist/audio/WebMidi.d.ts +58 -0
- package/dist/audio/WebMidi.d.ts.map +1 -0
- package/dist/audio/WebMidi.js +79 -0
- package/dist/audio/WebMidi.js.map +1 -0
- package/dist/audio/humanize/Onsets.d.ts.map +1 -1
- package/dist/audio/humanize/Onsets.js +1 -44
- package/dist/audio/humanize/Onsets.js.map +1 -1
- package/dist/audio/index.d.ts +43 -7
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +21 -3
- package/dist/audio/index.js.map +1 -1
- package/dist/battle/AttackPreview.d.ts.map +1 -1
- package/dist/battle/AttackPreview.js +8 -8
- package/dist/battle/AttackPreview.js.map +1 -1
- package/dist/battle/StatStages.d.ts.map +1 -1
- package/dist/battle/StatStages.js +2 -1
- package/dist/battle/StatStages.js.map +1 -1
- package/dist/board/Engine.d.ts +2 -0
- package/dist/board/Engine.d.ts.map +1 -1
- package/dist/board/Engine.js +1 -1
- package/dist/board/Engine.js.map +1 -1
- package/dist/board/HexSkirmish.d.ts.map +1 -1
- package/dist/board/HexSkirmish.js +11 -72
- package/dist/board/HexSkirmish.js.map +1 -1
- package/dist/board/Tactics.d.ts.map +1 -1
- package/dist/board/Tactics.js +3 -5
- package/dist/board/Tactics.js.map +1 -1
- package/dist/board/tourney.d.ts.map +1 -1
- package/dist/board/tourney.js +2 -19
- package/dist/board/tourney.js.map +1 -1
- package/dist/core/Handles.js +7 -7
- package/dist/core/Handles.js.map +1 -1
- package/dist/mw_games.global.js +25 -22
- package/dist/mwl/conditions.d.ts +12 -0
- package/dist/mwl/conditions.d.ts.map +1 -1
- package/dist/mwl/conditions.js +49 -8
- package/dist/mwl/conditions.js.map +1 -1
- package/dist/mwl/index.d.ts +13 -0
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +7 -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/package-container.d.ts +185 -0
- package/dist/mwl/package-container.d.ts.map +1 -0
- package/dist/mwl/package-container.js +446 -0
- package/dist/mwl/package-container.js.map +1 -0
- package/dist/mwl/package-document.d.ts +217 -0
- package/dist/mwl/package-document.d.ts.map +1 -0
- package/dist/mwl/package-document.js +639 -0
- package/dist/mwl/package-document.js.map +1 -0
- package/dist/mwl/package-rom.d.ts +37 -0
- package/dist/mwl/package-rom.d.ts.map +1 -0
- package/dist/mwl/package-rom.js +96 -0
- package/dist/mwl/package-rom.js.map +1 -0
- package/dist/mwl/package-verify.d.ts +34 -0
- package/dist/mwl/package-verify.d.ts.map +1 -0
- package/dist/mwl/package-verify.js +111 -0
- package/dist/mwl/package-verify.js.map +1 -0
- package/dist/mwl/package.d.ts +184 -0
- package/dist/mwl/package.d.ts.map +1 -0
- package/dist/mwl/package.js +382 -0
- package/dist/mwl/package.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/PhysicsWorld.d.ts.map +1 -1
- package/dist/physics/PhysicsWorld.js +4 -3
- package/dist/physics/PhysicsWorld.js.map +1 -1
- package/dist/physics/XpbdWorld.d.ts +23 -5
- package/dist/physics/XpbdWorld.d.ts.map +1 -1
- package/dist/physics/XpbdWorld.js +91 -6
- package/dist/physics/XpbdWorld.js.map +1 -1
- package/dist/testing/FakeAudio.d.ts +149 -0
- package/dist/testing/FakeAudio.d.ts.map +1 -0
- package/dist/testing/FakeAudio.js +219 -0
- package/dist/testing/FakeAudio.js.map +1 -0
- package/dist/testing/index.d.ts +18 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +22 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/threads/index.d.ts +2 -2
- package/dist/threads/index.d.ts.map +1 -1
- package/dist/threads/index.js +1 -1
- package/dist/threads/index.js.map +1 -1
- package/dist/threads/spawn.d.ts +32 -0
- package/dist/threads/spawn.d.ts.map +1 -1
- package/dist/threads/spawn.js +35 -11
- package/dist/threads/spawn.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 +32 -1
- package/dist/two-d/Game.d.ts.map +1 -1
- package/dist/two-d/Game.js +105 -14
- 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/HazeLayer.d.ts.map +1 -1
- package/dist/two-d/render/HazeLayer.js +2 -1
- package/dist/two-d/render/HazeLayer.js.map +1 -1
- package/dist/two-d/render/ImageModifiers.d.ts +4 -2
- package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
- package/dist/two-d/render/ImageModifiers.js +13 -8
- package/dist/two-d/render/ImageModifiers.js.map +1 -1
- package/dist/two-d/render/NeonGlow.d.ts.map +1 -1
- package/dist/two-d/render/NeonGlow.js +2 -1
- package/dist/two-d/render/NeonGlow.js.map +1 -1
- package/dist/two-d/render/Particles.d.ts.map +1 -1
- package/dist/two-d/render/Particles.js +2 -1
- package/dist/two-d/render/Particles.js.map +1 -1
- package/dist/two-d/render/SceneTransition.d.ts +301 -0
- package/dist/two-d/render/SceneTransition.d.ts.map +1 -0
- package/dist/two-d/render/SceneTransition.js +458 -0
- package/dist/two-d/render/SceneTransition.js.map +1 -0
- package/dist/two-d/render/ScreenEffects.d.ts.map +1 -1
- package/dist/two-d/render/ScreenEffects.js +2 -1
- package/dist/two-d/render/ScreenEffects.js.map +1 -1
- package/dist/two-d/render/SkeletalSprite.d.ts.map +1 -1
- package/dist/two-d/render/SkeletalSprite.js +2 -1
- package/dist/two-d/render/SkeletalSprite.js.map +1 -1
- package/dist/two-d/render/Types2D.d.ts +2 -2
- package/dist/two-d/render/Types2D.d.ts.map +1 -1
- package/dist/two-d/render/Types2D.js +2 -2
- package/dist/two-d/render/Types2D.js.map +1 -1
- 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/Bar.d.ts.map +1 -1
- package/dist/two-d/ui/Bar.js +3 -5
- package/dist/two-d/ui/Bar.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/Dropdown.d.ts +5 -1
- package/dist/two-d/ui/Dropdown.d.ts.map +1 -1
- package/dist/two-d/ui/Dropdown.js +8 -1
- package/dist/two-d/ui/Dropdown.js.map +1 -1
- package/dist/two-d/ui/DropdownView.d.ts +63 -3
- package/dist/two-d/ui/DropdownView.d.ts.map +1 -1
- package/dist/two-d/ui/DropdownView.js +168 -9
- package/dist/two-d/ui/DropdownView.js.map +1 -1
- package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingText.js +2 -1
- package/dist/two-d/ui/FloatingText.js.map +1 -1
- package/dist/two-d/ui/IconGrid.d.ts.map +1 -1
- package/dist/two-d/ui/IconGrid.js +2 -1
- package/dist/two-d/ui/IconGrid.js.map +1 -1
- package/dist/two-d/ui/ListView.d.ts.map +1 -1
- package/dist/two-d/ui/ListView.js +2 -1
- package/dist/two-d/ui/ListView.js.map +1 -1
- package/dist/two-d/ui/ScrollBox.d.ts +96 -0
- package/dist/two-d/ui/ScrollBox.d.ts.map +1 -1
- package/dist/two-d/ui/ScrollBox.js +218 -5
- package/dist/two-d/ui/ScrollBox.js.map +1 -1
- package/dist/two-d/ui/ScrollText.d.ts +131 -0
- package/dist/two-d/ui/ScrollText.d.ts.map +1 -0
- package/dist/two-d/ui/ScrollText.js +218 -0
- package/dist/two-d/ui/ScrollText.js.map +1 -0
- package/dist/two-d/ui/Slider.d.ts.map +1 -1
- package/dist/two-d/ui/Slider.js +4 -4
- package/dist/two-d/ui/Slider.js.map +1 -1
- 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/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +14 -3
- package/sbom.cdx.json +19 -260
- package/tools/audio.d.mts +75 -0
- package/tools/audio.mjs +437 -0
- 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 +2 -0
- package/tools/compile-resources.mjs +13 -1
- package/tools/emit-page.d.mts +6 -0
- package/tools/emit-page.mjs +43 -3
- package/tools/find-chrome.mjs +4 -2
- package/tools/init.mjs +11 -3
- package/tools/mwl.mjs +144 -2
- package/tools/play-uci.mjs +9 -5
- package/tools/vite.mjs +9 -1
- package/tools.md +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,132 @@ the public API may still change between minor versions.
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.44.2] - 2026-10-17
|
|
11
|
+
|
|
12
|
+
Hardening of `.mwp` content package loading, found by security reviews of 0.44.0 and 0.44.1.
|
|
13
|
+
Versions 0.44.0 and 0.44.1 were tagged and released on GitHub but their npm stages were rejected and
|
|
14
|
+
they were never published, so 0.44.2 is the first public release of everything in their entries below.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- A WAV header with a zero sample rate or block align fell back to its declared `byteRate`, so
|
|
19
|
+
a lying header could still shorten the measured duration. The rate is now the lowest of the
|
|
20
|
+
declared one, `sampleRate * blockAlign` and `sampleRate * channels * ceil(bits / 8)`, and a zero in
|
|
21
|
+
any of them refuses the file as unreadable.
|
|
22
|
+
|
|
23
|
+
## [0.44.1] - 2026-10-17
|
|
24
|
+
|
|
25
|
+
> Superseded by 0.44.2 and never published to npm.
|
|
26
|
+
|
|
27
|
+
Hardening of `.mwp` content package loading, found by a security review of 0.44.0.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- A package could multiply its work through repeated file imports: depth was bounded at 4 but
|
|
32
|
+
the total was not. Imports now share a budget of 64 reads per package document.
|
|
33
|
+
- An import or a `main` document named like an `Object.prototype` member (`constructor`) crashed
|
|
34
|
+
the loader with a `TypeError`; lookups are now own-property only and report a
|
|
35
|
+
`PackageFormatError`.
|
|
36
|
+
- A WAV header declaring a large `byteRate` could shorten the duration measured against
|
|
37
|
+
`maxAudioSeconds`; the lower of the declared rate and `sampleRate * blockAlign` is used.
|
|
38
|
+
|
|
39
|
+
## [0.44.0] - 2026-10-17
|
|
40
|
+
|
|
41
|
+
> Superseded by 0.44.2 and never published to npm.
|
|
42
|
+
|
|
43
|
+
Retro-audio integration contracts (a MIDI engine registry, streaming playback, budgets, content packages and a command line tool), player-facing audio settings, and the interface follow-ups of the last Pontifex round. The parked items 393, 394, Syzygy and NNUE are closed rather than waiting on files or weights that will not arrive, and a simplification pass over the whole tree removes duplicated code.
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- Pontifex follow-up points 51-54 (roadmap 443-446): `ScrollBox` now owns its `hitArea`
|
|
48
|
+
(its viewport, kept current by `resize`/`setContentHeight`, with a `viewportWidth`
|
|
49
|
+
getter); `DropdownView` and `ScrollText` gain `resize(width)`, and `DropdownViewOptions`
|
|
50
|
+
gains `maxWidth` capping `autoWidth`; a transition requested while one is playing is
|
|
51
|
+
ignored by default, with `queue: true` / `interrupt: true` in `TransitionOptions` and the
|
|
52
|
+
`transitionDisposition` rule exported, `Game.transitioning` also reporting a queued
|
|
53
|
+
transition; and `Dropdown.setHighlight` now opens the list as documented, so
|
|
54
|
+
`setHighlight(i); confirm()` picks a row and the next opening shows it.
|
|
55
|
+
- Retro-audio integration (roadmap 431-442): a named `MidiSynth` engine
|
|
56
|
+
registry with fallback diagnostics, the streaming `Playable` contract, bus render budgets
|
|
57
|
+
and decoder guards, `.mwp` content packages with capability policy and ROM refusal, data
|
|
58
|
+
sound effects, an off-main-thread audio host, adaptive music, the `mwg-audio` tool, and
|
|
59
|
+
the `testing` doubles (`FakeRetroPlayer`, fake WebAudio graph) - documented in the
|
|
60
|
+
README's "Retro audio" section, REFERENCE and `docs/listening-protocol.md`. Player-facing
|
|
61
|
+
settings (`AudioSettings` with PAL/NTSC, dynamics and softening rules), live Web MIDI
|
|
62
|
+
(`connectWebMidi`), dropped `.syx` banks with DX7 patch names and audition (`SyxBank`),
|
|
63
|
+
and the oscilloscope/tracker data cores (`waveformEnvelope`, `trackerPattern`) complete
|
|
64
|
+
item 441.
|
|
65
|
+
|
|
66
|
+
### Changed
|
|
67
|
+
|
|
68
|
+
- Simplification pass over the tree, with no change to the public API: one shared radix-2 FFT
|
|
69
|
+
in `audio`, `HexSkirmish.reachableCosts` built on `core`'s `weightedFlood` (a negative
|
|
70
|
+
terrain move cost is now ignored instead of accepted), `tourney` and `Tactics` using the
|
|
71
|
+
`Engine` job default and `core`'s `cellInside`, a single byte-joining helper in the
|
|
72
|
+
`mwl` package container, and hand-written clamps replaced by `core`'s `clamp` across
|
|
73
|
+
`ui`, `render`, `battle`, `physics` and `audio`.
|
|
74
|
+
- Roadmap: items 393 (Ableton and Reaper groove formats), 394 (the Magenta humanizer tier),
|
|
75
|
+
Syzygy and NNUE are closed; 401 is the only numbered item left, waiting on a port session.
|
|
76
|
+
|
|
77
|
+
## [0.43.0] - 2026-10-17
|
|
78
|
+
|
|
79
|
+
Scrolling, tooltips and screen transitions for interface-heavy games, a MIDI music player and
|
|
80
|
+
SoundFont tooling, and the shipped command line tools made to work from an installed package on
|
|
81
|
+
Node 20.19. `@datamoc/pyroeis` moves to 0.9.1, which ends the last moderate audit advisory.
|
|
82
|
+
|
|
83
|
+
### Added
|
|
84
|
+
|
|
85
|
+
- Scrolling and transitions asked for by a bridge game (Pontifex feedback 45-50):
|
|
86
|
+
- `ScrollBox`: `touch` (drag to scroll, coasting on release, `ScrollDrag`), `handleKey` for line, page and
|
|
87
|
+
ends, a draggable scrollbar thumb and a clickable track, and `autoHeight`. A drag no longer clicks the row
|
|
88
|
+
it ends on.
|
|
89
|
+
- `DropdownView`: `maxVisibleRows` (the list scrolls), an opaque backdrop, `autoWidth`, opening with the chosen
|
|
90
|
+
row in view, `handleKey`, `handleOutsideClick` and `fitBelow`.
|
|
91
|
+
- `ScrollText` (a wrapped label that sizes itself and scrolls), `MarqueeLabel` (a long line sliding inside
|
|
92
|
+
its width, still under reduced motion), `ellipsize` and `truncateLines`.
|
|
93
|
+
- `TooltipHost`: tooltips on a touch long-press and on disabled buttons, from rectangles instead of pointer
|
|
94
|
+
events. `Button` gains `tooltip`, `setIcon` and a second icon for the selected state (`iconSelected`).
|
|
95
|
+
- `Game.switchScene(Next, { transition, duration, easing, pause })`, `Game.transition(swap, options)` and
|
|
96
|
+
`Scene2D.transitionTo`: `tileShrink`, `shrink`, `pop`, `grow`, `slide`, `iris` and `dissolve` over a snapshot of
|
|
97
|
+
the outgoing scene, skipped under reduced motion, with `Game.transitioning` and `Game.active`. `Window.open`
|
|
98
|
+
and `Window.close` take an animation (`pop`, `grow`, `slide`, `fade`). The bridge example switches level
|
|
99
|
+
with `tileShrink`.
|
|
100
|
+
- The dist grew 3.8% with them; the size baseline is moved to match.
|
|
101
|
+
- `limits` (`SoundFontLimits`: `sampleFrames`, `totalFrames`) on `decodeSoundFont`, `loadSoundFont` and
|
|
102
|
+
`webAudioSampleDecoder` raises or lowers what a SoundFont 3 may decode to, instead of the fixed 16 M
|
|
103
|
+
frames per sample and 128 M per font. The sample-rate bounds stay: the format itself stops at 50 kHz.
|
|
104
|
+
- `@example`s for the `3d` functions that need no renderer (`gridPoint3D`, `cellAt`, `heightAt`,
|
|
105
|
+
`buildHeightIndex`, `resolveCapsuleAgainstGrid`, `parseVox`). The generated API reference now covers
|
|
106
|
+
the subpaths the root barrel does not re-export (`3d`, `physics`, `ai/lua`, `mwl/pyroeis`, `testing`,
|
|
107
|
+
`threads`, `headless`, `assets/binary`), so the examples index links 803 of 813 symbols to a page.
|
|
108
|
+
- `npm run build:bridge-tutorial` regenerates the bridge tutorial from `tools/templates` and the example's
|
|
109
|
+
own source. `npm run bins:smoke` packs the project, installs it in an empty folder and runs every `mwg-*`
|
|
110
|
+
bin with `--help`; CI runs it, with the library build, on Node 20.19.0, the floor `engines` claims.
|
|
111
|
+
- `.gitattributes` (`* text=auto eol=lf`) ends the CRLF warnings on Windows checkouts.
|
|
112
|
+
- An optional, reproducible 314 KB SoundFont subset covering six essential presets,
|
|
113
|
+
with provenance and synthetic fallback for the instruments outside it.
|
|
114
|
+
- `AudioBus` loads embedded audio and MIDI by default through `createAudioLoader`, with
|
|
115
|
+
shared decoding, worker MIDI rendering and optional SoundFont settings across lifetimes.
|
|
116
|
+
- `MusicPlayer` controls rendered MIDI or decoded buffers with pause, resume, seek,
|
|
117
|
+
volume and a track-completion signal; `toAudioBuffer` copies rendered stereo samples
|
|
118
|
+
into a Web Audio buffer. The README shows the supplied SoundFont and worker playback path.
|
|
119
|
+
- Page byte budgets and potentially unused asset reports in the standalone build pipeline.
|
|
120
|
+
- XPBD link payloads, explicit rupture, geometric queries and state comparison helpers.
|
|
121
|
+
- Schema-directed `nodesToObjects` conversion and optional literal unions generated from
|
|
122
|
+
MWL table columns. Optional attributes are documented alongside the schema.
|
|
123
|
+
- Generated starter projects include a pre-commit hook for typecheck and tests, an activation
|
|
124
|
+
command and ignores for local agent state.
|
|
125
|
+
- Sawtooth sound synthesis, playback attempt counts and a shared browser test exposure hook.
|
|
126
|
+
|
|
127
|
+
### Fixed
|
|
128
|
+
|
|
129
|
+
- `mwg-uci`, `mwg-openings` and `mwg-tablebases` imported `../src/...`, which the package does not ship, so
|
|
130
|
+
they failed in every installed copy; they load `dist` there, as the other tools already did.
|
|
131
|
+
- The bridge example keeps a saved best only when the new crossing costs less, or costs the same and sags
|
|
132
|
+
less, instead of replacing it by any cheaper one.
|
|
133
|
+
- Fixed waits that failed on a loaded machine: the browser smoke waits for drawn frames instead of 250 ms,
|
|
134
|
+
the driver test polls for its tap instead of sleeping 300 ms, and the WebP noise test is seeded.
|
|
135
|
+
|
|
10
136
|
## [0.42.0] - 2026-10-16
|
|
11
137
|
|
|
12
138
|
The documentation and asset follow-ups to 0.41.0: SVG minification, a policy for the instruments a
|
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,85 @@ 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
|
+
|
|
248
|
+
### Retro audio
|
|
249
|
+
|
|
250
|
+
A retro-instrument package (an MT-32, OPL or chip synth of its own) plugs into mwg through
|
|
251
|
+
contracts mwg owns, so the core never depends on the package. It registers a `MidiSynth`
|
|
252
|
+
under a name with `registerMidiSynth('retro:mt32', factory)`; content then asks for that
|
|
253
|
+
name and `resolveMidiSynth` walks the request, the SoundFont and the built-in synth in
|
|
254
|
+
order, firing `onMidiSynthFallback` whenever a different engine ends up voicing the file.
|
|
255
|
+
Endless or machine-code formats (NSF, SID, SPC) play through the streaming `Playable`
|
|
256
|
+
contract (`StreamPlayable`, `StreamChannel`) under named decoder guards
|
|
257
|
+
(`decoderLimits`, `DecodeBudgetError`): input ceilings, an instruction budget per second
|
|
258
|
+
of sound, and bounded inflation, so untrusted files fail by name instead of hanging.
|
|
259
|
+
Rendered tracks are budgeted (`TrackCache`, `createBudgetedLoader`): a five-minute stereo
|
|
260
|
+
render is about 100 MB, so the bus caps decoded megabytes rather than keeping every track
|
|
261
|
+
forever. Synthesised effects are data, not files: `SfxBank` builds them from definitions
|
|
262
|
+
with a fixed seed, so audio never disturbs a lockstep simulation (its triggers are
|
|
263
|
+
deterministic; the audio itself is not in the lockstep state, and A/V sync follows the
|
|
264
|
+
game frame counter, not the wall clock). The `mwg-audio` tool inspects, converts, renders,
|
|
265
|
+
budgets and compares these formats from the command line, and `spectralDistance` gives a
|
|
266
|
+
first automated check between two renders. What no automation settles is whether a render
|
|
267
|
+
sounds right: the A/B listening protocol in `docs/listening-protocol.md` is the place to
|
|
268
|
+
record those verdicts.
|
|
269
|
+
|
|
187
270
|
## Capability spec
|
|
188
271
|
|
|
189
272
|
The definition of done for 1.0, drawn from what each of the nine references actually
|
package/REFERENCE.md
CHANGED
|
@@ -530,6 +530,14 @@ batcher/high-shader internals are confined to `ColorTransformBatcher.ts`.
|
|
|
530
530
|
`sequence(steps)` (item 302) chains fade/hold/flash steps end to end as one call, for the
|
|
531
531
|
hold-then-fade-back transition the four individual methods cannot express alone; `update`
|
|
532
532
|
keeps returning false at every step boundary, true only once the whole sequence finishes.
|
|
533
|
+
- `SceneTransition`/`TransitionOptions`/`TransitionRunner`/`TransitionDisposition` + `transitionDisposition`, `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, a second
|
|
537
|
+
transition requested while one plays is ignored unless its options say `queue: true` or `interrupt: true`
|
|
538
|
+
(`transitionDisposition` is that rule as a plain function), and
|
|
539
|
+
reduced motion skips it. `tileDelays`, `tileProgress`, `popScale`, `openAnimationFrame` and `resolveEasing` are the
|
|
540
|
+
timing as plain functions.
|
|
533
541
|
- `ActorAnimator` - an idle/move/action animation state machine with one interruption rule.
|
|
534
542
|
- `StatusVisuals` - composes every active status effect's colour additively onto a sprite's
|
|
535
543
|
`colorAdd` (item 301), instead of one status winning by declaration order; each channel clips
|
|
@@ -663,7 +671,9 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
663
671
|
- `BitmapLabel`/`bitmapLabelStyle` - bitmap-font-backed text, for a HUD value redrawn
|
|
664
672
|
every frame where `Label`'s per-string texture re-render would be wasteful.
|
|
665
673
|
- `NinePatch` - a resizable nine-slice panel.
|
|
666
|
-
- `Window` - a themed panel container. `
|
|
674
|
+
- `Window` - a themed panel container. `open({ animation, from })` plays `pop`, `grow`, `slide` or `fade`
|
|
675
|
+
(`WindowAnimation`) and `close({ animation })` plays it backwards before closing, both driven by
|
|
676
|
+
`update(dt)` and skipped under reduced motion. `close()` frees the window and its contents and is
|
|
667
677
|
idempotent; `closed` is the guard for the caller's reference afterwards (input and `place` on a
|
|
668
678
|
closed window are no-ops), and `WindowStack.push` refuses one with a named error. A window's own
|
|
669
679
|
area is not clickable, so a click travels to whatever is under it; `blocker: true` adds the
|
|
@@ -695,9 +705,15 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
695
705
|
- `Spinner`/`SpinnerOptions` + `spinValue` - an up/down numeric stepper; `spinValue` is the rule
|
|
696
706
|
(snap to the step, then clamp at the ends or wrap past them).
|
|
697
707
|
- `DropdownView`/`DropdownViewOptions` - the widget over a `Dropdown` model: a button showing the choice
|
|
698
|
-
that opens a column of `Button`s, the
|
|
708
|
+
that opens a column of `Button`s on an opaque backdrop, the highlighted one marked with
|
|
709
|
+
`Button.setSelected`. `maxVisibleRows` makes a long list scroll (wheel, touch, keys, scrollbar) and it opens with
|
|
710
|
+
the chosen row in view; `autoWidth` fits the longest label, `maxWidth` caps that widening, and `resize(width)`
|
|
711
|
+
re-lays the button and list in place; `handleKey` (arrows, Page, Home/End, Enter,
|
|
712
|
+
Escape) and `handleOutsideClick` are what the scene forwards from its own input; `fitBelow` limits the
|
|
713
|
+
rows to what the viewport leaves under the button.
|
|
699
714
|
- `Dropdown`/`DropdownOption`/`DropdownOptions` - a renderer-free option button: a selected entry,
|
|
700
|
-
an open list, a highlight that starts on the selection and skips disabled options.
|
|
715
|
+
an open list, a highlight that starts on the selection and skips disabled options. `setHighlight(i)`
|
|
716
|
+
opens the list on that row, so `setHighlight(i); confirm()` is a whole programmatic pick.
|
|
701
717
|
- `TextModel`/`TextModelOptions` - the editing state behind a text field, fed by `core.Input`'s
|
|
702
718
|
`onText`: caret, anchor, selection-replacing edits, a length cap and a mask. Renderer-free.
|
|
703
719
|
`multiline` keeps real newlines with row navigation (`lineCount`, `caretLine`, `lineRange`,
|
|
@@ -727,8 +743,20 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
727
743
|
highlight.
|
|
728
744
|
- `TreeView`/`TreeNode`/`TreeRow`/`TreeViewOptions` - a renderer-free collapsible tree, flattened to
|
|
729
745
|
the rows on screen; collapsing the branch the highlight is inside lands it on the branch.
|
|
730
|
-
- `ScrollBox`/`ScrollBoxOptions` + `scrollOffset` - a clipped viewport with a themed scrollbar;
|
|
731
|
-
`scrollOffset` is the clamping rule the wheel, `scrollBy` and `scrollIntoView` all share.
|
|
746
|
+
- `ScrollBox`/`ScrollBoxOptions`/`ScrollKey` + `scrollOffset` - a clipped viewport with a themed scrollbar;
|
|
747
|
+
`scrollOffset` is the clamping rule the wheel, `scrollBy` and `scrollIntoView` all share. `touch: true` lets a
|
|
748
|
+
finger drag the content (a drag cancels the tap on the row under it) and coast on release, `handleKey`
|
|
749
|
+
scrolls by line, page and to either end, the thumb drags and the track jumps, and `autoHeight` sizes the
|
|
750
|
+
viewport to the content up to `height`. The box owns its `hitArea` (its viewport, kept current by `resize` and
|
|
751
|
+
`setContentHeight`), so the wheel reaches it without a row under the pointer. `update(dt)` drives the coasting.
|
|
752
|
+
- `ScrollDrag` - touch scrolling as plain numbers (`start`/`move`/`end`/`step`): how far the content follows a
|
|
753
|
+
finger and how it coasts after release, with no pointer events or clock of its own.
|
|
754
|
+
- `ScrollText`/`ScrollTextOptions` - a wrapped `Label` in a self-sizing `ScrollBox`: as tall as the text up to
|
|
755
|
+
`maxHeight`, scrolling beyond it, re-measured by `setText` and re-wrapped in place by `resize(width)`.
|
|
756
|
+
- `MarqueeLabel`/`MarqueeLabelOptions` + `marqueeOffset` - one line that stays on one line: a long text slides
|
|
757
|
+
back and forth in a clipped window (and holds still, cut short, under reduced motion).
|
|
758
|
+
- `ellipsize` and `truncateLines` - cut a text to a width or a number of lines, ending in an ellipsis, with the
|
|
759
|
+
caller supplying the measuring function.
|
|
732
760
|
- `flowLayout(items, width, { gap, align })` - left-to-right rows that wrap, returning each item's
|
|
733
761
|
position and the total height: a toolbar that takes two rows on a phone.
|
|
734
762
|
- `Grid`/`GridSpec`/`GridTrack` + `resolveAnchor`/`anchorAlign` with `Anchor`/`AnchorSpec`/
|
|
@@ -774,6 +802,10 @@ Windows, lists, message boxes, HUD widgets - all themed from one live-swappable
|
|
|
774
802
|
- `Toast` - a queued, timed pop-up notification (fade in, hold, fade out).
|
|
775
803
|
- `Tooltip` - a hover explanation over a themed `Window`: a frame-driven hover delay, and
|
|
776
804
|
edge-aware placement that flips rather than letting the panel run off screen.
|
|
805
|
+
- `TooltipHost`/`TooltipHostOptions`/`TooltipTarget` - drives one `Tooltip` from rectangles: a press that rests
|
|
806
|
+
for `longPress` seconds shows the text on a touch screen, and a disabled `Button` (which gets no pointer
|
|
807
|
+
events) is still explained from its bounds and its `tooltip`. `attach(pointer)` wires an
|
|
808
|
+
`Input.attachPointer` tracker.
|
|
777
809
|
- `HelpScreen` - a topic-list-plus-body help/controls screen.
|
|
778
810
|
- `StatsScreen` - a player-stats display screen.
|
|
779
811
|
- `LoadingScreen` - a progress-bar loading screen wired to `core.LoadQueue`.
|
|
@@ -895,11 +927,17 @@ reach the compiled asset map without it.
|
|
|
895
927
|
(`force`, smoothed `ratio`, `peakRatio`) and `onBreak` when a link passes its strength; `ground` (a height or a function of x) holds nodes
|
|
896
928
|
on a surface with `groundFriction`. The engine of
|
|
897
929
|
Pontifex, generalised: bridges, trusses, cranes, anything where the question is which beam fails.
|
|
930
|
+
`XpbdWorld<T>`/`XpbdLink<T>`/`XpbdLinkSpec<T>` carry game-owned link `data` without
|
|
931
|
+
affecting the solver. `breakLink(index)` dispatches rupture once. `linksInBox` includes
|
|
932
|
+
segment crossings; `linksNear` orders nearby unbroken segments by distance. `snapshot()`
|
|
933
|
+
returns positions, velocities, load state and queued forces as detached arrays; `hash()`
|
|
934
|
+
compares their exact numeric bits for runs with the same configuration. Payloads and ground
|
|
935
|
+
callbacks are excluded. The bridge example applies a moving vehicle's weight with `applyForce`.
|
|
898
936
|
- `PhysicsWorld` (`@datamoc/mw_games/physics`) - rigid bodies and constraints in plain TypeScript, no engine
|
|
899
937
|
dependency: `addBox`/`addCircle`/`addConstraint`/`remove`, `update(dt)` from
|
|
900
938
|
`Scene.update`, `onContact`, `alpha` for interpolation. A constraint with `maxStretch` breaks when pulled
|
|
901
939
|
that far past its rest length (`onBreak`, `stretchOf`). Bodies never spin; `angle` is a display offset.
|
|
902
|
-
- `synthesizeTone`/`synthesizeSound`/`playTone`/`Waveform` - a runtime square/triangle/sine/noise
|
|
940
|
+
- `synthesizeTone`/`synthesizeSound`/`playTone`/`Waveform` - a runtime square/triangle/sine/noise/sawtooth
|
|
903
941
|
waveform synth for a procedural tone or SFX, no sample library; `synthesizeSound` returns a
|
|
904
942
|
pooled `Sound` over the tone.
|
|
905
943
|
- `parseMidi`/`scheduleMidi`/`midiLoopStart`/`noteToFrequency`/`MidiPlayer` - a small `.mid` file player
|
|
@@ -964,12 +1002,72 @@ reach the compiled asset map without it.
|
|
|
964
1002
|
the page (FluidR3's piano, guitar and drums: 2.7 MB, against 20 MB decoded to a SoundFont 2),
|
|
965
1003
|
and `loadSoundFont` decodes it at load, so prefer it over a SoundFont 2 for anything but the
|
|
966
1004
|
smallest banks.
|
|
1005
|
+
- `createAudioLoader`/`AudioLoaderOptions` - a cached loader for embedded audio and MIDI,
|
|
1006
|
+
shared by all `AudioBus` lifetimes. MIDI is detected by header and rendered on a worker;
|
|
1007
|
+
other files use `decodeAudioData`. Options select SoundFont/render settings or a byte source.
|
|
1008
|
+
`AudioBus` uses this loader by default, so `playBgm` and `playSe` accept asset paths with
|
|
1009
|
+
the same volume, pitch and pan fields. BGM/BGS loop; ME/SE play once.
|
|
1010
|
+
- `MusicPlayer`/`MusicPlayerOptions`/`MusicPlayOptions` - one loaded `AudioBuffer` or
|
|
1011
|
+
`RenderedMidi`, with play, pause, resume, seek, position, volume and an `onEnded` signal.
|
|
1012
|
+
Call `update()` each frame to dispatch track completion and choose the next track.
|
|
1013
|
+
`toAudioBuffer(context, rendered)` copies rendered stereo samples into an `AudioBuffer`.
|
|
967
1014
|
- `Channel`/`AudioBus` - buffered WebAudio music: a `Channel` plays one looping track with
|
|
968
1015
|
volume, pitch, pan, a loop region and a seekable playhead, and an `AudioBus` wires two of
|
|
969
1016
|
them into independent BGM/BGS loops, ME jingles that suspend and resume the BGM,
|
|
970
1017
|
overlapping SE one-shots, save and replay with playheads, and plain-data snapshots.
|
|
971
1018
|
`AudioBus.setMixer(kind, { volume, muted })` scales a whole kind on top of each track's
|
|
972
1019
|
own volume, without changing what snapshots report.
|
|
1020
|
+
- `MidiSynth`/`MidiSynthFactory`/`MidiSynthRequest` + `registerMidiSynth`, `unregisterMidiSynth`,
|
|
1021
|
+
`midiSynthNames`, `resolveMidiSynth`, `renderMidiThroughSynth`, `renderMidiStems`,
|
|
1022
|
+
`onMidiSynthFallback` and `INTERNAL_MIDI_ENGINES` - the named-engine contract for MIDI:
|
|
1023
|
+
a package registers a synth under a name (`retro:mt32`), content asks for it by name, and
|
|
1024
|
+
resolution walks the request, then the internal names (`gm-soundfont`, `builtin`), firing
|
|
1025
|
+
`onMidiSynthFallback` whenever another engine voices the file instead. `timedMidiMessages`
|
|
1026
|
+
is the raw timed message stream a synth is driven by.
|
|
1027
|
+
- `StreamPlayable`/`StreamChannel` - the streaming half of the `Playable` contract: seek and
|
|
1028
|
+
`currentTime` on a synthesised stream, `playbackRate` by resampling, and a playhead counted
|
|
1029
|
+
in frames, so endless formats (NSF, SID) play without a whole-track render.
|
|
1030
|
+
- `TrackCache`/`createBudgetedLoader`/`BusBudgetPolicy` + `bufferBytes` - bus render budgets:
|
|
1031
|
+
an LRU cache of decoded tracks under a megabyte and entry cap, with `maxSeconds`, mono or
|
|
1032
|
+
half-rate rendering for ambience through `fitToBudget` and `resampleStereo`.
|
|
1033
|
+
- `DEFAULT_DECODER_LIMITS`/`decoderLimits`/`DecodeBudgetError`/`InstructionBudget` +
|
|
1034
|
+
`assertInputSize`, `guardBlockSource` and `inflateBounded` - the decoder guards for
|
|
1035
|
+
untrusted retro formats: input and inflate ceilings, an instruction budget per second of
|
|
1036
|
+
sound, and one named error per breach instead of a hang.
|
|
1037
|
+
- `SfxBank`/`SfxSound`/`SfxArbiter` + `sfxSeed` - data sound effects: synthesised chip
|
|
1038
|
+
effects with a deterministic seed, arbitrated against the `se` bus by priority, firing
|
|
1039
|
+
captions like a file effect.
|
|
1040
|
+
- `AudioSettings`/`AudioSettingsData`/`AudioRegion`/`OutputStage` + `regionPlaybackRate`,
|
|
1041
|
+
`softenCutoff` and `dynamicsLimit` - the player-facing audio settings in one plain-data
|
|
1042
|
+
object: volume per bus (applied to an `AudioBus` by `applyToBus`), the preferred MIDI
|
|
1043
|
+
engine name, PAL or NTSC region (a tune authored for one runs slower or faster on the
|
|
1044
|
+
other, `regionPlaybackRate`), a faithful or clean output stage, caption display, a
|
|
1045
|
+
dynamics control (a soft limiter, `dynamicsLimit`) and bright-sound softening (a falling
|
|
1046
|
+
low-pass cutoff, `softenCutoff`). A CPU and underrun indicator reads `AudioCoreHost`'s
|
|
1047
|
+
stats and `onAudioUnderrun`.
|
|
1048
|
+
- `waveformEnvelope`/`channelEnvelopes`/`WaveformBucket` and
|
|
1049
|
+
`trackerPattern`/`trackerNoteName`/`TrackerRow` - the data cores of the audio views: a
|
|
1050
|
+
waveform as per-bucket min/max pairs per channel (what an oscilloscope draws), and a
|
|
1051
|
+
MIDI file as tracker rows, one column per channel on the file's own tick grid. A game
|
|
1052
|
+
draws them with its own widgets; the reduction and the grid live here and are tested as
|
|
1053
|
+
arithmetic.
|
|
1054
|
+
- `SyxBank`/`SyxPatch` + `splitSysex` and `dx7PatchNames` - a `.syx` bank a player drops:
|
|
1055
|
+
its whole sysex messages, the patch names of every checksummed DX7 32-voice bulk dump in
|
|
1056
|
+
it (another maker's bank names nothing rather than guessing), and an `audition` that
|
|
1057
|
+
loads a patch into a `MidiSynth` and renders one note through it for a settings screen.
|
|
1058
|
+
- `connectWebMidi`/`requestWebMidiInput`/`WebMidiConnection` - live Web MIDI into a
|
|
1059
|
+
`MidiSynth`: every input port of a `MIDIAccess` is wired to the synth (channel messages
|
|
1060
|
+
to `event`, sysex to `sysex`, transport dropped), and `requestWebMidiInput` is the
|
|
1061
|
+
browser entry that returns null where Web MIDI does not exist or was refused.
|
|
1062
|
+
- `AdaptiveMusic` - adaptive tracks: mute and unmute stems of a running track with a fade,
|
|
1063
|
+
and switch between two versions at the same frame position; `loopRegionFromFile` reads
|
|
1064
|
+
the loop region a file declares as the default.
|
|
1065
|
+
- `AudioCoreHost` + `onAudioUnderrun` - the off-main-thread audio host: a synthesis core in
|
|
1066
|
+
a worker behind the same `Playable` facade, with a main-thread fallback and an underrun
|
|
1067
|
+
signal feeding bounded telemetry.
|
|
1068
|
+
- `encodeWav`/`decodeWav`/`wavInfo` and `spectralDistance` - WAVE as plain bytes (the
|
|
1069
|
+
`mwg-audio` tool's convert and compare verbs over the same code), and a spectral distance
|
|
1070
|
+
between two renders for a listening check that can be automated.
|
|
973
1071
|
- `parseVorbisLoopTags`/`loopRegionFromTags` - read the `LOOPSTART`/`LOOPLENGTH` comments an
|
|
974
1072
|
Ogg Vorbis file carries (a bounded walk of the first pages, null on anything malformed) and
|
|
975
1073
|
turn them into the seconds-based `LoopRegion` a `Channel` or the bus loader hands back.
|
|
@@ -1528,9 +1626,17 @@ nothing here reaches a shipped game unless a test imports it.
|
|
|
1528
1626
|
- `memoryStorage` - a `SaveStorage` held in memory, for `SaveSystem`, `Settings`, `Collection`,
|
|
1529
1627
|
`PlayerStats` and the other stored values; the same class `core` falls back to where
|
|
1530
1628
|
`localStorage` is missing.
|
|
1629
|
+
- `exposeForTests(name, object)` - publishes inspectable game state under
|
|
1630
|
+
`window.__mwg_test__[name]` for browser tests; does nothing outside a browser.
|
|
1531
1631
|
- `fakeAudio`/`fakeAudioWithEnded`/`FakePlayable` - a `Playable` for `Sound`/`Music`'s `create`
|
|
1532
1632
|
that counts its plays; the second also has the `onended` slot a backend reporting track ends
|
|
1533
1633
|
has.
|
|
1634
|
+
- `FakeAudioContext`/`FakeBuffer`/`FakeBufferSource`/`FakeGain`/`FakeBlockSource` - a fake
|
|
1635
|
+
WebAudio graph for the audio tests: sources, gains and buffers a test can inspect, and a
|
|
1636
|
+
block source for a `StreamChannel`, with no real `AudioContext` anywhere.
|
|
1637
|
+
- `FakeRetroPlayer` - a `MidiSynth` double for a retro engine: it records every message it
|
|
1638
|
+
was driven with and renders a flat level while a note is on, so a renderer test reads what
|
|
1639
|
+
was sent and what sounded, with `FakeAudioContext.advance()` as the clock it moves by hand.
|
|
1534
1640
|
- `FakeSocket` - a `WebSocketLike` for `LockstepClient`'s `create`: `sent`, `receive(message)`,
|
|
1535
1641
|
`receiveRaw(text)`, `close()`.
|
|
1536
1642
|
- `fakeFetch`/`FakeFetchCall` - a `fetch` for every `HttpTransport` client, answering with a
|
|
@@ -1549,6 +1655,10 @@ a module worker loaded from `file://` fails CORS whatever the policy says), and
|
|
|
1549
1655
|
spawns a worker pays nothing for it, and imports `@datamoc/mw_games/threads` when it
|
|
1550
1656
|
wants one.
|
|
1551
1657
|
|
|
1658
|
+
- `openWorker(source, events)` - opens a long-lived worker from a self-contained script
|
|
1659
|
+
(blob worker in a browser, `node:worker_threads` in Node, null where neither exists) and
|
|
1660
|
+
returns its message link; `spawn` is the one-task use of it, a service such as the audio
|
|
1661
|
+
core host keeps its worker and talks to it.
|
|
1552
1662
|
- `spawn(fn, args?, options?)` - runs `fn` on a worker and resolves with its value. The
|
|
1553
1663
|
task is serialized with `Function.prototype.toString`, so it must be self-contained:
|
|
1554
1664
|
no closures over module scope, no imports, no bound or native functions - everything it
|
|
@@ -1747,6 +1857,11 @@ consumes generated data and does not parse `.mwl` source files in the browser.
|
|
|
1747
1857
|
`id=rebels` validates) keys `world.sides` and `world.gold`, marks a map keep (`rebels Kh`), and
|
|
1748
1858
|
is what `unit.side`, `MwlMessage.side` and every `side`/`side_filter` filter hold. `MwlSideRef`
|
|
1749
1859
|
is just that id.
|
|
1860
|
+
- `nodesToObjects`/`MwlObject` - converts compiled nodes to JSON objects using the tag
|
|
1861
|
+
schema: declared numbers and booleans become typed fields, child tags become ordered
|
|
1862
|
+
arrays, and a `cardinality.max` of 1 selects a single object. Missing optional fields stay
|
|
1863
|
+
absent. Check the returned diagnostics for conversion errors and field collisions.
|
|
1864
|
+
Attributes not listed in `MwlTagSchema.required` are optional; use `number`, not `number?`.
|
|
1750
1865
|
- `readAttributes` / `readChildren` - shared typed readers for compiled nodes. They coerce
|
|
1751
1866
|
scalars and lists and return source-located diagnostics instead of silently guessing.
|
|
1752
1867
|
- `MwlNode.attributeLocations` / `valueLocations` - parsed JSON5 nodes retain the exact key and
|
|
@@ -1769,6 +1884,9 @@ consumes generated data and does not parse `.mwl` source files in the browser.
|
|
|
1769
1884
|
absent cell omits the key), and `emitTableTypes` emits one named row interface plus one
|
|
1770
1885
|
typed accessor per table for a game's build script to write and import, so a changed
|
|
1771
1886
|
column type or a removed column fails type-checking at every use.
|
|
1887
|
+
`emitTableTypes(tables, { literalColumns: ['id', 'sprite'] })` also exports sorted,
|
|
1888
|
+
deduplicated readonly value lists and literal unions such as `VehiclesSprite` from the
|
|
1889
|
+
current string-column values. Regenerate them with the catalog to catch stale game ids.
|
|
1772
1890
|
- `MwlValidationOptions.tableReferences`/`MwlTableReference` (item 362) - declarative
|
|
1773
1891
|
cross-table checks, so no game hand-rolls one validator per table: a column declares
|
|
1774
1892
|
`references: { table, column }` or `oneOf: [...]`, and `validateCatalog` checks every cell
|
|
@@ -1809,6 +1927,16 @@ consumes generated data and does not parse `.mwl` source files in the browser.
|
|
|
1809
1927
|
`emitHooksDeclaration`, `emitModule`, `emitTableTypes`, `encodeSave`, `evaluateExpression`,
|
|
1810
1928
|
`execute`,
|
|
1811
1929
|
`extractCatalog`, `hookTypes`, `inventoryItem`, `isGettext`, `isMwlId`, `itemDefinition`,
|
|
1930
|
+
`PACKAGE_TAGS`, `PACKAGE_ASSET_KINDS`, `PACKAGE_MEDIA_KINDS`, `PACKAGE_SCRIPT_ACTIONS`,
|
|
1931
|
+
`KNOWN_PACKAGE_CAPABILITIES`, `DEFAULT_PACKAGE_LIMITS`, `PackageError`, `PackageFormatError`,
|
|
1932
|
+
`PackageIntegrityError`, `PackageLimitError`, `PackagePathError`, `PackageConflictError`,
|
|
1933
|
+
`encodePackage`, `decodePackage`, `verifyPackage`, `loadPackage`, `mergePackage`,
|
|
1934
|
+
`compilePackages`, `packageContent`, `packageRef`, `parsePackageRef`, `parsePackageDocument`,
|
|
1935
|
+
`checkPackagePath`, `checkPackagePolicy`, `checkConditionSyntax`, `compareVersions`,
|
|
1936
|
+
`satisfiesVersionRange`, `requestedCapabilities`, `detectRomImage`, `registerRomDigest`,
|
|
1937
|
+
`generateCredits` and `sha256Hex` (self-contained `.mwp` content packages: one compressed
|
|
1938
|
+
container with per-entry digests and declared limits, a capability policy the game accepts
|
|
1939
|
+
or refuses, `id@version` namespaces in saves, and a ROM-image refusal with generated credits),
|
|
1812
1940
|
`parseExpression`, `parseHookReference`, `parseMapFile`, `parseTerrain`, `parseValue`, `preprocess`,
|
|
1813
1941
|
`schema01`, `typedRows`, `validateCatalogNodes`, `validateHookReferences`, `loadContent`, and `validateWorld`.
|
|
1814
1942
|
- `parseMapFile`/`MwlMapFile` (item 284) - reads a Wesnoth-shaped `.map`: the `key=value` header
|