@datamoc/mw_games 0.7.3 → 0.7.6
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 +37 -2
- package/README.md +16 -3
- package/dist/board/FogOfWar.d.ts +6 -0
- package/dist/board/FogOfWar.d.ts.map +1 -1
- package/dist/board/FogOfWar.js +15 -0
- package/dist/board/FogOfWar.js.map +1 -1
- package/dist/core/ActionJournal.d.ts +36 -0
- package/dist/core/ActionJournal.d.ts.map +1 -0
- package/dist/core/ActionJournal.js +60 -0
- package/dist/core/ActionJournal.js.map +1 -0
- 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/Pathfinding.d.ts +41 -0
- package/dist/core/Pathfinding.d.ts.map +1 -0
- package/dist/core/Pathfinding.js +86 -0
- package/dist/core/Pathfinding.js.map +1 -0
- package/dist/core/State.d.ts +59 -0
- package/dist/core/State.d.ts.map +1 -0
- package/dist/core/State.js +100 -0
- package/dist/core/State.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -0
- package/dist/core/index.js.map +1 -1
- package/dist/mw_games.global.js +97 -97
- package/dist/mwl/hooks.d.ts +3 -2
- package/dist/mwl/hooks.d.ts.map +1 -1
- package/dist/mwl/hooks.js.map +1 -1
- package/dist/mwl/index.d.ts +3 -3
- package/dist/mwl/index.d.ts.map +1 -1
- package/dist/mwl/index.js +1 -1
- package/dist/mwl/index.js.map +1 -1
- package/dist/mwl/report.d.ts +17 -1
- package/dist/mwl/report.d.ts.map +1 -1
- package/dist/mwl/report.js +33 -0
- package/dist/mwl/report.js.map +1 -1
- package/dist/mwl/runtime.d.ts +46 -1
- package/dist/mwl/runtime.d.ts.map +1 -1
- package/dist/mwl/runtime.js +237 -19
- package/dist/mwl/runtime.js.map +1 -1
- package/dist/mwl/schema.d.ts.map +1 -1
- package/dist/mwl/schema.js +136 -1
- package/dist/mwl/schema.js.map +1 -1
- package/dist/roguelike/FieldOfView.d.ts +3 -2
- package/dist/roguelike/FieldOfView.d.ts.map +1 -1
- package/dist/roguelike/FieldOfView.js +3 -2
- package/dist/roguelike/FieldOfView.js.map +1 -1
- package/dist/roguelike/Level.d.ts +9 -1
- package/dist/roguelike/Level.d.ts.map +1 -1
- package/dist/roguelike/Level.js +6 -2
- package/dist/roguelike/Level.js.map +1 -1
- package/dist/roguelike/Scheduler.d.ts +5 -1
- package/dist/roguelike/Scheduler.d.ts.map +1 -1
- package/dist/roguelike/Scheduler.js +10 -3
- package/dist/roguelike/Scheduler.js.map +1 -1
- package/dist/roguelike/Targeting.d.ts +30 -0
- package/dist/roguelike/Targeting.d.ts.map +1 -1
- package/dist/roguelike/Targeting.js +37 -0
- package/dist/roguelike/Targeting.js.map +1 -1
- package/dist/roguelike/index.d.ts +2 -2
- package/dist/roguelike/index.d.ts.map +1 -1
- package/dist/roguelike/index.js +1 -1
- package/dist/roguelike/index.js.map +1 -1
- package/dist/simulation/Campaign.d.ts +60 -0
- package/dist/simulation/Campaign.d.ts.map +1 -0
- package/dist/simulation/Campaign.js +85 -0
- package/dist/simulation/Campaign.js.map +1 -0
- package/dist/simulation/Harness.d.ts +26 -0
- package/dist/simulation/Harness.d.ts.map +1 -0
- package/dist/simulation/Harness.js +25 -0
- package/dist/simulation/Harness.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/render/ImageModifiers.d.ts +58 -0
- package/dist/two-d/render/ImageModifiers.d.ts.map +1 -0
- package/dist/two-d/render/ImageModifiers.js +145 -0
- package/dist/two-d/render/ImageModifiers.js.map +1 -0
- package/dist/two-d/render/Minimap.d.ts +8 -0
- package/dist/two-d/render/Minimap.d.ts.map +1 -1
- package/dist/two-d/render/Minimap.js +15 -10
- package/dist/two-d/render/Minimap.js.map +1 -1
- package/dist/two-d/render/Particles.d.ts +16 -0
- package/dist/two-d/render/Particles.d.ts.map +1 -1
- package/dist/two-d/render/Particles.js +15 -2
- package/dist/two-d/render/Particles.js.map +1 -1
- package/dist/two-d/render/index.d.ts +3 -1
- 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 +21 -2
- package/dist/two-d/ui/Bar.d.ts.map +1 -1
- package/dist/two-d/ui/Bar.js +27 -5
- package/dist/two-d/ui/Bar.js.map +1 -1
- package/dist/two-d/ui/FloatingText.d.ts +42 -1
- package/dist/two-d/ui/FloatingText.d.ts.map +1 -1
- package/dist/two-d/ui/FloatingText.js +52 -4
- package/dist/two-d/ui/FloatingText.js.map +1 -1
- package/dist/two-d/ui/FloatingTextStack.d.ts +77 -0
- package/dist/two-d/ui/FloatingTextStack.d.ts.map +1 -0
- package/dist/two-d/ui/FloatingTextStack.js +99 -0
- package/dist/two-d/ui/FloatingTextStack.js.map +1 -0
- package/dist/two-d/ui/index.d.ts +3 -1
- package/dist/two-d/ui/index.d.ts.map +1 -1
- package/dist/two-d/ui/index.js +2 -1
- 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 +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,43 @@ 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.6] - 2026-09-11
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `Blob.spread` returns the cells it just emptied, in reading order, so a game can turn the ground
|
|
13
|
+
under a fire to embers at the moment the flames leave it instead of diffing `cellsAbove` between
|
|
14
|
+
two steps to notice. Cells emptied before the step (a doused one) are not reported.
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- `pixi.js` moved from `dependencies` to an optional peer dependency, the treatment
|
|
19
|
+
`@babylonjs/core` already had (item 175's 1.0 decision). The install line now names it:
|
|
20
|
+
`npm install @datamoc/mw_games pixi.js vite`. A game that only uses the renderer-free modules
|
|
21
|
+
never downloads it. Verified from an empty directory on all three documented paths: the
|
|
22
|
+
registry install, the `npm pack` tarball installed by path, and the no-install
|
|
23
|
+
`mw_games.global.js` script tag.
|
|
24
|
+
|
|
25
|
+
## [0.7.4] - 2026-09-11
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
|
|
29
|
+
- `FloatingTextStack` owns the pop-ups a game spawns over world points: one `push` per number, one
|
|
30
|
+
`update(dt)` to drive them all, and pop-ups sharing a key and an origin stack apart instead of
|
|
31
|
+
overprinting. `FloatingText` gained a `hold` option for the classic hold-then-fade curve, and its
|
|
32
|
+
rise now moves an inner layer rather than the container itself - it used to overwrite the
|
|
33
|
+
container's own `y`, so a pop-up positioned at a world point jumped to `y = 0` on its first
|
|
34
|
+
update. The fade curve and the stacking rule are exported as pure functions, so both are tested
|
|
35
|
+
without a DOM.
|
|
36
|
+
- `ParticleEmitter` accepts a `frames` texture sequence, walked per particle across its own life -
|
|
37
|
+
the four frames of a flame, the puff of smoke - in place of the single `texture`. The current
|
|
38
|
+
frame is exposed as `Particle.frame` for the same reason the rest of the particle state is.
|
|
39
|
+
- `Bar` recolours its fill at runtime with `setColor(color)` and takes a `background` colour for its
|
|
40
|
+
track; both are readable back through `color`/`background`, and a runtime colour counts as
|
|
41
|
+
explicit so a later theme change cannot throw it away. These were the two blockers for a consumer
|
|
42
|
+
whose bars change colour with state (a health bar going red as it empties) and whose track is
|
|
43
|
+
black rather than the theme's panel fill.
|
|
44
|
+
|
|
8
45
|
## [0.7.3] - 2026-09-11
|
|
9
46
|
|
|
10
47
|
### Added
|
|
@@ -35,8 +72,6 @@ the public API may still change between minor versions.
|
|
|
35
72
|
- The standalone bundle baseline, API report, documentation, and roadmap were updated.
|
|
36
73
|
- Release checks now isolate npm audit and package-smoke caches from user-level npm settings.
|
|
37
74
|
|
|
38
|
-
## [Unreleased]
|
|
39
|
-
|
|
40
75
|
## [0.7.1] - 2026-09-10
|
|
41
76
|
|
|
42
77
|
### Added
|
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ playable in the browser with no download, plus the generated API reference.
|
|
|
15
15
|
planning or porting a demanding game with mwg, with links to the [mwg reference](REFERENCE.md)
|
|
16
16
|
and the [mwg Pixel Dungeon study](https://datamoc.github.io/mwg-pixel-dungeon/).
|
|
17
17
|
|
|
18
|
-
> **Status: pre-alpha (v0.7.
|
|
18
|
+
> **Status: pre-alpha (v0.7.4).** Every module in the shared floor below, plus optional
|
|
19
19
|
> 3D, mobile (Capacitor) and desktop (WebView2) packaging, is built and tested - see
|
|
20
20
|
> [ROADMAP.md](ROADMAP.md) for the full, numbered history.
|
|
21
21
|
>
|
|
@@ -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
|
package/dist/board/FogOfWar.d.ts
CHANGED
|
@@ -33,6 +33,12 @@ export declare class FactionFog {
|
|
|
33
33
|
private explored;
|
|
34
34
|
constructor(width: number, height: number);
|
|
35
35
|
sync(faction: string, sources: readonly VisionCell[], cells: (source: VisionCell) => Iterable<VisionCell>): void;
|
|
36
|
+
/**
|
|
37
|
+
* Mark cells explored without changing what is currently visible: the
|
|
38
|
+
* shroud-clearing half of `sync`, for effects (a revealed map, a scouted
|
|
39
|
+
* region) that lift the shroud where no unit stands watch.
|
|
40
|
+
*/
|
|
41
|
+
reveal(faction: string, cells: Iterable<VisionCell>): void;
|
|
36
42
|
isVisible(faction: string, x: number, y: number): boolean;
|
|
37
43
|
isExplored(faction: string, x: number, y: number): boolean;
|
|
38
44
|
visibleCells(faction: string): readonly number[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,UAAU;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAkC;IAElD,YAAY,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAIxC;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAW/G;IAED,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAExD;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzD;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAE/C;IACD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAEhD;IAED,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,KAAK;CAGb"}
|
|
1
|
+
{"version":3,"file":"FogOfWar.d.ts","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IAC1B,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACV;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,qBAAa,UAAU;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,QAAQ,CAAkC;IAElD,YAAY,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAIxC;IAED,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,UAAU,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAW/G;IAED;;;;OAIG;IACH,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC,UAAU,CAAC,GAAG,IAAI,CAOzD;IAED,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAExD;IAED,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzD;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAE/C;IACD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAEhD;IAED,OAAO,CAAC,MAAM;IAGd,OAAO,CAAC,KAAK;CAGb"}
|
package/dist/board/FogOfWar.js
CHANGED
|
@@ -46,6 +46,21 @@ export class FactionFog {
|
|
|
46
46
|
memory.add(cell);
|
|
47
47
|
this.explored.set(faction, memory);
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Mark cells explored without changing what is currently visible: the
|
|
51
|
+
* shroud-clearing half of `sync`, for effects (a revealed map, a scouted
|
|
52
|
+
* region) that lift the shroud where no unit stands watch.
|
|
53
|
+
*/
|
|
54
|
+
reveal(faction, cells) {
|
|
55
|
+
if (!faction)
|
|
56
|
+
throw new Error('fog needs a faction id');
|
|
57
|
+
const memory = this.explored.get(faction) ?? new Set();
|
|
58
|
+
for (const cell of cells) {
|
|
59
|
+
if (this.inside(cell.x, cell.y))
|
|
60
|
+
memory.add(this.index(cell.x, cell.y));
|
|
61
|
+
}
|
|
62
|
+
this.explored.set(faction, memory);
|
|
63
|
+
}
|
|
49
64
|
isVisible(faction, x, y) {
|
|
50
65
|
return this.visible.get(faction)?.has(this.index(x, y)) ?? false;
|
|
51
66
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FogOfWar.js","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,OAAO,UAAU;IAMtB,YAAY,KAAa,EAAE,MAAc;QAHjC,YAAO,GAAG,IAAI,GAAG,EAAuB,CAAC;QACzC,aAAQ,GAAG,IAAI,GAAG,EAAuB,CAAC;QAGjD,IAAI,KAAK,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;QAC9E,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACtB,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,OAA8B,EAAE,KAAmD;QACxG,IAAI,CAAC,OAAO;YAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC,CAAC;QACxD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;QAC/B,KAAK,MAAM,MAAM,IAAI,OAAO;YAC3B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;gBAClC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;oBAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;YACvE,CAAC;QACF,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QAC/D,KAAK,MAAM,IAAI,IAAI,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,SAAS,CAAC,OAAe,EAAE,CAAS,EAAE,CAAS;QAC9C,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IAClE,CAAC;IAED,UAAU,CAAC,OAAe,EAAE,CAAS,EAAE,CAAS;QAC/C,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC;IACnE,CAAC;IAED,YAAY,CAAC,OAAe;QAC3B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,aAAa,CAAC,OAAe;QAC5B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAChD,CAAC;IAEO,MAAM,CAAC,CAAS,EAAE,CAAS;QAClC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9D,CAAC;IACO,KAAK,CAAC,CAAS,EAAE,CAAS;QACjC,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;CACD"}
|
|
1
|
+
{"version":3,"file":"FogOfWar.js","sourceRoot":"","sources":["../../src/board/FogOfWar.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** One deterministic action and the framework/game events it produced. */
|
|
2
|
+
export interface ActionJournalEntry<Action, Event> {
|
|
3
|
+
readonly sequence: number;
|
|
4
|
+
readonly action: Action;
|
|
5
|
+
readonly events: readonly Event[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* A serializable, append-only action log for replay, undo checkpoints, synchronization and
|
|
9
|
+
* debugging. It does not interpret actions or events: the game decides how to reapply an
|
|
10
|
+
* action, while the journal preserves the exact order and resulting event batch.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { ActionJournal } from '@datamoc/mw_games/core';
|
|
15
|
+
*
|
|
16
|
+
* const journal = new ActionJournal<string, { type: string }>();
|
|
17
|
+
* journal.append('open-door', [{ type: 'door-opened' }]);
|
|
18
|
+
* const checkpoint = journal.mark();
|
|
19
|
+
* // An undo system can restore its state and replay journal.since(checkpoint).
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export declare class ActionJournal<Action, Event> {
|
|
23
|
+
private entries;
|
|
24
|
+
private nextSequence;
|
|
25
|
+
append(action: Action, events?: readonly Event[]): ActionJournalEntry<Action, Event>;
|
|
26
|
+
get size(): number;
|
|
27
|
+
get all(): readonly ActionJournalEntry<Action, Event>[];
|
|
28
|
+
/** Returns a sequence number that can be used as an undo or sync checkpoint. */
|
|
29
|
+
mark(): number;
|
|
30
|
+
since(sequence: number): ActionJournalEntry<Action, Event>[];
|
|
31
|
+
/** Drops entries at and after a checkpoint, as an undo operation would. */
|
|
32
|
+
truncate(sequence: number): void;
|
|
33
|
+
toJSON(): ActionJournalEntry<Action, Event>[];
|
|
34
|
+
static fromJSON<Action, Event>(entries: readonly ActionJournalEntry<Action, Event>[]): ActionJournal<Action, Event>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=ActionJournal.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionJournal.d.ts","sourceRoot":"","sources":["../../src/core/ActionJournal.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,WAAW,kBAAkB,CAAC,MAAM,EAAE,KAAK;IAChD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,SAAS,KAAK,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,aAAa,CAAC,MAAM,EAAE,KAAK;IACvC,OAAO,CAAC,OAAO,CAA2C;IAC1D,OAAO,CAAC,YAAY,CAAK;IAEzB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,GAAE,SAAS,KAAK,EAAO,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,CAIvF;IAED,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,GAAG,IAAI,SAAS,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAEtD;IAED,gFAAgF;IAChF,IAAI,IAAI,MAAM,CAEb;IAED,KAAK,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAE3D;IAED,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAG/B;IAED,MAAM,IAAI,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,CAE5C;IAED,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAC5B,OAAO,EAAE,SAAS,kBAAkB,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,GACnD,aAAa,CAAC,MAAM,EAAE,KAAK,CAAC,CAa9B;CACD"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A serializable, append-only action log for replay, undo checkpoints, synchronization and
|
|
3
|
+
* debugging. It does not interpret actions or events: the game decides how to reapply an
|
|
4
|
+
* action, while the journal preserves the exact order and resulting event batch.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* import { ActionJournal } from '@datamoc/mw_games/core';
|
|
9
|
+
*
|
|
10
|
+
* const journal = new ActionJournal<string, { type: string }>();
|
|
11
|
+
* journal.append('open-door', [{ type: 'door-opened' }]);
|
|
12
|
+
* const checkpoint = journal.mark();
|
|
13
|
+
* // An undo system can restore its state and replay journal.since(checkpoint).
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
export class ActionJournal {
|
|
17
|
+
constructor() {
|
|
18
|
+
this.entries = [];
|
|
19
|
+
this.nextSequence = 0;
|
|
20
|
+
}
|
|
21
|
+
append(action, events = []) {
|
|
22
|
+
const entry = { sequence: this.nextSequence++, action, events: structuredClone([...events]) };
|
|
23
|
+
this.entries.push(entry);
|
|
24
|
+
return structuredClone(entry);
|
|
25
|
+
}
|
|
26
|
+
get size() {
|
|
27
|
+
return this.entries.length;
|
|
28
|
+
}
|
|
29
|
+
get all() {
|
|
30
|
+
return structuredClone(this.entries);
|
|
31
|
+
}
|
|
32
|
+
/** Returns a sequence number that can be used as an undo or sync checkpoint. */
|
|
33
|
+
mark() {
|
|
34
|
+
return this.nextSequence;
|
|
35
|
+
}
|
|
36
|
+
since(sequence) {
|
|
37
|
+
return structuredClone(this.entries.filter((entry) => entry.sequence >= sequence));
|
|
38
|
+
}
|
|
39
|
+
/** Drops entries at and after a checkpoint, as an undo operation would. */
|
|
40
|
+
truncate(sequence) {
|
|
41
|
+
this.entries = this.entries.filter((entry) => entry.sequence < sequence);
|
|
42
|
+
this.nextSequence = sequence;
|
|
43
|
+
}
|
|
44
|
+
toJSON() {
|
|
45
|
+
return this.all;
|
|
46
|
+
}
|
|
47
|
+
static fromJSON(entries) {
|
|
48
|
+
const journal = new ActionJournal();
|
|
49
|
+
let expected = 0;
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (!Number.isSafeInteger(entry.sequence) || entry.sequence !== expected)
|
|
52
|
+
throw new Error('action journal sequences must be contiguous from zero');
|
|
53
|
+
journal.entries.push(structuredClone({ sequence: entry.sequence, action: entry.action, events: [...entry.events] }));
|
|
54
|
+
expected++;
|
|
55
|
+
}
|
|
56
|
+
journal.nextSequence = expected;
|
|
57
|
+
return journal;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=ActionJournal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionJournal.js","sourceRoot":"","sources":["../../src/core/ActionJournal.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;;;;GAcG;AACH,MAAM,OAAO,aAAa;IAA1B;QACS,YAAO,GAAwC,EAAE,CAAC;QAClD,iBAAY,GAAG,CAAC,CAAC;IAmD1B,CAAC;IAjDA,MAAM,CAAC,MAAc,EAAE,MAAM,GAAqB,EAAE;QACnD,MAAM,KAAK,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;QAC9F,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzB,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,IAAI,IAAI;QACP,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;IAC5B,CAAC;IAED,IAAI,GAAG;QACN,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACtC,CAAC;IAED,gFAAgF;IAChF,IAAI;QACH,OAAO,IAAI,CAAC,YAAY,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,QAAgB;QACrB,OAAO,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,IAAI,QAAQ,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,2EAA2E;IAC3E,QAAQ,CAAC,QAAgB;QACxB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAC,CAAC;QACzE,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;IAC9B,CAAC;IAED,MAAM;QACL,OAAO,IAAI,CAAC,GAA0C,CAAC;IACxD,CAAC;IAED,MAAM,CAAC,QAAQ,CACd,OAAqD;QAErD,MAAM,OAAO,GAAG,IAAI,aAAa,EAAiB,CAAC;QACnD,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ;gBACvE,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC1E,OAAO,CAAC,OAAO,CAAC,IAAI,CACnB,eAAe,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAC9F,CAAC;YACF,QAAQ,EAAE,CAAC;QACZ,CAAC;QACD,OAAO,CAAC,YAAY,GAAG,QAAQ,CAAC;QAChC,OAAO,OAAO,CAAC;IAChB,CAAC;CACD"}
|
package/dist/core/Blob.d.ts
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
* const fire = new Blob(20, 20);
|
|
20
20
|
* fire.seed(5, 5, 10); // a fireball lands
|
|
21
21
|
*
|
|
22
|
-
*
|
|
22
|
+
* // one turn of spreading; it hands back whatever it just burned out, so the ground under
|
|
23
|
+
* // the fire can turn to embers at the moment the flames leave it
|
|
24
|
+
* const burntOut = fire.spread((x, y) => x >= 0 && y >= 0 && x < 20 && y < 20, 0.25, 0.9);
|
|
25
|
+
* for (const cell of burntOut) console.log('burned out at', cell.x, cell.y);
|
|
26
|
+
*
|
|
23
27
|
* for (const cell of fire.cellsAbove(0.5)) console.log('burning at', cell.x, cell.y);
|
|
24
28
|
*
|
|
25
29
|
* fire.clear(5, 5); // someone douses that one tile
|
|
@@ -46,8 +50,15 @@ export declare class Blob {
|
|
|
46
50
|
* `decay` of what it ends up with. `decay: 1` conserves volume and only moves it around;
|
|
47
51
|
* anything lower thins the effect out over time. `open(x, y)` decides whether the effect
|
|
48
52
|
* may cross into a cell, so the caller chooses what blocks it.
|
|
53
|
+
*
|
|
54
|
+
* Returns the cells this step emptied, in reading order: those holding a volume before the
|
|
55
|
+
* step and none after it. That is the moment a game turns the grass under a fire to embers,
|
|
56
|
+
* and reporting it here saves the game diffing `cellsAbove` between two steps to notice.
|
|
49
57
|
*/
|
|
50
|
-
spread(open: (x: number, y: number) => boolean, spread?: number, decay?: number):
|
|
58
|
+
spread(open: (x: number, y: number) => boolean, spread?: number, decay?: number): Array<{
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
}>;
|
|
51
62
|
/** every cell holding at least `minimum` - the cells a game applies its effect on */
|
|
52
63
|
cellsAbove(minimum: number): Array<{
|
|
53
64
|
x: number;
|
package/dist/core/Blob.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Blob.d.ts","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"Blob.d.ts","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAUH,qBAAa,IAAI;IAChB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,OAAO,CAAC,MAAM,CAAe;IAE7B,YAAY,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAIxC;IAED,mEAAmE;IACnE,OAAO,CAAC,KAAK;IAKb,4DAA4D;IAC5D,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAGrC;IAED,iFAAiF;IACjF,KAAK,IAAI,MAAM,CAId;IAED,2EAA2E;IAC3E,IAAI,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAG/C;IAED,yFAAyF;IACzF,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAGhC;IAED;;;;;;;;;;OAUG;IACH,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,KAAK,OAAO,EAAE,MAAM,SAAO,EAAE,KAAK,SAAM,GAAG,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAsC3G;IAED,qFAAqF;IACrF,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAS3E;IAED,MAAM,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,CAE5D;IAED,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,IAAI,CAI/E;CACD"}
|
package/dist/core/Blob.js
CHANGED
|
@@ -19,7 +19,11 @@
|
|
|
19
19
|
* const fire = new Blob(20, 20);
|
|
20
20
|
* fire.seed(5, 5, 10); // a fireball lands
|
|
21
21
|
*
|
|
22
|
-
*
|
|
22
|
+
* // one turn of spreading; it hands back whatever it just burned out, so the ground under
|
|
23
|
+
* // the fire can turn to embers at the moment the flames leave it
|
|
24
|
+
* const burntOut = fire.spread((x, y) => x >= 0 && y >= 0 && x < 20 && y < 20, 0.25, 0.9);
|
|
25
|
+
* for (const cell of burntOut) console.log('burned out at', cell.x, cell.y);
|
|
26
|
+
*
|
|
23
27
|
* for (const cell of fire.cellsAbove(0.5)) console.log('burning at', cell.x, cell.y);
|
|
24
28
|
*
|
|
25
29
|
* fire.clear(5, 5); // someone douses that one tile
|
|
@@ -74,6 +78,10 @@ export class Blob {
|
|
|
74
78
|
* `decay` of what it ends up with. `decay: 1` conserves volume and only moves it around;
|
|
75
79
|
* anything lower thins the effect out over time. `open(x, y)` decides whether the effect
|
|
76
80
|
* may cross into a cell, so the caller chooses what blocks it.
|
|
81
|
+
*
|
|
82
|
+
* Returns the cells this step emptied, in reading order: those holding a volume before the
|
|
83
|
+
* step and none after it. That is the moment a game turns the grass under a fire to embers,
|
|
84
|
+
* and reporting it here saves the game diffing `cellsAbove` between two steps to notice.
|
|
77
85
|
*/
|
|
78
86
|
spread(open, spread = 0.25, decay = 0.9) {
|
|
79
87
|
const next = new Float32Array(this.volume.length);
|
|
@@ -102,11 +110,17 @@ export class Blob {
|
|
|
102
110
|
}
|
|
103
111
|
}
|
|
104
112
|
}
|
|
113
|
+
const emptied = [];
|
|
105
114
|
for (let i = 0; i < next.length; i++) {
|
|
106
115
|
const kept = next[i] * decay;
|
|
107
116
|
//snap float dust to zero so a burned-out effect actually reads as gone
|
|
108
|
-
|
|
117
|
+
const volume = kept < 0.001 ? 0 : kept;
|
|
118
|
+
if (volume === 0 && this.volume[i] > 0) {
|
|
119
|
+
emptied.push({ x: i % this.width, y: Math.floor(i / this.width) });
|
|
120
|
+
}
|
|
121
|
+
this.volume[i] = volume;
|
|
109
122
|
}
|
|
123
|
+
return emptied;
|
|
110
124
|
}
|
|
111
125
|
/** every cell holding at least `minimum` - the cells a game applies its effect on */
|
|
112
126
|
cellsAbove(minimum) {
|
package/dist/core/Blob.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Blob.js","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"Blob.js","sourceRoot":"","sources":["../../src/core/Blob.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/** A cell reached by a weighted flood. */
|
|
2
|
+
export interface WeightedCell<T> {
|
|
3
|
+
readonly cell: T;
|
|
4
|
+
readonly cost: number;
|
|
5
|
+
}
|
|
6
|
+
export interface WeightedFloodOptions<T, K> {
|
|
7
|
+
/** Stable identity for a cell; it may be a string, number, or object key. */
|
|
8
|
+
key: (cell: T) => K;
|
|
9
|
+
/** Adjacent cells, in whatever topology the game uses. */
|
|
10
|
+
neighbors: (cell: T) => Iterable<T>;
|
|
11
|
+
/** Cost of entering `to` from `from`; non-finite or negative costs are ignored. */
|
|
12
|
+
cost: (from: T, to: T) => number;
|
|
13
|
+
/** Maximum accumulated cost, inclusive. */
|
|
14
|
+
maxCost: number;
|
|
15
|
+
/** Optional game rule that rejects a candidate before it is reached. */
|
|
16
|
+
canEnter?: (cell: T, from: T, cost: number) => boolean;
|
|
17
|
+
/** Optional game rule that keeps a reached cell from expanding further. */
|
|
18
|
+
stop?: (cell: T, cost: number) => boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Dijkstra-style weighted flood fill, independent of a particular board or game rule.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { weightedFlood } from '@datamoc/mw_games/core';
|
|
26
|
+
*
|
|
27
|
+
* const cells = weightedFlood(0, {
|
|
28
|
+
* key: (cell) => cell,
|
|
29
|
+
* neighbors: (cell) => cell < 3 ? [cell + 1] : [],
|
|
30
|
+
* cost: () => 1,
|
|
31
|
+
* maxCost: 2,
|
|
32
|
+
* });
|
|
33
|
+
* console.log(cells.get(2)?.cost); // 2
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* The returned map includes `start` at cost 0. A game can use `canEnter` for blockers and
|
|
37
|
+
* `stop` for rules such as a zone-of-control boundary; neither callback is interpreted here.
|
|
38
|
+
* Equal-cost revisits are discarded, and the input cells are never mutated.
|
|
39
|
+
*/
|
|
40
|
+
export declare function weightedFlood<T, K>(start: T, options: WeightedFloodOptions<T, K>): Map<K, WeightedCell<T>>;
|
|
41
|
+
//# sourceMappingURL=Pathfinding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pathfinding.d.ts","sourceRoot":"","sources":["../../src/core/Pathfinding.ts"],"names":[],"mappings":"AAAA,0CAA0C;AAC1C,MAAM,WAAW,YAAY,CAAC,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IACjB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB,CAAC,CAAC,EAAE,CAAC;IACzC,6EAA6E;IAC7E,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC;IACpB,0DAA0D;IAC1D,SAAS,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpC,mFAAmF;IACnF,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC;IACjC,2CAA2C;IAC3C,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACvD,2EAA2E;IAC3E,IAAI,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC1C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,oBAAoB,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CA6B1G"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dijkstra-style weighted flood fill, independent of a particular board or game rule.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { weightedFlood } from '@datamoc/mw_games/core';
|
|
7
|
+
*
|
|
8
|
+
* const cells = weightedFlood(0, {
|
|
9
|
+
* key: (cell) => cell,
|
|
10
|
+
* neighbors: (cell) => cell < 3 ? [cell + 1] : [],
|
|
11
|
+
* cost: () => 1,
|
|
12
|
+
* maxCost: 2,
|
|
13
|
+
* });
|
|
14
|
+
* console.log(cells.get(2)?.cost); // 2
|
|
15
|
+
* ```
|
|
16
|
+
*
|
|
17
|
+
* The returned map includes `start` at cost 0. A game can use `canEnter` for blockers and
|
|
18
|
+
* `stop` for rules such as a zone-of-control boundary; neither callback is interpreted here.
|
|
19
|
+
* Equal-cost revisits are discarded, and the input cells are never mutated.
|
|
20
|
+
*/
|
|
21
|
+
export function weightedFlood(start, options) {
|
|
22
|
+
if (!Number.isFinite(options.maxCost) || options.maxCost < 0)
|
|
23
|
+
return new Map();
|
|
24
|
+
const startKey = options.key(start);
|
|
25
|
+
const reached = new Map([[startKey, { cell: start, cost: 0 }]]);
|
|
26
|
+
const queue = [{ cell: start, key: startKey, cost: 0 }];
|
|
27
|
+
while (queue.length > 0) {
|
|
28
|
+
const current = pop(queue);
|
|
29
|
+
const known = reached.get(current.key);
|
|
30
|
+
if (!known || current.cost !== known.cost)
|
|
31
|
+
continue;
|
|
32
|
+
if (options.stop?.(current.cell, current.cost))
|
|
33
|
+
continue;
|
|
34
|
+
for (const next of options.neighbors(current.cell)) {
|
|
35
|
+
const step = options.cost(current.cell, next);
|
|
36
|
+
if (!Number.isFinite(step) || step < 0)
|
|
37
|
+
continue;
|
|
38
|
+
const total = current.cost + step;
|
|
39
|
+
if (total > options.maxCost)
|
|
40
|
+
continue;
|
|
41
|
+
if (options.canEnter && !options.canEnter(next, current.cell, total))
|
|
42
|
+
continue;
|
|
43
|
+
const key = options.key(next);
|
|
44
|
+
if (total >= (reached.get(key)?.cost ?? Infinity))
|
|
45
|
+
continue;
|
|
46
|
+
const entry = { cell: next, key, cost: total };
|
|
47
|
+
reached.set(key, entry);
|
|
48
|
+
push(queue, entry);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return reached;
|
|
52
|
+
}
|
|
53
|
+
function push(queue, entry) {
|
|
54
|
+
queue.push(entry);
|
|
55
|
+
let index = queue.length - 1;
|
|
56
|
+
while (index > 0) {
|
|
57
|
+
const parent = Math.floor((index - 1) / 2);
|
|
58
|
+
if (queue[parent].cost <= queue[index].cost)
|
|
59
|
+
break;
|
|
60
|
+
[queue[parent], queue[index]] = [queue[index], queue[parent]];
|
|
61
|
+
index = parent;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function pop(queue) {
|
|
65
|
+
const first = queue[0];
|
|
66
|
+
const last = queue.pop();
|
|
67
|
+
if (!first || !last || queue.length === 0)
|
|
68
|
+
return first;
|
|
69
|
+
queue[0] = last;
|
|
70
|
+
let index = 0;
|
|
71
|
+
while (true) {
|
|
72
|
+
const left = index * 2 + 1;
|
|
73
|
+
const right = left + 1;
|
|
74
|
+
let smallest = index;
|
|
75
|
+
if (left < queue.length && queue[left].cost < queue[smallest].cost)
|
|
76
|
+
smallest = left;
|
|
77
|
+
if (right < queue.length && queue[right].cost < queue[smallest].cost)
|
|
78
|
+
smallest = right;
|
|
79
|
+
if (smallest === index)
|
|
80
|
+
break;
|
|
81
|
+
[queue[index], queue[smallest]] = [queue[smallest], queue[index]];
|
|
82
|
+
index = smallest;
|
|
83
|
+
}
|
|
84
|
+
return first;
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=Pathfinding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pathfinding.js","sourceRoot":"","sources":["../../src/core/Pathfinding.ts"],"names":[],"mappings":"AAqBA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,aAAa,CAAO,KAAQ,EAAE,OAAmC;IAChF,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,OAAO,GAAG,CAAC;QAAE,OAAO,IAAI,GAAG,EAAE,CAAC;IAE/E,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACpC,MAAM,OAAO,GAAG,IAAI,GAAG,CAAqB,CAAC,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACpF,MAAM,KAAK,GAAuB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IAE5E,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,GAAG,CAAC,KAAK,CAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,IAAI,OAAO,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI;YAAE,SAAS;QACpD,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC;YAAE,SAAS;QAEzD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YACpD,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC;gBAAE,SAAS;YACjD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC;YAClC,IAAI,KAAK,GAAG,OAAO,CAAC,OAAO;gBAAE,SAAS;YACtC,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,KAAK,CAAC;gBAAE,SAAS;YAE/E,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,KAAK,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,IAAI,IAAI,QAAQ,CAAC;gBAAE,SAAS;YAC5D,MAAM,KAAK,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;YAC/C,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACpB,CAAC;IACF,CAAC;IAED,OAAO,OAAO,CAAC;AAChB,CAAC;AAMD,SAAS,IAAI,CAAO,KAAyB,EAAE,KAAuB;IACrE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClB,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IAC7B,OAAO,KAAK,GAAG,CAAC,EAAE,CAAC;QAClB,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC3C,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI;YAAE,MAAM;QACnD,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAC9D,KAAK,GAAG,MAAM,CAAC;IAChB,CAAC;AACF,CAAC;AAED,SAAS,GAAG,CAAO,KAAyB;IAC3C,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IACzB,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxD,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC;IAChB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,IAAI,EAAE,CAAC;QACb,MAAM,IAAI,GAAG,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC;QACvB,IAAI,QAAQ,GAAG,KAAK,CAAC;QACrB,IAAI,IAAI,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI;YAAE,QAAQ,GAAG,IAAI,CAAC;QACpF,IAAI,KAAK,GAAG,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI;YAAE,QAAQ,GAAG,KAAK,CAAC;QACvF,IAAI,QAAQ,KAAK,KAAK;YAAE,MAAM;QAC9B,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAClE,KAAK,GAAG,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,KAAK,CAAC;AACd,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/** A JSON-compatible value owned by a game or one of its framework extensions. */
|
|
2
|
+
export type StateValue = null | boolean | number | string | StateValue[] | {
|
|
3
|
+
readonly [key: string]: StateValue;
|
|
4
|
+
};
|
|
5
|
+
export interface StateExtension<T extends StateValue = StateValue> {
|
|
6
|
+
/** stable key written into a snapshot */
|
|
7
|
+
readonly id: string;
|
|
8
|
+
/** capture the extension's complete serializable state */
|
|
9
|
+
readonly capture: () => T;
|
|
10
|
+
/** restore a previously captured state atomically */
|
|
11
|
+
readonly restore: (state: T) => void;
|
|
12
|
+
/** Current schema version for this extension, independent of other save data. */
|
|
13
|
+
readonly version?: number;
|
|
14
|
+
/** Migrations are keyed by destination version and run in order. */
|
|
15
|
+
readonly migrations?: Readonly<Record<number, (state: StateValue) => T>>;
|
|
16
|
+
/** Optional reset used when a save predates this extension. */
|
|
17
|
+
readonly reset?: () => void;
|
|
18
|
+
/** Optional cleanup when the extension is deliberately absent from a restored save. */
|
|
19
|
+
readonly remove?: () => void;
|
|
20
|
+
}
|
|
21
|
+
export interface StateSnapshot {
|
|
22
|
+
readonly extensions: Readonly<Record<string, StateValue>>;
|
|
23
|
+
readonly versions?: Readonly<Record<string, number>>;
|
|
24
|
+
}
|
|
25
|
+
export interface StateRestoreDiagnostic {
|
|
26
|
+
readonly extension: string;
|
|
27
|
+
readonly from: number;
|
|
28
|
+
readonly to: number;
|
|
29
|
+
readonly status: 'migrated' | 'unchanged' | 'reset' | 'removed';
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Coordinates game-owned save state without knowing its schema.
|
|
33
|
+
*
|
|
34
|
+
* Each extension owns its data and restore logic. `transaction` captures all registered
|
|
35
|
+
* extensions before running work and restores that snapshot if work throws, which prevents
|
|
36
|
+
* half-applied saves when one subsystem rejects an update.
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* import { StateRegistry } from '@datamoc/mw_games/core';
|
|
41
|
+
*
|
|
42
|
+
* let inventory = { gold: 10 };
|
|
43
|
+
* const state = new StateRegistry();
|
|
44
|
+
* state.register({ id: 'inventory', capture: () => inventory, restore: (saved) => { inventory = saved; } });
|
|
45
|
+
* state.transaction(() => { inventory.gold -= 5; });
|
|
46
|
+
* const save = state.snapshot();
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export declare class StateRegistry {
|
|
50
|
+
private extensions;
|
|
51
|
+
register<T extends StateValue>(extension: StateExtension<T>): () => void;
|
|
52
|
+
snapshot(): StateSnapshot;
|
|
53
|
+
restore(snapshot: StateSnapshot, options?: {
|
|
54
|
+
readonly missing?: 'keep' | 'reset' | 'remove';
|
|
55
|
+
readonly onDiagnostic?: (diagnostic: StateRestoreDiagnostic) => void;
|
|
56
|
+
}): readonly StateRestoreDiagnostic[];
|
|
57
|
+
transaction<T>(work: () => T): T;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=State.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"State.d.ts","sourceRoot":"","sources":["../../src/core/State.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,EAAE,GAAG;IAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAAA;CAAE,CAAC;AAElH,MAAM,WAAW,cAAc,CAAC,CAAC,SAAS,UAAU,GAAG,UAAU;IAChE,yCAAyC;IACzC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1B,qDAAqD;IACrD,QAAQ,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;IACrC,iFAAiF;IACjF,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,oEAAoE;IACpE,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,CAAC,CAAC,CAAC,CAAC;IACzE,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,IAAI,CAAC;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,aAAa;IAC7B,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC;IAC1D,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,sBAAsB;IACtC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,WAAW,GAAG,OAAO,GAAG,SAAS,CAAC;CAChE;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,aAAa;IACzB,OAAO,CAAC,UAAU,CAAqC;IAEvD,QAAQ,CAAC,CAAC,SAAS,UAAU,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAcvE;IAED,QAAQ,IAAI,aAAa,CAMxB;IAED,OAAO,CACN,QAAQ,EAAE,aAAa,EACvB,OAAO,GAAE;QACR,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;QAC/C,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,UAAU,EAAE,sBAAsB,KAAK,IAAI,CAAC;KAChE,GACJ,SAAS,sBAAsB,EAAE,CAiCnC;IAED,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,CAQ/B;CACD"}
|