intelligent-system-design-language 0.3.22 → 0.3.23

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 (91) hide show
  1. package/.claude/agents/langium-language-designer.md +38 -38
  2. package/.claude/agents/typescript-vscode-expert.md +29 -29
  3. package/.claude/agents/ui-ux-designer.md +36 -36
  4. package/.claude/settings.local.json +33 -33
  5. package/.idea/inspectionProfiles/Project_Default.xml +6 -6
  6. package/.idea/isdl.iml +13 -13
  7. package/.idea/modules.xml +8 -8
  8. package/.idea/vcs.xml +6 -6
  9. package/.idea/watcherTasks.xml +3 -3
  10. package/.vscodeignore +18 -18
  11. package/LICENSE +673 -673
  12. package/README.md +86 -86
  13. package/bin/cli.js +4 -4
  14. package/bin/lsp.js +8 -8
  15. package/out/_backgrounds.scss +91 -91
  16. package/out/_handlebars.scss +497 -497
  17. package/out/_isdlStyles.scss +1444 -1444
  18. package/out/_vuetifyOverrides.scss +425 -425
  19. package/out/_vuetifyStyles.scss +31957 -31957
  20. package/out/cli/components/_backgrounds.scss +91 -91
  21. package/out/cli/components/_handlebars.scss +497 -497
  22. package/out/cli/components/_isdlStyles.scss +1444 -1444
  23. package/out/cli/components/_vuetifyOverrides.scss +425 -425
  24. package/out/cli/components/_vuetifyStyles.scss +31957 -31957
  25. package/out/cli/components/active-effect-sheet-generator.js +453 -453
  26. package/out/cli/components/chat-card-generator.js +654 -654
  27. package/out/cli/components/css-generator.js +4 -4
  28. package/out/cli/components/damage-roll-generator.js +160 -160
  29. package/out/cli/components/datamodel-generator.js +257 -257
  30. package/out/cli/components/derived-data-generator.js +923 -923
  31. package/out/cli/components/hotbar-drop-hook-generator.js +82 -82
  32. package/out/cli/components/init-hook-generator.js +495 -495
  33. package/out/cli/components/measured-template-preview.js +221 -221
  34. package/out/cli/components/method-generator.js +548 -548
  35. package/out/cli/components/ready-hook-generator.js +404 -404
  36. package/out/cli/components/token-generator.js +116 -116
  37. package/out/cli/components/vue/base-components/vue-attribute.js +138 -138
  38. package/out/cli/components/vue/base-components/vue-boolean.js +64 -64
  39. package/out/cli/components/vue/base-components/vue-calculator.js +93 -93
  40. package/out/cli/components/vue/base-components/vue-damage-application.js +356 -356
  41. package/out/cli/components/vue/base-components/vue-damage-bonuses.js +165 -165
  42. package/out/cli/components/vue/base-components/vue-damage-resistances.js +196 -196
  43. package/out/cli/components/vue/base-components/vue-damage-track.js +121 -121
  44. package/out/cli/components/vue/base-components/vue-date-time.js +42 -42
  45. package/out/cli/components/vue/base-components/vue-dice.js +98 -98
  46. package/out/cli/components/vue/base-components/vue-die.js +73 -73
  47. package/out/cli/components/vue/base-components/vue-document-choice.js +149 -149
  48. package/out/cli/components/vue/base-components/vue-document-choices.js +179 -179
  49. package/out/cli/components/vue/base-components/vue-document-link.js +60 -60
  50. package/out/cli/components/vue/base-components/vue-extended-choice.js +88 -88
  51. package/out/cli/components/vue/base-components/vue-inventory.js +519 -519
  52. package/out/cli/components/vue/base-components/vue-macro-choice.js +138 -138
  53. package/out/cli/components/vue/base-components/vue-measured-template.js +530 -530
  54. package/out/cli/components/vue/base-components/vue-money.js +483 -483
  55. package/out/cli/components/vue/base-components/vue-number.js +174 -174
  56. package/out/cli/components/vue/base-components/vue-paperdoll.js +43 -43
  57. package/out/cli/components/vue/base-components/vue-parent-property-reference.js +76 -76
  58. package/out/cli/components/vue/base-components/vue-prosemirror.js +18 -18
  59. package/out/cli/components/vue/base-components/vue-resource.js +136 -136
  60. package/out/cli/components/vue/base-components/vue-roll-visualizer.js +285 -285
  61. package/out/cli/components/vue/base-components/vue-self-property-reference.js +62 -62
  62. package/out/cli/components/vue/base-components/vue-string-choice.js +98 -98
  63. package/out/cli/components/vue/base-components/vue-string-choices.js +203 -203
  64. package/out/cli/components/vue/base-components/vue-string.js +60 -60
  65. package/out/cli/components/vue/base-components/vue-text-field.js +53 -53
  66. package/out/cli/components/vue/base-components/vue-tracker.js +431 -431
  67. package/out/cli/components/vue/vue-action-component-generator.js +64 -64
  68. package/out/cli/components/vue/vue-active-effect-sheet-generator.js +856 -856
  69. package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +292 -292
  70. package/out/cli/components/vue/vue-datatable2-component-generator.js +824 -824
  71. package/out/cli/components/vue/vue-document-creation-app.js +121 -121
  72. package/out/cli/components/vue/vue-document-creation-sheet.js +94 -94
  73. package/out/cli/components/vue/vue-generator.js +40 -40
  74. package/out/cli/components/vue/vue-mixin.js +296 -296
  75. package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +260 -260
  76. package/out/cli/components/vue/vue-prompt-generator.js +80 -80
  77. package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +317 -317
  78. package/out/cli/components/vue/vue-sheet-application-generator.js +1171 -1171
  79. package/out/cli/components/vue/vue-sheet-class-generator.js +510 -510
  80. package/out/cli/generator.js +438 -438
  81. package/out/extension/github/githubManager.js +35 -35
  82. package/out/extension/github/githubQuickActions.js +120 -120
  83. package/out/extension/github/system-workflow.yml +47 -47
  84. package/out/extension/main.cjs.map +1 -1
  85. package/out/extension/package.json +419 -419
  86. package/out/language/generated/grammar.js +14240 -14240
  87. package/out/language/main.cjs.map +1 -1
  88. package/out/package.json +419 -419
  89. package/out/progressbar.min.js +6 -6
  90. package/out/styles.scss +762 -762
  91. package/package.json +419 -419
@@ -13,11 +13,11 @@ export function generateInitHookMjs(entry, id, destination) {
13
13
  }
14
14
  function generateTrackableResourceBars(document) {
15
15
  let resourceExps = getAllOfType(document.body, isResourceExp);
16
- return expandToNode `
17
- "${document.name.toLowerCase()}": {
18
- "bar": [${joinToNode(resourceExps, x => `"${x.name.toLowerCase()}"`, { separator: ',' })}],
19
- "value": []
20
- }
16
+ return expandToNode `
17
+ "${document.name.toLowerCase()}": {
18
+ "bar": [${joinToNode(resourceExps, x => `"${x.name.toLowerCase()}"`, { separator: ',' })}],
19
+ "value": []
20
+ }
21
21
  `;
22
22
  }
23
23
  function generateStatusEffect(document) {
@@ -30,15 +30,15 @@ export function generateInitHookMjs(entry, id, destination) {
30
30
  svg = "icons/svg/upgrade.svg";
31
31
  }
32
32
  let calculated = document.params.find(p => isStatusParamWhen(p)) ? true : false;
33
- return expandToNode `
34
- {
35
- id: "${document.name.toLowerCase()}",
36
- name: "${document.name}",
37
- label: "${document.name}",
38
- icon: "${svg}",
39
- img: "${svg}",
40
- calculated: ${calculated}
41
- }
33
+ return expandToNode `
34
+ {
35
+ id: "${document.name.toLowerCase()}",
36
+ name: "${document.name}",
37
+ label: "${document.name}",
38
+ icon: "${svg}",
39
+ img: "${svg}",
40
+ calculated: ${calculated}
41
+ }
42
42
  `;
43
43
  }
44
44
  function generateKeywordConfig(entry) {
@@ -46,13 +46,13 @@ export function generateInitHookMjs(entry, id, destination) {
46
46
  if (keywords.size === 0) {
47
47
  return expandToNode ``;
48
48
  }
49
- return joinToNode(Array.from(keywords.entries()), ([key, data]) => `${key}: {
50
- name: "${data.name}",
51
- description: "${data.description.replace(/"/g, '\\"')}",
52
- color: "${data.color}",
53
- icon: "${data.icon}",
54
- type: "${data.type}"${data.label ? `,
55
- label: "${data.label}"` : ''}
49
+ return joinToNode(Array.from(keywords.entries()), ([key, data]) => `${key}: {
50
+ name: "${data.name}",
51
+ description: "${data.description.replace(/"/g, '\\"')}",
52
+ color: "${data.color}",
53
+ icon: "${data.icon}",
54
+ type: "${data.type}"${data.label ? `,
55
+ label: "${data.label}"` : ''}
56
56
  }`, { separator: ',\n ' }) || expandToNode ``;
57
57
  }
58
58
  function generateMacroActionConfig(entry) {
@@ -74,24 +74,24 @@ export function generateInitHookMjs(entry, id, destination) {
74
74
  function generateRegisterStatusEffects() {
75
75
  let statusEffects = getAllOfType(entry.documents, isStatusProperty, false);
76
76
  if (statusEffects.length === 0)
77
- return expandToNode `
78
- function registerStatusEffects() {
79
- game.system.statusEffects = [];
80
- }
77
+ return expandToNode `
78
+ function registerStatusEffects() {
79
+ game.system.statusEffects = [];
80
+ }
81
81
  `;
82
82
  let hasDead = statusEffects.find(x => x.name.toLowerCase() === "dead");
83
- return expandToNode `
84
- function registerStatusEffects() {
85
- CONFIG.statusEffects = [
86
- ${hasDead ? '' : `{"id":"dead","name":"EFFECT.StatusDead","img":"icons/svg/skull.svg"},`}
87
- {"id":"unconscious","name":"EFFECT.StatusUnconscious","img":"icons/svg/unconscious.svg"},
88
- {"id":"invisible","name":"EFFECT.StatusInvisible","img":"icons/svg/invisible.svg"},
89
- ${joinToNode(statusEffects, generateStatusEffect, { appendNewLineIfNotEmpty: true, separator: ',' })}
90
- ];
91
-
92
- // Also store for journal generation
93
- game.system.statusEffects = CONFIG.statusEffects;
94
- }
83
+ return expandToNode `
84
+ function registerStatusEffects() {
85
+ CONFIG.statusEffects = [
86
+ ${hasDead ? '' : `{"id":"dead","name":"EFFECT.StatusDead","img":"icons/svg/skull.svg"},`}
87
+ {"id":"unconscious","name":"EFFECT.StatusUnconscious","img":"icons/svg/unconscious.svg"},
88
+ {"id":"invisible","name":"EFFECT.StatusInvisible","img":"icons/svg/invisible.svg"},
89
+ ${joinToNode(statusEffects, generateStatusEffect, { appendNewLineIfNotEmpty: true, separator: ',' })}
90
+ ];
91
+
92
+ // Also store for journal generation
93
+ game.system.statusEffects = CONFIG.statusEffects;
94
+ }
95
95
  `.appendNewLine();
96
96
  }
97
97
  let actorDocs = entry.documents.filter(d => isActor(d)).map(d => d);
@@ -127,474 +127,474 @@ export function generateInitHookMjs(entry, id, destination) {
127
127
  return `${document.name.toLowerCase()}${identity}: ${document.name}${identity}PromptApp`;
128
128
  }), { appendNewLineIfNotEmpty: true, separator: ',' });
129
129
  }
130
- const fileNode = expandToNode `
131
- ${joinToNode(entry.documents, document => `import ${document.name}TypeDataModel from "../datamodels/${isActor(document) ? "actor" : "item"}/${document.name.toLowerCase()}.mjs"`, { appendNewLineIfNotEmpty: true })}
132
- ${joinToNode(entry.documents, document => `import ${document.name}VueSheet from "../sheets/vue/${isActor(document) ? "actor" : "item"}/${document.name.toLowerCase()}-sheet.mjs"`, { appendNewLineIfNotEmpty: true })}
133
- import DataTableApp from "../sheets/vue/datatable-app.mjs";
134
- ${joinToNode(entry.documents, generateDocumentPromptImports, { appendNewLineIfNotEmpty: true })}
135
- import ${entry.config.name}EffectVueSheet from "../sheets/vue/active-effect-sheet.mjs";
136
- import ${entry.config.name}Actor from "../documents/actor.mjs";
137
- import ${entry.config.name}Item from "../documents/item.mjs";
138
- import ${entry.config.name}Combatant from "../documents/combatant.mjs";
139
- import ${entry.config.name}TokenDocument from "../documents/token.mjs";
140
- import ${entry.config.name}Token from "../canvas/token.mjs";
141
- import ${entry.config.name}Roll from "../rolls/roll.mjs";
142
- import ${entry.config.name}DamageRoll from "../rolls/damage-roll.mjs";
143
- import DocumentCreationVueDialog from "../sheets/vue/document-creation-dialog.mjs";
144
- import MeasuredTemplatePreview from "../placeables/measured-template-preview.mjs";
145
-
146
- export function init() {
147
- console.log('${id} | Initializing System');
148
-
149
- CONFIG.ActiveEffect.legacyTransferral = false;
150
-
151
- registerSettings();
152
- registerDataModels();
153
- registerDocumentSheets();
154
- registerDocumentClasses();
155
- registerPromptClasses();
156
- registerCanvasClasses();
157
- registerTypeInfo();
158
- registerHandlebarsHelpers();
159
- registerResourceBars();
160
- registerStatusEffects();
161
- registerMacroActions();
162
- registerKeywords();
163
- setupKeywordEnricher();
164
- registerUtils();
165
- //addVueImportMap();
166
-
167
- game.system.documentHooks = new Map();
168
- game.system.datatableApp = DataTableApp;
169
- game.system.rollClass = ${entry.config.name}Roll;
170
- game.system.damageRollClass = ${entry.config.name}DamageRoll;
171
- CONFIG.Dice.rolls.push(${entry.config.name}Roll);
172
- CONFIG.Dice.rolls.push(${entry.config.name}DamageRoll);
173
- }
174
-
175
- /* -------------------------------------------- */
176
-
177
- function registerSettings() {
178
-
179
- game.settings.register('${id}', 'createSystemJournal', {
180
- name: game.i18n.localize("SETTINGS.CreateSystemJournalName"),
181
- hint: game.i18n.localize("SETTINGS.CreateSystemJournalHint"),
182
- scope: 'world',
183
- config: true,
184
- default: true,
185
- type: Boolean
186
- });
187
-
188
- game.settings.register('${id}', 'roundUpDamageApplication', {
189
- name: game.i18n.localize("SETTINGS.RoundUpDamageApplicationName"),
190
- hint: game.i18n.localize("SETTINGS.RoundUpDamageApplicationHint"),
191
- scope: 'world',
192
- config: true,
193
- default: true,
194
- type: Boolean
195
- });
196
-
197
- game.settings.register('${id}', 'allowTargetDamageApplication', {
198
- name: game.i18n.localize('SETTINGS.AllowTargetDamageApplicationName'),
199
- hint: game.i18n.localize('SETTINGS.AllowTargetDamageApplicationHint'),
200
- scope: 'world',
201
- config: true,
202
- default: false,
203
- type: Boolean,
204
- requiresReload: true
205
- });
206
-
207
- game.settings.register('${id}', 'userTargetDamageApplicationType', {
208
- scope: 'client',
209
- config: false,
210
- default: 'selected',
211
- type: String
212
- });
213
-
214
- game.settings.register('${id}', 'damageApplicationChatCard', {
215
- name: game.i18n.localize('SETTINGS.DamageApplicationChatCardName'),
216
- hint: game.i18n.localize('SETTINGS.DamageApplicationChatCardHint'),
217
- scope: 'world',
218
- config: true,
219
- default: 'gm',
220
- type: String,
221
- choices: {
222
- 'none': game.i18n.localize('SETTINGS.DamageApplicationChatCard.None'),
223
- 'public': game.i18n.localize('SETTINGS.DamageApplicationChatCard.Public'),
224
- 'gm': game.i18n.localize('SETTINGS.DamageApplicationChatCard.GM')
225
- }
226
- });
227
-
228
- game.settings.register('${id}', 'hotReloadLastState', {
229
- scope: 'client',
230
- config: false,
231
- default: { openWindows: [] },
232
- type: Object
233
- });
234
-
235
- game.settings.register('${id}', 'documentColorThemes', {
236
- scope: 'client',
237
- config: false,
238
- default: {},
239
- type: Object
240
- });
241
-
242
- game.settings.register('${id}', 'documentLastState', {
243
- scope: 'client',
244
- config: false,
245
- default: {},
246
- type: Object
247
- });
248
-
249
- game.settings.register('${id}', 'documentTableColumns', {
250
- scope: 'client',
251
- config: false,
252
- default: {},
253
- type: Object
254
- });
255
- }
256
-
257
- /* -------------------------------------------- */
258
-
259
- function registerDataModels() {
260
- CONFIG.Actor.dataModels = {
261
- ${joinToNode(entry.documents.filter(d => isActor(d)), document => `${document.name.toLowerCase()}: ${document.name}TypeDataModel`, { appendNewLineIfNotEmpty: true, separator: ',' })}
262
- };
263
-
264
- CONFIG.Item.dataModels = {
265
- ${joinToNode(entry.documents.filter(d => isItem(d)), document => `${document.name.toLowerCase()}: ${document.name}TypeDataModel`, { appendNewLineIfNotEmpty: true, separator: ',' })}
266
- };
267
- }
268
-
269
- /* -------------------------------------------- */
270
-
271
- function registerDocumentSheets() {
272
- Actors.unregisterSheet("core", ActorSheet);
273
- Items.unregisterSheet("core", ItemSheet);
274
-
275
- // Actors
276
- ${joinToNode(entry.documents.filter(d => isActor(d)), document => `Actors.registerSheet("${id}", ${document.name}VueSheet, {types: ["${document.name.toLowerCase()}"], makeDefault: true});`, { appendNewLineIfNotEmpty: true })}
277
-
278
- // Items
279
- ${joinToNode(entry.documents.filter(d => isItem(d)), document => `Items.registerSheet("${id}", ${document.name}VueSheet, {types: ["${document.name.toLowerCase()}"], makeDefault: true});`, { appendNewLineIfNotEmpty: true })}
280
-
281
- // Active Effects
282
- DocumentSheetConfig.registerSheet(ActiveEffect, "${id}", ${entry.config.name}EffectVueSheet, { makeDefault: true });
283
- }
284
-
285
- /* -------------------------------------------- */
286
-
287
- function registerDocumentClasses() {
288
- CONFIG.Actor.documentClass = ${entry.config.name}Actor;
289
- CONFIG.Item.documentClass = ${entry.config.name}Item;
290
- CONFIG.Combatant.documentClass = ${entry.config.name}Combatant;
291
- CONFIG.Token.documentClass = ${entry.config.name}TokenDocument;
292
-
293
- game.system.measuredTemplatePreviewClass = MeasuredTemplatePreview;
294
- }
295
-
296
- /* -------------------------------------------- */
297
-
298
- function registerPromptClasses() {
299
- game.system.prompts = {
300
- ${joinToNode(entry.documents, document => generateDocumentPromptAssignment(document), { separator: ',\n' })}
301
- };
302
- game.system.documentCreateDialog = DocumentCreationVueDialog;
303
- }
304
-
305
- /* -------------------------------------------- */
306
-
307
- function registerCanvasClasses() {
308
- CONFIG.Token.objectClass = ${entry.config.name}Token;
309
- }
310
-
311
- /* -------------------------------------------- */
312
-
313
- function registerTypeInfo() {
314
- CONFIG.Actor.defaultType = "${actorDefaultType}";
315
- CONFIG.Item.defaultType = "${itemDefaultType}";
316
-
317
- CONFIG.Actor.typeArtworks = {
130
+ const fileNode = expandToNode `
131
+ ${joinToNode(entry.documents, document => `import ${document.name}TypeDataModel from "../datamodels/${isActor(document) ? "actor" : "item"}/${document.name.toLowerCase()}.mjs"`, { appendNewLineIfNotEmpty: true })}
132
+ ${joinToNode(entry.documents, document => `import ${document.name}VueSheet from "../sheets/vue/${isActor(document) ? "actor" : "item"}/${document.name.toLowerCase()}-sheet.mjs"`, { appendNewLineIfNotEmpty: true })}
133
+ import DataTableApp from "../sheets/vue/datatable-app.mjs";
134
+ ${joinToNode(entry.documents, generateDocumentPromptImports, { appendNewLineIfNotEmpty: true })}
135
+ import ${entry.config.name}EffectVueSheet from "../sheets/vue/active-effect-sheet.mjs";
136
+ import ${entry.config.name}Actor from "../documents/actor.mjs";
137
+ import ${entry.config.name}Item from "../documents/item.mjs";
138
+ import ${entry.config.name}Combatant from "../documents/combatant.mjs";
139
+ import ${entry.config.name}TokenDocument from "../documents/token.mjs";
140
+ import ${entry.config.name}Token from "../canvas/token.mjs";
141
+ import ${entry.config.name}Roll from "../rolls/roll.mjs";
142
+ import ${entry.config.name}DamageRoll from "../rolls/damage-roll.mjs";
143
+ import DocumentCreationVueDialog from "../sheets/vue/document-creation-dialog.mjs";
144
+ import MeasuredTemplatePreview from "../placeables/measured-template-preview.mjs";
145
+
146
+ export function init() {
147
+ console.log('${id} | Initializing System');
148
+
149
+ CONFIG.ActiveEffect.legacyTransferral = false;
150
+
151
+ registerSettings();
152
+ registerDataModels();
153
+ registerDocumentSheets();
154
+ registerDocumentClasses();
155
+ registerPromptClasses();
156
+ registerCanvasClasses();
157
+ registerTypeInfo();
158
+ registerHandlebarsHelpers();
159
+ registerResourceBars();
160
+ registerStatusEffects();
161
+ registerMacroActions();
162
+ registerKeywords();
163
+ setupKeywordEnricher();
164
+ registerUtils();
165
+ //addVueImportMap();
166
+
167
+ game.system.documentHooks = new Map();
168
+ game.system.datatableApp = DataTableApp;
169
+ game.system.rollClass = ${entry.config.name}Roll;
170
+ game.system.damageRollClass = ${entry.config.name}DamageRoll;
171
+ CONFIG.Dice.rolls.push(${entry.config.name}Roll);
172
+ CONFIG.Dice.rolls.push(${entry.config.name}DamageRoll);
173
+ }
174
+
175
+ /* -------------------------------------------- */
176
+
177
+ function registerSettings() {
178
+
179
+ game.settings.register('${id}', 'createSystemJournal', {
180
+ name: game.i18n.localize("SETTINGS.CreateSystemJournalName"),
181
+ hint: game.i18n.localize("SETTINGS.CreateSystemJournalHint"),
182
+ scope: 'world',
183
+ config: true,
184
+ default: true,
185
+ type: Boolean
186
+ });
187
+
188
+ game.settings.register('${id}', 'roundUpDamageApplication', {
189
+ name: game.i18n.localize("SETTINGS.RoundUpDamageApplicationName"),
190
+ hint: game.i18n.localize("SETTINGS.RoundUpDamageApplicationHint"),
191
+ scope: 'world',
192
+ config: true,
193
+ default: true,
194
+ type: Boolean
195
+ });
196
+
197
+ game.settings.register('${id}', 'allowTargetDamageApplication', {
198
+ name: game.i18n.localize('SETTINGS.AllowTargetDamageApplicationName'),
199
+ hint: game.i18n.localize('SETTINGS.AllowTargetDamageApplicationHint'),
200
+ scope: 'world',
201
+ config: true,
202
+ default: false,
203
+ type: Boolean,
204
+ requiresReload: true
205
+ });
206
+
207
+ game.settings.register('${id}', 'userTargetDamageApplicationType', {
208
+ scope: 'client',
209
+ config: false,
210
+ default: 'selected',
211
+ type: String
212
+ });
213
+
214
+ game.settings.register('${id}', 'damageApplicationChatCard', {
215
+ name: game.i18n.localize('SETTINGS.DamageApplicationChatCardName'),
216
+ hint: game.i18n.localize('SETTINGS.DamageApplicationChatCardHint'),
217
+ scope: 'world',
218
+ config: true,
219
+ default: 'gm',
220
+ type: String,
221
+ choices: {
222
+ 'none': game.i18n.localize('SETTINGS.DamageApplicationChatCard.None'),
223
+ 'public': game.i18n.localize('SETTINGS.DamageApplicationChatCard.Public'),
224
+ 'gm': game.i18n.localize('SETTINGS.DamageApplicationChatCard.GM')
225
+ }
226
+ });
227
+
228
+ game.settings.register('${id}', 'hotReloadLastState', {
229
+ scope: 'client',
230
+ config: false,
231
+ default: { openWindows: [] },
232
+ type: Object
233
+ });
234
+
235
+ game.settings.register('${id}', 'documentColorThemes', {
236
+ scope: 'client',
237
+ config: false,
238
+ default: {},
239
+ type: Object
240
+ });
241
+
242
+ game.settings.register('${id}', 'documentLastState', {
243
+ scope: 'client',
244
+ config: false,
245
+ default: {},
246
+ type: Object
247
+ });
248
+
249
+ game.settings.register('${id}', 'documentTableColumns', {
250
+ scope: 'client',
251
+ config: false,
252
+ default: {},
253
+ type: Object
254
+ });
255
+ }
256
+
257
+ /* -------------------------------------------- */
258
+
259
+ function registerDataModels() {
260
+ CONFIG.Actor.dataModels = {
261
+ ${joinToNode(entry.documents.filter(d => isActor(d)), document => `${document.name.toLowerCase()}: ${document.name}TypeDataModel`, { appendNewLineIfNotEmpty: true, separator: ',' })}
262
+ };
263
+
264
+ CONFIG.Item.dataModels = {
265
+ ${joinToNode(entry.documents.filter(d => isItem(d)), document => `${document.name.toLowerCase()}: ${document.name}TypeDataModel`, { appendNewLineIfNotEmpty: true, separator: ',' })}
266
+ };
267
+ }
268
+
269
+ /* -------------------------------------------- */
270
+
271
+ function registerDocumentSheets() {
272
+ Actors.unregisterSheet("core", ActorSheet);
273
+ Items.unregisterSheet("core", ItemSheet);
274
+
275
+ // Actors
276
+ ${joinToNode(entry.documents.filter(d => isActor(d)), document => `Actors.registerSheet("${id}", ${document.name}VueSheet, {types: ["${document.name.toLowerCase()}"], makeDefault: true});`, { appendNewLineIfNotEmpty: true })}
277
+
278
+ // Items
279
+ ${joinToNode(entry.documents.filter(d => isItem(d)), document => `Items.registerSheet("${id}", ${document.name}VueSheet, {types: ["${document.name.toLowerCase()}"], makeDefault: true});`, { appendNewLineIfNotEmpty: true })}
280
+
281
+ // Active Effects
282
+ DocumentSheetConfig.registerSheet(ActiveEffect, "${id}", ${entry.config.name}EffectVueSheet, { makeDefault: true });
283
+ }
284
+
285
+ /* -------------------------------------------- */
286
+
287
+ function registerDocumentClasses() {
288
+ CONFIG.Actor.documentClass = ${entry.config.name}Actor;
289
+ CONFIG.Item.documentClass = ${entry.config.name}Item;
290
+ CONFIG.Combatant.documentClass = ${entry.config.name}Combatant;
291
+ CONFIG.Token.documentClass = ${entry.config.name}TokenDocument;
292
+
293
+ game.system.measuredTemplatePreviewClass = MeasuredTemplatePreview;
294
+ }
295
+
296
+ /* -------------------------------------------- */
297
+
298
+ function registerPromptClasses() {
299
+ game.system.prompts = {
300
+ ${joinToNode(entry.documents, document => generateDocumentPromptAssignment(document), { separator: ',\n' })}
301
+ };
302
+ game.system.documentCreateDialog = DocumentCreationVueDialog;
303
+ }
304
+
305
+ /* -------------------------------------------- */
306
+
307
+ function registerCanvasClasses() {
308
+ CONFIG.Token.objectClass = ${entry.config.name}Token;
309
+ }
310
+
311
+ /* -------------------------------------------- */
312
+
313
+ function registerTypeInfo() {
314
+ CONFIG.Actor.defaultType = "${actorDefaultType}";
315
+ CONFIG.Item.defaultType = "${itemDefaultType}";
316
+
317
+ CONFIG.Actor.typeArtworks = {
318
318
  ${joinToNode(actorArtworks, document => {
319
319
  var _a, _b;
320
320
  const path = (_b = (_a = document.params.find(p => isDocumentSvgParam(p))) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '';
321
321
  const fullPath = path.startsWith('/icons/') ? path : `systems/${id}/${path}`;
322
322
  return `"${document.name.toLowerCase()}": "${fullPath}"`;
323
- }, { appendNewLineIfNotEmpty: true, separator: ',' })}
324
- };
325
-
326
- CONFIG.Item.typeArtworks = {
323
+ }, { appendNewLineIfNotEmpty: true, separator: ',' })}
324
+ };
325
+
326
+ CONFIG.Item.typeArtworks = {
327
327
  ${joinToNode(itemArtworks, document => {
328
328
  var _a, _b;
329
329
  const path = (_b = (_a = document.params.find(p => isDocumentSvgParam(p))) === null || _a === void 0 ? void 0 : _a.value) !== null && _b !== void 0 ? _b : '';
330
330
  const fullPath = path.startsWith('/icons/') ? path : `systems/${id}/${path}`;
331
331
  return `"${document.name.toLowerCase()}": "${fullPath}"`;
332
- }, { appendNewLineIfNotEmpty: true, separator: ',' })}
333
- };
334
-
335
- CONFIG.Actor.typeDescriptions = {
336
- ${joinToNode(actorDescriptions, document => { var _a; return `"${document.name.toLowerCase()}": "${(_a = document.params.find(p => isDocumentDescriptionParam(p))) === null || _a === void 0 ? void 0 : _a.value}"`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
337
- }
338
- CONFIG.Item.typeDescriptions = {
339
- ${joinToNode(itemDescriptions, document => { var _a; return `"${document.name.toLowerCase()}": "${(_a = document.params.find(p => isDocumentDescriptionParam(p))) === null || _a === void 0 ? void 0 : _a.value}"`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
340
- }
341
-
342
- CONFIG.Actor.typeCreatables = {
343
- ${joinToNode(actorCreatables, document => { var _a; return `"${document.name.toLowerCase()}": ${(_a = document.params.find(p => isDocumentCreatableParam(p))) === null || _a === void 0 ? void 0 : _a.value}`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
344
- }
345
- CONFIG.Item.typeCreatables = {
346
- ${joinToNode(itemCreatables, document => { var _a; return `"${document.name.toLowerCase()}": ${(_a = document.params.find(p => isDocumentCreatableParam(p))) === null || _a === void 0 ? void 0 : _a.value}`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
347
- }
348
- }
349
-
350
- /* -------------------------------------------- */
351
-
352
- function registerHandlebarsHelpers() {
353
-
354
- // Convert a type and value to a localized label
355
- Handlebars.registerHelper("typeLabel", (type, value) => {
356
- return game.i18n.localize(CONFIG.SYSTEM[type][value]?.label);
357
- });
358
-
359
- // Truncate a string to a certain length with an ellipsis
360
- Handlebars.registerHelper("truncate", (str, len) => {
361
- if (str.length > len) {
362
- return \`\${str.slice(0, len)}...\`;
363
- }
364
- return str;
365
- });
366
-
367
- // Get a property on an object using a string key
368
- Handlebars.registerHelper("getProperty", (obj, key) => {
369
- if (obj == null) return "";
370
- return foundry.utils.getProperty(obj, key);
371
- });
372
-
373
- // Humanize a string
374
- Handlebars.registerHelper("humanize", (str) => {
375
- let humanized = str.replace(/_/g, " ");
376
- humanized = humanized.replace("system.", "").replaceAll(".", " ");
377
- humanized = humanized.charAt(0).toUpperCase() + humanized.slice(1);
378
- return humanized;
379
- });
380
- }
381
-
382
- /* -------------------------------------------- */
383
-
384
- function registerResourceBars() {
385
- CONFIG.Actor.trackableAttributes = {
386
- ${joinToNode(entry.documents.filter(d => isActor(d)), document => generateTrackableResourceBars(document), { appendNewLineIfNotEmpty: true, separator: ',' })}
387
- };
388
- }
389
-
390
- /* -------------------------------------------- */
391
-
392
- ${generateRegisterStatusEffects()}
393
-
394
- /* -------------------------------------------- */
395
-
396
- function registerMacroActions() {
397
- game.system.macroActions = {
398
- ${generateMacroActionConfig(entry)}
399
- };
400
- }
401
-
402
- /* -------------------------------------------- */
403
-
404
- function registerKeywords() {
405
- game.system.keywords = {
406
- ${generateKeywordConfig(entry)}
407
- };
408
- }
409
-
410
- /* -------------------------------------------- */
411
-
412
- function setupKeywordEnricher() {
413
- // Register keyword text enricher
414
- CONFIG.TextEditor.enrichers.push({
415
- pattern: /@([a-zA-Z][a-zA-Z0-9]*)/gi,
416
- replaceParent: true,
417
- enricher: async (match, options) => {
418
- const keywordName = match[1].toLowerCase();
419
- const keyword = game.system.keywords[keywordName];
420
-
421
- if (!keyword) return null;
422
-
423
- // Find the system documentation journal and determine which page the keyword is on
424
- const keywordsJournal = game.journal.find(j => j.getFlag('${id}', 'systemJournal') === true || j.getFlag('core', 'keywordsJournal') === true);
425
- const isDamageType = keyword.type === 'damage-type';
426
- const pageType = isDamageType ? 'damage-types' : 'keywords';
427
- const targetPage = keywordsJournal?.pages.find(p => p.getFlag('core', 'pageType') === pageType);
428
-
429
- const element = document.createElement('span');
430
- element.className = 'keyword-reference';
431
- element.setAttribute('data-keyword', keywordName);
432
- element.setAttribute('data-journal-uuid', keywordsJournal?.uuid || '');
433
- element.setAttribute('data-page-uuid', targetPage?.uuid || '');
434
- element.setAttribute('data-anchor', keywordName);
435
- element.title = keyword.description;
436
-
437
- if (keyword.icon) {
438
- const icon = document.createElement('i');
439
- icon.className = keyword.icon;
440
- icon.style.color = keyword.color;
441
- element.appendChild(icon);
442
- }
443
-
444
- element.appendChild(document.createTextNode(keyword.name));
445
-
446
- return element;
447
- }
448
- });
449
-
450
- // Register click handler for keyword references
451
- $(document).on('click', '.keyword-reference', async function() {
452
- const journalUuid = $(this).data('journal-uuid');
453
- const pageUuid = $(this).data('page-uuid');
454
- const anchor = $(this).data('anchor');
455
-
456
- if (journalUuid && pageUuid) {
457
- const journal = await fromUuid(journalUuid);
458
- const page = await fromUuid(pageUuid);
459
- if (journal && page) {
460
- journal.sheet.render(true, {pageId: page.id, anchor: anchor});
461
- }
462
- } else if (journalUuid) {
463
- const journal = await fromUuid(journalUuid);
464
- journal?.sheet.render(true);
465
- }
466
- });
467
- }
468
-
469
- /** -------------------------------------------- */
470
-
471
- function addVueImportMap() {
472
- let script = document.createElement('script');
473
- script.type = 'importmap';
474
- script.text = \`{
475
- "imports": {
476
- "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
477
- }
478
- }\`;
479
- document.head.appendChild(script);
480
- }
481
-
482
- /* -------------------------------------------- */
483
-
484
- function registerUtils() {
485
- game.system.utils = {};
486
-
487
- function flattenObject(obj, _d=0) {
488
- const flat = {};
489
- if ( _d > 100 ) {
490
- throw new Error("Maximum depth exceeded");
491
- }
492
- for ( let [k, v] of Object.entries(obj) ) {
493
- let t = foundry.utils.getType(v);
494
- if ( t === "Object" ) {
495
- if ( k == "parent" ) continue;
496
- if ( foundry.utils.isEmpty(v) ) flat[k] = v;
497
- let inner = flattenObject(v, _d+1);
498
- for ( let [ik, iv] of Object.entries(inner) ) {
499
- flat[\`\${k}.\${ik}\`] = iv;
500
- }
501
- }
502
- else flat[k] = v;
503
- }
504
- return flat;
505
- }
506
-
507
- game.system.utils.flattenObject = flattenObject;
508
-
509
- function toNearest(interval=1, method="round") {
510
- if (!Number.isNumeric(this)) {
511
- throw new Error("toNearest() must be called on a numeric looking value");
512
- }
513
- const number = Number.fromString(this);
514
- return number.toNearest(interval, method);
515
- }
516
-
517
- Object.defineProperties(String.prototype, {
518
- toNearest: {value: toNearest}
519
- });
520
-
521
- async function callAllAsync(hook, ...args) {
522
- if ( CONFIG.debug.hooks ) {
523
- console.log(\`DEBUG | Calling async \${hook} hook with args:\`);
524
- console.log(args);
525
- }
526
- const events = Hooks.events;
527
- if ( !(hook in events) ) return true;
528
- for ( const entry of Array.from(events[hook]) ) {
529
- await entry.fn(...args);
530
- }
531
- return true;
532
- }
533
-
534
- Hooks.callAllAsync = callAllAsync;
535
-
536
- let audioSources = new Map();
537
- let gainNodes = new Map();
538
-
539
- async function playAudio(id, url, onEndCallback, volume=0.5) {
540
- let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
541
- let source = audioCtx.createBufferSource();
542
- let gainNode = audioCtx.createGain();
543
- audioSources.set(id, source);
544
- gainNodes.set(id, gainNode);
545
- source.connect(gainNode).connect(audioCtx.destination);
546
-
547
- let request = new XMLHttpRequest();
548
- request.open('GET', url, true);
549
- request.responseType = 'arraybuffer';
550
-
551
- request.onload = () => {
552
- let audioData = request.response;
553
- audioCtx.decodeAudioData(audioData,
554
- (buffer) => {
555
- source.buffer = buffer;
556
- gainNode.gain.value = volume;
557
- source.start(0);
558
- source.onended = () => {
559
- audioSources.delete(id);
560
- gainNodes.delete(id);
561
- onEndCallback();
562
- };
563
- },
564
- (e) => {
565
- ui.notifications.error("An error occurred while decoding audio data");
566
- console.log(url);
567
- console.log(audioData);
568
- console.log(e);
569
- onEndCallback();
570
- });
571
- };
572
- try {
573
- request.send();
574
- }
575
- catch (e) {
576
- console.error("Error playing sound effect:", e);
577
- onEndCallback();
578
- }
579
- }
580
-
581
- async function playSfx(url, volume=0.5) {
582
- // Invoke the playAudio function with the provided parameters and wait for it to complete vis the onEndCallback
583
- let finishedPromise = new Promise(async (resolve) => {
584
- let onEndCallback = () => {
585
- resolve();
586
- };
587
- // Attach base url
588
- if (!url.startsWith("http")) {
589
- url = \`\${window.location.origin}/systems/${id}/\${url}\`;
590
- }
591
-
592
- await playAudio(foundry.utils.randomID(), url, onEndCallback, volume);
593
- });
594
- return finishedPromise;
595
- }
596
- game.system.utils.playSfx = playSfx;
597
- }
332
+ }, { appendNewLineIfNotEmpty: true, separator: ',' })}
333
+ };
334
+
335
+ CONFIG.Actor.typeDescriptions = {
336
+ ${joinToNode(actorDescriptions, document => { var _a; return `"${document.name.toLowerCase()}": "${(_a = document.params.find(p => isDocumentDescriptionParam(p))) === null || _a === void 0 ? void 0 : _a.value}"`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
337
+ }
338
+ CONFIG.Item.typeDescriptions = {
339
+ ${joinToNode(itemDescriptions, document => { var _a; return `"${document.name.toLowerCase()}": "${(_a = document.params.find(p => isDocumentDescriptionParam(p))) === null || _a === void 0 ? void 0 : _a.value}"`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
340
+ }
341
+
342
+ CONFIG.Actor.typeCreatables = {
343
+ ${joinToNode(actorCreatables, document => { var _a; return `"${document.name.toLowerCase()}": ${(_a = document.params.find(p => isDocumentCreatableParam(p))) === null || _a === void 0 ? void 0 : _a.value}`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
344
+ }
345
+ CONFIG.Item.typeCreatables = {
346
+ ${joinToNode(itemCreatables, document => { var _a; return `"${document.name.toLowerCase()}": ${(_a = document.params.find(p => isDocumentCreatableParam(p))) === null || _a === void 0 ? void 0 : _a.value}`; }, { appendNewLineIfNotEmpty: true, separator: ',' })}
347
+ }
348
+ }
349
+
350
+ /* -------------------------------------------- */
351
+
352
+ function registerHandlebarsHelpers() {
353
+
354
+ // Convert a type and value to a localized label
355
+ Handlebars.registerHelper("typeLabel", (type, value) => {
356
+ return game.i18n.localize(CONFIG.SYSTEM[type][value]?.label);
357
+ });
358
+
359
+ // Truncate a string to a certain length with an ellipsis
360
+ Handlebars.registerHelper("truncate", (str, len) => {
361
+ if (str.length > len) {
362
+ return \`\${str.slice(0, len)}...\`;
363
+ }
364
+ return str;
365
+ });
366
+
367
+ // Get a property on an object using a string key
368
+ Handlebars.registerHelper("getProperty", (obj, key) => {
369
+ if (obj == null) return "";
370
+ return foundry.utils.getProperty(obj, key);
371
+ });
372
+
373
+ // Humanize a string
374
+ Handlebars.registerHelper("humanize", (str) => {
375
+ let humanized = str.replace(/_/g, " ");
376
+ humanized = humanized.replace("system.", "").replaceAll(".", " ");
377
+ humanized = humanized.charAt(0).toUpperCase() + humanized.slice(1);
378
+ return humanized;
379
+ });
380
+ }
381
+
382
+ /* -------------------------------------------- */
383
+
384
+ function registerResourceBars() {
385
+ CONFIG.Actor.trackableAttributes = {
386
+ ${joinToNode(entry.documents.filter(d => isActor(d)), document => generateTrackableResourceBars(document), { appendNewLineIfNotEmpty: true, separator: ',' })}
387
+ };
388
+ }
389
+
390
+ /* -------------------------------------------- */
391
+
392
+ ${generateRegisterStatusEffects()}
393
+
394
+ /* -------------------------------------------- */
395
+
396
+ function registerMacroActions() {
397
+ game.system.macroActions = {
398
+ ${generateMacroActionConfig(entry)}
399
+ };
400
+ }
401
+
402
+ /* -------------------------------------------- */
403
+
404
+ function registerKeywords() {
405
+ game.system.keywords = {
406
+ ${generateKeywordConfig(entry)}
407
+ };
408
+ }
409
+
410
+ /* -------------------------------------------- */
411
+
412
+ function setupKeywordEnricher() {
413
+ // Register keyword text enricher
414
+ CONFIG.TextEditor.enrichers.push({
415
+ pattern: /@([a-zA-Z][a-zA-Z0-9]*)/gi,
416
+ replaceParent: true,
417
+ enricher: async (match, options) => {
418
+ const keywordName = match[1].toLowerCase();
419
+ const keyword = game.system.keywords[keywordName];
420
+
421
+ if (!keyword) return null;
422
+
423
+ // Find the system documentation journal and determine which page the keyword is on
424
+ const keywordsJournal = game.journal.find(j => j.getFlag('${id}', 'systemJournal') === true || j.getFlag('core', 'keywordsJournal') === true);
425
+ const isDamageType = keyword.type === 'damage-type';
426
+ const pageType = isDamageType ? 'damage-types' : 'keywords';
427
+ const targetPage = keywordsJournal?.pages.find(p => p.getFlag('core', 'pageType') === pageType);
428
+
429
+ const element = document.createElement('span');
430
+ element.className = 'keyword-reference';
431
+ element.setAttribute('data-keyword', keywordName);
432
+ element.setAttribute('data-journal-uuid', keywordsJournal?.uuid || '');
433
+ element.setAttribute('data-page-uuid', targetPage?.uuid || '');
434
+ element.setAttribute('data-anchor', keywordName);
435
+ element.title = keyword.description;
436
+
437
+ if (keyword.icon) {
438
+ const icon = document.createElement('i');
439
+ icon.className = keyword.icon;
440
+ icon.style.color = keyword.color;
441
+ element.appendChild(icon);
442
+ }
443
+
444
+ element.appendChild(document.createTextNode(keyword.name));
445
+
446
+ return element;
447
+ }
448
+ });
449
+
450
+ // Register click handler for keyword references
451
+ $(document).on('click', '.keyword-reference', async function() {
452
+ const journalUuid = $(this).data('journal-uuid');
453
+ const pageUuid = $(this).data('page-uuid');
454
+ const anchor = $(this).data('anchor');
455
+
456
+ if (journalUuid && pageUuid) {
457
+ const journal = await fromUuid(journalUuid);
458
+ const page = await fromUuid(pageUuid);
459
+ if (journal && page) {
460
+ journal.sheet.render(true, {pageId: page.id, anchor: anchor});
461
+ }
462
+ } else if (journalUuid) {
463
+ const journal = await fromUuid(journalUuid);
464
+ journal?.sheet.render(true);
465
+ }
466
+ });
467
+ }
468
+
469
+ /** -------------------------------------------- */
470
+
471
+ function addVueImportMap() {
472
+ let script = document.createElement('script');
473
+ script.type = 'importmap';
474
+ script.text = \`{
475
+ "imports": {
476
+ "vue": "https://unpkg.com/vue@3/dist/vue.esm-browser.js"
477
+ }
478
+ }\`;
479
+ document.head.appendChild(script);
480
+ }
481
+
482
+ /* -------------------------------------------- */
483
+
484
+ function registerUtils() {
485
+ game.system.utils = {};
486
+
487
+ function flattenObject(obj, _d=0) {
488
+ const flat = {};
489
+ if ( _d > 100 ) {
490
+ throw new Error("Maximum depth exceeded");
491
+ }
492
+ for ( let [k, v] of Object.entries(obj) ) {
493
+ let t = foundry.utils.getType(v);
494
+ if ( t === "Object" ) {
495
+ if ( k == "parent" ) continue;
496
+ if ( foundry.utils.isEmpty(v) ) flat[k] = v;
497
+ let inner = flattenObject(v, _d+1);
498
+ for ( let [ik, iv] of Object.entries(inner) ) {
499
+ flat[\`\${k}.\${ik}\`] = iv;
500
+ }
501
+ }
502
+ else flat[k] = v;
503
+ }
504
+ return flat;
505
+ }
506
+
507
+ game.system.utils.flattenObject = flattenObject;
508
+
509
+ function toNearest(interval=1, method="round") {
510
+ if (!Number.isNumeric(this)) {
511
+ throw new Error("toNearest() must be called on a numeric looking value");
512
+ }
513
+ const number = Number.fromString(this);
514
+ return number.toNearest(interval, method);
515
+ }
516
+
517
+ Object.defineProperties(String.prototype, {
518
+ toNearest: {value: toNearest}
519
+ });
520
+
521
+ async function callAllAsync(hook, ...args) {
522
+ if ( CONFIG.debug.hooks ) {
523
+ console.log(\`DEBUG | Calling async \${hook} hook with args:\`);
524
+ console.log(args);
525
+ }
526
+ const events = Hooks.events;
527
+ if ( !(hook in events) ) return true;
528
+ for ( const entry of Array.from(events[hook]) ) {
529
+ await entry.fn(...args);
530
+ }
531
+ return true;
532
+ }
533
+
534
+ Hooks.callAllAsync = callAllAsync;
535
+
536
+ let audioSources = new Map();
537
+ let gainNodes = new Map();
538
+
539
+ async function playAudio(id, url, onEndCallback, volume=0.5) {
540
+ let audioCtx = new (window.AudioContext || window.webkitAudioContext)();
541
+ let source = audioCtx.createBufferSource();
542
+ let gainNode = audioCtx.createGain();
543
+ audioSources.set(id, source);
544
+ gainNodes.set(id, gainNode);
545
+ source.connect(gainNode).connect(audioCtx.destination);
546
+
547
+ let request = new XMLHttpRequest();
548
+ request.open('GET', url, true);
549
+ request.responseType = 'arraybuffer';
550
+
551
+ request.onload = () => {
552
+ let audioData = request.response;
553
+ audioCtx.decodeAudioData(audioData,
554
+ (buffer) => {
555
+ source.buffer = buffer;
556
+ gainNode.gain.value = volume;
557
+ source.start(0);
558
+ source.onended = () => {
559
+ audioSources.delete(id);
560
+ gainNodes.delete(id);
561
+ onEndCallback();
562
+ };
563
+ },
564
+ (e) => {
565
+ ui.notifications.error("An error occurred while decoding audio data");
566
+ console.log(url);
567
+ console.log(audioData);
568
+ console.log(e);
569
+ onEndCallback();
570
+ });
571
+ };
572
+ try {
573
+ request.send();
574
+ }
575
+ catch (e) {
576
+ console.error("Error playing sound effect:", e);
577
+ onEndCallback();
578
+ }
579
+ }
580
+
581
+ async function playSfx(url, volume=0.5) {
582
+ // Invoke the playAudio function with the provided parameters and wait for it to complete vis the onEndCallback
583
+ let finishedPromise = new Promise(async (resolve) => {
584
+ let onEndCallback = () => {
585
+ resolve();
586
+ };
587
+ // Attach base url
588
+ if (!url.startsWith("http")) {
589
+ url = \`\${window.location.origin}/systems/${id}/\${url}\`;
590
+ }
591
+
592
+ await playAudio(foundry.utils.randomID(), url, onEndCallback, volume);
593
+ });
594
+ return finishedPromise;
595
+ }
596
+ game.system.utils.playSfx = playSfx;
597
+ }
598
598
  `.appendNewLineIfNotEmpty();
599
599
  fs.writeFileSync(generatedFilePath, toString(fileNode));
600
600
  }