@datamoc/mw_games 0.7.0 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/CHANGELOG.md +964 -0
  2. package/README.md +43 -3
  3. package/dist/actors/Affix.d.ts +7 -1
  4. package/dist/actors/Affix.d.ts.map +1 -1
  5. package/dist/actors/Affix.js +4 -2
  6. package/dist/actors/Affix.js.map +1 -1
  7. package/dist/actors/StatBlock.d.ts +20 -0
  8. package/dist/actors/StatBlock.d.ts.map +1 -1
  9. package/dist/actors/StatBlock.js +14 -3
  10. package/dist/actors/StatBlock.js.map +1 -1
  11. package/dist/actors/index.d.ts +2 -2
  12. package/dist/actors/index.d.ts.map +1 -1
  13. package/dist/actors/index.js +1 -1
  14. package/dist/actors/index.js.map +1 -1
  15. package/dist/ai/index.d.ts +118 -0
  16. package/dist/ai/index.d.ts.map +1 -0
  17. package/dist/ai/index.js +203 -0
  18. package/dist/ai/index.js.map +1 -0
  19. package/dist/ai/lua.d.ts +47 -0
  20. package/dist/ai/lua.d.ts.map +1 -0
  21. package/dist/ai/lua.js +195 -0
  22. package/dist/ai/lua.js.map +1 -0
  23. package/dist/ai/search.d.ts +59 -0
  24. package/dist/ai/search.d.ts.map +1 -0
  25. package/dist/ai/search.js +100 -0
  26. package/dist/ai/search.js.map +1 -0
  27. package/dist/board/Engine.d.ts +14 -0
  28. package/dist/board/Engine.d.ts.map +1 -1
  29. package/dist/board/Engine.js +33 -45
  30. package/dist/board/Engine.js.map +1 -1
  31. package/dist/board/index.d.ts +1 -1
  32. package/dist/board/index.d.ts.map +1 -1
  33. package/dist/board/index.js +1 -1
  34. package/dist/board/index.js.map +1 -1
  35. package/dist/core/Csv.d.ts +10 -0
  36. package/dist/core/Csv.d.ts.map +1 -1
  37. package/dist/core/Csv.js +34 -3
  38. package/dist/core/Csv.js.map +1 -1
  39. package/dist/core/Entity.d.ts +2 -2
  40. package/dist/core/Entity.d.ts.map +1 -1
  41. package/dist/core/Entity.js +18 -4
  42. package/dist/core/Entity.js.map +1 -1
  43. package/dist/index.d.ts +1 -0
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +1 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/mw_games.global.js +97 -97
  48. package/dist/mwl/actors.d.ts +3 -1
  49. package/dist/mwl/actors.d.ts.map +1 -1
  50. package/dist/mwl/actors.js +15 -2
  51. package/dist/mwl/actors.js.map +1 -1
  52. package/dist/mwl/catalog.d.ts.map +1 -1
  53. package/dist/mwl/catalog.js +16 -9
  54. package/dist/mwl/catalog.js.map +1 -1
  55. package/dist/mwl/compiler.d.ts +20 -0
  56. package/dist/mwl/compiler.d.ts.map +1 -1
  57. package/dist/mwl/compiler.js +76 -3
  58. package/dist/mwl/compiler.js.map +1 -1
  59. package/dist/mwl/conditions.d.ts +9 -0
  60. package/dist/mwl/conditions.d.ts.map +1 -0
  61. package/dist/mwl/conditions.js +162 -0
  62. package/dist/mwl/conditions.js.map +1 -0
  63. package/dist/mwl/content.d.ts +61 -0
  64. package/dist/mwl/content.d.ts.map +1 -1
  65. package/dist/mwl/content.js +95 -34
  66. package/dist/mwl/content.js.map +1 -1
  67. package/dist/mwl/expression.d.ts.map +1 -1
  68. package/dist/mwl/expression.js +16 -6
  69. package/dist/mwl/expression.js.map +1 -1
  70. package/dist/mwl/fengari.d.ts +8 -0
  71. package/dist/mwl/fengari.d.ts.map +1 -0
  72. package/dist/mwl/fengari.js +136 -0
  73. package/dist/mwl/fengari.js.map +1 -0
  74. package/dist/mwl/hooks.d.ts +1 -1
  75. package/dist/mwl/hooks.d.ts.map +1 -1
  76. package/dist/mwl/hooks.js.map +1 -1
  77. package/dist/mwl/index.d.ts +16 -7
  78. package/dist/mwl/index.d.ts.map +1 -1
  79. package/dist/mwl/index.js +7 -3
  80. package/dist/mwl/index.js.map +1 -1
  81. package/dist/mwl/persistence.d.ts.map +1 -1
  82. package/dist/mwl/persistence.js +10 -2
  83. package/dist/mwl/persistence.js.map +1 -1
  84. package/dist/mwl/readers.d.ts +18 -0
  85. package/dist/mwl/readers.d.ts.map +1 -0
  86. package/dist/mwl/readers.js +58 -0
  87. package/dist/mwl/readers.js.map +1 -0
  88. package/dist/mwl/report.d.ts +9 -0
  89. package/dist/mwl/report.d.ts.map +1 -0
  90. package/dist/mwl/report.js +36 -0
  91. package/dist/mwl/report.js.map +1 -0
  92. package/dist/mwl/runtime.d.ts +65 -2
  93. package/dist/mwl/runtime.d.ts.map +1 -1
  94. package/dist/mwl/runtime.js +245 -26
  95. package/dist/mwl/runtime.js.map +1 -1
  96. package/dist/mwl/schema.d.ts +13 -1
  97. package/dist/mwl/schema.d.ts.map +1 -1
  98. package/dist/mwl/schema.js +222 -7
  99. package/dist/mwl/schema.js.map +1 -1
  100. package/dist/mwl/scripts.d.ts +15 -0
  101. package/dist/mwl/scripts.d.ts.map +1 -0
  102. package/dist/mwl/scripts.js +23 -0
  103. package/dist/mwl/scripts.js.map +1 -0
  104. package/dist/mwl/utils.d.ts +8 -0
  105. package/dist/mwl/utils.d.ts.map +1 -0
  106. package/dist/mwl/utils.js +37 -0
  107. package/dist/mwl/utils.js.map +1 -0
  108. package/dist/two-d/render/Types2D.d.ts +19 -4
  109. package/dist/two-d/render/Types2D.d.ts.map +1 -1
  110. package/dist/two-d/render/Types2D.js +25 -0
  111. package/dist/two-d/render/Types2D.js.map +1 -1
  112. package/dist/two-d/render/index.d.ts +2 -2
  113. package/dist/two-d/render/index.d.ts.map +1 -1
  114. package/dist/two-d/render/index.js +1 -1
  115. package/dist/two-d/render/index.js.map +1 -1
  116. package/dist/version.d.ts +1 -1
  117. package/dist/version.js +1 -1
  118. package/package.json +27 -6
  119. package/tools/extract-html.d.mts +18 -0
  120. package/tools/extract-html.mjs +235 -0
  121. package/tools/mwl.mjs +86 -16
package/tools/mwl.mjs CHANGED
@@ -6,6 +6,9 @@ import { pathToFileURL } from 'node:url';
6
6
  import {
7
7
  collectHookReferences,
8
8
  compile,
9
+ compileAndEmitSources,
10
+ compileSources,
11
+ contentReport,
9
12
  emitHooksDeclaration,
10
13
  emitModule,
11
14
  extractCatalog,
@@ -25,36 +28,53 @@ import {
25
28
  */
26
29
 
27
30
  const args = process.argv.slice(2);
28
- const commands = ['validate', 'compile', 'extract-i18n', 'assets', 'hooks', 'build'];
31
+ const commands = ['validate', 'compile', 'extract-i18n', 'assets', 'report', 'hooks', 'build'];
29
32
  const [command, input] = args;
30
33
  const outputFlag = args.indexOf('-o');
31
34
  const output = outputFlag === -1 ? undefined : args[outputFlag + 1];
32
35
  const manifestFlag = args.indexOf('--manifest');
33
36
  const manifestPath = manifestFlag === -1 ? undefined : args[manifestFlag + 1];
37
+ const assetRootFlag = args.indexOf('--asset-root');
38
+ const assetRoot = assetRootFlag === -1 ? undefined : args[assetRootFlag + 1];
34
39
 
35
40
  if (!command || !input || !commands.includes(command)) {
36
- console.error('Usage: mwl <validate|compile|extract-i18n|assets|hooks|build> input.mwl [-o output] [--manifest hooks.json]');
41
+ console.error(
42
+ 'Usage: mwl <validate|compile|extract-i18n|assets|report|hooks|build> input.mwl [-o output] [--manifest hooks.json] [--asset-root directory]',
43
+ );
37
44
  process.exitCode = 2;
38
45
  } else {
39
- const source = fs.readFileSync(input, 'utf8');
46
+ const files = fs.statSync(input).isDirectory()
47
+ ? collectSources(input)
48
+ : [{ file: sourceLabel(input), source: fs.readFileSync(input, 'utf8') }];
40
49
  if (command === 'validate') {
41
- const diagnostics = validate(parse(preprocess(source, { file: input }), input));
50
+ const diagnostics = files.flatMap((file) =>
51
+ validate(parse(preprocess(file.source, { file: file.file }), file.file)),
52
+ );
42
53
  if (diagnostics.length) {
43
54
  for (const diagnostic of diagnostics)
44
55
  console.error(
45
56
  `${diagnostic.location.file}:${diagnostic.location.line}:${diagnostic.location.column}: ${diagnostic.message}`,
46
57
  );
47
58
  process.exitCode = 1;
48
- } else console.log(`${input}: valid MWL`);
59
+ } else {
60
+ try {
61
+ compileSources(files);
62
+ console.log(`${input}: valid MWL`);
63
+ } catch (error) {
64
+ console.error(error instanceof Error ? error.message : String(error));
65
+ process.exitCode = 1;
66
+ }
67
+ }
49
68
  } else if (command === 'hooks') {
50
- await hooks(source);
69
+ await hooks(files);
51
70
  } else if (command === 'build') {
52
- build(source);
71
+ build(files);
53
72
  } else {
54
- const result = compile(source, { file: input });
73
+ const result = compileSources(files);
55
74
  const values =
56
75
  command === 'extract-i18n' ? extractCatalog(result) : command === 'assets' ? result.assets : result;
57
- const text = command === 'compile' ? emitModule(result) : `${JSON.stringify(values, null, '\t')}\n`;
76
+ const report = command === 'report' ? contentReport(result) : values;
77
+ const text = command === 'compile' ? emitModule(result) : `${JSON.stringify(report, null, '\t')}\n`;
58
78
  if (output) {
59
79
  fs.mkdirSync(path.dirname(path.resolve(output)), { recursive: true });
60
80
  fs.writeFileSync(output, text);
@@ -62,25 +82,75 @@ if (!command || !input || !commands.includes(command)) {
62
82
  }
63
83
  }
64
84
 
85
+ function collectSources(directory) {
86
+ const files = [];
87
+ const root = path.resolve(directory);
88
+ const label = sourceLabel(root);
89
+ const visit = (current) => {
90
+ for (const entry of fs
91
+ .readdirSync(current, { withFileTypes: true })
92
+ .sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0))) {
93
+ const full = path.join(current, entry.name);
94
+ if (entry.isDirectory()) visit(full);
95
+ else if (/\.mwl$/i.test(entry.name)) {
96
+ const relative = path.relative(root, full);
97
+ files.push({
98
+ file: path.join(label, relative).split(path.sep).join('/'),
99
+ source: fs.readFileSync(full, 'utf8'),
100
+ });
101
+ }
102
+ }
103
+ };
104
+ visit(root);
105
+ if (!files.length) throw new Error(`no .mwl files found in ${directory}`);
106
+ return files;
107
+ }
108
+
109
+ function sourceLabel(file) {
110
+ const relative = path.relative(process.cwd(), path.resolve(file));
111
+ return (relative || path.basename(path.resolve(file))).split(path.sep).join('/');
112
+ }
113
+
65
114
  /** One deterministic build step for the three files every MWL game consumes. */
66
- function build(source) {
67
- const game = compile(source, { file: input });
115
+ function build(files) {
116
+ const game = compileSources(files);
117
+ if (assetRoot) validateAssets(game, path.resolve(assetRoot));
68
118
  const directory = path.resolve(output ?? 'generated');
69
119
  fs.mkdirSync(directory, { recursive: true });
70
- fs.writeFileSync(path.join(directory, 'game-data.ts'), emitModule(game));
71
- fs.writeFileSync(path.join(directory, 'i18n.json'), `${JSON.stringify(extractCatalog(game), null, '\t')}\n`);
72
- fs.writeFileSync(path.join(directory, 'assets.json'), `${JSON.stringify({ assets: game.assets }, null, '\t')}\n`);
120
+ const artifacts = compileAndEmitSources(files);
121
+ for (const artifact of artifacts) fs.writeFileSync(path.join(directory, artifact.name), artifact.content);
73
122
  console.log(`built MWL into ${directory}`);
74
123
  }
75
124
 
125
+ function validateAssets(game, root) {
126
+ const missing = game.assets.filter(
127
+ (asset) => !asset.startsWith('data:') && !fs.existsSync(path.resolve(root, asset)),
128
+ );
129
+ if (!missing.length) return;
130
+ const locations = new Map();
131
+ const visit = (node) => {
132
+ for (const value of Object.values(node.attributes))
133
+ for (const asset of missing)
134
+ if (value.includes(asset) && !locations.has(asset)) locations.set(asset, node.location);
135
+ node.children.forEach(visit);
136
+ };
137
+ game.roots.forEach(visit);
138
+ for (const asset of missing) {
139
+ const location = locations.get(asset);
140
+ const prefix = location ? `${location.file}:${location.line}:${location.column}: ` : '';
141
+ console.error(`${prefix}missing MWL asset "${asset}" under ${root}`);
142
+ }
143
+ throw new Error(`${missing.length} MWL asset(s) not found`);
144
+ }
145
+
76
146
  /**
77
147
  * `mwl hooks game.mwl` prints the hook references content needs.
78
148
  * `mwl hooks game.mwl --manifest hooks.json -o generated/hooks.mjs` resolves
79
149
  * each reference against the manifest, bundles the hook sources with esbuild,
80
150
  * and writes the matching declaration next to the bundle.
81
151
  */
82
- async function hooks(source) {
83
- const game = compile(source, { file: input });
152
+ async function hooks(files) {
153
+ const game = compileSources(files);
84
154
  const references = collectHookReferences(game);
85
155
  if (!output) {
86
156
  process.stdout.write(