@datamoc/mw_games 0.7.4 → 0.7.7
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 +287 -2
- package/README.md +15 -2
- package/dist/actors/Inventory.d.ts +7 -1
- package/dist/actors/Inventory.d.ts.map +1 -1
- package/dist/actors/Inventory.js.map +1 -1
- package/dist/actors/craft.d.ts +35 -14
- package/dist/actors/craft.d.ts.map +1 -1
- package/dist/actors/craft.js +56 -24
- package/dist/actors/craft.js.map +1 -1
- package/dist/actors/index.d.ts +1 -1
- package/dist/actors/index.d.ts.map +1 -1
- package/dist/ai/Heuristics.d.ts +232 -0
- package/dist/ai/Heuristics.d.ts.map +1 -0
- package/dist/ai/Heuristics.js +298 -0
- package/dist/ai/Heuristics.js.map +1 -0
- package/dist/ai/index.d.ts +4 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +2 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/score.d.ts +126 -0
- package/dist/ai/score.d.ts.map +1 -0
- package/dist/ai/score.js +121 -0
- package/dist/ai/score.js.map +1 -0
- package/dist/assets/loader.d.ts +18 -4
- package/dist/assets/loader.d.ts.map +1 -1
- package/dist/assets/loader.js +40 -11
- package/dist/assets/loader.js.map +1 -1
- package/dist/audio/Positional.d.ts +125 -0
- package/dist/audio/Positional.d.ts.map +1 -0
- package/dist/audio/Positional.js +135 -0
- package/dist/audio/Positional.js.map +1 -0
- package/dist/audio/Sound.d.ts +7 -1
- package/dist/audio/Sound.d.ts.map +1 -1
- package/dist/audio/Sound.js +8 -2
- package/dist/audio/Sound.js.map +1 -1
- package/dist/audio/index.d.ts +2 -0
- package/dist/audio/index.d.ts.map +1 -1
- package/dist/audio/index.js +1 -0
- package/dist/audio/index.js.map +1 -1
- package/dist/battle/AttackDialog.d.ts +75 -0
- package/dist/battle/AttackDialog.d.ts.map +1 -0
- package/dist/battle/AttackDialog.js +115 -0
- package/dist/battle/AttackDialog.js.map +1 -0
- package/dist/battle/AttackPreview.d.ts +70 -0
- package/dist/battle/AttackPreview.d.ts.map +1 -0
- package/dist/battle/AttackPreview.js +78 -0
- package/dist/battle/AttackPreview.js.map +1 -0
- package/dist/battle/BattleStats.d.ts +45 -0
- package/dist/battle/BattleStats.d.ts.map +1 -0
- package/dist/battle/BattleStats.js +63 -0
- package/dist/battle/BattleStats.js.map +1 -0
- package/dist/battle/UnitSelector.d.ts +72 -0
- package/dist/battle/UnitSelector.d.ts.map +1 -0
- package/dist/battle/UnitSelector.js +117 -0
- package/dist/battle/UnitSelector.js.map +1 -0
- package/dist/battle/Whiteboard.d.ts +50 -0
- package/dist/battle/Whiteboard.d.ts.map +1 -0
- package/dist/battle/Whiteboard.js +94 -0
- package/dist/battle/Whiteboard.js.map +1 -0
- package/dist/battle/index.d.ts +10 -0
- package/dist/battle/index.d.ts.map +1 -1
- package/dist/battle/index.js +5 -0
- package/dist/battle/index.js.map +1 -1
- package/dist/board/FogOfWar.d.ts +32 -2
- package/dist/board/FogOfWar.d.ts.map +1 -1
- package/dist/board/FogOfWar.js +70 -6
- package/dist/board/FogOfWar.js.map +1 -1
- package/dist/core/Achievements.d.ts +40 -7
- package/dist/core/Achievements.d.ts.map +1 -1
- package/dist/core/Achievements.js +51 -7
- package/dist/core/Achievements.js.map +1 -1
- package/dist/core/Blob.d.ts +13 -2
- package/dist/core/Blob.d.ts.map +1 -1
- package/dist/core/Blob.js +16 -2
- package/dist/core/Blob.js.map +1 -1
- package/dist/core/Hex.d.ts +26 -6
- package/dist/core/Hex.d.ts.map +1 -1
- package/dist/core/Hex.js +40 -13
- package/dist/core/Hex.js.map +1 -1
- package/dist/core/Input.d.ts +36 -0
- package/dist/core/Input.d.ts.map +1 -1
- package/dist/core/Input.js +63 -1
- package/dist/core/Input.js.map +1 -1
- package/dist/core/Random.d.ts +93 -0
- package/dist/core/Random.d.ts.map +1 -1
- package/dist/core/Random.js +177 -0
- package/dist/core/Random.js.map +1 -1
- package/dist/core/SyncGuard.d.ts +57 -0
- package/dist/core/SyncGuard.d.ts.map +1 -0
- package/dist/core/SyncGuard.js +116 -0
- package/dist/core/SyncGuard.js.map +1 -0
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -1
- package/dist/core/index.js.map +1 -1
- package/dist/i18n/Po.d.ts +51 -0
- package/dist/i18n/Po.d.ts.map +1 -0
- package/dist/i18n/Po.js +176 -0
- package/dist/i18n/Po.js.map +1 -0
- package/dist/i18n/index.d.ts +2 -0
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js +1 -0
- package/dist/i18n/index.js.map +1 -1
- package/dist/mw_games.global.js +104 -98
- package/dist/mwl/MapFile.d.ts +41 -0
- package/dist/mwl/MapFile.d.ts.map +1 -0
- package/dist/mwl/MapFile.js +43 -0
- package/dist/mwl/MapFile.js.map +1 -0
- package/dist/mwl/campaign.d.ts +51 -0
- package/dist/mwl/campaign.d.ts.map +1 -0
- package/dist/mwl/campaign.js +58 -0
- package/dist/mwl/campaign.js.map +1 -0
- package/dist/mwl/carryover.d.ts +79 -0
- package/dist/mwl/carryover.d.ts.map +1 -0
- package/dist/mwl/carryover.js +63 -0
- package/dist/mwl/carryover.js.map +1 -0
- package/dist/mwl/content.d.ts +10 -0
- package/dist/mwl/content.d.ts.map +1 -1
- package/dist/mwl/content.js +8 -0
- package/dist/mwl/content.js.map +1 -1
- package/dist/mwl/hooks.d.ts +6 -5
- package/dist/mwl/hooks.d.ts.map +1 -1
- package/dist/mwl/hooks.js.map +1 -1
- package/dist/mwl/index.d.ts +8 -2
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +4 -1
- package/dist/mwl/index.js.map +1 -1
- package/dist/mwl/runtime.d.ts +140 -7
- package/dist/mwl/runtime.d.ts.map +1 -1
- package/dist/mwl/runtime.js +427 -33
- package/dist/mwl/runtime.js.map +1 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/schema.js +97 -18
- package/dist/mwl/schema.js.map +1 -1
- package/dist/roguelike/MultiTurnBeam.d.ts +50 -12
- package/dist/roguelike/MultiTurnBeam.d.ts.map +1 -1
- package/dist/roguelike/MultiTurnBeam.js +100 -53
- package/dist/roguelike/MultiTurnBeam.js.map +1 -1
- package/dist/roguelike/Targeting.d.ts +94 -0
- package/dist/roguelike/Targeting.d.ts.map +1 -1
- package/dist/roguelike/Targeting.js +123 -0
- package/dist/roguelike/Targeting.js.map +1 -1
- package/dist/roguelike/index.d.ts +3 -1
- package/dist/roguelike/index.d.ts.map +1 -1
- package/dist/roguelike/index.js +1 -0
- package/dist/roguelike/index.js.map +1 -1
- package/dist/simulation/CampaignSave.d.ts +68 -0
- package/dist/simulation/CampaignSave.d.ts.map +1 -0
- package/dist/simulation/CampaignSave.js +54 -0
- package/dist/simulation/CampaignSave.js.map +1 -0
- package/dist/simulation/EventPresentation.d.ts +111 -0
- package/dist/simulation/EventPresentation.d.ts.map +1 -0
- package/dist/simulation/EventPresentation.js +120 -0
- package/dist/simulation/EventPresentation.js.map +1 -0
- package/dist/simulation/index.d.ts +4 -0
- package/dist/simulation/index.d.ts.map +1 -1
- package/dist/simulation/index.js +2 -0
- package/dist/simulation/index.js.map +1 -1
- package/dist/two-d/pixi-interop.d.ts +18 -6
- package/dist/two-d/pixi-interop.d.ts.map +1 -1
- package/dist/two-d/pixi-interop.js +18 -6
- package/dist/two-d/pixi-interop.js.map +1 -1
- package/dist/two-d/render/AnimatedSprite.d.ts +74 -7
- package/dist/two-d/render/AnimatedSprite.d.ts.map +1 -1
- package/dist/two-d/render/AnimatedSprite.js +88 -26
- package/dist/two-d/render/AnimatedSprite.js.map +1 -1
- package/dist/two-d/render/Camera.d.ts +64 -4
- package/dist/two-d/render/Camera.d.ts.map +1 -1
- package/dist/two-d/render/Camera.js +132 -11
- package/dist/two-d/render/Camera.js.map +1 -1
- package/dist/two-d/render/Halo.d.ts +58 -0
- package/dist/two-d/render/Halo.d.ts.map +1 -0
- package/dist/two-d/render/Halo.js +48 -0
- package/dist/two-d/render/Halo.js.map +1 -0
- package/dist/two-d/render/ImageModifiers.d.ts +174 -0
- package/dist/two-d/render/ImageModifiers.d.ts.map +1 -1
- package/dist/two-d/render/ImageModifiers.js +438 -1
- package/dist/two-d/render/ImageModifiers.js.map +1 -1
- package/dist/two-d/render/LightningArc.d.ts +57 -0
- package/dist/two-d/render/LightningArc.d.ts.map +1 -0
- package/dist/two-d/render/LightningArc.js +91 -0
- package/dist/two-d/render/LightningArc.js.map +1 -0
- package/dist/two-d/render/PaletteRemap.d.ts +135 -0
- package/dist/two-d/render/PaletteRemap.d.ts.map +1 -0
- package/dist/two-d/render/PaletteRemap.js +181 -0
- package/dist/two-d/render/PaletteRemap.js.map +1 -0
- package/dist/two-d/render/Particles.d.ts +26 -0
- package/dist/two-d/render/Particles.d.ts.map +1 -1
- package/dist/two-d/render/Particles.js +23 -2
- package/dist/two-d/render/Particles.js.map +1 -1
- package/dist/two-d/render/SpriteAttachment.d.ts +55 -0
- package/dist/two-d/render/SpriteAttachment.d.ts.map +1 -0
- package/dist/two-d/render/SpriteAttachment.js +60 -0
- package/dist/two-d/render/SpriteAttachment.js.map +1 -0
- package/dist/two-d/render/TerrainGraphics.d.ts +142 -0
- package/dist/two-d/render/TerrainGraphics.d.ts.map +1 -0
- package/dist/two-d/render/TerrainGraphics.js +161 -0
- package/dist/two-d/render/TerrainGraphics.js.map +1 -0
- package/dist/two-d/render/index.d.ts +13 -3
- package/dist/two-d/render/index.d.ts.map +1 -1
- package/dist/two-d/render/index.js +6 -1
- package/dist/two-d/render/index.js.map +1 -1
- package/dist/two-d/stage/StoryScreen.d.ts +110 -0
- package/dist/two-d/stage/StoryScreen.d.ts.map +1 -0
- package/dist/two-d/stage/StoryScreen.js +178 -0
- package/dist/two-d/stage/StoryScreen.js.map +1 -0
- package/dist/two-d/stage/index.d.ts +2 -0
- package/dist/two-d/stage/index.d.ts.map +1 -1
- package/dist/two-d/stage/index.js +1 -0
- package/dist/two-d/stage/index.js.map +1 -1
- package/dist/two-d/ui/Checkbox.d.ts +44 -0
- package/dist/two-d/ui/Checkbox.d.ts.map +1 -0
- package/dist/two-d/ui/Checkbox.js +89 -0
- package/dist/two-d/ui/Checkbox.js.map +1 -0
- package/dist/two-d/ui/DataTable.d.ts +79 -0
- package/dist/two-d/ui/DataTable.d.ts.map +1 -0
- package/dist/two-d/ui/DataTable.js +164 -0
- package/dist/two-d/ui/DataTable.js.map +1 -0
- package/dist/two-d/ui/Dropdown.d.ts +72 -0
- package/dist/two-d/ui/Dropdown.d.ts.map +1 -0
- package/dist/two-d/ui/Dropdown.js +126 -0
- package/dist/two-d/ui/Dropdown.js.map +1 -0
- package/dist/two-d/ui/FloatingText.d.ts +43 -4
- package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingText.js +52 -5
- package/dist/two-d/ui/FloatingText.js.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.d.ts +86 -13
- package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.js +89 -18
- package/dist/two-d/ui/FloatingTextStack.js.map +1 -1
- package/dist/two-d/ui/Layout.d.ts +105 -0
- package/dist/two-d/ui/Layout.d.ts.map +1 -0
- package/dist/two-d/ui/Layout.js +174 -0
- package/dist/two-d/ui/Layout.js.map +1 -0
- package/dist/two-d/ui/ScrollBox.d.ts +67 -0
- package/dist/two-d/ui/ScrollBox.d.ts.map +1 -0
- package/dist/two-d/ui/ScrollBox.js +127 -0
- package/dist/two-d/ui/ScrollBox.js.map +1 -0
- package/dist/two-d/ui/Skins.d.ts +59 -0
- package/dist/two-d/ui/Skins.d.ts.map +1 -0
- package/dist/two-d/ui/Skins.js +77 -0
- package/dist/two-d/ui/Skins.js.map +1 -0
- package/dist/two-d/ui/Slider.d.ts +91 -0
- package/dist/two-d/ui/Slider.d.ts.map +1 -0
- package/dist/two-d/ui/Slider.js +170 -0
- package/dist/two-d/ui/Slider.js.map +1 -0
- package/dist/two-d/ui/Spinner.d.ts +67 -0
- package/dist/two-d/ui/Spinner.d.ts.map +1 -0
- package/dist/two-d/ui/Spinner.js +137 -0
- package/dist/two-d/ui/Spinner.js.map +1 -0
- package/dist/two-d/ui/TabbedList.d.ts +118 -0
- package/dist/two-d/ui/TabbedList.d.ts.map +1 -0
- package/dist/two-d/ui/TabbedList.js +221 -0
- package/dist/two-d/ui/TabbedList.js.map +1 -0
- package/dist/two-d/ui/TextModel.d.ts +65 -0
- package/dist/two-d/ui/TextModel.d.ts.map +1 -0
- package/dist/two-d/ui/TextModel.js +126 -0
- package/dist/two-d/ui/TextModel.js.map +1 -0
- package/dist/two-d/ui/TreeView.d.ts +68 -0
- package/dist/two-d/ui/TreeView.d.ts.map +1 -0
- package/dist/two-d/ui/TreeView.js +133 -0
- package/dist/two-d/ui/TreeView.js.map +1 -0
- package/dist/two-d/ui/Window.d.ts +13 -0
- package/dist/two-d/ui/Window.d.ts.map +1 -1
- package/dist/two-d/ui/Window.js +23 -0
- package/dist/two-d/ui/Window.js.map +1 -1
- package/dist/two-d/ui/WindowStack.d.ts.map +1 -1
- package/dist/two-d/ui/WindowStack.js +2 -0
- package/dist/two-d/ui/WindowStack.js.map +1 -1
- package/dist/two-d/ui/index.d.ts +27 -3
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +14 -2
- package/dist/two-d/ui/index.js.map +1 -1
- package/dist/two-d/ui/markup.d.ts +127 -0
- package/dist/two-d/ui/markup.d.ts.map +1 -0
- package/dist/two-d/ui/markup.js +322 -0
- package/dist/two-d/ui/markup.js.map +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/world/SideTurns.d.ts +115 -0
- package/dist/world/SideTurns.d.ts.map +1 -0
- package/dist/world/SideTurns.js +138 -0
- package/dist/world/SideTurns.js.map +1 -0
- package/dist/world/index.d.ts +2 -0
- package/dist/world/index.d.ts.map +1 -1
- package/dist/world/index.js +1 -0
- package/dist/world/index.js.map +1 -1
- package/package.json +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,293 @@ All notable changes to `mwg` are documented here. Format follows
|
|
|
5
5
|
[Semantic Versioning](https://semver.org/) as of this first release - a 0.y.z version means
|
|
6
6
|
the public API may still change between minor versions.
|
|
7
7
|
|
|
8
|
+
## [0.7.7] - 2026-09-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Heuristic AI (item 269): `Aspects`, `Difficulty`, `Goals`, `RecruitmentPattern`, and a
|
|
13
|
+
`HeuristicAI` candidate/stage pipeline with `defaultWeigh`/`keepAwayScore`/`goalScore`. Aspects
|
|
14
|
+
ship no values; the numbers stay content, and `keep_away` is a real penalty.
|
|
15
|
+
- Achievements and battle statistics (item 273): `core.Achievements` gained sub-achievements
|
|
16
|
+
(multi-criterion unlocks via `AchievementDef.criteria`, read back with `subProgress`), and
|
|
17
|
+
`battle.BattleStats` tracks recruits/recalls/advances/kills/deaths/damage dealt/damage taken by
|
|
18
|
+
unit type for a `PlayerStats`/`RunHistory` summary to fold in.
|
|
19
|
+
- Free map rotation (item 286): `Camera.rotateTo`/`animateRotationTo` turn the view to any angle,
|
|
20
|
+
not only a grid's whole steps, sharing 285's layer-level transform and taking the shorter way
|
|
21
|
+
around a full turn when animated.
|
|
22
|
+
- Battle UI models (item 265): `AttackPreview` (damage totals and an animated frame timeline from
|
|
23
|
+
the game's own numbers), `AttackDialog` (the selector plus the preview and its clock),
|
|
24
|
+
`UnitSelector` (attacker then target), and `Whiteboard` (planned orders with undo/redo). All
|
|
25
|
+
renderer-free; the dialog's pixels are `Bar`/`Label` a game composes.
|
|
26
|
+
- Story screens (item 264): `StoryScreen` (a full-screen backdrop, title and text advanced by a
|
|
27
|
+
click) over the renderer-free `StorySequence` (`advance`/`back`/`goTo`/`skip`/`restart`, with the
|
|
28
|
+
current beat's `music` reported as a signal). `StoryBeat` matches `MwlWorld.story`'s shape.
|
|
29
|
+
- Data-driven shell layout and skins (item 263): `Layout` (`resolveAnchor`/`anchorAlign` over nine
|
|
30
|
+
named anchors plus `fill`, and a `Grid` of `size`-or-`grow` tracks) and `Skins` (per-widget,
|
|
31
|
+
per-state looks with a widget-state -> widget-idle -> wildcard fallback chain, and `Skins.from`
|
|
32
|
+
for plain data). Both renderer-free.
|
|
33
|
+
- The missing UI widgets (item 261): `Slider` (`sliderFraction`/`sliderValueAt`), `Checkbox`,
|
|
34
|
+
`Spinner` (`spinValue`), `Dropdown`, `TextModel`, `DataTable`, `TreeView` and `ScrollBox`
|
|
35
|
+
(`scrollOffset`). The rule-bearing parts are pure functions or renderer-free models tested
|
|
36
|
+
headlessly, the drawing is a thin theme-driven shell, and text fields pair `TextModel` with a
|
|
37
|
+
`Label` rather than a separate `TextArea`.
|
|
38
|
+
- Fixed-angle view rotation on `Camera` (item 285): `grid: 'square' | 'hex'` sets four quarter turns
|
|
39
|
+
or six 60-degree steps, `setRotationStep`/`rotate` move through them, `toScreen`/`toWorld` invert
|
|
40
|
+
the turn, `view` reports the box around the turned viewport for culling, and `uprightRotation`
|
|
41
|
+
keeps a label drawn into the world upright.
|
|
42
|
+
- WML action vocabulary in `[event]` (item 250): `fire_event`, `store_unit`/`unstore_unit`/`recall`,
|
|
43
|
+
`modify_unit`/`heal_unit`, `set_terrain`, `capture_village`, `clear_shroud`, and the scenario-level
|
|
44
|
+
`[role]`/`[object]`/`[story]`, backed by `MwlWorld.villages`/`clearedShroud`/`roles`/`objects`/
|
|
45
|
+
`story` (all saved). WML's scenario `[item]` is deliberately not added: the tag already means an
|
|
46
|
+
inventory item definition.
|
|
47
|
+
- `blendPixels`/`rotatePixels` in `ImageModifiers.ts`: `~BLEND` and `~ROTATE` moved from a
|
|
48
|
+
runtime sprite-property/`ColorMatrixFilter` approximation to an exact, baked-once per-pixel
|
|
49
|
+
operation in `applyTextureModifiers` (`~ROTATE` in particular now rotates the source pixels
|
|
50
|
+
and expands the surface, which a sprite's own `rotation` cannot do - needed for terrain and
|
|
51
|
+
anything else that must keep tiling after the rotation). `blendMatrix` stays available for a
|
|
52
|
+
caller that explicitly wants the cheaper live approximation instead.
|
|
53
|
+
- `PaletteRemapMode` (`'exact'` | `'nearest'`) on `remapPixels`/`recolorTexture`, defaulting to
|
|
54
|
+
`'exact'`: a pixel is now repainted only on an exact match to a `from` colour unless
|
|
55
|
+
`'nearest'` is asked for explicitly. The old always-nearest behaviour silently recoloured an
|
|
56
|
+
entire image the first time `~RC`/`~PAL` met a short, specific palette instead of a full
|
|
57
|
+
covering; `paletteRangeMapping`'s own gradient usage opts into `'nearest'` explicitly, since
|
|
58
|
+
that is the one case it is correct for.
|
|
59
|
+
- `ImageTextureProbe.resolveColor` and the exported `parseColorPairs`/`parsePaletteLists`: `~RC`
|
|
60
|
+
and `~PAL` now accept a named colour (`~RC(magenta>red)`), not hex only, and `~PAL`'s two
|
|
61
|
+
comma-separated colour lists parse correctly instead of only the first colour of each list
|
|
62
|
+
(`parseImagePath`'s own comma split does not respect that list's internal boundary, which
|
|
63
|
+
`parsePaletteLists` now reconstructs).
|
|
64
|
+
- `~BLIT`/`~MASK` now hand `resolveTexture` the argument exactly as written, nested modifiers
|
|
65
|
+
included (`~BLIT(unit.png~RC(magenta>red),0,0)` calls it with the full string, not a bare
|
|
66
|
+
path with the modifier silently dropped).
|
|
67
|
+
- `withTextureCanvas` is now exported from `two-d/render`, so a caller building its own
|
|
68
|
+
texture-level modifier can reuse the same canvas bookkeeping `recolorTexture`/`~BLIT`/`~MASK`/
|
|
69
|
+
`~BLEND`/`~ROTATE` all share instead of duplicating it.
|
|
70
|
+
|
|
71
|
+
- `mwl.parseMapFile`/`MwlMapFile` (item 284): a Wesnoth-shaped `.map` loader. The `key=value`
|
|
72
|
+
header is split off and kept (keys not interpreted), and the comma-separated grid goes through
|
|
73
|
+
the same `parseTerrain` an inline `[map] terrain=` uses.
|
|
74
|
+
- `actors.craft` category-shaped ingredients (item 289): `Ingredient` now accepts `id` as a string
|
|
75
|
+
or a list, a `category` matched against the item definition's new `InventoryItem.category`
|
|
76
|
+
(kind-level, so it stays out of saves), or a `matches(item)` predicate. `craft` allocates against
|
|
77
|
+
a working copy so two flexible ingredients cannot count one stack twice.
|
|
78
|
+
- `two-d/render/SpriteAttachment.ts`: `SpriteAttachment` (item 291), ties a second sprite's
|
|
79
|
+
position to a first one's - `follow(x, y)` applies an offset, an optional `duration` makes
|
|
80
|
+
`update(dt)` report `done` once it elapses, so a permanent shadow and a temporary status icon
|
|
81
|
+
are the same class with different options. Renderer-neutral like `Projectile`/`LightningArc`.
|
|
82
|
+
|
|
83
|
+
- `two-d/render/LightningArc.ts`: `LightningArc` (item 292), the position data for a jittered
|
|
84
|
+
line between two points - a lightning bolt, a tether, a chain-lightning link - geometry only,
|
|
85
|
+
drawn by the caller through `Shape2D`'s `Graphics`. `retarget` moves either endpoint for a
|
|
86
|
+
tether following two moving units; an optional `flickerInterval` re-rolls the jitter on a
|
|
87
|
+
timer instead of holding one fixed shape.
|
|
88
|
+
- `assets.load`'s `optional`/`onMissing` options (item 287): paths a game can do without load
|
|
89
|
+
separately from the required batch, so one missing file never aborts the rest; `texture`/`get`
|
|
90
|
+
take a `fallback` value returned instead of a throw for a path that never loaded.
|
|
91
|
+
|
|
92
|
+
- `MultiTurnBeam` fronts, `blocker`, `onCell` and saved shape identity (item 278): a beam is now a
|
|
93
|
+
sequence of per-turn fronts rather than one captured cell per turn, so a game-supplied
|
|
94
|
+
`fronts(previous, turn)` expresses a cone, burst, fork or moving front. `BeamBlocker` replaces
|
|
95
|
+
the implicit blocker flags with one policy, `BeamStep.cells` reports every reached cell, and a
|
|
96
|
+
save carries the shape name so `fromJSON` refuses a mismatched shape; pre-fronts saves still load.
|
|
97
|
+
- `two-d/render/PaletteRemap.ts`: `remapPixels`/`paletteRangeMapping`/`recolorTexture` (item
|
|
98
|
+
256), a nearest-colour palette remap for team-colour-by-range - `paletteRangeMapping` places a
|
|
99
|
+
reference palette's own lightest-to-darkest order along a `min -> mid -> max` gradient,
|
|
100
|
+
`remapPixels` is the renderer-free pixel core, and `recolorTexture` (via the new shared
|
|
101
|
+
`withTextureCanvas` helper) is the canvas-backed wrapper.
|
|
102
|
+
- Eleven more `image~MOD` suffixes in `ImageModifiers.ts` (item 255): `~O`, `~R`/`~G`/`~B`,
|
|
103
|
+
`~BLEND` and `~CHAN` join `~FL`/`~SCALE`/`~GS`/`~CS` as sprite-property/`ColorMatrixFilter`
|
|
104
|
+
operations on `applyImageModifiers`; `~ROTATE` sets the sprite's own rotation. `~RC`, `~PAL`,
|
|
105
|
+
`~BLIT` and `~MASK` needed real pixel access or a sibling texture, so they are the new
|
|
106
|
+
`applyTextureModifiers`, built on 256's `withTextureCanvas`. `~GS` no longer replaces a filter
|
|
107
|
+
already on the sprite (was clobbering, now appends).
|
|
108
|
+
- `two-d/render/TerrainGraphics.ts`: `resolveTerrainGraphics`/`TerrainRule`/`matchTerrainRule`/
|
|
109
|
+
`squareRotate`/`hexRotate` (item 257), a rule-driven `[terrain_graphics]`-style transition pass
|
|
110
|
+
generalising `Autotile`'s fixed 47-shape blob table: arbitrary flag conditions at arbitrary
|
|
111
|
+
offsets, multi-image placement (so a piece bigger than one cell is one rule's own data, not
|
|
112
|
+
something `TileMap`'s one-sprite-per-cell grid has to hold), rotation and probability. Returns
|
|
113
|
+
plain placement data; drawing it through a renderer is still open.
|
|
114
|
+
- `markupToHtml` now renders colour and size as an inline style; `markupAccessibilityText` is a
|
|
115
|
+
proper accessibility projection for markup (an image becomes a caller-described string instead
|
|
116
|
+
of its raw path); `layoutMarkupLines` wraps already-parsed spans word by word, each word
|
|
117
|
+
measured under its own span's style, so a bold or larger run wraps where its own wider glyphs
|
|
118
|
+
actually land (item 258's remaining acceptance, in part - see ROADMAP for what is still open).
|
|
119
|
+
|
|
120
|
+
- `ParticleEmitter` spawn bounds (item 279): `ParticleSpawnArea` is an optional `spawn` on the
|
|
121
|
+
emitter's options, a `rect` or `ellipse` in local space so a burst spreads across an extent
|
|
122
|
+
instead of every particle starting at the emitter's single origin (a forge mouth, a flame
|
|
123
|
+
column, rain across a room). A point emitter draws nothing extra, so the seeded sequence an
|
|
124
|
+
existing replay recorded is unchanged.
|
|
125
|
+
- `roguelike.TargetingController` (item 280): the input-facing half of targeting, renderer-free.
|
|
126
|
+
A cursor moved by `move(dx, dy)` or a caller-resolved `moveTo(cell)`, range and line-of-sight
|
|
127
|
+
validation with an optional `validate` hook, a `preview()` of the shape's cells and a
|
|
128
|
+
`confirm()`/`cancel()` result carrying cells only, so damage, legality details and drawing stay
|
|
129
|
+
with the game. Hex levels step by their own six neighbours and measure in hex distance.
|
|
130
|
+
- `AudioListener`/`SoundSource`/`audioGain`/`audioPan`/`AudioFalloff` (item 271): positional
|
|
131
|
+
audio, renderer-free. A listener the game moves and turns, a `Sound` placed at a world point,
|
|
132
|
+
and the distance gain and stereo pan between them. `SoundSource.playFor(listener)` applies the
|
|
133
|
+
gain through `Sound.play`, which now takes an optional gain argument.
|
|
134
|
+
- `Input.onText`/`Input.onComposition`/`Input.textFromKey`/`Input.dispatchText`/
|
|
135
|
+
`Input.dispatchComposition` (item 262): the character a key press produces, and the three
|
|
136
|
+
phases of an input-method composition, so a free text field can be built. `onText` is a
|
|
137
|
+
stack-mode signal a focused field can consume.
|
|
138
|
+
- `MersenneTwister`/`MersenneTwisterState`/`RandomStreams` (item 266): MT19937 with the reference
|
|
139
|
+
`mt_rng`'s `seed`/`discard`/`discardCount` bookkeeping (verified against the standard test
|
|
140
|
+
vector) and named, independent streams derived from one base seed, so per-entity and per-usage
|
|
141
|
+
draws never shift each other. C++'s `uniform_int_distribution` is implementation-defined, so the
|
|
142
|
+
int mapping is MWG's own and documented as such.
|
|
143
|
+
- `i18n.parsePo`/`PoOptions` (item 270): a gettext `.po` loader into the same `Catalog` surface
|
|
144
|
+
as `parseFTL`, with `msgctxt`, `msgstr[N]` mapped onto the locale's CLDR categories,
|
|
145
|
+
untranslated entries left out for base fallback, and gettext `domain`-prefixed keys.
|
|
146
|
+
- `world.SideTurns`/`SideTurn`/`TimeOfDay`/`TimeArea`/`Alignment`/`alignmentBonus` (item 267): the
|
|
147
|
+
reference's outer turn model, sides in order with a schedule that steps per round, and a per-hex
|
|
148
|
+
`lawfulBonusAt` where a `TimeArea` overrides the global time of day.
|
|
149
|
+
- `ui.TabbedList`/`ListTab`/`TabbedListOptions` (item 281): a renderer-free model for a tabbed,
|
|
150
|
+
filtered, paged list with selection and a detail/close state, over caller-supplied rows and
|
|
151
|
+
predicates. The page is derived from the selection, so the two cannot disagree.
|
|
152
|
+
- `simulation.EventPresentation`/`EventPresentationOptions` (item 282): the documented recipe
|
|
153
|
+
tying a `SimulationRuntime` to a `PresentationQueue` - command result, animation lock,
|
|
154
|
+
scheduled secondary actor, cancellation, and save/load that resumes idle.
|
|
155
|
+
- `MwlWorld.sideStatus` and per-side `[victory]`/`[defeat]` under `[side]` (item 283): a side's
|
|
156
|
+
own condition is evaluated after the scenario-wide `[objectives]` ones, its result recorded by
|
|
157
|
+
side id, and `[win]`/`[lose]` record the side they name too. `MwlCommand`'s `win`/`lose` gained an
|
|
158
|
+
optional `side`.
|
|
159
|
+
- `simulation.CampaignSave`/`CampaignSaveState`/`CampaignSaveParts` (item 249): one `SaveSystem`
|
|
160
|
+
slot for a whole campaign, holding the campaign, world and simulation snapshots together, with
|
|
161
|
+
the world payload left opaque.
|
|
162
|
+
- `core.stateChecksum`/`core.SyncGuard` (item 272): a key-order-stable 32-bit checksum of JSON
|
|
163
|
+
state and a guard comparing the checksums peers compute per tick, so a lockstep run can notice
|
|
164
|
+
and locate a divergence.
|
|
165
|
+
- `ai.personalScoreView`/`ai.sideScoreView`/`ai.scoreWith`, over `ScoreSubject`/`ScoreView`/
|
|
166
|
+
`ScorePersonality` (items 274-276): the numbers a mind needs when a search is the wrong tool.
|
|
167
|
+
The game supplies what anything is worth, the framework assembles the view (own, allies and
|
|
168
|
+
enemies kept apart, with a `seen` count), and a personality is weights over the three, so a
|
|
169
|
+
selfish scout and a loyal one are content rather than two AI implementations. A unit's sight and a
|
|
170
|
+
whole side's read through the same two functions and differ only in the visibility set they are
|
|
171
|
+
handed, and `board.FactionFog.sees(side)` is that set as a predicate: it reads the shroud as it is
|
|
172
|
+
when called, not as it was when the predicate was made.
|
|
173
|
+
- `Animation`/`AnimatedSprite` frame timing (item 254): a frame is now a texture or
|
|
174
|
+
`{ texture, duration, offsetX, offsetY }`, so a frame carries its own time and its own place, as
|
|
175
|
+
Wesnoth's `image=a.png:120,b.png:80` does, and `AnimationOptions.startTime` is signed the way
|
|
176
|
+
Wesnoth writes it: positive holds the first frame, negative starts partway in (`start_time=-450`
|
|
177
|
+
over frames of 100ms opens on the fifth, which is how a swing is lined up with its damage frame).
|
|
178
|
+
`Animation.frameAt`/`frameIndexAt` are the rule, the sprite advances on its own elapsed time
|
|
179
|
+
rather than a leftover timer, and a frame's offset is reported through `AnimatedSprite.frameOffset`
|
|
180
|
+
for the caller to add where it already positions the sprite.
|
|
181
|
+
- `FactionFog.share(factions)` (item 268): factions put on one map, what is lit now and the shroud
|
|
182
|
+
they remember alike, which is what Wesnoth's `share_vision` means. Nothing shares until asked,
|
|
183
|
+
growing a group widens it rather than replacing it, and a share declared after a `sync` still
|
|
184
|
+
counts because the group is read when a cell is asked about. `sees(side)` follows it, so a side's
|
|
185
|
+
score view reads the team's eyes.
|
|
186
|
+
- `campaignChain` (item 247): a `[campaign]` pointed at `simulation.Campaign`. `first_scenario` opens
|
|
187
|
+
it, each `[scenario]`'s `next_scenario` is where it goes when it is won, and the chain ends where a
|
|
188
|
+
scenario has no next one. The order is content and the playing stays the game's, since the runner
|
|
189
|
+
is a callback; a scenario that decides for itself keeps its decision, which is where a runtime
|
|
190
|
+
`[endlevel]` will hook in. Malformed chains (no scenarios, one id twice, an unknown
|
|
191
|
+
`first_scenario`) throw by name. The `[campaign]` schema now accepts `first_scenario`, and
|
|
192
|
+
`contentCatalog` exposes each campaign's scenario links.
|
|
193
|
+
- `[endlevel]` and carry-over (item 248): a scenario ends with `result=victory|defeat`, and what it
|
|
194
|
+
hands on is `endLevelCarryover` - `bonus` plus a share of the side's gold (`carryover_percentage`,
|
|
195
|
+
80% when unset, which is the reference's own default rather than an invented one), the units of
|
|
196
|
+
that side still standing as the recall list, and `next_scenario` for where the campaign goes (the
|
|
197
|
+
hook `campaignChain` already honours). `carryoverIntoScenario` is the other half of the rule: the
|
|
198
|
+
carried share is added to what the next scenario declares when `carryover_add` asks for it, and is
|
|
199
|
+
otherwise a floor under it. `MWL_DEFAULT_CARRYOVER_PERCENTAGE` names the default.
|
|
200
|
+
- `[kill]` as a filter (item 251): naming a unit that is not there still throws, while a `[kill]`
|
|
201
|
+
whose attributes are a filter over the world's units is a no-op when it matches nobody, as WML's
|
|
202
|
+
own `[kill]` is. It reads the same attributes an event filter does, an empty filter matches every
|
|
203
|
+
unit (there as here), and the schema entry is open on attributes so a filter can say what it
|
|
204
|
+
means.
|
|
205
|
+
- A scenario turn limit, as a native condition (item 253): `[game] turn_limit=N` makes the runtime
|
|
206
|
+
set `time_over` as a world variable and fire the `time_over` event once the turn counter passes N.
|
|
207
|
+
Content tests it like any variable and answers it like any event, `[endlevel]` included, because a
|
|
208
|
+
limit is usually a defeat and sometimes the whole scenario.
|
|
209
|
+
- An inline-markup contract, first half of item 258: `MarkupSpan` plus `parseMarkup`/`stripMarkup`/
|
|
210
|
+
`markupToHtml`/`escapeHtml` in `src/two-d/ui/markup.ts`. Nested emphasis, named and hex colours, a
|
|
211
|
+
size, a line break, an image span carrying a path, `$name` interpolation supplied by the caller,
|
|
212
|
+
and the five entities as escapes. Unknown or malformed tags and unset variables stay literal
|
|
213
|
+
rather than vanishing, and output text is escaped so no raw HTML can leak. What the item's
|
|
214
|
+
acceptance still wants (backend equivalence, wrapping after styling, images through the asset
|
|
215
|
+
resolver, an accessibility projection) is recorded on the item rather than implied here.
|
|
216
|
+
- The `[side]` surface, and one key of it with behaviour (item 252): the Wesnoth keys `team_name`,
|
|
217
|
+
`user_team_name`, `share_vision`, `village_gold`, `heal`, `fog`, `shroud`, `hidden` and `flag` are
|
|
218
|
+
read onto `MwlWorld.sides` as written (`yes`/`no` as booleans), and `sideVisionGroups` turns
|
|
219
|
+
`team_name` with `share_vision` into the groups `FactionFog.share` takes.
|
|
220
|
+
- Hex projection options, first half of item 259: `hexToPixel`/`pixelToHex` take
|
|
221
|
+
`HexShape`/`HexOrientation`/`HexOffset`, so a grid can be flat-top or pointy-top with either
|
|
222
|
+
offset parity, and the default is the flat-top odd-q layout the module always had. The inverse is
|
|
223
|
+
the nearest cell centre, which cannot disagree with the projection at a hex's edge.
|
|
224
|
+
- `Halo` (item 260): the glow around a unit, an aura, a shrine's light. An `AnimatedSprite` that
|
|
225
|
+
`follow(x, y)`s a target with its own offset applied once, additive by default, with z-order left
|
|
226
|
+
to the caller. `[halo_frame]` needed nothing new after 254 gave frames their own timing.
|
|
227
|
+
|
|
228
|
+
### Fixed
|
|
229
|
+
|
|
230
|
+
- `FloatingTextStack` stacked the wrong way round: it moved the **newcomer** down by
|
|
231
|
+
`height + 1`, where Java's `FloatingText.push()` anchors the newcomer on the target and lifts
|
|
232
|
+
the lines already there to `below.top - height - 4`. A second pop-up on one target in one turn
|
|
233
|
+
therefore landed *below* the target instead of above the first - found by a consumer measuring
|
|
234
|
+
it, not by the tests, which asserted the offset's magnitude and never its direction.
|
|
235
|
+
`floatingTextStackOffset` is replaced by `floatingTextStackLift` (the position the older line
|
|
236
|
+
must take, rather than an offset the caller adds, so the sign cannot be got wrong quietly) and
|
|
237
|
+
by `floatingTextStackMoves`, the whole policy: which lines move, newest first, where each one
|
|
238
|
+
ends up, and what age each is forced to. The loop is arithmetic now, so it is checked as
|
|
239
|
+
arithmetic rather than through a rendered label. `FLOATING_TEXT_STACK_GAP` names Java's 4px gap.
|
|
240
|
+
- The lift survived exactly one frame. `FloatingText.update` wrote its own `y` from a base captured
|
|
241
|
+
on the first frame, so a line the stack had just moved snapped back on the next one: measured, a
|
|
242
|
+
line lifted to 166.6 was back at 194.4 beside the newcomer at 200, still overlapping it. The rise
|
|
243
|
+
now moves the pop-up's inner layer and never its position, which belongs to the caller and to the
|
|
244
|
+
stack; `baseY` is gone. A consumer repositioning a pop-up mid-flight no longer loses the position
|
|
245
|
+
on the next frame either.
|
|
246
|
+
- A pop-up scaled *after* `push` was measured before the scale, because a `FloatingText`'s `height`
|
|
247
|
+
includes its own scale. A port rasterising at 21px and drawing at 7 therefore spaced lifted lines
|
|
248
|
+
33.4 world pixels apart for lines 9.8 tall. `push` now takes a `scale`, applied before the pop-up
|
|
249
|
+
is measured, so the entry records the size the line is actually drawn at.
|
|
250
|
+
|
|
251
|
+
### Changed
|
|
252
|
+
|
|
253
|
+
- `FloatingText.shortenLife(seconds)` becomes `FloatingText.ageAtLeast(seconds)`, which does what
|
|
254
|
+
Java's rule says rather than something close: `above.timeLeft = Math.min(above.timeLeft,
|
|
255
|
+
LIFESPAN - numBelow / 5f)` caps what is *left*, and a cap on what is left is a floor under the
|
|
256
|
+
age. A line that has already lived past the floor keeps the age it has, where subtracting the
|
|
257
|
+
floor shortened it a second time. `floatingTextAgeAtLeast` is that arithmetic as a pure function,
|
|
258
|
+
so the difference is checked by a test; `floatingTextStackLifePenalty(linesBelow)` stays the
|
|
259
|
+
floor itself, a fifth of a second per line below.
|
|
260
|
+
- A stack keys on `key` alone, as Java's `stacks.get(key)` does. The old rule wanted the same key
|
|
261
|
+
*and* the same origin, which this framework had invented. A caller that reused one key for two
|
|
262
|
+
different targets now sees their lines stacked together, and has to give each target its own key.
|
|
263
|
+
|
|
264
|
+
### Changed
|
|
265
|
+
|
|
266
|
+
- One side identity in `MwlWorld` (item 277): `unit.side`, `MwlMessage.side`, `MwlMap.starts` keys
|
|
267
|
+
and every `side`/`side_filter` attribute now hold the string `id` `[side]` declares, so a named
|
|
268
|
+
side (`id=rebels`) works throughout. `MwlSideRef` is just that id. The `[side] id` and the other
|
|
269
|
+
side-identifying schema attributes accept a string (numbers still validate), and the map
|
|
270
|
+
`<side> <code>` keep marker accepts a name.
|
|
271
|
+
|
|
272
|
+
### Fixed
|
|
273
|
+
|
|
274
|
+
- `ui.Window` (item 290): `closed` is now a getter to guard a caller's reference after `close()`,
|
|
275
|
+
`close()` is idempotent, input and layout on a closed window are no-ops rather than throws on
|
|
276
|
+
freed Pixi internals, and `WindowStack.push` refuses a closed window with a named error.
|
|
277
|
+
|
|
278
|
+
## [0.7.6] - 2026-09-11
|
|
279
|
+
|
|
280
|
+
### Added
|
|
281
|
+
|
|
282
|
+
- `Blob.spread` returns the cells it just emptied, in reading order, so a game can turn the ground
|
|
283
|
+
under a fire to embers at the moment the flames leave it instead of diffing `cellsAbove` between
|
|
284
|
+
two steps to notice. Cells emptied before the step (a doused one) are not reported.
|
|
285
|
+
|
|
286
|
+
### Changed
|
|
287
|
+
|
|
288
|
+
- `pixi.js` moved from `dependencies` to an optional peer dependency, the treatment
|
|
289
|
+
`@babylonjs/core` already had (item 175's 1.0 decision). The install line now names it:
|
|
290
|
+
`npm install @datamoc/mw_games pixi.js vite`. A game that only uses the renderer-free modules
|
|
291
|
+
never downloads it. Verified from an empty directory on all three documented paths: the
|
|
292
|
+
registry install, the `npm pack` tarball installed by path, and the no-install
|
|
293
|
+
`mw_games.global.js` script tag.
|
|
294
|
+
|
|
8
295
|
## [0.7.4] - 2026-09-11
|
|
9
296
|
|
|
10
297
|
### Added
|
|
@@ -55,8 +342,6 @@ the public API may still change between minor versions.
|
|
|
55
342
|
- The standalone bundle baseline, API report, documentation, and roadmap were updated.
|
|
56
343
|
- Release checks now isolate npm audit and package-smoke caches from user-level npm settings.
|
|
57
344
|
|
|
58
|
-
## [Unreleased]
|
|
59
|
-
|
|
60
345
|
## [0.7.1] - 2026-09-10
|
|
61
346
|
|
|
62
347
|
### Added
|
package/README.md
CHANGED
|
@@ -28,6 +28,14 @@ and the [mwg Pixel Dungeon study](https://datamoc.github.io/mwg-pixel-dungeon/).
|
|
|
28
28
|
> Each future major version is meant to carry a name, one per letter of the alphabet: 1.0 takes
|
|
29
29
|
> a name beginning with A, 2.0 one beginning with B, and so on. None of the names are chosen
|
|
30
30
|
> yet, and nothing depends on them until they are.
|
|
31
|
+
>
|
|
32
|
+
> **API stability:** everything documented in [REFERENCE.md](REFERENCE.md) and
|
|
33
|
+
> [API_REPORT.md](API_REPORT.md) is meant to be stable from 1.0 on. Anything that is not carries
|
|
34
|
+
> an `@experimental` tag in its own doc comment, and is called out as experimental in the release
|
|
35
|
+
> notes that introduce it, so it can still change or disappear without a major version. A rename
|
|
36
|
+
> or a removal is deprecated first: the old name keeps working, its doc comment carries
|
|
37
|
+
> `@deprecated` naming the replacement, and the release notes list it. 1.0 is the last release
|
|
38
|
+
> where either can move silently.
|
|
31
39
|
|
|
32
40
|
## What it is for
|
|
33
41
|
|
|
@@ -86,19 +94,24 @@ for free: a finished game is still a folder you double-click.
|
|
|
86
94
|
**With a toolchain**, if you already use a bundler:
|
|
87
95
|
|
|
88
96
|
```
|
|
89
|
-
npm install @datamoc/mw_games
|
|
97
|
+
npm install @datamoc/mw_games pixi.js
|
|
90
98
|
```
|
|
91
99
|
```ts
|
|
92
100
|
import { Game, Scene } from '@datamoc/mw_games/core';
|
|
93
101
|
import { Sprite } from '@datamoc/mw_games/two-d/render';
|
|
94
102
|
```
|
|
95
103
|
|
|
104
|
+
`pixi.js` is the 2D renderer, and it is an optional peer dependency rather than an installed
|
|
105
|
+
one: a game that only uses `mwg/core`, `mwg/roguelike`, `mwg/actors`, `mwg/i18n` or the other
|
|
106
|
+
logic modules never downloads it. Name it on the install line when a game draws, exactly as
|
|
107
|
+
`@babylonjs/core` is named for 3D.
|
|
108
|
+
|
|
96
109
|
No npm registry access (an offline machine, a pinned artifact for reproducible builds)?
|
|
97
110
|
`npm pack @datamoc/mw_games` downloads the exact published tarball; install it by path
|
|
98
111
|
instead of by name:
|
|
99
112
|
|
|
100
113
|
```
|
|
101
|
-
npm install ./datamoc-mw_games
|
|
114
|
+
npm install ./datamoc-mw_games-<version>.tgz pixi.js
|
|
102
115
|
```
|
|
103
116
|
|
|
104
117
|
**Without either.** Drop one file next to your page and open it: this path exists because
|
|
@@ -24,6 +24,12 @@ export interface InventoryItem {
|
|
|
24
24
|
quantity: number;
|
|
25
25
|
stackable?: boolean;
|
|
26
26
|
weight?: number;
|
|
27
|
+
/**
|
|
28
|
+
* A kind-level grouping - `'herb'`, `'runestone'`, `'potion'` - so a recipe can ask for "any
|
|
29
|
+
* herb" rather than one exact id. Like `stackable` and `weight` it describes the item's kind,
|
|
30
|
+
* so it comes from the game's item definitions on load and is not part of a save.
|
|
31
|
+
*/
|
|
32
|
+
category?: string;
|
|
27
33
|
/**
|
|
28
34
|
* Distinguishes otherwise-identical instances of the same item id - two `"sword"`s, one
|
|
29
35
|
* enchanted and one not, or two enchanted differently - so they never silently merge into
|
|
@@ -95,7 +101,7 @@ export declare class Inventory {
|
|
|
95
101
|
static fromJSON(defs: ReadonlyMap<string, ItemDefinition>, data: SavedInventory): Inventory;
|
|
96
102
|
}
|
|
97
103
|
/** the kind-level fields a game's own item table supplies, rather than the save file */
|
|
98
|
-
export type ItemDefinition = Pick<InventoryItem, 'stackable' | 'weight'>;
|
|
104
|
+
export type ItemDefinition = Pick<InventoryItem, 'stackable' | 'weight' | 'category'>;
|
|
99
105
|
/** one slot as saved: instance state only, no kind-level fields */
|
|
100
106
|
export interface SavedInventoryItem {
|
|
101
107
|
id: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Inventory.d.ts","sourceRoot":"","sources":["../../src/actors/Inventory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,aAAa;IAC7B,mGAAmG;IACnG,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,SAAS;IACrB,OAAO,CAAC,KAAK,CAAuB;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,YAAY,OAAO,GAAE,gBAAqB,EAEzC;IAED,IAAI,KAAK,IAAI,SAAS,aAAa,EAAE,CAEpC;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,OAAO,CAAC,QAAQ;IAIhB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAI/D;IAED;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAmBhC;IAED,0FAA0F;IAC1F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAU/D;IAED;;;OAGG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,SAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAc7E;IAED;;;;;;;;;;;OAWG;IACH,MAAM,IAAI,cAAc,CAiBvB;IAED;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,SAAS,CAsB1F;CACD;AAED,wFAAwF;AACxF,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,QAAQ,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"Inventory.d.ts","sourceRoot":"","sources":["../../src/actors/Inventory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,MAAM,WAAW,aAAa;IAC7B,mGAAmG;IACnG,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,mFAAmF;IACnF,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,sFAAsF;IACtF,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,yEAAyE;IACzE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,oFAAoF;IACpF,QAAQ,CAAC,EAAE,SAAS,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAChC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,SAAS;IACrB,OAAO,CAAC,KAAK,CAAuB;IACpC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAE3B,YAAY,OAAO,GAAE,gBAAqB,EAEzC;IAED,IAAI,KAAK,IAAI,SAAS,aAAa,EAAE,CAEpC;IAED,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,OAAO,CAAC,QAAQ;IAIhB,8FAA8F;IAC9F,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAI/D;IAED;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAmBhC;IAED,0FAA0F;IAC1F,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAU/D;IAED;;;OAGG;IACH,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,SAAI,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,SAAS,CAc7E;IAED;;;;;;;;;;;OAWG;IACH,MAAM,IAAI,cAAc,CAiBvB;IAED;;;;;;OAMG;IACH,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC,MAAM,EAAE,cAAc,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,SAAS,CAsB1F;CACD;AAED,wFAAwF;AACxF,MAAM,MAAM,cAAc,GAAG,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,QAAQ,GAAG,UAAU,CAAC,CAAC;AAEtF,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,cAAc,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,kBAAkB,EAAE,CAAC;CAC5B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Inventory.js","sourceRoot":"","sources":["../../src/actors/Inventory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;
|
|
1
|
+
{"version":3,"file":"Inventory.js","sourceRoot":"","sources":["../../src/actors/Inventory.ts"],"names":[],"mappings":"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"}
|
package/dist/actors/craft.d.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
import type { Inventory, InventoryItem } from './Inventory.ts';
|
|
2
|
+
/**
|
|
3
|
+
* What one recipe ingredient consumes. `id` is the simple case: one exact item, or any of
|
|
4
|
+
* several. `category` asks for any item of a kind ("any herb plus any runestone"), matching the
|
|
5
|
+
* `category` an item's definition carries. `matches` is the escape hatch when neither fits.
|
|
6
|
+
* Give exactly one of the three.
|
|
7
|
+
*/
|
|
8
|
+
export interface Ingredient {
|
|
9
|
+
/** one exact item id, or any of several */
|
|
10
|
+
readonly id?: string | readonly string[];
|
|
11
|
+
/** any item whose `category` is this */
|
|
12
|
+
readonly category?: string;
|
|
13
|
+
/** full control: an item this returns true for */
|
|
14
|
+
readonly matches?: (item: InventoryItem) => boolean;
|
|
15
|
+
/** how many items matching this the recipe needs */
|
|
16
|
+
readonly quantity: number;
|
|
17
|
+
}
|
|
2
18
|
export interface Recipe {
|
|
3
|
-
/** what gets consumed, by id
|
|
4
|
-
ingredients:
|
|
5
|
-
id: string;
|
|
6
|
-
quantity: number;
|
|
7
|
-
}>;
|
|
19
|
+
/** what gets consumed, by exact id, by category, or by predicate */
|
|
20
|
+
ingredients: Ingredient[];
|
|
8
21
|
/** what one crafting resolves to - a full item, since it needs its own weight/stackable */
|
|
9
22
|
result: InventoryItem;
|
|
10
23
|
}
|
|
@@ -15,10 +28,15 @@ export interface Recipe {
|
|
|
15
28
|
* adds/removes what a game already hands it; nothing there turns one stack of items into a
|
|
16
29
|
* different one, which is the entire gap this closes.
|
|
17
30
|
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
31
|
+
* An ingredient may name an exact `id` (or any of several), a `category` ("any herb", matched
|
|
32
|
+
* against the item definition's own `category`), or a `matches` predicate, and a quantity is
|
|
33
|
+
* taken from as many matching stacks as it spans.
|
|
34
|
+
*
|
|
35
|
+
* All or nothing: if any ingredient is missing or too few, nothing is touched. The allocation
|
|
36
|
+
* runs against a working copy of the stacks first, so two ingredients that could both take the
|
|
37
|
+
* same stack cannot each count it. If every ingredient is present but the result would not fit
|
|
38
|
+
* (`Inventory`'s own capacity, unrelated to crafting itself), the ingredients are put back
|
|
39
|
+
* exactly as they were rather than being spent for nothing.
|
|
22
40
|
*
|
|
23
41
|
* @returns whether the recipe resolved
|
|
24
42
|
*
|
|
@@ -27,15 +45,18 @@ export interface Recipe {
|
|
|
27
45
|
* import { craft, Inventory, type Recipe } from '@datamoc/mw_games/actors';
|
|
28
46
|
*
|
|
29
47
|
* const bag = new Inventory();
|
|
30
|
-
* bag.add({ id: '
|
|
31
|
-
* bag.add({ id: '
|
|
48
|
+
* bag.add({ id: 'sage', quantity: 2, stackable: true, category: 'herb' });
|
|
49
|
+
* bag.add({ id: 'ember', quantity: 1, stackable: true, category: 'runestone' });
|
|
32
50
|
*
|
|
33
51
|
* const recipe: Recipe = {
|
|
34
|
-
* ingredients: [
|
|
35
|
-
*
|
|
52
|
+
* ingredients: [
|
|
53
|
+
* { category: 'herb', quantity: 2 },
|
|
54
|
+
* { category: 'runestone', quantity: 1 },
|
|
55
|
+
* ],
|
|
56
|
+
* result: { id: 'elixir', quantity: 1 },
|
|
36
57
|
* };
|
|
37
58
|
*
|
|
38
|
-
* const made = craft(bag, recipe); // true -
|
|
59
|
+
* const made = craft(bag, recipe); // true - any herb plus any runestone becomes an elixir
|
|
39
60
|
* ```
|
|
40
61
|
*/
|
|
41
62
|
export declare function craft(inventory: Inventory, recipe: Recipe): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"craft.d.ts","sourceRoot":"","sources":["../../src/actors/craft.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/D,MAAM,WAAW,MAAM;
|
|
1
|
+
{"version":3,"file":"craft.d.ts","sourceRoot":"","sources":["../../src/actors/craft.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAE/D;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IAC1B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IACzC,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC;IAEpD,oDAAoD;IACpD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,MAAM;IACtB,oEAAoE;IACpE,WAAW,EAAE,UAAU,EAAE,CAAC;IAE1B,2FAA2F;IAC3F,MAAM,EAAE,aAAa,CAAC;CACtB;AAaD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,KAAK,CAAC,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CA+BnE"}
|
package/dist/actors/craft.js
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
function matchesIngredient(item, ingredient) {
|
|
2
|
+
if (ingredient.matches)
|
|
3
|
+
return ingredient.matches(item);
|
|
4
|
+
if (ingredient.category !== undefined)
|
|
5
|
+
return item.category === ingredient.category;
|
|
6
|
+
if (typeof ingredient.id === 'string')
|
|
7
|
+
return item.id === ingredient.id;
|
|
8
|
+
return (ingredient.id ?? []).includes(item.id);
|
|
9
|
+
}
|
|
10
|
+
function ingredientHasMatcher(ingredient) {
|
|
11
|
+
return ingredient.matches !== undefined || ingredient.category !== undefined || ingredient.id !== undefined;
|
|
12
|
+
}
|
|
1
13
|
/**
|
|
2
14
|
* Resolves a recipe against an inventory: every ingredient checked, consumed, and the
|
|
3
15
|
* result added, all in one call - the same small-focused-function shape `skillCheck`
|
|
@@ -5,10 +17,15 @@
|
|
|
5
17
|
* adds/removes what a game already hands it; nothing there turns one stack of items into a
|
|
6
18
|
* different one, which is the entire gap this closes.
|
|
7
19
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
20
|
+
* An ingredient may name an exact `id` (or any of several), a `category` ("any herb", matched
|
|
21
|
+
* against the item definition's own `category`), or a `matches` predicate, and a quantity is
|
|
22
|
+
* taken from as many matching stacks as it spans.
|
|
23
|
+
*
|
|
24
|
+
* All or nothing: if any ingredient is missing or too few, nothing is touched. The allocation
|
|
25
|
+
* runs against a working copy of the stacks first, so two ingredients that could both take the
|
|
26
|
+
* same stack cannot each count it. If every ingredient is present but the result would not fit
|
|
27
|
+
* (`Inventory`'s own capacity, unrelated to crafting itself), the ingredients are put back
|
|
28
|
+
* exactly as they were rather than being spent for nothing.
|
|
12
29
|
*
|
|
13
30
|
* @returns whether the recipe resolved
|
|
14
31
|
*
|
|
@@ -17,38 +34,53 @@
|
|
|
17
34
|
* import { craft, Inventory, type Recipe } from '@datamoc/mw_games/actors';
|
|
18
35
|
*
|
|
19
36
|
* const bag = new Inventory();
|
|
20
|
-
* bag.add({ id: '
|
|
21
|
-
* bag.add({ id: '
|
|
37
|
+
* bag.add({ id: 'sage', quantity: 2, stackable: true, category: 'herb' });
|
|
38
|
+
* bag.add({ id: 'ember', quantity: 1, stackable: true, category: 'runestone' });
|
|
22
39
|
*
|
|
23
40
|
* const recipe: Recipe = {
|
|
24
|
-
* ingredients: [
|
|
25
|
-
*
|
|
41
|
+
* ingredients: [
|
|
42
|
+
* { category: 'herb', quantity: 2 },
|
|
43
|
+
* { category: 'runestone', quantity: 1 },
|
|
44
|
+
* ],
|
|
45
|
+
* result: { id: 'elixir', quantity: 1 },
|
|
26
46
|
* };
|
|
27
47
|
*
|
|
28
|
-
* const made = craft(bag, recipe); // true -
|
|
48
|
+
* const made = craft(bag, recipe); // true - any herb plus any runestone becomes an elixir
|
|
29
49
|
* ```
|
|
30
50
|
*/
|
|
31
51
|
export function craft(inventory, recipe) {
|
|
32
|
-
|
|
52
|
+
//a working copy of every stack's quantity, so an earlier ingredient consumes this allocation
|
|
53
|
+
//even when a later one matches the same category or predicate
|
|
54
|
+
const available = new Map();
|
|
55
|
+
for (const item of inventory.items)
|
|
56
|
+
available.set(item, item.quantity);
|
|
57
|
+
const taken = new Map();
|
|
33
58
|
for (const ingredient of recipe.ingredients) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
59
|
+
if (!ingredientHasMatcher(ingredient))
|
|
60
|
+
throw new Error('recipe ingredient must name an id, a category, or a matches predicate');
|
|
61
|
+
if (!Number.isFinite(ingredient.quantity) || ingredient.quantity <= 0)
|
|
62
|
+
throw new Error('recipe ingredient quantity must be a positive number');
|
|
63
|
+
let needed = ingredient.quantity;
|
|
64
|
+
for (const item of inventory.items) {
|
|
65
|
+
if (needed <= 0)
|
|
66
|
+
break;
|
|
67
|
+
const held = available.get(item) ?? 0;
|
|
68
|
+
if (held <= 0 || !matchesIngredient(item, ingredient))
|
|
69
|
+
continue;
|
|
70
|
+
const take = Math.min(held, needed);
|
|
71
|
+
available.set(item, held - take);
|
|
72
|
+
needed -= take;
|
|
73
|
+
taken.set(item, (taken.get(item) ?? 0) + take);
|
|
74
|
+
}
|
|
75
|
+
if (needed > 0)
|
|
39
76
|
return false;
|
|
40
77
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
...inventory.find(ingredient.id),
|
|
44
|
-
quantity: ingredient.quantity,
|
|
45
|
-
}));
|
|
46
|
-
for (const ingredient of recipe.ingredients)
|
|
47
|
-
inventory.remove(ingredient.id, ingredient.quantity);
|
|
78
|
+
for (const [item, quantity] of taken)
|
|
79
|
+
inventory.remove(item.id, quantity, item.instanceId);
|
|
48
80
|
if (inventory.add(recipe.result))
|
|
49
81
|
return true;
|
|
50
|
-
for (const item of
|
|
51
|
-
inventory.add(item);
|
|
82
|
+
for (const [item, quantity] of taken)
|
|
83
|
+
inventory.add({ ...item, quantity });
|
|
52
84
|
return false;
|
|
53
85
|
}
|
|
54
86
|
//# sourceMappingURL=craft.js.map
|
package/dist/actors/craft.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"craft.js","sourceRoot":"","sources":["../../src/actors/craft.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"craft.js","sourceRoot":"","sources":["../../src/actors/craft.ts"],"names":[],"mappings":"AA4BA,SAAS,iBAAiB,CAAC,IAAmB,EAAE,UAAsB;IACrE,IAAI,UAAU,CAAC,OAAO;QAAE,OAAO,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC,QAAQ,KAAK,UAAU,CAAC,QAAQ,CAAC;IACpF,IAAI,OAAO,UAAU,CAAC,EAAE,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC,EAAE,KAAK,UAAU,CAAC,EAAE,CAAC;IACxE,OAAO,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,oBAAoB,CAAC,UAAsB;IACnD,OAAO,UAAU,CAAC,OAAO,KAAK,SAAS,IAAI,UAAU,CAAC,QAAQ,KAAK,SAAS,IAAI,UAAU,CAAC,EAAE,KAAK,SAAS,CAAC;AAC7G,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,UAAU,KAAK,CAAC,SAAoB,EAAE,MAAc;IACzD,6FAA6F;IAC7F,8DAA8D;IAC9D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IACnD,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK;QAAE,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;IAEvE,MAAM,KAAK,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC/C,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QAC7C,IAAI,CAAC,oBAAoB,CAAC,UAAU,CAAC;YACpC,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;QAC1F,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,QAAQ,IAAI,CAAC;YACpE,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAC;QAEzE,IAAI,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC;QACjC,KAAK,MAAM,IAAI,IAAI,SAAS,CAAC,KAAK,EAAE,CAAC;YACpC,IAAI,MAAM,IAAI,CAAC;gBAAE,MAAM;YACvB,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,IAAI,EAAE,UAAU,CAAC;gBAAE,SAAS;YAChE,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACpC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC,CAAC;YACjC,MAAM,IAAI,IAAI,CAAC;YACf,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;QACD,IAAI,MAAM,GAAG,CAAC;YAAE,OAAO,KAAK,CAAC;IAC9B,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,KAAK;QAAE,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;IAC3F,IAAI,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC;QAAE,OAAO,IAAI,CAAC;IAE9C,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,KAAK;QAAE,SAAS,CAAC,GAAG,CAAC,EAAE,GAAG,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3E,OAAO,KAAK,CAAC;AACd,CAAC"}
|
package/dist/actors/index.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export type { EquippableItem, EquipmentOptions, SavedEquipment } from './Equipme
|
|
|
10
10
|
export { Inventory } from './Inventory.ts';
|
|
11
11
|
export type { InventoryItem, InventoryOptions, ItemDefinition, SavedInventory, SavedInventoryItem, } from './Inventory.ts';
|
|
12
12
|
export { craft } from './craft.ts';
|
|
13
|
-
export type { Recipe } from './craft.ts';
|
|
13
|
+
export type { Ingredient, Recipe } from './craft.ts';
|
|
14
14
|
export { applyStatusEffect } from './StatusEffect.ts';
|
|
15
15
|
export type { EffectClock, StatusEffectOptions, StatusEffectHandle } from './StatusEffect.ts';
|
|
16
16
|
export { identify, enchant, damageItem, repairItem } from './ItemState.ts';
|