@datamoc/mw_games 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,65 @@
1
+ /** One of RPG Maker MV's four autotile atlas families. */
2
+ export type RpgmAutotileSlot = 0 | 1 | 2 | 3;
3
+ /** A source quadrant in the RPG Maker autotile atlas, in source pixels. */
4
+ export interface RpgmAutotileQuadrant {
5
+ sourceX: number;
6
+ sourceY: number;
7
+ destinationX: number;
8
+ destinationY: number;
9
+ }
10
+ /** The four source quadrants that make up one 48 by 48 RPG Maker autotile. */
11
+ export interface RpgmAutotileFrame {
12
+ tileId: number;
13
+ slot: RpgmAutotileSlot;
14
+ shape: number;
15
+ destinationX: number;
16
+ destinationY: number;
17
+ quadrants: readonly [RpgmAutotileQuadrant, RpgmAutotileQuadrant, RpgmAutotileQuadrant, RpgmAutotileQuadrant];
18
+ }
19
+ /** Four atlas-cell offsets, in 24px source cells, for one autotile shape. */
20
+ export type RpgmAutotileShape = readonly [
21
+ readonly [number, number],
22
+ readonly [number, number],
23
+ readonly [number, number],
24
+ readonly [number, number]
25
+ ];
26
+ export type RpgmAutotileShapeTable = readonly RpgmAutotileShape[];
27
+ /**
28
+ * Describes how RPG Maker MV composes one autotile from four 24px atlas quadrants.
29
+ *
30
+ * The returned geometry is renderer-neutral: a Pixi, canvas, or native renderer can draw
31
+ * each source rectangle into its destination rectangle without this module knowing about it.
32
+ * `tileId` is the output atlas index used by RPG Maker's 16-column generated atlas, while
33
+ * `slot` selects the source atlas family and `shape` indexes the caller's four-quadrant table.
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * import { rpgmAutotileFrame } from '@datamoc/mw_games/two-d/render';
38
+ *
39
+ * const shapeTable = Array.from({ length: 48 }, () => [[0, 0], [1, 0], [0, 1], [1, 1]] as const);
40
+ * const frame = rpgmAutotileFrame(7, 0, 3, shapeTable);
41
+ * console.log(frame.quadrants[0].sourceX); // source pixel x coordinate
42
+ * ```
43
+ */
44
+ export declare function rpgmAutotileFrame(tileId: number, slot: RpgmAutotileSlot, shape: number, table: RpgmAutotileShapeTable): RpgmAutotileFrame;
45
+ /**
46
+ * Caches RPG Maker autotile frame descriptions for one fixed shape table.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import { RpgmAutotileAtlas, type RpgmAutotileShapeTable } from '@datamoc/mw_games/two-d/render';
51
+ *
52
+ * declare const shapeTable: RpgmAutotileShapeTable;
53
+ * const atlas = new RpgmAutotileAtlas(shapeTable);
54
+ * const frame = atlas.get(7, 0, 3);
55
+ * console.log(frame.quadrants.length); // 4
56
+ * ```
57
+ */
58
+ export declare class RpgmAutotileAtlas {
59
+ private readonly cache;
60
+ private readonly table;
61
+ constructor(table: RpgmAutotileShapeTable);
62
+ get(tileId: number, slot: RpgmAutotileSlot, shape: number): RpgmAutotileFrame;
63
+ clear(): void;
64
+ }
65
+ //# sourceMappingURL=RpgmAutotile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RpgmAutotile.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/RpgmAutotile.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,MAAM,MAAM,gBAAgB,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAE7C,2EAA2E;AAC3E,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;CACrB;AAED,8EAA8E;AAC9E,MAAM,WAAW,iBAAiB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,gBAAgB,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC,oBAAoB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,oBAAoB,CAAC,CAAC;CAC7G;AAED,6EAA6E;AAC7E,MAAM,MAAM,iBAAiB,GAAG,SAAS;IACxC,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG,SAAS,iBAAiB,EAAE,CAAC;AA2ClE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAChC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,sBAAsB,GAC3B,iBAAiB,CAqBnB;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;IAC7B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAC9D,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAyB;IAE/C,YAAY,KAAK,EAAE,sBAAsB,EAGxC;IAED,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,CAO5E;IAED,KAAK,IAAI,IAAI,CAEZ;CACD"}
@@ -0,0 +1,110 @@
1
+ const SOURCE_QUADRANT_SIZE = 24;
2
+ const OUTPUT_TILE_SIZE = 48;
3
+ const SLOT_BASE_KINDS = [0, 16, 48, 80];
4
+ function assertInteger(name, value) {
5
+ if (!Number.isInteger(value))
6
+ throw new Error(`${name} must be an integer, got ${value}`);
7
+ }
8
+ function assertSlot(slot) {
9
+ if (slot !== 0 && slot !== 1 && slot !== 2 && slot !== 3) {
10
+ throw new Error(`RPG Maker autotile slot must be 0, 1, 2, or 3, got ${slot}`);
11
+ }
12
+ }
13
+ function validateShapeTable(table) {
14
+ if (table.length === 0)
15
+ throw new Error('RPG Maker autotile shape table cannot be empty');
16
+ for (let shape = 0; shape < table.length; shape++) {
17
+ if (table[shape].length !== 4) {
18
+ throw new Error(`RPG Maker autotile shape ${shape} needs exactly 4 quadrants`);
19
+ }
20
+ for (const quadrant of table[shape]) {
21
+ if (quadrant.length !== 2 || !quadrant.every(Number.isInteger) || quadrant.some((value) => value < 0)) {
22
+ throw new Error(`RPG Maker autotile shape ${shape} has an invalid quadrant offset`);
23
+ }
24
+ }
25
+ }
26
+ }
27
+ function sourceBase(tileId, slot) {
28
+ const kind = SLOT_BASE_KINDS[slot] + Math.floor(tileId / 48);
29
+ const tx = kind % 8;
30
+ const ty = Math.floor(kind / 8);
31
+ if (slot === 0) {
32
+ return [Math.floor(tx / 4) * 8, ty * 6 + (Math.floor(tx / 2) % 2) * 3];
33
+ }
34
+ if (slot === 1)
35
+ return [tx * 2, (ty - 2) * 3];
36
+ if (slot === 2)
37
+ return [tx * 2, (ty - 6) * 2];
38
+ return [tx * 2, Math.floor((ty - 10) * 2.5 + (ty % 2 === 1 ? 0.5 : 0))];
39
+ }
40
+ /**
41
+ * Describes how RPG Maker MV composes one autotile from four 24px atlas quadrants.
42
+ *
43
+ * The returned geometry is renderer-neutral: a Pixi, canvas, or native renderer can draw
44
+ * each source rectangle into its destination rectangle without this module knowing about it.
45
+ * `tileId` is the output atlas index used by RPG Maker's 16-column generated atlas, while
46
+ * `slot` selects the source atlas family and `shape` indexes the caller's four-quadrant table.
47
+ *
48
+ * @example
49
+ * ```ts
50
+ * import { rpgmAutotileFrame } from '@datamoc/mw_games/two-d/render';
51
+ *
52
+ * const shapeTable = Array.from({ length: 48 }, () => [[0, 0], [1, 0], [0, 1], [1, 1]] as const);
53
+ * const frame = rpgmAutotileFrame(7, 0, 3, shapeTable);
54
+ * console.log(frame.quadrants[0].sourceX); // source pixel x coordinate
55
+ * ```
56
+ */
57
+ export function rpgmAutotileFrame(tileId, slot, shape, table) {
58
+ assertInteger('RPG Maker autotile tileId', tileId);
59
+ if (tileId < 0)
60
+ throw new Error(`RPG Maker autotile tileId must be non-negative, got ${tileId}`);
61
+ assertSlot(slot);
62
+ assertInteger('RPG Maker autotile shape', shape);
63
+ validateShapeTable(table);
64
+ if (shape < 0 || shape >= table.length) {
65
+ throw new Error(`RPG Maker autotile shape ${shape} is outside the table, which holds ${table.length}`);
66
+ }
67
+ const [baseX, baseY] = sourceBase(tileId, slot);
68
+ const destinationX = (tileId % 16) * OUTPUT_TILE_SIZE;
69
+ const destinationY = Math.floor(tileId / 16) * OUTPUT_TILE_SIZE;
70
+ const quadrants = table[shape].map(([x, y], quadrant) => ({
71
+ sourceX: (baseX * 2 + x) * SOURCE_QUADRANT_SIZE,
72
+ sourceY: (baseY * 2 + y) * SOURCE_QUADRANT_SIZE,
73
+ destinationX: (quadrant % 2) * SOURCE_QUADRANT_SIZE,
74
+ destinationY: Math.floor(quadrant / 2) * SOURCE_QUADRANT_SIZE,
75
+ }));
76
+ return { tileId, slot, shape, destinationX, destinationY, quadrants };
77
+ }
78
+ /**
79
+ * Caches RPG Maker autotile frame descriptions for one fixed shape table.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * import { RpgmAutotileAtlas, type RpgmAutotileShapeTable } from '@datamoc/mw_games/two-d/render';
84
+ *
85
+ * declare const shapeTable: RpgmAutotileShapeTable;
86
+ * const atlas = new RpgmAutotileAtlas(shapeTable);
87
+ * const frame = atlas.get(7, 0, 3);
88
+ * console.log(frame.quadrants.length); // 4
89
+ * ```
90
+ */
91
+ export class RpgmAutotileAtlas {
92
+ constructor(table) {
93
+ this.cache = new Map();
94
+ this.table = table;
95
+ validateShapeTable(table);
96
+ }
97
+ get(tileId, slot, shape) {
98
+ const key = `${tileId}:${slot}:${shape}`;
99
+ const cached = this.cache.get(key);
100
+ if (cached)
101
+ return cached;
102
+ const frame = rpgmAutotileFrame(tileId, slot, shape, this.table);
103
+ this.cache.set(key, frame);
104
+ return frame;
105
+ }
106
+ clear() {
107
+ this.cache.clear();
108
+ }
109
+ }
110
+ //# sourceMappingURL=RpgmAutotile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RpgmAutotile.js","sourceRoot":"","sources":["../../../src/two-d/render/RpgmAutotile.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,8 @@ export { StatusVisuals } from './StatusVisuals.ts';
35
35
  export type { TintTarget, StatusVisualStyle, StatusVisualsOptions } from './StatusVisuals.ts';
36
36
  export { blobIndex, autotileFrames, BLOB_SHAPES } from './Autotile.ts';
37
37
  export type { NeighborMask } from './Autotile.ts';
38
+ export { RpgmAutotileAtlas, rpgmAutotileFrame } from './RpgmAutotile.ts';
39
+ export type { RpgmAutotileFrame, RpgmAutotileQuadrant, RpgmAutotileShape, RpgmAutotileShapeTable, RpgmAutotileSlot, } from './RpgmAutotile.ts';
38
40
  export { hexRotate, matchTerrainRule, resolveTerrainGraphics, squareRotate } from './TerrainGraphics.ts';
39
41
  export type { ResolveTerrainGraphicsOptions, TerrainCondition, TerrainFlagsAt, TerrainImage, TerrainPlacement, TerrainRotate, TerrainRule, } from './TerrainGraphics.ts';
40
42
  export { TerrainGraphicsLayer } from './TerrainGraphicsLayer.ts';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC5G,YAAY,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAE9D,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,UAAU,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,iBAAiB,GACjB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACX,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,UAAU,EACV,aAAa,GACb,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AACjG,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC7D,YAAY,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC3F,YAAY,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACzE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACzF,YAAY,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtF,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AACjD,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACjD,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAEtF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAElF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAEpF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE9F,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACvE,YAAY,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzG,YAAY,EACX,6BAA6B,EAC7B,gBAAgB,EAChB,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,aAAa,EACb,WAAW,GACX,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,YAAY,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAE7E,OAAO,EAAE,2BAA2B,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACnG,YAAY,EACX,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EACjB,eAAe,GACf,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3E,YAAY,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEtG,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACxG,YAAY,EACX,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,WAAW,EACX,kBAAkB,GAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAC7C,YAAY,EACX,YAAY,EACZ,gBAAgB,EAChB,YAAY,EACZ,cAAc,EACd,WAAW,EACX,UAAU,GACV,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/two-d/render/index.ts"],"names":[],"mappings":"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"}
@@ -18,6 +18,7 @@ export { ScreenEffects } from './ScreenEffects.js';
18
18
  export { ActorAnimator } from './ActorAnimator.js';
19
19
  export { StatusVisuals } from './StatusVisuals.js';
20
20
  export { blobIndex, autotileFrames, BLOB_SHAPES } from './Autotile.js';
21
+ export { RpgmAutotileAtlas, rpgmAutotileFrame } from './RpgmAutotile.js';
21
22
  export { hexRotate, matchTerrainRule, resolveTerrainGraphics, squareRotate } from './TerrainGraphics.js';
22
23
  export { TerrainGraphicsLayer } from './TerrainGraphicsLayer.js';
23
24
  export { inspectGraphicsCapabilities, detectWebGpu, RENDERING_DECISIONS } from './Capabilities.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/two-d/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG5G,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,UAAU,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,iBAAiB,GACjB,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAG3F,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEzF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGvE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAWzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGjE,OAAO,EAAE,2BAA2B,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AASnG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEtG,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAUxG,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/two-d/render/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAG5G,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACzE,OAAO,EACN,sBAAsB,EACtB,mBAAmB,EACnB,qBAAqB,EACrB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,aAAa,EACb,UAAU,EACV,eAAe,EACf,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,YAAY,EACZ,iBAAiB,GACjB,MAAM,qBAAqB,CAAC;AAS7B,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAEhE,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAE5D,OAAO,EAAE,0BAA0B,EAAE,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAG3F,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAEzF,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAEzD,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAGnD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEvE,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AASzE,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAWzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAGjE,OAAO,EAAE,2BAA2B,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AASnG,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEtG,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAUxG,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
package/dist/version.d.ts CHANGED
@@ -9,5 +9,5 @@
9
9
  * this from `package.json` automatically, chosen because this project's own build already
10
10
  * has no step before `tsc`/`vite` that this would need to run ahead of.
11
11
  */
12
- export declare const version = "0.9.0";
12
+ export declare const version = "0.10.0";
13
13
  //# sourceMappingURL=version.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,OAAO,UAAU,CAAC"}
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,eAAO,MAAM,OAAO,WAAW,CAAC"}
package/dist/version.js CHANGED
@@ -9,5 +9,5 @@
9
9
  * this from `package.json` automatically, chosen because this project's own build already
10
10
  * has no step before `tsc`/`vite` that this would need to run ahead of.
11
11
  */
12
- export const version = '0.9.0';
12
+ export const version = '0.10.0';
13
13
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datamoc/mw_games",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "A local-file game framework: renderer-free game logic, drawn through PixiJS in 2D or Babylon.js in 3D, with mobile and desktop packaging.",
5
5
  "license": "MPL-2.0",
6
6
  "repository": {
@@ -45,6 +45,8 @@
45
45
  "tools/compress-dist.d.mts",
46
46
  "tools/classic-html.mjs",
47
47
  "tools/classic-html.d.mts",
48
+ "tools/single-file.mjs",
49
+ "tools/single-file.d.mts",
48
50
  "tools/mwl.mjs",
49
51
  "LICENSE",
50
52
  "CHANGELOG.md",
@@ -0,0 +1,16 @@
1
+ export interface SingleFileOptions {
2
+ dist: string;
3
+ compress?: boolean;
4
+ level?: number;
5
+ splash?: boolean;
6
+ output?: string;
7
+ }
8
+
9
+ export interface SingleFileResult {
10
+ path: string;
11
+ scripts: number;
12
+ rawBytes: number;
13
+ embeddedBytes: number;
14
+ }
15
+
16
+ export function buildSingleFile(options: SingleFileOptions): Promise<SingleFileResult>;
@@ -0,0 +1,180 @@
1
+ import { readFile, writeFile } from 'node:fs/promises';
2
+ import { join, basename, resolve } from 'node:path';
3
+ import { gzipSync } from 'node:zlib';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ /**
7
+ * Turns an `emit-page.mjs` output folder (`index.html` + sibling asset/bundle `.js` files)
8
+ * into one genuinely standalone HTML file with no sibling script at all - roadmap item 349,
9
+ * the "single-file" leg of the build-options matrix `emit-page.mjs --single-file` exposes.
10
+ *
11
+ * The existing multi-file output already opens by double-clicking (that's the whole point of
12
+ * `tools/compile-resources.mjs` and `tools/classic-html.mjs`), but it is still an `index.html`
13
+ * plus N `.js` files that have to travel together. This inlines every `<script src>` the page
14
+ * loads directly into the page, in the same order, so the folder can be reduced to one file
15
+ * before it is shared or archived.
16
+ *
17
+ * Compression (`compress: true`) gzips each script's source before it is base64-embedded, and
18
+ * unpacks it at load with the browser's own `DecompressionStream('gzip')` - a real Web
19
+ * Streams API, not a shipped decoder. That beats a pure-JS decompressor such as `js-lzma` (the
20
+ * roadmap item's other candidate) on every axis that matters here: it adds zero bytes to the
21
+ * page, needs no license or maintenance, and gzip is exactly what `tools/compress-dist.mjs`
22
+ * already uses for the server-negotiated path, so the project only carries one compression
23
+ * story. The one real cost is browser support: `DecompressionStream` is unavailable on Safari
24
+ * before 16.4 and a handful of older engines, where the bootstrap throws a clear error rather
25
+ * than silently failing - `compress: false` (the default) has no such requirement, since it is
26
+ * just base64, which every browser this project targets can decode.
27
+ *
28
+ * Decoding is async even when uncompressed (kept uniform, and `atob` on a very large payload
29
+ * still benefits from not blocking the parser), so a splash screen (`splash: true`, the
30
+ * default) covers the page until every script has run. It is deliberately minimal - inline
31
+ * styles and a CSS spinner, no asset of its own - since this is a framework-level fallback a
32
+ * game is free to replace, not a themed loading screen.
33
+ *
34
+ * @param {object} options
35
+ * @param {string} options.dist path to the folder `emit-page.mjs` already wrote
36
+ * @param {boolean} [options.compress] gzip each inlined script (default false)
37
+ * @param {number} [options.level] gzip level 1-9 when `compress` is set (default 9)
38
+ * @param {boolean} [options.splash] show a splash screen while scripts decode (default true)
39
+ * @param {string} [options.output] output file name, written inside `dist` (default
40
+ * `standalone.html`) - additive, so the existing multi-file `index.html` is untouched
41
+ * @returns {Promise<{path: string, scripts: number, rawBytes: number, embeddedBytes: number}>}
42
+ */
43
+ export async function buildSingleFile({ dist, compress = false, level = 9, splash = true, output = 'standalone.html' } = {}) {
44
+ if (!dist) throw new Error('buildSingleFile needs a `dist` folder');
45
+
46
+ const htmlPath = join(dist, 'index.html');
47
+ const html = await readFile(htmlPath, 'utf8');
48
+
49
+ const scriptTag = /[ \t]*<script\b[^>]*\bsrc="([^"]+)"[^>]*><\/script>\n?/g;
50
+ const sources = [];
51
+ let firstMatchIndex = -1;
52
+ let match;
53
+ while ((match = scriptTag.exec(html))) {
54
+ if (firstMatchIndex === -1) firstMatchIndex = match.index;
55
+ sources.push(match[1]);
56
+ }
57
+ if (sources.length === 0) {
58
+ throw new Error(`${htmlPath}: no <script src="..."> tags found - is this an emit-page.mjs output folder?`);
59
+ }
60
+
61
+ let rawBytes = 0;
62
+ let embeddedBytes = 0;
63
+ const payloads = [];
64
+ for (const src of sources) {
65
+ const filePath = join(dist, src.replace(/^\.\//, ''));
66
+ const code = await readFile(filePath, 'utf8');
67
+ rawBytes += Buffer.byteLength(code, 'utf8');
68
+ const bytes = compress ? gzipSync(Buffer.from(code, 'utf8'), { level }) : Buffer.from(code, 'utf8');
69
+ const b64 = bytes.toString('base64');
70
+ embeddedBytes += b64.length;
71
+ payloads.push({ name: basename(src), b64 });
72
+ }
73
+
74
+ const withoutScripts = html.replace(scriptTag, '').replace(/\n{3,}/g, '\n\n');
75
+ const bootstrap = renderBootstrap(payloads, compress);
76
+ const splashCss = splash ? renderSplashStyle() : '';
77
+ const splashHtml = splash ? renderSplashMarkup() : '';
78
+
79
+ let page = withoutScripts;
80
+ page = page.includes('</head>') ? page.replace('</head>', `${splashCss}</head>`) : splashCss + page;
81
+ page = page.replace(/(<body[^>]*>)/, `$1\n${splashHtml}`);
82
+ page = page.replace(/<\/body>/, `\t${bootstrap}\n</body>`);
83
+
84
+ const outPath = join(dist, output);
85
+ await writeFile(outPath, page, 'utf8');
86
+
87
+ return { path: outPath, scripts: payloads.length, rawBytes, embeddedBytes };
88
+ }
89
+
90
+ function renderSplashStyle() {
91
+ return (
92
+ '\t<style id="mwg-splash-style">\n' +
93
+ '\t\t#mwg-splash{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;' +
94
+ 'flex-direction:column;gap:12px;background:#14161c;color:#eee;font:14px sans-serif;z-index:9999}\n' +
95
+ '\t\t#mwg-splash .mwg-spinner{width:28px;height:28px;border-radius:50%;' +
96
+ 'border:3px solid rgba(255,255,255,0.25);border-top-color:#fff;animation:mwg-spin 0.8s linear infinite}\n' +
97
+ '\t\t@keyframes mwg-spin{to{transform:rotate(360deg)}}\n' +
98
+ '\t</style>\n'
99
+ );
100
+ }
101
+
102
+ function renderSplashMarkup() {
103
+ return '\t<div id="mwg-splash"><div class="mwg-spinner"></div><div>Loading...</div></div>\n';
104
+ }
105
+
106
+ function renderBootstrap(payloads, compress) {
107
+ const json = JSON.stringify(payloads);
108
+ return (
109
+ '<script>\n' +
110
+ '(function(){\n' +
111
+ // Seeded synchronously, before any decoding starts: several examples' own dev/build
112
+ // detection scripts (see examples/*/index.html) check `window.__MWG_ASSETS__` at
113
+ // DOMContentLoaded to tell a built page from the unbuilt dev template, a check that only
114
+ // works because the multi-file build's asset script is a *deferred* <script src>, which
115
+ // finishes before that event. This bootstrap decodes asynchronously instead, so without
116
+ // this line those checks would fire too early and show a false "not built" notice. The
117
+ // compiled asset script itself creates this object with the same `||` guard once it
118
+ // actually runs (see tools/compile-resources.mjs) - this only moves that existence
119
+ // earlier, not the entries themselves, which are still filled in strictly before the
120
+ // game bundle that reads them executes.
121
+ '\twindow.__MWG_ASSETS__=window.__MWG_ASSETS__||{};\n' +
122
+ `\tvar PAYLOADS=${json};\n` +
123
+ `\tvar COMPRESSED=${compress ? 'true' : 'false'};\n` +
124
+ '\tfunction b64ToBytes(b64){var bin=atob(b64);var bytes=new Uint8Array(bin.length);' +
125
+ 'for(var i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);return bytes;}\n' +
126
+ '\tfunction decodeText(bytes){\n' +
127
+ '\t\tif(!COMPRESSED)return Promise.resolve(new TextDecoder().decode(bytes));\n' +
128
+ '\t\tif(typeof DecompressionStream==="undefined")return Promise.reject(new Error(' +
129
+ '"mwg standalone build: this browser has no DecompressionStream, needed to unpack the compressed bundle. ' +
130
+ 'Use the multi-file build instead."));\n' +
131
+ '\t\tvar stream=new Blob([bytes]).stream().pipeThrough(new DecompressionStream("gzip"));\n' +
132
+ '\t\treturn new Response(stream).arrayBuffer().then(function(buf){return new TextDecoder().decode(buf);});\n' +
133
+ '\t}\n' +
134
+ '\tfunction removeSplash(){var el=document.getElementById("mwg-splash");if(el&&el.parentNode)el.parentNode.removeChild(el);}\n' +
135
+ '\tfunction runNext(i){\n' +
136
+ '\t\tif(i>=PAYLOADS.length){removeSplash();return;}\n' +
137
+ '\t\tvar p=PAYLOADS[i];\n' +
138
+ '\t\tdecodeText(b64ToBytes(p.b64)).then(function(code){\n' +
139
+ '\t\t\tvar s=document.createElement("script");s.text=code;document.body.appendChild(s);\n' +
140
+ '\t\t\trunNext(i+1);\n' +
141
+ '\t\t}).catch(function(err){\n' +
142
+ '\t\t\tremoveSplash();\n' +
143
+ '\t\t\tconsole.error("mwg standalone build failed to load "+p.name, err);\n' +
144
+ '\t\t});\n' +
145
+ '\t}\n' +
146
+ '\trunNext(0);\n' +
147
+ '})();\n' +
148
+ '</script>'
149
+ );
150
+ }
151
+
152
+ // ---------------------------------------------------------------- CLI
153
+
154
+ const isMain = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
155
+
156
+ if (isMain) {
157
+ const args = process.argv.slice(2);
158
+ const dist = args[0];
159
+ if (!dist || dist.startsWith('--')) {
160
+ console.error(
161
+ 'usage: node tools/single-file.mjs <dist folder> [--compress[=level]] [--no-splash] [--output name.html]',
162
+ );
163
+ process.exit(1);
164
+ }
165
+ const compressFlag = args.find((a) => a === '--compress' || a.startsWith('--compress='));
166
+ const compress = Boolean(compressFlag);
167
+ const level = compressFlag && compressFlag.includes('=') ? Number(compressFlag.split('=')[1]) : 9;
168
+ const splash = !args.includes('--no-splash');
169
+ const outputIndex = args.indexOf('--output');
170
+ const output = outputIndex !== -1 ? args[outputIndex + 1] : 'standalone.html';
171
+
172
+ const result = await buildSingleFile({ dist: resolve(dist), compress, level, splash, output });
173
+ const kb = (n) => (n / 1024).toFixed(1) + ' KB';
174
+ console.log(`\n ${result.path}`);
175
+ console.log(
176
+ ` ${result.scripts} script(s) inlined, ${kb(result.rawBytes)} raw -> ${kb(result.embeddedBytes)} embedded` +
177
+ (compress ? ` (gzip level ${level})` : ' (uncompressed)'),
178
+ );
179
+ console.log(' one file, no server, no sibling script - open it directly');
180
+ }