@datamoc/mw_games 0.15.0 → 0.17.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.
Files changed (139) hide show
  1. package/CHANGELOG.md +154 -1
  2. package/README.md +21 -1
  3. package/dist/ai/lua.d.ts.map +1 -1
  4. package/dist/ai/lua.js +1 -0
  5. package/dist/ai/lua.js.map +1 -1
  6. package/dist/core/ActionJournal.d.ts +5 -0
  7. package/dist/core/ActionJournal.d.ts.map +1 -1
  8. package/dist/core/ActionJournal.js +12 -1
  9. package/dist/core/ActionJournal.js.map +1 -1
  10. package/dist/core/Clone.d.ts +31 -0
  11. package/dist/core/Clone.d.ts.map +1 -0
  12. package/dist/core/Clone.js +84 -0
  13. package/dist/core/Clone.js.map +1 -0
  14. package/dist/core/Collection.d.ts +1 -0
  15. package/dist/core/Collection.d.ts.map +1 -1
  16. package/dist/core/Collection.js +6 -2
  17. package/dist/core/Collection.js.map +1 -1
  18. package/dist/core/HttpTransport.d.ts +17 -0
  19. package/dist/core/HttpTransport.d.ts.map +1 -1
  20. package/dist/core/HttpTransport.js +61 -0
  21. package/dist/core/HttpTransport.js.map +1 -1
  22. package/dist/core/Multiplayer.d.ts +9 -0
  23. package/dist/core/Multiplayer.d.ts.map +1 -1
  24. package/dist/core/Multiplayer.js +48 -4
  25. package/dist/core/Multiplayer.js.map +1 -1
  26. package/dist/core/News.d.ts.map +1 -1
  27. package/dist/core/News.js +1 -2
  28. package/dist/core/News.js.map +1 -1
  29. package/dist/core/Sanitize.d.ts +19 -0
  30. package/dist/core/Sanitize.d.ts.map +1 -1
  31. package/dist/core/Sanitize.js +37 -0
  32. package/dist/core/Sanitize.js.map +1 -1
  33. package/dist/core/Save.d.ts +18 -9
  34. package/dist/core/Save.d.ts.map +1 -1
  35. package/dist/core/Save.js +37 -21
  36. package/dist/core/Save.js.map +1 -1
  37. package/dist/core/SaveSync.js +4 -4
  38. package/dist/core/SaveSync.js.map +1 -1
  39. package/dist/core/Settings.d.ts.map +1 -1
  40. package/dist/core/Settings.js +2 -1
  41. package/dist/core/Settings.js.map +1 -1
  42. package/dist/core/State.d.ts.map +1 -1
  43. package/dist/core/State.js +8 -7
  44. package/dist/core/State.js.map +1 -1
  45. package/dist/core/StoredValue.d.ts +1 -1
  46. package/dist/core/StoredValue.d.ts.map +1 -1
  47. package/dist/core/StoredValue.js +3 -2
  48. package/dist/core/StoredValue.js.map +1 -1
  49. package/dist/core/Telemetry.d.ts +19 -1
  50. package/dist/core/Telemetry.d.ts.map +1 -1
  51. package/dist/core/Telemetry.js +26 -1
  52. package/dist/core/Telemetry.js.map +1 -1
  53. package/dist/core/index.d.ts +2 -1
  54. package/dist/core/index.d.ts.map +1 -1
  55. package/dist/core/index.js +2 -1
  56. package/dist/core/index.js.map +1 -1
  57. package/dist/mw_games.global.js +107 -107
  58. package/dist/mwl/MapFile.js +11 -1
  59. package/dist/mwl/MapFile.js.map +1 -1
  60. package/dist/mwl/expression.js +38 -5
  61. package/dist/mwl/expression.js.map +1 -1
  62. package/dist/mwl/fengari.d.ts +8 -0
  63. package/dist/mwl/fengari.d.ts.map +1 -1
  64. package/dist/mwl/fengari.js +72 -2
  65. package/dist/mwl/fengari.js.map +1 -1
  66. package/dist/mwl/persistence.d.ts.map +1 -1
  67. package/dist/mwl/persistence.js +2 -1
  68. package/dist/mwl/persistence.js.map +1 -1
  69. package/dist/mwl/runtime.d.ts.map +1 -1
  70. package/dist/mwl/runtime.js +6 -1
  71. package/dist/mwl/runtime.js.map +1 -1
  72. package/dist/rpg/Marshal.d.ts.map +1 -1
  73. package/dist/rpg/Marshal.js +13 -0
  74. package/dist/rpg/Marshal.js.map +1 -1
  75. package/dist/simulation/Campaign.d.ts.map +1 -1
  76. package/dist/simulation/Campaign.js +9 -5
  77. package/dist/simulation/Campaign.js.map +1 -1
  78. package/dist/simulation/Harness.d.ts.map +1 -1
  79. package/dist/simulation/Harness.js +2 -1
  80. package/dist/simulation/Harness.js.map +1 -1
  81. package/dist/simulation/Runtime.d.ts +10 -0
  82. package/dist/simulation/Runtime.d.ts.map +1 -1
  83. package/dist/simulation/Runtime.js +25 -4
  84. package/dist/simulation/Runtime.js.map +1 -1
  85. package/dist/testing/index.d.ts +105 -0
  86. package/dist/testing/index.d.ts.map +1 -0
  87. package/dist/testing/index.js +134 -0
  88. package/dist/testing/index.js.map +1 -0
  89. package/dist/two-d/render/RpgmAutotile.d.ts +217 -0
  90. package/dist/two-d/render/RpgmAutotile.d.ts.map +1 -1
  91. package/dist/two-d/render/RpgmAutotile.js +756 -0
  92. package/dist/two-d/render/RpgmAutotile.js.map +1 -1
  93. package/dist/two-d/render/TileMap.d.ts +90 -0
  94. package/dist/two-d/render/TileMap.d.ts.map +1 -1
  95. package/dist/two-d/render/TileMap.js +319 -31
  96. package/dist/two-d/render/TileMap.js.map +1 -1
  97. package/dist/two-d/render/index.d.ts +3 -3
  98. package/dist/two-d/render/index.d.ts.map +1 -1
  99. package/dist/two-d/render/index.js +1 -1
  100. package/dist/two-d/render/index.js.map +1 -1
  101. package/dist/version.d.ts +1 -1
  102. package/dist/version.js +1 -1
  103. package/package.json +90 -25
  104. package/sbom.cdx.json +5589 -0
  105. package/tools/benchmark-browser.d.mts +33 -0
  106. package/tools/benchmark-browser.mjs +163 -0
  107. package/tools/benchmark-history.d.mts +8 -0
  108. package/tools/benchmark-history.mjs +31 -0
  109. package/tools/browser-smoke.d.mts +30 -0
  110. package/tools/browser-smoke.mjs +167 -0
  111. package/tools/bundle-size.d.mts +11 -0
  112. package/tools/bundle-size.mjs +138 -0
  113. package/tools/csp.d.mts +7 -0
  114. package/tools/csp.mjs +56 -0
  115. package/tools/emit-page.d.mts +44 -0
  116. package/tools/emit-page.mjs +188 -0
  117. package/tools/extract-rgssad.d.mts +5 -0
  118. package/tools/extract-rgssad.mjs +64 -0
  119. package/tools/find-chrome.d.mts +7 -0
  120. package/tools/find-chrome.mjs +104 -0
  121. package/tools/i18n-edit.d.mts +29 -0
  122. package/tools/i18n-edit.mjs +748 -0
  123. package/tools/make-example-assets.d.mts +2 -0
  124. package/tools/make-example-assets.mjs +727 -0
  125. package/tools/multiplayer-server.d.mts +27 -0
  126. package/tools/multiplayer-server.mjs +195 -0
  127. package/tools/mwl.mjs +1 -0
  128. package/tools/sbom.d.mts +83 -0
  129. package/tools/sbom.mjs +330 -0
  130. package/tools/vendored/rgssad-wasm/LICENSE-MIT +21 -0
  131. package/tools/vendored/rgssad-wasm/README.md +15 -0
  132. package/tools/vendored/rgssad-wasm/package.json +17 -0
  133. package/tools/vendored/rgssad-wasm/rgssad_wasm.d.ts +41 -0
  134. package/tools/vendored/rgssad-wasm/rgssad_wasm.js +6 -0
  135. package/tools/vendored/rgssad-wasm/rgssad_wasm_bg.js +399 -0
  136. package/tools/vendored/rgssad-wasm/rgssad_wasm_bg.wasm +0 -0
  137. package/tools/vendored/rgssad-wasm/rgssad_wasm_bg.wasm.d.ts +17 -0
  138. package/tools/vite.d.mts +17 -0
  139. package/tools/vite.mjs +83 -0
@@ -107,4 +107,760 @@ export class RpgmAutotileAtlas {
107
107
  this.cache.clear();
108
108
  }
109
109
  }
110
+ /**
111
+ * The first MV tile id of each autotile family (A1-A4): slot 0 covers
112
+ * 2048-2815, slot 1 covers 2816-4351, slot 2 covers 4352-5887 and slot 3
113
+ * covers 5888-8191. Measured against real A-sheets rather than assumed.
114
+ */
115
+ export const RPGM_AUTOTILE_SLOT_BASES = [2048, 2816, 4352, 5888];
116
+ /**
117
+ * How many MV tile ids each family holds: 16, 32, 32 and 48 kinds of
118
+ * 48 shapes each. A sheet holding fewer cells than its slot needs is a
119
+ * truncated custom sheet, not a different layout.
120
+ */
121
+ export const RPGM_AUTOTILE_SLOT_COUNTS = [768, 1536, 1536, 2304];
122
+ /**
123
+ * Which MV autotile family a raw map tile id belongs to, or null when the id
124
+ * is not an autotile at all (an `EMPTY` cell, a static B-E tile, an A5 tile).
125
+ *
126
+ * @example
127
+ * ```ts
128
+ * import {
129
+ * rpgmAutotileSlot,
130
+ * RPGM_AUTOTILE_SLOT_BASES,
131
+ * RPGM_AUTOTILE_SLOT_COUNTS,
132
+ * } from '@datamoc/mw_games/two-d/render';
133
+ *
134
+ * console.log(rpgmAutotileSlot(2048)); // 0 - the first A1 tile
135
+ * console.log(rpgmAutotileSlot(3000)); // 1 - inside the A2 range
136
+ * console.log(rpgmAutotileSlot(42)); // null - a static tile, not an autotile
137
+ * console.log(rpgmAutotileSlot(-1)); // null - EMPTY reads as blank, never as a slot
138
+ * console.log(RPGM_AUTOTILE_SLOT_BASES[0]); // 2048 - where slot 0 starts
139
+ * console.log(RPGM_AUTOTILE_SLOT_COUNTS[0]); // 768 - 16 kinds times 48 shapes
140
+ * ```
141
+ */
142
+ export function rpgmAutotileSlot(tile) {
143
+ if (!Number.isInteger(tile))
144
+ return null;
145
+ for (let slot = 0; slot < 4; slot++) {
146
+ if (tile >= RPGM_AUTOTILE_SLOT_BASES[slot] &&
147
+ tile < RPGM_AUTOTILE_SLOT_BASES[slot] + RPGM_AUTOTILE_SLOT_COUNTS[slot]) {
148
+ return slot;
149
+ }
150
+ }
151
+ return null;
152
+ }
153
+ /**
154
+ * The standard MV floor-autotile shape table (48 shapes), for slots 0-1:
155
+ * which four atlas quadrants each baked shape assembles, in 24px source
156
+ * cells. Entries are `[x, y]` offsets, row-major `[topLeft, topRight,
157
+ * bottomLeft, bottomRight]`, exactly the shape numbering MV bakes into map
158
+ * tile ids (`shape = (tile - base) % 48`).
159
+ *
160
+ * Functional interop data, the same category as `BLOB_SHAPES`: every game
161
+ * reading MV maps needs these numbers, so they live here once rather than
162
+ * pasted into every port.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * import { RPGM_FLOOR_AUTOTILE_TABLE, RPGM_WALL_AUTOTILE_TABLE } from '@datamoc/mw_games/two-d/render';
167
+ *
168
+ * console.log(RPGM_FLOOR_AUTOTILE_TABLE.length); // 48 - one entry per baked shape
169
+ * console.log(RPGM_WALL_AUTOTILE_TABLE.length); // 16 - walls cycle every 16 shapes
170
+ * console.log(RPGM_FLOOR_AUTOTILE_TABLE[47]); // [[0,0],[1,0],[0,1],[1,1]] - the full tile
171
+ * ```
172
+ */
173
+ export const RPGM_FLOOR_AUTOTILE_TABLE = [
174
+ [
175
+ [2, 4],
176
+ [1, 4],
177
+ [2, 3],
178
+ [1, 3],
179
+ ],
180
+ [
181
+ [2, 0],
182
+ [1, 4],
183
+ [2, 3],
184
+ [1, 3],
185
+ ],
186
+ [
187
+ [2, 4],
188
+ [3, 0],
189
+ [2, 3],
190
+ [1, 3],
191
+ ],
192
+ [
193
+ [2, 0],
194
+ [3, 0],
195
+ [2, 3],
196
+ [1, 3],
197
+ ],
198
+ [
199
+ [2, 4],
200
+ [1, 4],
201
+ [2, 3],
202
+ [3, 1],
203
+ ],
204
+ [
205
+ [2, 0],
206
+ [1, 4],
207
+ [2, 3],
208
+ [3, 1],
209
+ ],
210
+ [
211
+ [2, 4],
212
+ [3, 0],
213
+ [2, 3],
214
+ [3, 1],
215
+ ],
216
+ [
217
+ [2, 0],
218
+ [3, 0],
219
+ [2, 3],
220
+ [3, 1],
221
+ ],
222
+ [
223
+ [2, 4],
224
+ [1, 4],
225
+ [2, 1],
226
+ [1, 3],
227
+ ],
228
+ [
229
+ [2, 0],
230
+ [1, 4],
231
+ [2, 1],
232
+ [1, 3],
233
+ ],
234
+ [
235
+ [2, 4],
236
+ [3, 0],
237
+ [2, 1],
238
+ [1, 3],
239
+ ],
240
+ [
241
+ [2, 0],
242
+ [3, 0],
243
+ [2, 1],
244
+ [1, 3],
245
+ ],
246
+ [
247
+ [2, 4],
248
+ [1, 4],
249
+ [2, 1],
250
+ [3, 1],
251
+ ],
252
+ [
253
+ [2, 0],
254
+ [1, 4],
255
+ [2, 1],
256
+ [3, 1],
257
+ ],
258
+ [
259
+ [2, 4],
260
+ [3, 0],
261
+ [2, 1],
262
+ [3, 1],
263
+ ],
264
+ [
265
+ [2, 0],
266
+ [3, 0],
267
+ [2, 1],
268
+ [3, 1],
269
+ ],
270
+ [
271
+ [0, 4],
272
+ [1, 4],
273
+ [0, 3],
274
+ [1, 3],
275
+ ],
276
+ [
277
+ [0, 4],
278
+ [3, 0],
279
+ [0, 3],
280
+ [1, 3],
281
+ ],
282
+ [
283
+ [0, 4],
284
+ [1, 4],
285
+ [0, 3],
286
+ [3, 1],
287
+ ],
288
+ [
289
+ [0, 4],
290
+ [3, 0],
291
+ [0, 3],
292
+ [3, 1],
293
+ ],
294
+ [
295
+ [2, 2],
296
+ [1, 2],
297
+ [2, 3],
298
+ [1, 3],
299
+ ],
300
+ [
301
+ [2, 2],
302
+ [1, 2],
303
+ [2, 3],
304
+ [3, 1],
305
+ ],
306
+ [
307
+ [2, 2],
308
+ [1, 2],
309
+ [2, 1],
310
+ [1, 3],
311
+ ],
312
+ [
313
+ [2, 2],
314
+ [1, 2],
315
+ [2, 1],
316
+ [3, 1],
317
+ ],
318
+ [
319
+ [2, 4],
320
+ [3, 4],
321
+ [2, 3],
322
+ [3, 3],
323
+ ],
324
+ [
325
+ [2, 4],
326
+ [3, 4],
327
+ [2, 1],
328
+ [3, 3],
329
+ ],
330
+ [
331
+ [2, 0],
332
+ [3, 4],
333
+ [2, 3],
334
+ [3, 3],
335
+ ],
336
+ [
337
+ [2, 0],
338
+ [3, 4],
339
+ [2, 1],
340
+ [3, 3],
341
+ ],
342
+ [
343
+ [2, 4],
344
+ [1, 4],
345
+ [2, 5],
346
+ [1, 5],
347
+ ],
348
+ [
349
+ [2, 0],
350
+ [1, 4],
351
+ [2, 5],
352
+ [1, 5],
353
+ ],
354
+ [
355
+ [2, 4],
356
+ [3, 0],
357
+ [2, 5],
358
+ [1, 5],
359
+ ],
360
+ [
361
+ [2, 0],
362
+ [3, 0],
363
+ [2, 5],
364
+ [1, 5],
365
+ ],
366
+ [
367
+ [0, 4],
368
+ [3, 4],
369
+ [0, 3],
370
+ [3, 3],
371
+ ],
372
+ [
373
+ [2, 2],
374
+ [1, 2],
375
+ [2, 5],
376
+ [1, 5],
377
+ ],
378
+ [
379
+ [0, 2],
380
+ [1, 2],
381
+ [0, 3],
382
+ [1, 3],
383
+ ],
384
+ [
385
+ [0, 2],
386
+ [1, 2],
387
+ [0, 3],
388
+ [3, 1],
389
+ ],
390
+ [
391
+ [2, 2],
392
+ [3, 2],
393
+ [2, 3],
394
+ [3, 3],
395
+ ],
396
+ [
397
+ [2, 2],
398
+ [3, 2],
399
+ [2, 1],
400
+ [3, 3],
401
+ ],
402
+ [
403
+ [2, 4],
404
+ [3, 4],
405
+ [2, 5],
406
+ [3, 5],
407
+ ],
408
+ [
409
+ [2, 0],
410
+ [3, 4],
411
+ [2, 5],
412
+ [3, 5],
413
+ ],
414
+ [
415
+ [0, 4],
416
+ [1, 4],
417
+ [0, 5],
418
+ [1, 5],
419
+ ],
420
+ [
421
+ [0, 4],
422
+ [3, 0],
423
+ [0, 5],
424
+ [1, 5],
425
+ ],
426
+ [
427
+ [0, 2],
428
+ [3, 2],
429
+ [0, 3],
430
+ [3, 3],
431
+ ],
432
+ [
433
+ [0, 2],
434
+ [1, 2],
435
+ [0, 5],
436
+ [1, 5],
437
+ ],
438
+ [
439
+ [0, 4],
440
+ [3, 4],
441
+ [0, 5],
442
+ [3, 5],
443
+ ],
444
+ [
445
+ [2, 2],
446
+ [3, 2],
447
+ [2, 5],
448
+ [3, 5],
449
+ ],
450
+ [
451
+ [0, 2],
452
+ [3, 2],
453
+ [0, 3],
454
+ [3, 5],
455
+ ],
456
+ [
457
+ [0, 0],
458
+ [1, 0],
459
+ [0, 1],
460
+ [1, 1],
461
+ ],
462
+ ];
463
+ /**
464
+ * The standard MV wall-autotile shape table (16 shapes), for slots 2-3.
465
+ * Wall shapes cycle every 16 (`table[shape % 16]`), so a wall family holds
466
+ * three full cycles where a floor family holds one run of 48.
467
+ */
468
+ export const RPGM_WALL_AUTOTILE_TABLE = [
469
+ [
470
+ [2, 2],
471
+ [1, 2],
472
+ [2, 1],
473
+ [1, 1],
474
+ ],
475
+ [
476
+ [0, 2],
477
+ [1, 2],
478
+ [0, 1],
479
+ [1, 1],
480
+ ],
481
+ [
482
+ [2, 0],
483
+ [1, 0],
484
+ [2, 1],
485
+ [1, 1],
486
+ ],
487
+ [
488
+ [0, 0],
489
+ [1, 0],
490
+ [0, 1],
491
+ [1, 1],
492
+ ],
493
+ [
494
+ [2, 2],
495
+ [3, 2],
496
+ [2, 1],
497
+ [3, 1],
498
+ ],
499
+ [
500
+ [0, 2],
501
+ [3, 2],
502
+ [0, 1],
503
+ [3, 1],
504
+ ],
505
+ [
506
+ [2, 0],
507
+ [3, 0],
508
+ [2, 1],
509
+ [3, 1],
510
+ ],
511
+ [
512
+ [0, 0],
513
+ [3, 0],
514
+ [0, 1],
515
+ [3, 1],
516
+ ],
517
+ [
518
+ [2, 2],
519
+ [1, 2],
520
+ [2, 3],
521
+ [1, 3],
522
+ ],
523
+ [
524
+ [0, 2],
525
+ [1, 2],
526
+ [0, 3],
527
+ [1, 3],
528
+ ],
529
+ [
530
+ [2, 0],
531
+ [1, 0],
532
+ [2, 3],
533
+ [1, 3],
534
+ ],
535
+ [
536
+ [0, 0],
537
+ [1, 0],
538
+ [0, 3],
539
+ [1, 3],
540
+ ],
541
+ [
542
+ [2, 2],
543
+ [3, 2],
544
+ [2, 3],
545
+ [3, 3],
546
+ ],
547
+ [
548
+ [0, 2],
549
+ [3, 2],
550
+ [0, 3],
551
+ [3, 3],
552
+ ],
553
+ [
554
+ [2, 0],
555
+ [3, 0],
556
+ [2, 3],
557
+ [3, 3],
558
+ ],
559
+ [
560
+ [0, 0],
561
+ [3, 0],
562
+ [0, 3],
563
+ [3, 3],
564
+ ],
565
+ ];
566
+ /**
567
+ * The standard XP autotile pattern table (48 patterns). An XP autotile image
568
+ * is a 96 by 128 template: a 6-column grid of 16px mini-blocks, and each
569
+ * baked pattern assembles four of them. `tile % 48` selects the pattern;
570
+ * `tile / 48` (floored) selects which of the map's autotile images.
571
+ *
572
+ * Miniblock entries are 0-based here (block `n` sits at `(n % 6) * 16`,
573
+ * `(n / 6) * 16`). Learned from real XP autotile images and map data rather
574
+ * than assumed: the 6 by 8 mini-grid fits the measured 96 by 128 template
575
+ * exactly, and the neighbor table below reproduces baked map patterns.
576
+ *
577
+ * @example
578
+ * ```ts
579
+ * import { XP_AUTOTILE_PATTERNS, XP_NEIGHBORS_TO_PATTERN } from '@datamoc/mw_games/two-d/render';
580
+ *
581
+ * console.log(XP_AUTOTILE_PATTERNS.length); // 48 - one entry per baked pattern
582
+ * console.log(XP_AUTOTILE_PATTERNS[0]); // [26,27,32,33] - the filled interior tile
583
+ * console.log(XP_NEIGHBORS_TO_PATTERN.length); // 256 - one entry per 8-neighbour mask
584
+ * console.log(XP_NEIGHBORS_TO_PATTERN[255]); // 0 - surrounded on all sides is the interior
585
+ * ```
586
+ */
587
+ export const XP_AUTOTILE_PATTERNS = [
588
+ [26, 27, 32, 33],
589
+ [4, 27, 32, 33],
590
+ [26, 5, 32, 33],
591
+ [4, 5, 32, 33],
592
+ [26, 27, 32, 11],
593
+ [4, 27, 32, 11],
594
+ [26, 5, 32, 11],
595
+ [4, 5, 32, 11],
596
+ [26, 27, 10, 33],
597
+ [4, 27, 10, 33],
598
+ [26, 5, 10, 33],
599
+ [4, 5, 10, 33],
600
+ [26, 27, 10, 11],
601
+ [4, 27, 10, 11],
602
+ [26, 5, 10, 11],
603
+ [4, 5, 10, 11],
604
+ [24, 25, 30, 31],
605
+ [24, 5, 30, 31],
606
+ [24, 25, 30, 11],
607
+ [24, 5, 30, 11],
608
+ [14, 15, 20, 21],
609
+ [14, 15, 20, 11],
610
+ [14, 15, 10, 21],
611
+ [14, 15, 10, 11],
612
+ [28, 29, 34, 35],
613
+ [28, 29, 10, 35],
614
+ [4, 29, 34, 35],
615
+ [4, 29, 10, 35],
616
+ [38, 39, 44, 45],
617
+ [4, 39, 44, 45],
618
+ [38, 5, 44, 45],
619
+ [4, 5, 44, 45],
620
+ [24, 29, 30, 35],
621
+ [14, 15, 44, 45],
622
+ [12, 13, 18, 19],
623
+ [12, 13, 18, 11],
624
+ [16, 17, 22, 23],
625
+ [16, 17, 10, 23],
626
+ [40, 41, 46, 47],
627
+ [4, 41, 46, 47],
628
+ [36, 37, 42, 43],
629
+ [36, 5, 42, 43],
630
+ [12, 17, 18, 23],
631
+ [12, 13, 42, 43],
632
+ [36, 41, 42, 47],
633
+ [16, 17, 46, 47],
634
+ [12, 17, 42, 47],
635
+ [0, 1, 6, 7],
636
+ ];
637
+ /**
638
+ * Which XP pattern an 8-neighbourhood of same-autotile cells bakes to. Index
639
+ * with a bit mask - north `0x01`, northeast `0x02`, east `0x04`, southeast
640
+ * `0x08`, south `0x10`, southwest `0x20`, west `0x40`, northwest `0x80` - for
641
+ * games computing patterns procedurally instead of reading baked map data.
642
+ */
643
+ export const XP_NEIGHBORS_TO_PATTERN = [
644
+ 46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40, 42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34,
645
+ 17, 34, 16, 46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40, 42, 32, 42, 32, 35, 19, 35, 18, 42, 32,
646
+ 42, 32, 34, 17, 34, 16, 45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29, 37, 27, 37, 27, 23, 15, 23,
647
+ 13, 37, 27, 37, 27, 22, 11, 22, 9, 45, 39, 45, 39, 33, 31, 33, 29, 45, 39, 45, 39, 33, 31, 33, 29, 36, 26, 36, 26,
648
+ 21, 7, 21, 5, 36, 26, 36, 26, 20, 3, 20, 1, 46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43, 40, 42, 32,
649
+ 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16, 46, 44, 46, 44, 43, 41, 43, 40, 46, 44, 46, 44, 43, 41, 43,
650
+ 40, 42, 32, 42, 32, 35, 19, 35, 18, 42, 32, 42, 32, 34, 17, 34, 16, 45, 38, 45, 38, 33, 30, 33, 28, 45, 38, 45, 38,
651
+ 33, 30, 33, 28, 37, 25, 37, 25, 23, 14, 23, 12, 37, 25, 37, 25, 22, 10, 22, 8, 45, 38, 45, 38, 33, 30, 33, 28, 45,
652
+ 38, 45, 38, 33, 30, 33, 28, 36, 24, 36, 24, 21, 6, 21, 4, 36, 24, 36, 24, 20, 2, 20, 0,
653
+ ];
654
+ /**
655
+ * Decodes a raw XP map cell into its autotile image and pattern, or null
656
+ * when the cell holds no autotile reference: `EMPTY`, a static tileset tile
657
+ * (384 and up), or anything outside the 1-383 autotile range.
658
+ *
659
+ * @example
660
+ * ```ts
661
+ * import { xpAutotileRef } from '@datamoc/mw_games/two-d/render';
662
+ *
663
+ * console.log(xpAutotileRef(97)); // { index: 2, pattern: 1 } - second image, second pattern
664
+ * console.log(xpAutotileRef(0)); // null - EMPTY is blank, never an autotile
665
+ * console.log(xpAutotileRef(384)); // null - the first static tileset tile
666
+ * ```
667
+ */
668
+ export function xpAutotileRef(tile) {
669
+ if (!Number.isInteger(tile) || tile <= 0 || tile >= 384)
670
+ return null;
671
+ return { index: Math.floor(tile / 48), pattern: tile % 48 };
672
+ }
673
+ /**
674
+ * Which XP pattern a cell's 8-neighbourhood bakes to, for maps computed
675
+ * procedurally rather than read from baked data. `sameTerrain` is asked once
676
+ * per direction, with `(dx, dy)` offsets (`(0, -1)` is north); off the map is
677
+ * the caller's to answer (false reads as a border, the edge cell's own value
678
+ * clamps like the editor).
679
+ *
680
+ * @example
681
+ * ```ts
682
+ * import { xpAutotilePattern } from '@datamoc/mw_games/two-d/render';
683
+ *
684
+ * const filled = new Set(['0,0', '1,0', '0,1', '1,1', '-1,0', '0,-1', '2,1', '1,2']);
685
+ * console.log(xpAutotilePattern((dx, dy) => filled.has(`${dx},${dy}`))); // 0 - the interior
686
+ * console.log(xpAutotilePattern(() => false)); // 46 - isolated on all sides
687
+ * ```
688
+ */
689
+ export function xpAutotilePattern(sameTerrain) {
690
+ let mask = 0;
691
+ if (sameTerrain(0, -1))
692
+ mask |= 0x01;
693
+ if (sameTerrain(1, -1))
694
+ mask |= 0x02;
695
+ if (sameTerrain(1, 0))
696
+ mask |= 0x04;
697
+ if (sameTerrain(1, 1))
698
+ mask |= 0x08;
699
+ if (sameTerrain(0, 1))
700
+ mask |= 0x10;
701
+ if (sameTerrain(-1, 1))
702
+ mask |= 0x20;
703
+ if (sameTerrain(-1, 0))
704
+ mask |= 0x40;
705
+ if (sameTerrain(-1, -1))
706
+ mask |= 0x80;
707
+ return XP_NEIGHBORS_TO_PATTERN[mask];
708
+ }
709
+ /** the atlas kinds each MV family holds, for validating animation cycles */
710
+ const RPGM_AUTOTILE_KIND_COUNTS = [16, 32, 32, 48];
711
+ /**
712
+ * Checks a resolved autotile layout once, up front. Throws naming the first
713
+ * problem: a bad slot, an empty shape table, an empty animation cycle, a
714
+ * cycle kind outside its slot's kinds, a bad XP index, or a frame count
715
+ * below one.
716
+ *
717
+ * @example
718
+ * ```ts
719
+ * import {
720
+ * assertAutotileLayout,
721
+ * RPGM_FLOOR_AUTOTILE_TABLE,
722
+ * } from '@datamoc/mw_games/two-d/render';
723
+ *
724
+ * assertAutotileLayout({ format: 'rpgm-mv', slot: 0, table: RPGM_FLOOR_AUTOTILE_TABLE, cycles: [[0, 1, 2]] });
725
+ * assertAutotileLayout({ format: 'rpgm-xp', index: 2, frames: 1, single: false });
726
+ * ```
727
+ */
728
+ export function assertAutotileLayout(layout) {
729
+ if (layout.format === 'rpgm-mv') {
730
+ assertSlot(layout.slot);
731
+ validateShapeTable(layout.table);
732
+ const kinds = RPGM_AUTOTILE_KIND_COUNTS[layout.slot];
733
+ const firstKind = SLOT_BASE_KINDS[layout.slot];
734
+ for (const cycle of layout.cycles) {
735
+ if (cycle.length === 0)
736
+ throw new Error('an autotile animation cycle needs at least one kind');
737
+ for (const kind of cycle) {
738
+ if (!Number.isInteger(kind) || kind < firstKind || kind >= firstKind + kinds) {
739
+ throw new Error(`animation cycle kind ${kind} is outside slot ${layout.slot}'s kinds ${firstKind}-${firstKind + kinds - 1}`);
740
+ }
741
+ }
742
+ }
743
+ return;
744
+ }
745
+ if (!Number.isInteger(layout.index) || layout.index < 0 || layout.index > 7) {
746
+ throw new Error(`an XP autotile index must be 0-7, got ${layout.index}`);
747
+ }
748
+ if (!Number.isInteger(layout.frames) || layout.frames < 1) {
749
+ throw new Error(`an XP autotile frame count must be at least 1, got ${layout.frames}`);
750
+ }
751
+ }
752
+ /** source pixels of one MV quadrant piece: the format's fixed 24px halves */
753
+ const RPGM_QUADRANT_SIZE = 24;
754
+ /** source pixels of one XP quadrant piece: the format's fixed 16px halves */
755
+ const XP_QUADRANT_SIZE = 16;
756
+ /** columns of 16px mini-blocks in an XP template image */
757
+ const XP_TEMPLATE_COLUMNS = 6;
758
+ /** width of one XP animation stripe, in source pixels */
759
+ const XP_FRAME_WIDTH = 96;
760
+ /** size of one XP single-tile frame, in source pixels */
761
+ const XP_SINGLE_TILE_SIZE = 32;
762
+ /**
763
+ * Resolves one autotile cell to its drawn pieces: four quadrant halves for a
764
+ * template cell, one whole-tile piece for an XP single-tile strip, or null
765
+ * for a blank cell (`tile <= 0` reads as blank; `EMPTY` is -1).
766
+ *
767
+ * The layout is assumed valid (`assertAutotileLayout`); only the cell and
768
+ * frame are checked here. `tile` must be an integer the layout claims - an
769
+ * MV id in the layout's slot range, an XP id in `index * 48` to
770
+ * `index * 48 + 47` - and `frame` an integer at or above zero, wrapping
771
+ * around the animation cycles (MV) or frame stripes (XP).
772
+ *
773
+ * @example
774
+ * ```ts
775
+ * import {
776
+ * autotileCellParts,
777
+ * RPGM_FLOOR_AUTOTILE_TABLE,
778
+ * } from '@datamoc/mw_games/two-d/render';
779
+ *
780
+ * const parts = autotileCellParts(
781
+ * { format: 'rpgm-mv', slot: 0, table: RPGM_FLOOR_AUTOTILE_TABLE, cycles: [] },
782
+ * 2048, 0,
783
+ * );
784
+ * console.log(parts?.length); // 4 - one piece per quadrant
785
+ * console.log(parts?.[0].sourceWidth); // 24 - MV quadrant halves
786
+ * console.log(autotileCellParts(
787
+ * { format: 'rpgm-xp', index: 2, frames: 1, single: false },
788
+ * 97, 0,
789
+ * )?.[0].sourceWidth); // 16 - XP quadrant halves
790
+ * ```
791
+ */
792
+ export function autotileCellParts(layout, tile, frame) {
793
+ if (tile <= 0)
794
+ return null;
795
+ if (!Number.isInteger(frame) || frame < 0) {
796
+ throw new Error(`an autotile animation frame must be an integer at or above zero, got ${frame}`);
797
+ }
798
+ if (layout.format === 'rpgm-mv')
799
+ return rpgmCellParts(layout, tile, frame);
800
+ return xpCellParts(layout, tile, frame);
801
+ }
802
+ function rpgmCellParts(layout, tile, frame) {
803
+ const base = RPGM_AUTOTILE_SLOT_BASES[layout.slot];
804
+ const count = RPGM_AUTOTILE_SLOT_COUNTS[layout.slot];
805
+ if (!Number.isInteger(tile) || tile < base || tile >= base + count) {
806
+ throw new Error(`tile ${tile} is outside slot ${layout.slot}'s ids ${base}-${base + count - 1}`);
807
+ }
808
+ const offset = tile - base;
809
+ //shorter tables cycle the same way the player's own indexing does
810
+ //(`table[shape % table.length]` over `shape = offset % 48`)
811
+ const shape = (offset % 48) % layout.table.length;
812
+ let kind = SLOT_BASE_KINDS[layout.slot] + Math.floor(offset / 48);
813
+ for (const cycle of layout.cycles) {
814
+ const at = cycle.indexOf(kind);
815
+ if (at >= 0) {
816
+ kind = cycle[(at + frame) % cycle.length];
817
+ break;
818
+ }
819
+ }
820
+ //rpgmAutotileFrame's tileId is the base-relative offset, so a remapped
821
+ //kind keeps the cell's shape and only swaps the kind block
822
+ const described = rpgmAutotileFrame((kind - SLOT_BASE_KINDS[layout.slot]) * 48 + (offset % 48), layout.slot, shape, layout.table);
823
+ return described.quadrants.map((quadrant, index) => ({
824
+ sourceX: quadrant.sourceX,
825
+ sourceY: quadrant.sourceY,
826
+ sourceWidth: RPGM_QUADRANT_SIZE,
827
+ sourceHeight: RPGM_QUADRANT_SIZE,
828
+ destX: (index % 2) / 2,
829
+ destY: Math.floor(index / 2) / 2,
830
+ destWidth: 1 / 2,
831
+ destHeight: 1 / 2,
832
+ }));
833
+ }
834
+ function xpCellParts(layout, tile, frame) {
835
+ if (!Number.isInteger(tile) || tile < layout.index * 48 || tile >= layout.index * 48 + 48 || tile <= 0) {
836
+ throw new Error(`tile ${tile} is outside XP autotile ${layout.index}'s ids ${layout.index * 48}-${layout.index * 48 + 47}`);
837
+ }
838
+ if (layout.single) {
839
+ const stripe = (frame % layout.frames) * XP_SINGLE_TILE_SIZE;
840
+ return [
841
+ {
842
+ sourceX: stripe,
843
+ sourceY: 0,
844
+ sourceWidth: XP_SINGLE_TILE_SIZE,
845
+ sourceHeight: XP_SINGLE_TILE_SIZE,
846
+ destX: 0,
847
+ destY: 0,
848
+ destWidth: 1,
849
+ destHeight: 1,
850
+ },
851
+ ];
852
+ }
853
+ const pattern = XP_AUTOTILE_PATTERNS[tile % 48];
854
+ const stripe = (frame % layout.frames) * XP_FRAME_WIDTH;
855
+ return pattern.map((block, index) => ({
856
+ sourceX: (block % XP_TEMPLATE_COLUMNS) * XP_QUADRANT_SIZE + stripe,
857
+ sourceY: Math.floor(block / XP_TEMPLATE_COLUMNS) * XP_QUADRANT_SIZE,
858
+ sourceWidth: XP_QUADRANT_SIZE,
859
+ sourceHeight: XP_QUADRANT_SIZE,
860
+ destX: (index % 2) / 2,
861
+ destY: Math.floor(index / 2) / 2,
862
+ destWidth: 1 / 2,
863
+ destHeight: 1 / 2,
864
+ }));
865
+ }
110
866
  //# sourceMappingURL=RpgmAutotile.js.map