intelligent-system-design-language 0.3.21 → 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 (116) 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 -1381
  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 -1381
  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 -651
  27. package/out/cli/components/chat-card-generator.js.map +1 -1
  28. package/out/cli/components/css-generator.js +4 -4
  29. package/out/cli/components/damage-roll-generator.js +160 -160
  30. package/out/cli/components/datamodel-generator.js +264 -257
  31. package/out/cli/components/datamodel-generator.js.map +1 -1
  32. package/out/cli/components/derived-data-generator.js +923 -923
  33. package/out/cli/components/hotbar-drop-hook-generator.js +82 -82
  34. package/out/cli/components/init-hook-generator.js +495 -495
  35. package/out/cli/components/language-generator.js +1 -1
  36. package/out/cli/components/language-generator.js.map +1 -1
  37. package/out/cli/components/measured-template-preview.js +221 -221
  38. package/out/cli/components/method-generator.js +979 -887
  39. package/out/cli/components/method-generator.js.map +1 -1
  40. package/out/cli/components/ready-hook-generator.js +404 -404
  41. package/out/cli/components/token-generator.js +116 -116
  42. package/out/cli/components/vue/base-components/vue-attribute.js +138 -138
  43. package/out/cli/components/vue/base-components/vue-boolean.js +64 -64
  44. package/out/cli/components/vue/base-components/vue-calculator.js +93 -93
  45. package/out/cli/components/vue/base-components/vue-damage-application.js +356 -356
  46. package/out/cli/components/vue/base-components/vue-damage-bonuses.js +165 -165
  47. package/out/cli/components/vue/base-components/vue-damage-resistances.js +196 -196
  48. package/out/cli/components/vue/base-components/vue-damage-track.js +121 -121
  49. package/out/cli/components/vue/base-components/vue-date-time.js +42 -42
  50. package/out/cli/components/vue/base-components/vue-dice.js +98 -98
  51. package/out/cli/components/vue/base-components/vue-die.js +73 -73
  52. package/out/cli/components/vue/base-components/vue-document-choice.js +149 -149
  53. package/out/cli/components/vue/base-components/vue-document-choices.js +179 -179
  54. package/out/cli/components/vue/base-components/vue-document-link.js +60 -60
  55. package/out/cli/components/vue/base-components/vue-extended-choice.js +88 -88
  56. package/out/cli/components/vue/base-components/vue-inventory.js +519 -519
  57. package/out/cli/components/vue/base-components/vue-macro-choice.js +138 -138
  58. package/out/cli/components/vue/base-components/vue-measured-template.js +530 -530
  59. package/out/cli/components/vue/base-components/vue-money.js +483 -483
  60. package/out/cli/components/vue/base-components/vue-number.js +174 -174
  61. package/out/cli/components/vue/base-components/vue-paperdoll.js +43 -43
  62. package/out/cli/components/vue/base-components/vue-parent-property-reference.js +76 -76
  63. package/out/cli/components/vue/base-components/vue-prosemirror.js +18 -18
  64. package/out/cli/components/vue/base-components/vue-resource.js +136 -136
  65. package/out/cli/components/vue/base-components/vue-roll-visualizer.js +286 -109
  66. package/out/cli/components/vue/base-components/vue-roll-visualizer.js.map +1 -1
  67. package/out/cli/components/vue/base-components/vue-self-property-reference.js +62 -62
  68. package/out/cli/components/vue/base-components/vue-string-choice.js +98 -98
  69. package/out/cli/components/vue/base-components/vue-string-choices.js +203 -203
  70. package/out/cli/components/vue/base-components/vue-string.js +60 -60
  71. package/out/cli/components/vue/base-components/vue-text-field.js +53 -53
  72. package/out/cli/components/vue/base-components/vue-tracker.js +431 -431
  73. package/out/cli/components/vue/vue-action-component-generator.js +64 -64
  74. package/out/cli/components/vue/vue-active-effect-sheet-generator.js +856 -856
  75. package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +292 -292
  76. package/out/cli/components/vue/vue-datatable2-component-generator.js +824 -824
  77. package/out/cli/components/vue/vue-document-creation-app.js +121 -121
  78. package/out/cli/components/vue/vue-document-creation-sheet.js +94 -94
  79. package/out/cli/components/vue/vue-generator.js +40 -40
  80. package/out/cli/components/vue/vue-mixin.js +296 -296
  81. package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +260 -260
  82. package/out/cli/components/vue/vue-prompt-generator.js +91 -76
  83. package/out/cli/components/vue/vue-prompt-generator.js.map +1 -1
  84. package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +317 -317
  85. package/out/cli/components/vue/vue-sheet-application-generator.js +1177 -1167
  86. package/out/cli/components/vue/vue-sheet-application-generator.js.map +1 -1
  87. package/out/cli/components/vue/vue-sheet-class-generator.js +510 -510
  88. package/out/cli/generator.js +438 -433
  89. package/out/cli/generator.js.map +1 -1
  90. package/out/extension/github/githubAuthProvider.js +71 -29
  91. package/out/extension/github/githubAuthProvider.js.map +1 -1
  92. package/out/extension/github/githubGistManager.js +4 -3
  93. package/out/extension/github/githubGistManager.js.map +1 -1
  94. package/out/extension/github/githubManager.js +40 -38
  95. package/out/extension/github/githubManager.js.map +1 -1
  96. package/out/extension/github/githubQuickActions.js +120 -120
  97. package/out/extension/github/system-workflow.yml +47 -47
  98. package/out/extension/main.cjs +909 -532
  99. package/out/extension/main.cjs.map +3 -3
  100. package/out/extension/package.json +419 -419
  101. package/out/language/generated/ast.js +51 -2
  102. package/out/language/generated/ast.js.map +1 -1
  103. package/out/language/generated/grammar.js +14240 -13991
  104. package/out/language/generated/grammar.js.map +1 -1
  105. package/out/language/intelligent-system-design-language-validator.js +32 -2
  106. package/out/language/intelligent-system-design-language-validator.js.map +1 -1
  107. package/out/language/isdl-scope-provider.js +14 -1
  108. package/out/language/isdl-scope-provider.js.map +1 -1
  109. package/out/language/main.cjs +913 -569
  110. package/out/language/main.cjs.map +3 -3
  111. package/out/package.json +419 -419
  112. package/out/progressbar.min.js +6 -6
  113. package/out/styles.scss +762 -747
  114. package/out/test/validating/diagnostics.test.js +40 -0
  115. package/out/test/validating/diagnostics.test.js.map +1 -1
  116. package/package.json +419 -419
@@ -13,20 +13,20 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
13
13
  function generateExtendedDocumentClass(type, entry) {
14
14
  function translateMethodOrValueOrStored(property, param) {
15
15
  if (param == undefined) {
16
- return expandToNode `
17
- return system.${property.name.toLowerCase()} ?? 0
16
+ return expandToNode `
17
+ return system.${property.name.toLowerCase()} ?? 0
18
18
  `;
19
19
  }
20
20
  if (isMethodBlock(param.value)) {
21
21
  if (isNumberParamValue(param)) {
22
22
  toBeReapplied.add("system." + property.name.toLowerCase());
23
23
  }
24
- return expandToNode `
25
- ${translateExpression(entry, id, param.value, true, property)}
24
+ return expandToNode `
25
+ ${translateExpression(entry, id, param.value, true, property)}
26
26
  `;
27
27
  }
28
- return expandToNode `
29
- return ${param.value}
28
+ return expandToNode `
29
+ return ${param.value}
30
30
  `;
31
31
  }
32
32
  const generatedFilePath = path.join(generatedFileDir, `${type.toLowerCase()}.mjs`);
@@ -42,21 +42,21 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
42
42
  if (stringValue.value == "")
43
43
  return;
44
44
  if (typeof stringValue.value == "string") {
45
- return expandToNode `
46
- // ${property.name} String Derived Data
47
- this.system.${property.name.toLowerCase()} = "${stringValue.value}";
45
+ return expandToNode `
46
+ // ${property.name} String Derived Data
47
+ this.system.${property.name.toLowerCase()} = "${stringValue.value}";
48
48
  `.appendNewLineIfNotEmpty();
49
49
  }
50
50
  else {
51
- return expandToNode `
52
- // ${property.name} String Derived Data
53
- const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
54
- const context = {
55
- object: this
56
- };
57
- ${translateExpression(entry, id, stringValue.value, true, property)}
58
- };
59
- this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}CurrentValueFunc(this.system);
51
+ return expandToNode `
52
+ // ${property.name} String Derived Data
53
+ const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
54
+ const context = {
55
+ object: this
56
+ };
57
+ ${translateExpression(entry, id, stringValue.value, true, property)}
58
+ };
59
+ this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}CurrentValueFunc(this.system);
60
60
  `.appendNewLineIfNotEmpty();
61
61
  }
62
62
  }
@@ -66,102 +66,102 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
66
66
  const minParam = property.params.find(p => isNumberParamMin(p));
67
67
  const maxParam = property.params.find(p => isNumberParamMax(p));
68
68
  if (valueParam) {
69
- return expandToNode `
70
- // ${property.name} Number Calculated Data
71
-
72
-
73
- ${minParam != undefined ? expandToNode `
74
- const ${property.name.toLowerCase()}MinFunc = (system) => {
75
- const context = {
76
- object: this
77
- };
78
- ${translateMethodOrValueOrStored(property, minParam)}
79
- };
80
- let ${property.name.toLowerCase()}Min = ${property.name.toLowerCase()}MinFunc(this.system);
81
- if (isNaN(${property.name.toLowerCase()}Min) || ${property.name.toLowerCase()}Min === undefined || ${property.name.toLowerCase()}Min === null) {
82
- ${property.name.toLowerCase()}Min = 0;
83
- }
84
- `.appendNewLine() : ""}
85
-
86
- ${maxParam != undefined ? expandToNode `
87
- const ${property.name.toLowerCase()}MaxFunc = (system) => {
88
- const context = {
89
- object: this
90
- };
91
- ${translateMethodOrValueOrStored(property, maxParam)}
92
- };
93
- let ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}MaxFunc(this.system);
94
- if (isNaN(${property.name.toLowerCase()}Max) || ${property.name.toLowerCase()}Max === undefined || ${property.name.toLowerCase()}Max === null) {
95
- ${property.name.toLowerCase()}Max = 0;
96
- }
97
- `.appendNewLine() : ""}
98
-
99
- // ${property.name} Number Derived Data
100
- const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
101
- const context = {
102
- object: this
103
- };
104
- ${translateMethodOrValueOrStored(property, valueParam)}
105
- };
106
- Object.defineProperty(this.system, "${property.name.toLowerCase()}", {
107
- get: () => {
108
- let current = ${property.name.toLowerCase()}CurrentValueFunc(this.system);
109
- // Protect against NaN from invalid operations (e.g., "A" + 2)
110
- if (isNaN(current) || current === undefined || current === null) {
111
- current = 0;
112
- }
113
- ${minParam != undefined ? expandToNode `
114
- if ( current < ${property.name.toLowerCase()}Min ) {
115
- current = ${property.name.toLowerCase()}Min;
116
- }
117
- `.appendNewLine() : ""}
118
- ${maxParam != undefined ? expandToNode `
119
- if ( current > ${property.name.toLowerCase()}Max ) {
120
- current = ${property.name.toLowerCase()}Max;
121
- }
122
- `.appendNewLine() : ""}
123
- return current;
124
- },
125
- configurable: true
126
- });
127
-
69
+ return expandToNode `
70
+ // ${property.name} Number Calculated Data
71
+
72
+
73
+ ${minParam != undefined ? expandToNode `
74
+ const ${property.name.toLowerCase()}MinFunc = (system) => {
75
+ const context = {
76
+ object: this
77
+ };
78
+ ${translateMethodOrValueOrStored(property, minParam)}
79
+ };
80
+ let ${property.name.toLowerCase()}Min = ${property.name.toLowerCase()}MinFunc(this.system);
81
+ if (isNaN(${property.name.toLowerCase()}Min) || ${property.name.toLowerCase()}Min === undefined || ${property.name.toLowerCase()}Min === null) {
82
+ ${property.name.toLowerCase()}Min = 0;
83
+ }
84
+ `.appendNewLine() : ""}
85
+
86
+ ${maxParam != undefined ? expandToNode `
87
+ const ${property.name.toLowerCase()}MaxFunc = (system) => {
88
+ const context = {
89
+ object: this
90
+ };
91
+ ${translateMethodOrValueOrStored(property, maxParam)}
92
+ };
93
+ let ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}MaxFunc(this.system);
94
+ if (isNaN(${property.name.toLowerCase()}Max) || ${property.name.toLowerCase()}Max === undefined || ${property.name.toLowerCase()}Max === null) {
95
+ ${property.name.toLowerCase()}Max = 0;
96
+ }
97
+ `.appendNewLine() : ""}
98
+
99
+ // ${property.name} Number Derived Data
100
+ const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
101
+ const context = {
102
+ object: this
103
+ };
104
+ ${translateMethodOrValueOrStored(property, valueParam)}
105
+ };
106
+ Object.defineProperty(this.system, "${property.name.toLowerCase()}", {
107
+ get: () => {
108
+ let current = ${property.name.toLowerCase()}CurrentValueFunc(this.system);
109
+ // Protect against NaN from invalid operations (e.g., "A" + 2)
110
+ if (isNaN(current) || current === undefined || current === null) {
111
+ current = 0;
112
+ }
113
+ ${minParam != undefined ? expandToNode `
114
+ if ( current < ${property.name.toLowerCase()}Min ) {
115
+ current = ${property.name.toLowerCase()}Min;
116
+ }
117
+ `.appendNewLine() : ""}
118
+ ${maxParam != undefined ? expandToNode `
119
+ if ( current > ${property.name.toLowerCase()}Max ) {
120
+ current = ${property.name.toLowerCase()}Max;
121
+ }
122
+ `.appendNewLine() : ""}
123
+ return current;
124
+ },
125
+ configurable: true
126
+ });
127
+
128
128
  `.appendNewLineIfNotEmpty();
129
129
  }
130
- return expandToNode `
131
- // ${property.name} Number Derived Data
132
- const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
133
- const context = {
134
- object: this
135
- };
136
- ${translateMethodOrValueOrStored(property, valueParam)}
137
- };
138
- this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}CurrentValueFunc(this.system);
139
-
140
- ${minParam != undefined ? expandToNode `
141
- const ${property.name.toLowerCase()}MinFunc = (system) => {
142
- const context = {
143
- object: this
144
- };
145
- ${translateMethodOrValueOrStored(property, minParam)}
146
- };
147
- const ${property.name.toLowerCase()}Min = ${property.name.toLowerCase()}MinFunc(this.system);
148
- if ( this.system.${property.name.toLowerCase()} < ${property.name.toLowerCase()}Min ) {
149
- this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}Min;
150
- }
151
- `.appendNewLine() : ""}
152
-
153
- ${maxParam != undefined ? expandToNode `
154
- const ${property.name.toLowerCase()}MaxFunc = (system) => {
155
- const context = {
156
- object: this
157
- };
158
- ${translateMethodOrValueOrStored(property, maxParam)}
159
- };
160
- const ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}MaxFunc(this.system);
161
- if ( this.system.${property.name.toLowerCase()} > ${property.name.toLowerCase()}Max ) {
162
- this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}Max;
163
- }
164
- `.appendNewLine() : ""}
130
+ return expandToNode `
131
+ // ${property.name} Number Derived Data
132
+ const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
133
+ const context = {
134
+ object: this
135
+ };
136
+ ${translateMethodOrValueOrStored(property, valueParam)}
137
+ };
138
+ this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}CurrentValueFunc(this.system);
139
+
140
+ ${minParam != undefined ? expandToNode `
141
+ const ${property.name.toLowerCase()}MinFunc = (system) => {
142
+ const context = {
143
+ object: this
144
+ };
145
+ ${translateMethodOrValueOrStored(property, minParam)}
146
+ };
147
+ const ${property.name.toLowerCase()}Min = ${property.name.toLowerCase()}MinFunc(this.system);
148
+ if ( this.system.${property.name.toLowerCase()} < ${property.name.toLowerCase()}Min ) {
149
+ this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}Min;
150
+ }
151
+ `.appendNewLine() : ""}
152
+
153
+ ${maxParam != undefined ? expandToNode `
154
+ const ${property.name.toLowerCase()}MaxFunc = (system) => {
155
+ const context = {
156
+ object: this
157
+ };
158
+ ${translateMethodOrValueOrStored(property, maxParam)}
159
+ };
160
+ const ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}MaxFunc(this.system);
161
+ if ( this.system.${property.name.toLowerCase()} > ${property.name.toLowerCase()}Max ) {
162
+ this.system.${property.name.toLowerCase()} = ${property.name.toLowerCase()}Max;
163
+ }
164
+ `.appendNewLine() : ""}
165
165
  `.appendNewLineIfNotEmpty();
166
166
  }
167
167
  if (isMoneyField(property)) {
@@ -173,65 +173,65 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
173
173
  console.warn(`Money field ${property.name} has both denominations and value parameter. Value parameter will be ignored for multi-denomination money.`);
174
174
  return;
175
175
  }
176
- return expandToNode `
177
- // ${property.name} Money Calculated Data
178
- const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
179
- const context = {
180
- object: this
181
- };
182
- ${translateMethodOrValueOrStored(property, valueParam)}
183
- };
184
- Object.defineProperty(this.system, "${property.name.toLowerCase()}", {
185
- get: () => {
186
- return ${property.name.toLowerCase()}CurrentValueFunc(this.system);
187
- },
188
- configurable: true
189
- });
190
-
176
+ return expandToNode `
177
+ // ${property.name} Money Calculated Data
178
+ const ${property.name.toLowerCase()}CurrentValueFunc = (system) => {
179
+ const context = {
180
+ object: this
181
+ };
182
+ ${translateMethodOrValueOrStored(property, valueParam)}
183
+ };
184
+ Object.defineProperty(this.system, "${property.name.toLowerCase()}", {
185
+ get: () => {
186
+ return ${property.name.toLowerCase()}CurrentValueFunc(this.system);
187
+ },
188
+ configurable: true
189
+ });
190
+
191
191
  `.appendNewLineIfNotEmpty();
192
192
  }
193
193
  }
194
194
  if (isAttributeExp(property)) {
195
195
  console.log("Processing Derived Attribute: " + property.name);
196
196
  const modParam = property.params.find(p => isAttributeParamMod(p));
197
- return expandToNode `
198
-
199
- // ${property.name} Attribute Derived Data
200
- const ${property.name.toLowerCase()}CurrentValue = this.system.${property.name.toLowerCase()}?.value ?? 0;
201
- const ${property.name.toLowerCase()}CurrentMax = this.system.${property.name.toLowerCase()}?.max ?? 0;
202
- const ${property.name.toLowerCase()}ModFunc = (system) => {
203
- const context = {
204
- object: this
205
- };
206
- ${modParam ? translateExpression(entry, id, modParam.method, true, property) : `return ${property.name.toLowerCase()}CurrentValue`}
207
- };
208
- let ${property.name.toLowerCase()}Mod = ${property.name.toLowerCase()}ModFunc(this.system);
209
- // Protect against NaN from invalid operations
210
- if (isNaN(${property.name.toLowerCase()}Mod) || ${property.name.toLowerCase()}Mod === undefined || ${property.name.toLowerCase()}Mod === null) {
211
- ${property.name.toLowerCase()}Mod = 0;
212
- }
213
- this.system.${property.name.toLowerCase()} = {
214
- value: ${property.name.toLowerCase()}CurrentValue,
215
- max: ${property.name.toLowerCase()}CurrentMax,
216
- mod: ${property.name.toLowerCase()}Mod
217
- };
218
- if ( this.system.${property.name.toLowerCase()}.value > this.system.${property.name.toLowerCase()}.max ) {
219
- this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.max;
220
- }
197
+ return expandToNode `
198
+
199
+ // ${property.name} Attribute Derived Data
200
+ const ${property.name.toLowerCase()}CurrentValue = this.system.${property.name.toLowerCase()}?.value ?? 0;
201
+ const ${property.name.toLowerCase()}CurrentMax = this.system.${property.name.toLowerCase()}?.max ?? 0;
202
+ const ${property.name.toLowerCase()}ModFunc = (system) => {
203
+ const context = {
204
+ object: this
205
+ };
206
+ ${modParam ? translateExpression(entry, id, modParam.method, true, property) : `return ${property.name.toLowerCase()}CurrentValue`}
207
+ };
208
+ let ${property.name.toLowerCase()}Mod = ${property.name.toLowerCase()}ModFunc(this.system);
209
+ // Protect against NaN from invalid operations
210
+ if (isNaN(${property.name.toLowerCase()}Mod) || ${property.name.toLowerCase()}Mod === undefined || ${property.name.toLowerCase()}Mod === null) {
211
+ ${property.name.toLowerCase()}Mod = 0;
212
+ }
213
+ this.system.${property.name.toLowerCase()} = {
214
+ value: ${property.name.toLowerCase()}CurrentValue,
215
+ max: ${property.name.toLowerCase()}CurrentMax,
216
+ mod: ${property.name.toLowerCase()}Mod
217
+ };
218
+ if ( this.system.${property.name.toLowerCase()}.value > this.system.${property.name.toLowerCase()}.max ) {
219
+ this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.max;
220
+ }
221
221
  `.appendNewLineIfNotEmpty();
222
222
  }
223
223
  ;
224
224
  function generateValueOrMethod(value) {
225
225
  if (isMethodBlock(value)) {
226
- return expandToNode `
227
- const context = {
228
- object: this
229
- };
230
- ${translateExpression(entry, id, value, true, undefined)}
226
+ return expandToNode `
227
+ const context = {
228
+ object: this
229
+ };
230
+ ${translateExpression(entry, id, value, true, undefined)}
231
231
  `;
232
232
  }
233
- return expandToNode `
234
- return ${value};
233
+ return expandToNode `
234
+ return ${value};
235
235
  `;
236
236
  }
237
237
  if (isTrackerExp(property)) {
@@ -241,43 +241,43 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
241
241
  const valueParam = property.params.find(x => isNumberParamValue(x));
242
242
  if (maxParam == undefined && minParam == undefined && valueParam == undefined)
243
243
  return;
244
- return expandToNode `
245
- // ${property.name} Tracker Derived Data
246
- const ${property.name.toLowerCase()}TempValue = this.system.${property.name.toLowerCase()}.temp ?? 0;
247
- const ${property.name.toLowerCase()}CurrentMin = (system) => {
248
- ${minParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.min ?? 0;` : generateValueOrMethod(minParam.value)};
249
- }
250
- const ${property.name.toLowerCase()}CurrentValue = (system) => {
251
- ${valueParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.value ?? 0;` : generateValueOrMethod(valueParam.value)};
252
- }
253
- const ${property.name.toLowerCase()}CurrentMax = (system) => {
254
- ${maxParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.max ?? 0;` : generateValueOrMethod(maxParam.value)};
255
- }
256
- let ${property.name.toLowerCase()}Min = ${property.name.toLowerCase()}CurrentMin(this.system);
257
- let ${property.name.toLowerCase()}Value = ${property.name.toLowerCase()}CurrentValue(this.system);
258
- let ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}CurrentMax(this.system);
259
- // Protect against NaN from invalid operations
260
- if (isNaN(${property.name.toLowerCase()}Min) || ${property.name.toLowerCase()}Min === undefined || ${property.name.toLowerCase()}Min === null) {
261
- ${property.name.toLowerCase()}Min = 0;
262
- }
263
- if (isNaN(${property.name.toLowerCase()}Value) || ${property.name.toLowerCase()}Value === undefined || ${property.name.toLowerCase()}Value === null) {
264
- ${property.name.toLowerCase()}Value = 0;
265
- }
266
- if (isNaN(${property.name.toLowerCase()}Max) || ${property.name.toLowerCase()}Max === undefined || ${property.name.toLowerCase()}Max === null) {
267
- ${property.name.toLowerCase()}Max = 0;
268
- }
269
- this.system.${property.name.toLowerCase()} = {
270
- min: ${property.name.toLowerCase()}Min,
271
- value: ${property.name.toLowerCase()}Value,
272
- temp: ${property.name.toLowerCase()}TempValue,
273
- max: ${property.name.toLowerCase()}Max,
274
- };
275
- if ( !editMode && this.system.${property.name.toLowerCase()}.value < this.system.${property.name.toLowerCase()}.min ) {
276
- this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.min;
277
- }
278
- else if ( !editMode && this.system.${property.name.toLowerCase()}.value > this.system.${property.name.toLowerCase()}.max ) {
279
- this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.max;
280
- }
244
+ return expandToNode `
245
+ // ${property.name} Tracker Derived Data
246
+ const ${property.name.toLowerCase()}TempValue = this.system.${property.name.toLowerCase()}.temp ?? 0;
247
+ const ${property.name.toLowerCase()}CurrentMin = (system) => {
248
+ ${minParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.min ?? 0;` : generateValueOrMethod(minParam.value)};
249
+ }
250
+ const ${property.name.toLowerCase()}CurrentValue = (system) => {
251
+ ${valueParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.value ?? 0;` : generateValueOrMethod(valueParam.value)};
252
+ }
253
+ const ${property.name.toLowerCase()}CurrentMax = (system) => {
254
+ ${maxParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.max ?? 0;` : generateValueOrMethod(maxParam.value)};
255
+ }
256
+ let ${property.name.toLowerCase()}Min = ${property.name.toLowerCase()}CurrentMin(this.system);
257
+ let ${property.name.toLowerCase()}Value = ${property.name.toLowerCase()}CurrentValue(this.system);
258
+ let ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}CurrentMax(this.system);
259
+ // Protect against NaN from invalid operations
260
+ if (isNaN(${property.name.toLowerCase()}Min) || ${property.name.toLowerCase()}Min === undefined || ${property.name.toLowerCase()}Min === null) {
261
+ ${property.name.toLowerCase()}Min = 0;
262
+ }
263
+ if (isNaN(${property.name.toLowerCase()}Value) || ${property.name.toLowerCase()}Value === undefined || ${property.name.toLowerCase()}Value === null) {
264
+ ${property.name.toLowerCase()}Value = 0;
265
+ }
266
+ if (isNaN(${property.name.toLowerCase()}Max) || ${property.name.toLowerCase()}Max === undefined || ${property.name.toLowerCase()}Max === null) {
267
+ ${property.name.toLowerCase()}Max = 0;
268
+ }
269
+ this.system.${property.name.toLowerCase()} = {
270
+ min: ${property.name.toLowerCase()}Min,
271
+ value: ${property.name.toLowerCase()}Value,
272
+ temp: ${property.name.toLowerCase()}TempValue,
273
+ max: ${property.name.toLowerCase()}Max,
274
+ };
275
+ if ( !editMode && this.system.${property.name.toLowerCase()}.value < this.system.${property.name.toLowerCase()}.min ) {
276
+ this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.min;
277
+ }
278
+ else if ( !editMode && this.system.${property.name.toLowerCase()}.value > this.system.${property.name.toLowerCase()}.max ) {
279
+ this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.max;
280
+ }
281
281
  `.appendNewLineIfNotEmpty();
282
282
  }
283
283
  ;
@@ -289,80 +289,80 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
289
289
  if (maxParam == undefined && minParam == undefined && valueParam == undefined)
290
290
  return;
291
291
  //toBeReapplied.add("system." + property.name.toLowerCase() + ".max");
292
- return expandToNode `
293
- // ${property.name} Resource Derived Data
294
- const ${property.name.toLowerCase()}TempValue = this.system.${property.name.toLowerCase()}.temp ?? 0;
295
- const ${property.name.toLowerCase()}CurrentMin = (system) => {
296
- ${minParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.min ?? 0;` : generateValueOrMethod(minParam.value)};
297
- }
298
- const ${property.name.toLowerCase()}CurrentValue = (system) => {
299
- ${valueParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.value ?? 0;` : generateValueOrMethod(valueParam.value)};
300
- }
301
- const ${property.name.toLowerCase()}CurrentMax = (system) => {
302
- ${maxParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.max ?? 0;` : generateValueOrMethod(maxParam.value)};
303
- }
304
- let ${property.name.toLowerCase()}Value = ${property.name.toLowerCase()}CurrentValue(this.system);
305
- let ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}CurrentMax(this.system);
306
- // Protect against NaN from invalid operations
307
- if (isNaN(${property.name.toLowerCase()}Value) || ${property.name.toLowerCase()}Value === undefined || ${property.name.toLowerCase()}Value === null) {
308
- ${property.name.toLowerCase()}Value = 0;
309
- }
310
- if (isNaN(${property.name.toLowerCase()}Max) || ${property.name.toLowerCase()}Max === undefined || ${property.name.toLowerCase()}Max === null) {
311
- ${property.name.toLowerCase()}Max = 0;
312
- }
313
- this.system.${property.name.toLowerCase()} = {
314
- value: ${property.name.toLowerCase()}Value,
315
- temp: ${property.name.toLowerCase()}TempValue,
316
- max: ${property.name.toLowerCase()}Max
317
- };
318
- this.reapplyActiveEffectsForName("system.${property.name.toLowerCase()}.max");
319
- if ( !editMode && this.system.${property.name.toLowerCase()}.value < ${property.name.toLowerCase()}CurrentMin(this.system) ) {
320
- this.system.${property.name.toLowerCase()}.value = ${property.name.toLowerCase()}CurrentMin(this.system);
321
- }
322
- else if ( !editMode && this.system.${property.name.toLowerCase()}.value > this.system.${property.name.toLowerCase()}.max ) {
323
- this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.max;
324
- }
292
+ return expandToNode `
293
+ // ${property.name} Resource Derived Data
294
+ const ${property.name.toLowerCase()}TempValue = this.system.${property.name.toLowerCase()}.temp ?? 0;
295
+ const ${property.name.toLowerCase()}CurrentMin = (system) => {
296
+ ${minParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.min ?? 0;` : generateValueOrMethod(minParam.value)};
297
+ }
298
+ const ${property.name.toLowerCase()}CurrentValue = (system) => {
299
+ ${valueParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.value ?? 0;` : generateValueOrMethod(valueParam.value)};
300
+ }
301
+ const ${property.name.toLowerCase()}CurrentMax = (system) => {
302
+ ${maxParam == undefined ? expandToNode `return this.system.${property.name.toLowerCase()}?.max ?? 0;` : generateValueOrMethod(maxParam.value)};
303
+ }
304
+ let ${property.name.toLowerCase()}Value = ${property.name.toLowerCase()}CurrentValue(this.system);
305
+ let ${property.name.toLowerCase()}Max = ${property.name.toLowerCase()}CurrentMax(this.system);
306
+ // Protect against NaN from invalid operations
307
+ if (isNaN(${property.name.toLowerCase()}Value) || ${property.name.toLowerCase()}Value === undefined || ${property.name.toLowerCase()}Value === null) {
308
+ ${property.name.toLowerCase()}Value = 0;
309
+ }
310
+ if (isNaN(${property.name.toLowerCase()}Max) || ${property.name.toLowerCase()}Max === undefined || ${property.name.toLowerCase()}Max === null) {
311
+ ${property.name.toLowerCase()}Max = 0;
312
+ }
313
+ this.system.${property.name.toLowerCase()} = {
314
+ value: ${property.name.toLowerCase()}Value,
315
+ temp: ${property.name.toLowerCase()}TempValue,
316
+ max: ${property.name.toLowerCase()}Max
317
+ };
318
+ this.reapplyActiveEffectsForName("system.${property.name.toLowerCase()}.max");
319
+ if ( !editMode && this.system.${property.name.toLowerCase()}.value < ${property.name.toLowerCase()}CurrentMin(this.system) ) {
320
+ this.system.${property.name.toLowerCase()}.value = ${property.name.toLowerCase()}CurrentMin(this.system);
321
+ }
322
+ else if ( !editMode && this.system.${property.name.toLowerCase()}.value > this.system.${property.name.toLowerCase()}.max ) {
323
+ this.system.${property.name.toLowerCase()}.value = this.system.${property.name.toLowerCase()}.max;
324
+ }
325
325
  `.appendNewLineIfNotEmpty();
326
326
  }
327
327
  if (isTableField(property)) {
328
328
  console.log("Processing Derived Document Array: " + property.name);
329
329
  const whereParam = property.params.find(p => isWhereParam(p));
330
330
  if (whereParam) {
331
- return expandToNode `
332
- // ${property.name} Document Array Derived Data
333
- this.system.${property.name.toLowerCase()} = this.items.filter((item) => {
334
- if ( item.type !== "${(_a = property.document.ref) === null || _a === void 0 ? void 0 : _a.name.toLowerCase()}") return false;
335
- return ${translateExpression(entry, id, whereParam.value, true, property)};
336
- });
331
+ return expandToNode `
332
+ // ${property.name} Document Array Derived Data
333
+ this.system.${property.name.toLowerCase()} = this.items.filter((item) => {
334
+ if ( item.type !== "${(_a = property.document.ref) === null || _a === void 0 ? void 0 : _a.name.toLowerCase()}") return false;
335
+ return ${translateExpression(entry, id, whereParam.value, true, property)};
336
+ });
337
337
  `.appendNewLineIfNotEmpty();
338
338
  }
339
- return expandToNode `
340
- // ${property.name} Document Array Derived Data
341
- this.system.${property.name.toLowerCase()} = this.items.filter((item) => item.type == "${(_b = property.document.ref) === null || _b === void 0 ? void 0 : _b.name.toLowerCase()}");
339
+ return expandToNode `
340
+ // ${property.name} Document Array Derived Data
341
+ this.system.${property.name.toLowerCase()} = this.items.filter((item) => item.type == "${(_b = property.document.ref) === null || _b === void 0 ? void 0 : _b.name.toLowerCase()}");
342
342
  `.appendNewLineIfNotEmpty();
343
343
  }
344
344
  if (isMeasuredTemplateField(property)) {
345
- return expandToNode `
346
- // ${property.name} Measured Template Field Derived Data
347
-
348
- const ${property.name.toLowerCase()}Summary = () => {
349
- let sum = \`\${this.system.${property.name.toLowerCase()}.direction}° \${this.system.${property.name.toLowerCase()}.type} (\${this.system.${property.name.toLowerCase()}.distance} squares)\`;
350
- if (this.system.${property.name.toLowerCase()}.type === 'cone') sum += \` \${this.system.${property.name.toLowerCase()}.angle}° angle\`;
351
- if (this.system.${property.name.toLowerCase()}.type === 'ray') sum += \` \${this.system.${property.name.toLowerCase()}.width} squares wide\`;
352
- return sum;
353
- }
354
- this.system.${property.name.toLowerCase()}.summary = ${property.name.toLowerCase()}Summary();
345
+ return expandToNode `
346
+ // ${property.name} Measured Template Field Derived Data
347
+
348
+ const ${property.name.toLowerCase()}Summary = () => {
349
+ let sum = \`\${this.system.${property.name.toLowerCase()}.direction}° \${this.system.${property.name.toLowerCase()}.type} (\${this.system.${property.name.toLowerCase()}.distance} squares)\`;
350
+ if (this.system.${property.name.toLowerCase()}.type === 'cone') sum += \` \${this.system.${property.name.toLowerCase()}.angle}° angle\`;
351
+ if (this.system.${property.name.toLowerCase()}.type === 'ray') sum += \` \${this.system.${property.name.toLowerCase()}.width} squares wide\`;
352
+ return sum;
353
+ }
354
+ this.system.${property.name.toLowerCase()}.summary = ${property.name.toLowerCase()}Summary();
355
355
  `.appendNewLineIfNotEmpty();
356
356
  }
357
357
  if (isDiceField(property)) {
358
- return expandToNode `
359
- // ${property.name} Dice Field Derived Data
360
-
361
- const ${property.name.toLowerCase()}Value = () => {
362
- // Output a string of num + die
363
- return this.system.${property.name.toLowerCase()}.number + this.system.${property.name.toLowerCase()}.die;
364
- }
365
- this.system.${property.name.toLowerCase()}.value = ${property.name.toLowerCase()}Value();
358
+ return expandToNode `
359
+ // ${property.name} Dice Field Derived Data
360
+
361
+ const ${property.name.toLowerCase()}Value = () => {
362
+ // Output a string of num + die
363
+ return this.system.${property.name.toLowerCase()}.number + this.system.${property.name.toLowerCase()}.die;
364
+ }
365
+ this.system.${property.name.toLowerCase()}.value = ${property.name.toLowerCase()}Value();
366
366
  `.appendNewLineIfNotEmpty();
367
367
  }
368
368
  // if (isParentPropertyRefExp(property)) {
@@ -644,78 +644,78 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
644
644
  .filter(f => functionIsDerivedSafe(f));
645
645
  function generateDerivedFunction(functionDef) {
646
646
  const params = functionDef.params.map(p => p.param.name);
647
- return expandToNode `
648
- const function_${functionDef.name} = (system${params.length ? ", " + params.join(", ") : ""}) => {
649
- const context = { object: this };
650
- ${translateBodyExpressionToJavascript(entry, id, functionDef.method.body, true, functionDef)}
651
- };
647
+ return expandToNode `
648
+ const function_${functionDef.name} = (system${params.length ? ", " + params.join(", ") : ""}) => {
649
+ const context = { object: this };
650
+ ${translateBodyExpressionToJavascript(entry, id, functionDef.method.body, true, functionDef)}
651
+ };
652
652
  `;
653
653
  }
654
- return expandToNode `
655
- async _prepare${document.name}DerivedData() {
656
- const editMode = this.flags["${id}"]?.["edit-mode"] ?? true;
657
-
658
- ${joinToNode(derivedFunctions, generateDerivedFunction, { appendNewLineIfNotEmpty: true })}
659
-
660
- ${cycles.length > 0 ? expandToNode `
661
- // WARNING: Dependency cycles detected: ${cycles.map(cycle => cycle.join(' -> ')).join(', ')}
662
- // Properties will be processed in original order to prevent infinite loops.
663
- `.appendNewLine() : ""}
664
-
665
- ${joinToNode(sorted.map(dep => dep.property), property => generateDerivedAttribute(property), { appendNewLineIfNotEmpty: true })}
666
-
667
- ${isActor(document) ? expandToNode `
668
- // Reapply Active Effects for calculated values
669
- ${joinToNode(toBeReapplied, name => expandToNode `this.reapplyActiveEffectsForName("${name}");`, { appendNewLineIfNotEmpty: true })}
670
-
671
- // Bridge fighter.system.* active effect overrides into system.* for flat (non-nested) fields.
672
- // allApplicableEffects cannot strip the type prefix in V14 (changes is getter-only),
673
- // so Foundry applies flat fields (e.g. bonusdamage, resistanceflat) to actor.fighter.system.*
674
- // Nested fields (e.g. fight.value) are handled by reapplyActiveEffectsForName above.
675
- const _typeOverrides = this.overrides?.${document.name.toLowerCase()}?.system ?? {};
676
- for (const [_k, _v] of Object.entries(_typeOverrides)) {
677
- if (typeof _v !== 'object' && _k in this.system) {
678
- this.system[_k] = _v;
679
- }
680
- }
681
- ` : ""}
682
- }
654
+ return expandToNode `
655
+ async _prepare${document.name}DerivedData() {
656
+ const editMode = this.flags["${id}"]?.["edit-mode"] ?? true;
657
+
658
+ ${joinToNode(derivedFunctions, generateDerivedFunction, { appendNewLineIfNotEmpty: true })}
659
+
660
+ ${cycles.length > 0 ? expandToNode `
661
+ // WARNING: Dependency cycles detected: ${cycles.map(cycle => cycle.join(' -> ')).join(', ')}
662
+ // Properties will be processed in original order to prevent infinite loops.
663
+ `.appendNewLine() : ""}
664
+
665
+ ${joinToNode(sorted.map(dep => dep.property), property => generateDerivedAttribute(property), { appendNewLineIfNotEmpty: true })}
666
+
667
+ ${isActor(document) ? expandToNode `
668
+ // Reapply Active Effects for calculated values
669
+ ${joinToNode(toBeReapplied, name => expandToNode `this.reapplyActiveEffectsForName("${name}");`, { appendNewLineIfNotEmpty: true })}
670
+
671
+ // Bridge fighter.system.* active effect overrides into system.* for flat (non-nested) fields.
672
+ // allApplicableEffects cannot strip the type prefix in V14 (changes is getter-only),
673
+ // so Foundry applies flat fields (e.g. bonusdamage, resistanceflat) to actor.fighter.system.*
674
+ // Nested fields (e.g. fight.value) are handled by reapplyActiveEffectsForName above.
675
+ const _typeOverrides = this.overrides?.${document.name.toLowerCase()}?.system ?? {};
676
+ for (const [_k, _v] of Object.entries(_typeOverrides)) {
677
+ if (typeof _v !== 'object' && _k in this.system) {
678
+ this.system[_k] = _v;
679
+ }
680
+ }
681
+ ` : ""}
682
+ }
683
683
  `.appendNewLineIfNotEmpty().appendNewLine();
684
684
  }
685
685
  function generateActorPreUpdate(document) {
686
686
  const allResources = getAllOfType(document.body, isResourceExp);
687
687
  const healthResource = allResources.find(x => x.tag == "health");
688
688
  if (healthResource == undefined)
689
- return expandToNode `
690
- _handlePreUpdate${document.name}Delta(changes, deltas) {
691
- // No health resource defined
692
- }
689
+ return expandToNode `
690
+ _handlePreUpdate${document.name}Delta(changes, deltas) {
691
+ // No health resource defined
692
+ }
693
693
  `.appendNewLine();
694
- return expandToNode `
695
- _handlePreUpdate${document.name}Delta(changes, deltas) {
696
- // Health resource updates
697
- if (changes.system.${healthResource.name.toLowerCase()} === undefined) return;
698
-
699
- // Store value and temp changes
700
- const valueChange = changes.system.${healthResource.name.toLowerCase()}.value;
701
- const tempChange = changes.system.${healthResource.name.toLowerCase()}.temp;
702
-
703
- // Calculate delta
704
- if (valueChange !== undefined) {
705
- const delta = valueChange - this.system.${healthResource.name.toLowerCase()}.value;
706
- if (delta !== 0) {
707
- deltas.${healthResource.name.toLowerCase()} = delta;
708
- }
709
- }
710
-
711
- // Calculate temp delta
712
- if (tempChange !== undefined) {
713
- const tempDelta = tempChange - this.system.${healthResource.name.toLowerCase()}.temp;
714
- if (tempDelta !== 0) {
715
- deltas.${healthResource.name.toLowerCase()}Temp = tempDelta;
716
- }
717
- }
718
- }
694
+ return expandToNode `
695
+ _handlePreUpdate${document.name}Delta(changes, deltas) {
696
+ // Health resource updates
697
+ if (changes.system.${healthResource.name.toLowerCase()} === undefined) return;
698
+
699
+ // Store value and temp changes
700
+ const valueChange = changes.system.${healthResource.name.toLowerCase()}.value;
701
+ const tempChange = changes.system.${healthResource.name.toLowerCase()}.temp;
702
+
703
+ // Calculate delta
704
+ if (valueChange !== undefined) {
705
+ const delta = valueChange - this.system.${healthResource.name.toLowerCase()}.value;
706
+ if (delta !== 0) {
707
+ deltas.${healthResource.name.toLowerCase()} = delta;
708
+ }
709
+ }
710
+
711
+ // Calculate temp delta
712
+ if (tempChange !== undefined) {
713
+ const tempDelta = tempChange - this.system.${healthResource.name.toLowerCase()}.temp;
714
+ if (tempDelta !== 0) {
715
+ deltas.${healthResource.name.toLowerCase()}Temp = tempDelta;
716
+ }
717
+ }
718
+ }
719
719
  `.appendNewLine();
720
720
  }
721
721
  function generateDeltas(documents) {
@@ -726,179 +726,179 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
726
726
  const healthResource = getAllOfType(document.body, isResourceExp).find(x => x.tag == "health");
727
727
  if (healthResource == undefined)
728
728
  return;
729
- return expandToNode `
730
- case "${document.name.toLowerCase()}": {
731
- // ${healthResource.name} health resource
732
-
733
- if ( !data.prototypeToken.bar1.attribute ) data.prototypeToken.bar1.attribute = "${healthResource.name.toLowerCase()}";
734
- if ( !data.prototypeToken.displayBars ) data.prototypeToken.displayBars = CONST.TOKEN_DISPLAY_MODES.ALWAYS;
735
- }
729
+ return expandToNode `
730
+ case "${document.name.toLowerCase()}": {
731
+ // ${healthResource.name} health resource
732
+
733
+ if ( !data.prototypeToken.bar1.attribute ) data.prototypeToken.bar1.attribute = "${healthResource.name.toLowerCase()}";
734
+ if ( !data.prototypeToken.displayBars ) data.prototypeToken.displayBars = CONST.TOKEN_DISPLAY_MODES.ALWAYS;
735
+ }
736
736
  `.appendNewLineIfNotEmpty();
737
737
  }
738
- return expandToNode `
739
- switch ( data.type ) {
740
- ${joinToNode(documents.filter(x => isActor(x)), document => documentHealthResource(document), { appendNewLineIfNotEmpty: true })}
741
- }
738
+ return expandToNode `
739
+ switch ( data.type ) {
740
+ ${joinToNode(documents.filter(x => isActor(x)), document => documentHealthResource(document), { appendNewLineIfNotEmpty: true })}
741
+ }
742
742
  `.appendNewLineIfNotEmpty();
743
743
  }
744
- return expandToNode `
745
- async _preUpdate(data, options, userId) {
746
- await super._preUpdate(data, options, userId);
747
- if (!options.diff || data === undefined) return;
748
- let changes = {};
749
-
750
- // Foundry v12 no longer has diffed data during _preUpdate, so we need to compute it ourselves.
751
- if (game.release.version >= 12) {
752
- // Retrieve a copy of the existing actor data.
753
- let newData = game.system.utils.flattenObject(data);
754
- let oldData = game.system.utils.flattenObject(this);
755
-
756
- // Limit data to just the new data.
757
- const diffData = foundry.utils.diffObject(oldData, newData);
758
- changes = foundry.utils.expandObject(diffData);
759
- }
760
- else {
761
- changes = foundry.utils.duplicate(data);
762
- }
763
-
764
- // Handle name changes
765
- if (changes.name) {
766
- let tokenData = {};
767
-
768
- // Propagate name update to prototype token if same as actor
769
- if (changes.name && this.name == this.prototypeToken.name) {
770
- data.prototypeToken = {name: data.name};
771
- }
772
-
773
- // Update tokens.
774
- let tokens = this.getActiveTokens();
775
- tokens.forEach(token => {
776
- let updateData = foundry.utils.duplicate(tokenData);
777
-
778
- // Propagate name update to token if same as actor
779
- if (data.name && this.name == token.name) {
780
- updateData.name = data.name;
781
- }
782
- token.document.update(updateData);
783
- });
784
- }
785
-
786
- if (changes.system === undefined) return; // Nothing more to do
787
-
788
- const deltas = {};
789
-
790
- ${joinToNode(documents.filter(x => isActor(x)), document => expandToNode `
791
- if (this.type == "${document.name.toLowerCase()}") this._handlePreUpdate${document.name}Delta(changes, deltas);
792
- `, { appendNewLineIfNotEmpty: true })}
793
-
794
- options.fromPreUpdate = deltas;
795
- }
796
-
797
- /* -------------------------------------------- */
798
-
799
- ${joinToNode(documents.filter(x => isActor(x)), document => generateActorPreUpdate(document), { appendNewLineIfNotEmpty: true })}
800
-
801
- /* -------------------------------------------- */
802
-
803
- async _onUpdate(data, options, userId) {
804
- await super._onUpdate(data, options, userId);
805
-
806
- // Iterate over all objects in fromPreUpdate, showing scrolling text for each.
807
- if (options.fromPreUpdate) {
808
- for (const [key, delta] of Object.entries(options.fromPreUpdate)) {
809
- this._showScrollingText(delta, key);
810
- }
811
- }
812
-
813
- // Add / remove status effects
814
- const calculatedStatusEffects = CONFIG.statusEffects.filter(effect => effect.calculated);
815
- for (const effect of calculatedStatusEffects) {
816
- const key = effect.id;
817
- const active = this.system[key] ?? false;
818
- const existing = this.effects.find(e => e.statuses.has(key));
819
-
820
- if ((active && existing) || (!active && !existing)) continue;
821
-
822
- // If the effect is active the AE doesn't exist, add it
823
- if (active && !existing) {
824
- const cls = getDocumentClass("ActiveEffect");
825
- const createData = foundry.utils.deepClone(effect);
826
- createData.statuses = [key];
827
- delete createData.id;
828
- createData.name = game.i18n.localize(createData.name);
829
- await cls.create(createData, {parent: this});
830
- if (key == "dead") Hooks.callAll("death", this);
831
- }
832
-
833
- // If the effect is active the AE doesn't exist, add it
834
- if (!active && existing) {
835
- this.deleteEmbeddedDocuments("ActiveEffect", [existing.id]);
836
- }
837
- }
838
- }
839
-
840
- /* -------------------------------------------- */
841
-
842
- async _onCreate(data, options, userId) {
843
- await super._onCreate(data, options, userId);
844
-
845
- console.log("onCreate", data, options, userId);
846
-
847
- ${generateHealthResourceAssignments(documents)}
848
- }
849
-
850
- /* -------------------------------------------- */
851
-
852
- _showScrollingText(delta, suffix="", overrideOptions={}) {
853
- // Show scrolling text of hp update
854
- const tokens = this.isToken ? [this.token?.object] : this.getActiveTokens(true);
855
- if (delta != 0 && tokens.length > 0) {
856
- let color = delta < 0 ? 0xcc0000 : 0x00cc00;
857
- for ( let token of tokens ) {
858
- let textOptions = {
859
- anchor: CONST.TEXT_ANCHOR_POINTS.CENTER,
860
- direction: CONST.TEXT_ANCHOR_POINTS.TOP,
861
- fontSize: 32,
862
- fill: color,
863
- stroke: 0x000000,
864
- strokeThickness: 4,
865
- duration: 3000
866
- };
867
- canvas.interface.createScrollingText(
868
- token.center,
869
- delta.signedString()+" "+suffix,
870
- foundry.utils.mergeObject(textOptions, overrideOptions)
871
- );
872
- // Flash dynamic token rings.
873
- if (token?.ring) {
874
- const flashColor = delta < 0 ? Color.fromString('#ff0000') : Color.fromString('#00ff00');
875
- token.ring.flashColor(flashColor, {
876
- duration: 600,
877
- easing: foundry.canvas.tokens.TokenRing.easeTwoPeaks,
878
- });
879
- }
880
- }
881
- }
882
- }
744
+ return expandToNode `
745
+ async _preUpdate(data, options, userId) {
746
+ await super._preUpdate(data, options, userId);
747
+ if (!options.diff || data === undefined) return;
748
+ let changes = {};
749
+
750
+ // Foundry v12 no longer has diffed data during _preUpdate, so we need to compute it ourselves.
751
+ if (game.release.version >= 12) {
752
+ // Retrieve a copy of the existing actor data.
753
+ let newData = game.system.utils.flattenObject(data);
754
+ let oldData = game.system.utils.flattenObject(this);
755
+
756
+ // Limit data to just the new data.
757
+ const diffData = foundry.utils.diffObject(oldData, newData);
758
+ changes = foundry.utils.expandObject(diffData);
759
+ }
760
+ else {
761
+ changes = foundry.utils.duplicate(data);
762
+ }
763
+
764
+ // Handle name changes
765
+ if (changes.name) {
766
+ let tokenData = {};
767
+
768
+ // Propagate name update to prototype token if same as actor
769
+ if (changes.name && this.name == this.prototypeToken.name) {
770
+ data.prototypeToken = {name: data.name};
771
+ }
772
+
773
+ // Update tokens.
774
+ let tokens = this.getActiveTokens();
775
+ tokens.forEach(token => {
776
+ let updateData = foundry.utils.duplicate(tokenData);
777
+
778
+ // Propagate name update to token if same as actor
779
+ if (data.name && this.name == token.name) {
780
+ updateData.name = data.name;
781
+ }
782
+ token.document.update(updateData);
783
+ });
784
+ }
785
+
786
+ if (changes.system === undefined) return; // Nothing more to do
787
+
788
+ const deltas = {};
789
+
790
+ ${joinToNode(documents.filter(x => isActor(x)), document => expandToNode `
791
+ if (this.type == "${document.name.toLowerCase()}") this._handlePreUpdate${document.name}Delta(changes, deltas);
792
+ `, { appendNewLineIfNotEmpty: true })}
793
+
794
+ options.fromPreUpdate = deltas;
795
+ }
796
+
797
+ /* -------------------------------------------- */
798
+
799
+ ${joinToNode(documents.filter(x => isActor(x)), document => generateActorPreUpdate(document), { appendNewLineIfNotEmpty: true })}
800
+
801
+ /* -------------------------------------------- */
802
+
803
+ async _onUpdate(data, options, userId) {
804
+ await super._onUpdate(data, options, userId);
805
+
806
+ // Iterate over all objects in fromPreUpdate, showing scrolling text for each.
807
+ if (options.fromPreUpdate) {
808
+ for (const [key, delta] of Object.entries(options.fromPreUpdate)) {
809
+ this._showScrollingText(delta, key);
810
+ }
811
+ }
812
+
813
+ // Add / remove status effects
814
+ const calculatedStatusEffects = CONFIG.statusEffects.filter(effect => effect.calculated);
815
+ for (const effect of calculatedStatusEffects) {
816
+ const key = effect.id;
817
+ const active = this.system[key] ?? false;
818
+ const existing = this.effects.find(e => e.statuses.has(key));
819
+
820
+ if ((active && existing) || (!active && !existing)) continue;
821
+
822
+ // If the effect is active the AE doesn't exist, add it
823
+ if (active && !existing) {
824
+ const cls = getDocumentClass("ActiveEffect");
825
+ const createData = foundry.utils.deepClone(effect);
826
+ createData.statuses = [key];
827
+ delete createData.id;
828
+ createData.name = game.i18n.localize(createData.name);
829
+ await cls.create(createData, {parent: this});
830
+ if (key == "dead") Hooks.callAll("death", this);
831
+ }
832
+
833
+ // If the effect is active the AE doesn't exist, add it
834
+ if (!active && existing) {
835
+ this.deleteEmbeddedDocuments("ActiveEffect", [existing.id]);
836
+ }
837
+ }
838
+ }
839
+
840
+ /* -------------------------------------------- */
841
+
842
+ async _onCreate(data, options, userId) {
843
+ await super._onCreate(data, options, userId);
844
+
845
+ console.log("onCreate", data, options, userId);
846
+
847
+ ${generateHealthResourceAssignments(documents)}
848
+ }
849
+
850
+ /* -------------------------------------------- */
851
+
852
+ _showScrollingText(delta, suffix="", overrideOptions={}) {
853
+ // Show scrolling text of hp update
854
+ const tokens = this.isToken ? [this.token?.object] : this.getActiveTokens(true);
855
+ if (delta != 0 && tokens.length > 0) {
856
+ let color = delta < 0 ? 0xcc0000 : 0x00cc00;
857
+ for ( let token of tokens ) {
858
+ let textOptions = {
859
+ anchor: CONST.TEXT_ANCHOR_POINTS.CENTER,
860
+ direction: CONST.TEXT_ANCHOR_POINTS.TOP,
861
+ fontSize: 32,
862
+ fill: color,
863
+ stroke: 0x000000,
864
+ strokeThickness: 4,
865
+ duration: 3000
866
+ };
867
+ canvas.interface.createScrollingText(
868
+ token.center,
869
+ delta.signedString()+" "+suffix,
870
+ foundry.utils.mergeObject(textOptions, overrideOptions)
871
+ );
872
+ // Flash dynamic token rings.
873
+ if (token?.ring) {
874
+ const flashColor = delta < 0 ? Color.fromString('#ff0000') : Color.fromString('#00ff00');
875
+ token.ring.flashColor(flashColor, {
876
+ duration: 600,
877
+ easing: foundry.canvas.tokens.TokenRing.easeTwoPeaks,
878
+ });
879
+ }
880
+ }
881
+ }
882
+ }
883
883
  `.appendNewLineIfNotEmpty();
884
884
  }
885
885
  function generateInitiativeFormula(document) {
886
886
  var _a;
887
887
  const initiativeAttribute = getAllOfType(document.body, isInitiativeProperty);
888
888
  if (initiativeAttribute.length == 0) {
889
- return expandToNode `
890
- case "${document.name.toLowerCase()}": return "0";
889
+ return expandToNode `
890
+ case "${document.name.toLowerCase()}": return "0";
891
891
  `.appendNewLineIfNotEmpty();
892
892
  }
893
893
  let initiative = (_a = initiativeAttribute[0]) === null || _a === void 0 ? void 0 : _a.value;
894
894
  if (initiative == undefined) {
895
- return expandToNode `
896
- case "${document.name.toLowerCase()}": return "0";
895
+ return expandToNode `
896
+ case "${document.name.toLowerCase()}": return "0";
897
897
  `.appendNewLineIfNotEmpty();
898
898
  }
899
899
  console.log("Initiative Formula");
900
- return expandToNode `
901
- case "${document.name.toLowerCase()}": return "${translateExpression(entry, id, initiative, true, initiativeAttribute[0])}";
900
+ return expandToNode `
901
+ case "${document.name.toLowerCase()}": return "${translateExpression(entry, id, initiative, true, initiativeAttribute[0])}";
902
902
  `.appendNewLineIfNotEmpty();
903
903
  }
904
904
  function generateDocumentHooks(document) {
@@ -907,177 +907,177 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
907
907
  var _a;
908
908
  let name = hook.name;
909
909
  function generateBody() {
910
- return expandToNode `
911
- const ${entry.config.name}Roll = game.system.rollClass;
912
- const ${entry.config.name}DamageRoll = game.system.damageRollClass;
913
- const context = {
914
- object: document,
915
- target: game.user.getTargetOrNothing()
916
- };
917
- const system = document.system;
918
- let update = {};
919
- let embeddedUpdate = {};
920
- let parentUpdate = {};
921
- let parentEmbeddedUpdate = {};
922
- let targetUpdate = {};
923
- let targetEmbeddedUpdate = {};
924
- let selfDeleted = false;
925
-
926
- ${translateBodyExpressionToJavascript(entry, id, hook.body, false, hook)}
927
-
928
- if (!selfDeleted && Object.keys(update).length > 0) {
929
- await document.update(update);
930
- }
931
- if (!selfDeleted && Object.keys(embeddedUpdate).length > 0) {
932
- for (let key of Object.keys(embeddedUpdate)) {
933
- await document.updateEmbeddedDocuments("Item", embeddedUpdate[key]);
934
- }
935
- }
936
- if (Object.keys(parentUpdate).length > 0) {
937
- await document.parent.update(parentUpdate);
938
- }
939
- if (Object.keys(parentEmbeddedUpdate).length > 0) {
940
- for (let key of Object.keys(parentEmbeddedUpdate)) {
941
- await document.parent.updateEmbeddedDocuments("Item", parentEmbeddedUpdate[key]);
942
- }
943
- }
944
- if (Object.keys(targetUpdate).length > 0) {
945
- await context.target.update(targetUpdate);
946
- }
947
- if (Object.keys(targetEmbeddedUpdate).length > 0) {
948
- for (let key of Object.keys(targetEmbeddedUpdate)) {
949
- await context.target.updateEmbeddedDocuments("Item", targetEmbeddedUpdate[key]);
950
- }
951
- }
910
+ return expandToNode `
911
+ const ${entry.config.name}Roll = game.system.rollClass;
912
+ const ${entry.config.name}DamageRoll = game.system.damageRollClass;
913
+ const context = {
914
+ object: document,
915
+ target: game.user.getTargetOrNothing()
916
+ };
917
+ const system = document.system;
918
+ let update = {};
919
+ let embeddedUpdate = {};
920
+ let parentUpdate = {};
921
+ let parentEmbeddedUpdate = {};
922
+ let targetUpdate = {};
923
+ let targetEmbeddedUpdate = {};
924
+ let selfDeleted = false;
925
+
926
+ ${translateBodyExpressionToJavascript(entry, id, hook.body, false, hook)}
927
+
928
+ if (!selfDeleted && Object.keys(update).length > 0) {
929
+ await document.update(update);
930
+ }
931
+ if (!selfDeleted && Object.keys(embeddedUpdate).length > 0) {
932
+ for (let key of Object.keys(embeddedUpdate)) {
933
+ await document.updateEmbeddedDocuments("Item", embeddedUpdate[key]);
934
+ }
935
+ }
936
+ if (Object.keys(parentUpdate).length > 0) {
937
+ await document.parent.update(parentUpdate);
938
+ }
939
+ if (Object.keys(parentEmbeddedUpdate).length > 0) {
940
+ for (let key of Object.keys(parentEmbeddedUpdate)) {
941
+ await document.parent.updateEmbeddedDocuments("Item", parentEmbeddedUpdate[key]);
942
+ }
943
+ }
944
+ if (Object.keys(targetUpdate).length > 0) {
945
+ await context.target.update(targetUpdate);
946
+ }
947
+ if (Object.keys(targetEmbeddedUpdate).length > 0) {
948
+ for (let key of Object.keys(targetEmbeddedUpdate)) {
949
+ await context.target.updateEmbeddedDocuments("Item", targetEmbeddedUpdate[key]);
950
+ }
951
+ }
952
952
  `;
953
953
  }
954
954
  switch (name) {
955
955
  case "combatStart":
956
- return expandToNode `
957
- Hooks.on("combatStart", async (${hook.params.map(p => p.name).join(", ")}) => {
958
- ${generateBody()}
959
- });
956
+ return expandToNode `
957
+ Hooks.on("combatStart", async (${hook.params.map(p => p.name).join(", ")}) => {
958
+ ${generateBody()}
959
+ });
960
960
  `.appendNewLineIfNotEmpty();
961
961
  case "combatEnd":
962
- return expandToNode `
963
- Hooks.on("deleteCombat", async (${hook.params.map(p => p.name).join(", ")}) => {
964
- ${generateBody()}
965
- });
962
+ return expandToNode `
963
+ Hooks.on("deleteCombat", async (${hook.params.map(p => p.name).join(", ")}) => {
964
+ ${generateBody()}
965
+ });
966
966
  `.appendNewLineIfNotEmpty();
967
967
  case "roundStart":
968
- return expandToNode `
969
- Hooks.on("combatRound", async (combat, updateData, updateOptions) => {
970
- const roundStart = async (${hook.params.map(p => p.name).join(", ")}) => {
971
- ${generateBody()}
972
- }
973
-
974
- if (updateData.turn == 0) {
975
- await roundStart(updateData.round);
976
- }
977
- });
968
+ return expandToNode `
969
+ Hooks.on("combatRound", async (combat, updateData, updateOptions) => {
970
+ const roundStart = async (${hook.params.map(p => p.name).join(", ")}) => {
971
+ ${generateBody()}
972
+ }
973
+
974
+ if (updateData.turn == 0) {
975
+ await roundStart(updateData.round);
976
+ }
977
+ });
978
978
  `.appendNewLineIfNotEmpty();
979
979
  case "roundEnd":
980
- return expandToNode `
981
- Hooks.on("combatRound", async (combat, updateData, updateOptions) => {
982
- const roundEnd = async (${hook.params.map(p => p.name).join(", ")}) => {
983
- ${generateBody()}
984
- }
985
-
986
- if (updateData.round > 0) {
987
- roundEnd(updateData.round - 1);
988
- }
989
- });
980
+ return expandToNode `
981
+ Hooks.on("combatRound", async (combat, updateData, updateOptions) => {
982
+ const roundEnd = async (${hook.params.map(p => p.name).join(", ")}) => {
983
+ ${generateBody()}
984
+ }
985
+
986
+ if (updateData.round > 0) {
987
+ roundEnd(updateData.round - 1);
988
+ }
989
+ });
990
990
  `.appendNewLineIfNotEmpty();
991
991
  case "turnStart":
992
- return expandToNode `
993
- Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
994
- const turnStart = async () => {
995
- ${generateBody()}
996
- }
997
- if (combat.combatant.actor.uuid == document.uuid) {
998
- await turnStart();
999
- }
1000
- });
992
+ return expandToNode `
993
+ Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
994
+ const turnStart = async () => {
995
+ ${generateBody()}
996
+ }
997
+ if (combat.combatant.actor.uuid == document.uuid) {
998
+ await turnStart();
999
+ }
1000
+ });
1001
1001
  `.appendNewLineIfNotEmpty();
1002
1002
  case "turnEnd":
1003
- return expandToNode `
1004
- Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
1005
- const turnEnd = async () => {
1006
- ${generateBody()}
1007
- }
1008
- const previousCombatant = combat.combatants.get(combat.previous?.combatantId);
1009
- if (previousCombatant?.actor?.uuid == document.uuid) {
1010
- await turnEnd();
1011
- }
1012
- });
1003
+ return expandToNode `
1004
+ Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
1005
+ const turnEnd = async () => {
1006
+ ${generateBody()}
1007
+ }
1008
+ const previousCombatant = combat.combatants.get(combat.previous?.combatantId);
1009
+ if (previousCombatant?.actor?.uuid == document.uuid) {
1010
+ await turnEnd();
1011
+ }
1012
+ });
1013
1013
  `.appendNewLineIfNotEmpty();
1014
1014
  case "turnIsNext":
1015
- return expandToNode `
1016
- Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
1017
- const turnIsNext = async () => {
1018
- ${generateBody()}
1019
- };
1020
- if (combat.nextCombatant.actor.uuid == document.uuid) {
1021
- await turnIsNext();
1022
- }
1023
- });
1015
+ return expandToNode `
1016
+ Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
1017
+ const turnIsNext = async () => {
1018
+ ${generateBody()}
1019
+ };
1020
+ if (combat.nextCombatant.actor.uuid == document.uuid) {
1021
+ await turnIsNext();
1022
+ }
1023
+ });
1024
1024
  `.appendNewLineIfNotEmpty();
1025
1025
  case "death":
1026
- return expandToNode `
1027
- Hooks.on("death", async (deadDocument) => {
1028
- const onDeath = async () => {
1029
- ${generateBody()}
1030
- };
1031
- if ( deadDocument.uuid == document.uuid ) {
1032
- await onDeath();
1033
- }
1034
- });
1026
+ return expandToNode `
1027
+ Hooks.on("death", async (deadDocument) => {
1028
+ const onDeath = async () => {
1029
+ ${generateBody()}
1030
+ };
1031
+ if ( deadDocument.uuid == document.uuid ) {
1032
+ await onDeath();
1033
+ }
1034
+ });
1035
1035
  `.appendNewLineIfNotEmpty();
1036
1036
  case "preApplyDamage":
1037
1037
  case "preApplyHealing":
1038
1038
  case "preApplyTemp":
1039
- return expandToNode `
1040
- if (game.system.documentHooks.has("${name}-" + this.uuid)) return;
1041
- const on${name} = async (document, context) => {
1042
- const preApply = async (${hook.params.map(p => p.name).join(", ")}) => {
1043
- ${generateBody()}
1044
- return ${(_a = hook.params.shift()) === null || _a === void 0 ? void 0 : _a.name};
1045
- }
1046
- if (document.uuid == this.uuid) {
1047
- context.amount = await preApply(context.amount, context.damageType, context.damageMetadata);
1048
- }
1049
- }
1050
- game.system.documentHooks.set("${name}-" + this.uuid, on${name});
1051
- Hooks.on("${name}", on${name});
1039
+ return expandToNode `
1040
+ if (game.system.documentHooks.has("${name}-" + this.uuid)) return;
1041
+ const on${name} = async (document, context) => {
1042
+ const preApply = async (${hook.params.map(p => p.name).join(", ")}) => {
1043
+ ${generateBody()}
1044
+ return ${(_a = hook.params.shift()) === null || _a === void 0 ? void 0 : _a.name};
1045
+ }
1046
+ if (document.uuid == this.uuid) {
1047
+ context.amount = await preApply(context.amount, context.damageType, context.damageMetadata);
1048
+ }
1049
+ }
1050
+ game.system.documentHooks.set("${name}-" + this.uuid, on${name});
1051
+ Hooks.on("${name}", on${name});
1052
1052
  `.appendNewLineIfNotEmpty();
1053
1053
  case "appliedDamage":
1054
1054
  case "appliedHealing":
1055
1055
  case "appliedTemp":
1056
- return expandToNode `
1057
- if (game.system.documentHooks.has("${name}-" + this.uuid)) return;
1058
- const on${name} = async (document, context) => {
1059
- const applied = async (${hook.params.map(p => p.name).join(", ")}) => {
1060
- ${generateBody()}
1061
- }
1062
- if (document.uuid == this.uuid) {
1063
- await applied(context.amount, context.damageType, context.damageMetadata);
1064
- }
1065
- };
1066
- game.system.documentHooks.set("${name}-" + this.uuid, on${name});
1067
- Hooks.on("${name}", on${name});
1056
+ return expandToNode `
1057
+ if (game.system.documentHooks.has("${name}-" + this.uuid)) return;
1058
+ const on${name} = async (document, context) => {
1059
+ const applied = async (${hook.params.map(p => p.name).join(", ")}) => {
1060
+ ${generateBody()}
1061
+ }
1062
+ if (document.uuid == this.uuid) {
1063
+ await applied(context.amount, context.damageType, context.damageMetadata);
1064
+ }
1065
+ };
1066
+ game.system.documentHooks.set("${name}-" + this.uuid, on${name});
1067
+ Hooks.on("${name}", on${name});
1068
1068
  `.appendNewLineIfNotEmpty();
1069
1069
  default:
1070
- return expandToNode `
1071
- Hooks.on("${name}", async (${hook.params.map(p => p.name).join(", ")}) => {
1072
- ${generateBody()}
1073
- });
1070
+ return expandToNode `
1071
+ Hooks.on("${name}", async (${hook.params.map(p => p.name).join(", ")}) => {
1072
+ ${generateBody()}
1073
+ });
1074
1074
  `.appendNewLineIfNotEmpty();
1075
1075
  }
1076
1076
  }
1077
- return expandToNode `
1078
- _register${document.name}Hooks(document) {
1079
- ${joinToNode(hooks, generateHook, { appendNewLineIfNotEmpty: true })}
1080
- }
1077
+ return expandToNode `
1078
+ _register${document.name}Hooks(document) {
1079
+ ${joinToNode(hooks, generateHook, { appendNewLineIfNotEmpty: true })}
1080
+ }
1081
1081
  `.appendNewLineIfNotEmpty().appendNewLine();
1082
1082
  }
1083
1083
  // Number fields whose `initial:` is an expression (method block) can't be a static schema
@@ -1094,27 +1094,27 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
1094
1094
  });
1095
1095
  if (exprInitialFields.length === 0)
1096
1096
  return undefined;
1097
- return expandToNode `
1098
- case "${document.name.toLowerCase()}": {
1097
+ return expandToNode `
1098
+ case "${document.name.toLowerCase()}": {
1099
1099
  ${joinToNode(exprInitialFields, f => {
1100
1100
  const initParam = f.params.find(p => isNumberParamInitial(p));
1101
1101
  const field = f.name.toLowerCase();
1102
- return expandToNode `
1103
- // ${f.name} expression initial
1104
- if (foundry.utils.getProperty(data, "system.${field}") === undefined) {
1105
- const ${field}InitialFunc = (system) => {
1106
- const context = { object: this };
1107
- ${translateExpression(entry, id, initParam.value, true, f)}
1108
- };
1109
- const ${field}Initial = ${field}InitialFunc(this.system);
1110
- if (typeof ${field}Initial === "number" && !isNaN(${field}Initial)) {
1111
- initialUpdates["system.${field}"] = ${field}Initial;
1112
- }
1113
- }
1102
+ return expandToNode `
1103
+ // ${f.name} expression initial
1104
+ if (foundry.utils.getProperty(data, "system.${field}") === undefined) {
1105
+ const ${field}InitialFunc = (system) => {
1106
+ const context = { object: this };
1107
+ ${translateExpression(entry, id, initParam.value, true, f)}
1108
+ };
1109
+ const ${field}Initial = ${field}InitialFunc(this.system);
1110
+ if (typeof ${field}Initial === "number" && !isNaN(${field}Initial)) {
1111
+ initialUpdates["system.${field}"] = ${field}Initial;
1112
+ }
1113
+ }
1114
1114
  `;
1115
- }, { appendNewLineIfNotEmpty: true })}
1116
- break;
1117
- }
1115
+ }, { appendNewLineIfNotEmpty: true })}
1116
+ break;
1117
+ }
1118
1118
  `.appendNewLineIfNotEmpty();
1119
1119
  }
1120
1120
  const cases = entry.documents
@@ -1123,300 +1123,300 @@ export function generateExtendedDocumentClasses(entry, id, destination) {
1123
1123
  .filter((x) => x !== undefined);
1124
1124
  if (cases.length === 0)
1125
1125
  return expandToNode ``;
1126
- return expandToNode `
1127
- /** @override */
1128
- async _preCreate(data, options, user) {
1129
- await super._preCreate(data, options, user);
1130
- const initialUpdates = {};
1131
- switch ( this.type ) {
1132
- ${joinToNode(cases, c => c, { appendNewLineIfNotEmpty: true })}
1133
- }
1134
- if (Object.keys(initialUpdates).length > 0) this.updateSource(initialUpdates);
1135
- }
1126
+ return expandToNode `
1127
+ /** @override */
1128
+ async _preCreate(data, options, user) {
1129
+ await super._preCreate(data, options, user);
1130
+ const initialUpdates = {};
1131
+ switch ( this.type ) {
1132
+ ${joinToNode(cases, c => c, { appendNewLineIfNotEmpty: true })}
1133
+ }
1134
+ if (Object.keys(initialUpdates).length > 0) this.updateSource(initialUpdates);
1135
+ }
1136
1136
  `.appendNewLineIfNotEmpty();
1137
1137
  }
1138
- const fileNode = expandToNode `
1139
- export default class ${entry.config.name}${type} extends ${type} {
1140
- /** @override */
1141
- prepareDerivedData() {
1142
- switch ( this.type ) {
1143
- ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => `case "${document.name.toLowerCase()}": return this._prepare${document.name}DerivedData();`, { appendNewLineIfNotEmpty: true })}
1144
- }
1145
- }
1146
-
1147
- /* -------------------------------------------- */
1148
-
1149
- ${generateExpressionInitials()}
1150
-
1151
- /* -------------------------------------------- */
1152
-
1153
- ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => generateDerivedData(document), { appendNewLineIfNotEmpty: true })}
1154
-
1155
- /* -------------------------------------------- */
1156
-
1157
- ${generateDeltas(entry.documents)}
1158
-
1159
- /* -------------------------------------------- */
1160
-
1161
- reapplyActiveEffectsForName(name) {
1162
- if (this.documentName !== "Actor") return;
1163
- for (const effect of this.appliedEffects) {
1164
- for (const change of effect.changes) {
1165
- if (change.key == name) {
1166
- const changes = effect.apply(this, change);
1167
- Object.assign(this.overrides, changes);
1168
- }
1169
- }
1170
- }
1171
- }
1172
-
1173
- /* -------------------------------------------- */
1174
-
1175
- /** @override */
1176
- _initialize(options = {}) {
1177
- super._initialize(options);
1178
-
1179
- switch ( this.type ) {
1180
- ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => `case "${document.name.toLowerCase()}": return this._register${document.name}Hooks(this);`, { appendNewLineIfNotEmpty: true })}
1181
- }
1182
- }
1183
-
1184
- /* -------------------------------------------- */
1185
-
1186
- ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), generateDocumentHooks, { appendNewLineIfNotEmpty: true })}
1187
-
1188
- /* -------------------------------------------- */
1189
-
1190
- // In order to support per-document type effects, we need to override the allApplicableEffects method to yield virtualized effects with only changes that match the document type
1191
- /** @override */
1192
- *allApplicableEffects() {
1193
- const systemFlags = this.flags["${id}"] ?? {};
1194
- const edit = systemFlags["edit-mode"] ?? true;
1195
-
1196
- function getTypedEffect(type, edit, effect, source) {
1197
- // Pre-build data to avoid mutating ActiveEffect getters (changes is getter-only in v14).
1198
- const data = foundry.utils.duplicate(effect);
1199
- data.changes = (data.changes ?? []).filter(c => c.key.startsWith(type));
1200
- for ( const change of data.changes ) {
1201
- if (change.mode == 0) continue;
1202
- change.key = change.key.replace(type + ".", "");
1203
- }
1204
- if ( edit ) data.disabled = true;
1205
- data.flags ??= {};
1206
- data.flags["${id}"] ??= {};
1207
- data.flags["${id}"].source = source;
1208
- return new ActiveEffect(data, {parent: effect.parent});
1209
- }
1210
-
1211
- for ( const effect of this.effects ) {
1212
- yield getTypedEffect(this.type, edit, effect, game.i18n.localize("Self"));
1213
- }
1214
- ${type == "Actor" ? expandToNode `
1215
- for ( const item of this.items ) {
1216
- for ( const effect of item.effects ) {
1217
- if ( effect.transfer ) yield getTypedEffect(this.type, edit, effect, item.name);
1218
- }
1219
- }
1220
- `.appendNewLine() : ""}
1221
- }
1222
-
1223
-
1224
- /* -------------------------------------------- */
1225
-
1226
- _onCreateDescendantDocuments(parent, collection, documents, data, options, userId) {
1227
- super._onCreateDescendantDocuments(parent, collection, documents, data, options, userId);
1228
-
1229
- for (const document of documents) {
1230
- if (document.documentName !== "ActiveEffect") continue;
1231
-
1232
- for (const change of document.changes) {
1233
- if (change.mode != 0) continue;
1234
- const customMode = foundry.utils.getProperty(document.flags["${id}"], change.key + "-custommode");
1235
- switch (customMode) {
1236
- case 1: // Add Once
1237
- this._effectAddOnce(parent, document, change);
1238
- break;
1239
- default:
1240
- console.error("Unknown custom mode", customMode);
1241
- break;
1242
- }
1243
- }
1244
- }
1245
- }
1246
-
1247
- /* -------------------------------------------- */
1248
-
1249
- _effectAddOnce(parent, ae, change) {
1250
- console.dir("AddOnce", parent, ae, change);
1251
-
1252
- const key = change.key.replace(parent.type + ".", "");
1253
- const currentValue = foundry.utils.getProperty(parent, key);
1254
-
1255
- // Create an update for the parent
1256
- const update = {
1257
- [key]: currentValue + parseInt(change.value)
1258
- };
1259
- parent.update(update);
1260
-
1261
- // Create a chat card
1262
- const chatData = {
1263
- user: game.user._id,
1264
- speaker: ChatMessage.getSpeaker({ actor: parent }),
1265
- content: \`<p>Added "\${ae.name}" once</p>\`
1266
- };
1267
- ChatMessage.create(chatData);
1268
- }
1269
-
1270
- /* -------------------------------------------- */
1271
-
1272
- static async createDialog(data = {}, { parent = null, pack = null, types = null, ...options } = {}) {
1273
- types ??= game.documentTypes[this.documentName].filter(t => (t !== CONST.BASE_DOCUMENT_TYPE) && (CONFIG[this.documentName].typeCreatables[t] !== false));
1274
- if (!types.length) return null;
1275
-
1276
- const collection = parent ? null : pack ? game.packs.get(pack) : game.collections.get(this.documentName);
1277
- const folders = collection?._formatFolderSelectOptions() ?? [];
1278
-
1279
- const label = game.i18n.localize(this.metadata.label);
1280
- const title = game.i18n.format("DOCUMENT.Create", { type: label });
1281
- const name = data.name || game.i18n.format("DOCUMENT.New", { type: label });
1282
-
1283
- let type = data.type || CONFIG[this.documentName]?.defaultType;
1284
- if (!types.includes(type)) type = types[0];
1285
-
1286
- // If there's only one type, no need to prompt
1287
- if (types.length === 1) {
1288
- let createName = this.defaultName();
1289
- const createData = {
1290
- name: createName,
1291
- type
1292
- };
1293
- return this.create(createData, { parent, pack, renderSheet: true });
1294
- }
1295
-
1296
- const createResponse = await game.system.documentCreateDialog.prompt({
1297
- type,
1298
- types: types.reduce((arr, typer) => {
1299
- arr.push({
1300
- type: typer,
1301
- label: game.i18n.has(typer) ? game.i18n.localize(typer) : typer,
1302
- icon: this.getDefaultArtwork({ type: typer })?.img ?? "icons/svg/item-bag.svg",
1303
- description: CONFIG[this.documentName]?.typeDescriptions?.[typer] ?? "",
1304
- selected: type === typer
1305
- });
1306
- return arr;
1307
- }, []).sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang)),
1308
- name,
1309
- title,
1310
- label,
1311
- folders,
1312
- folder: data.folder
1313
- });
1314
-
1315
- const createData = foundry.utils.mergeObject(data, createResponse, { inplace: false });
1316
- createData.type = createData.type || type;
1317
- // The type field can come back as the autocomplete's display label (e.g. the
1318
- // localized "Basic Hero") rather than the machine type ("basichero"). Lower-casing
1319
- // alone doesn't recover a multi-word machine type, so map the response back against
1320
- // the known types -- matching either the raw machine type or its displayed label --
1321
- // and fall back to the resolved default type if nothing matches.
1322
- if (!types.includes(createData.type)) {
1323
- createData.type = types.find(t => t === createData.type || (game.i18n.has(t) ? game.i18n.localize(t) : t) === createData.type) ?? type;
1324
- }
1325
- if (!createData.folder) delete createData.folder;
1326
- if (!createData.name?.trim()) createData.name = this.defaultName();
1327
- return this.create(createData, { parent, pack, renderSheet: true });
1328
-
1329
- const content = await renderTemplate("systems/${id}/system/templates/document-create.hbs", {
1330
- folders, name, type,
1331
- folder: data.folder,
1332
- hasFolders: folders.length > 0,
1333
- types: types.reduce((arr, typer) => {
1334
- arr.push({
1335
- type: typer,
1336
- label: game.i18n.has(typer) ? game.i18n.localize(typer) : typer,
1337
- icon: this.getDefaultArtwork({ type: typer })?.img ?? "icons/svg/item-bag.svg",
1338
- description: CONFIG[this.documentName]?.typeDescriptions?.[typer] ?? "",
1339
- selected: type === typer
1340
- });
1341
- return arr;
1342
- }, []).sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang))
1343
- });
1344
- return Dialog.prompt({
1345
- title, content,
1346
- label: title,
1347
- render: html => {
1348
- const app = html.closest(".app");
1349
- const folder = app.querySelector("select");
1350
- if (folder) app.querySelector(".dialog-buttons").insertAdjacentElement("afterbegin", folder);
1351
- app.querySelectorAll(".window-header .header-button").forEach(btn => {
1352
- const label = btn.innerText;
1353
- const icon = btn.querySelector("i");
1354
- btn.innerHTML = icon.outerHTML;
1355
- btn.dataset.tooltip = label;
1356
- btn.setAttribute("aria-label", label);
1357
- });
1358
- app.querySelector(".document-name").select();
1359
- },
1360
- callback: html => {
1361
- const form = html.querySelector("form");
1362
- const fd = new FormDataExtended(form);
1363
- const createData = foundry.utils.mergeObject(data, fd.object, { inplace: false });
1364
-
1365
- },
1366
- rejectClose: false,
1367
- options: { ...options, jQuery: false, width: 700, height: 'auto', classes: ["${id}", "create-document", "dialog"] }
1368
- });
1369
- }
1370
-
1371
- /* -------------------------------------------- */
1372
-
1373
- static getDefaultArtwork(itemData = {}) {
1374
- const { type } = itemData;
1375
- const { img, texture } = super.getDefaultArtwork(itemData);
1376
- return { img: CONFIG[this.documentName]?.typeArtworks?.[type] ?? img, texture: texture };
1377
- }
1378
-
1379
- /* -------------------------------------------- */
1380
-
1381
- getRollData() {
1382
- const data = super.getRollData();
1383
- const rollData = foundry.utils.duplicate(data);
1384
- rollData.system = this.system;
1385
- return rollData;
1386
- }
1387
-
1388
- /* -------------------------------------------- */
1389
-
1390
- /** @override */
1391
- async modifyTokenAttribute(attribute, value, isDelta, isBar) {
1392
- const resource = foundry.utils.getProperty(this.system, attribute);
1393
-
1394
- if (isDelta && value < 0) {
1395
- // Apply to temp first
1396
- resource.temp += value;
1397
-
1398
- // If temp is negative, apply to value
1399
- if (resource.temp < 0) {
1400
- resource.value += resource.temp;
1401
- resource.temp = 0;
1402
- }
1403
- await this.update({ ["system." + attribute]: resource });
1404
- return;
1405
- }
1406
-
1407
- return super.modifyTokenAttribute(attribute, value, isDelta, isBar);
1408
- }
1409
-
1410
- ${type == "Actor" ? expandToNode `
1411
- /* -------------------------------------------- */
1412
-
1413
- getInitiativeFormula() {
1414
- switch ( this.type ) {
1415
- ${joinToNode(entry.documents.filter(d => isActor(d)), document => generateInitiativeFormula(document), { appendNewLineIfNotEmpty: true })}
1416
- }
1417
- }
1418
- `.appendNewLine() : ""}
1419
- }
1138
+ const fileNode = expandToNode `
1139
+ export default class ${entry.config.name}${type} extends ${type} {
1140
+ /** @override */
1141
+ prepareDerivedData() {
1142
+ switch ( this.type ) {
1143
+ ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => `case "${document.name.toLowerCase()}": return this._prepare${document.name}DerivedData();`, { appendNewLineIfNotEmpty: true })}
1144
+ }
1145
+ }
1146
+
1147
+ /* -------------------------------------------- */
1148
+
1149
+ ${generateExpressionInitials()}
1150
+
1151
+ /* -------------------------------------------- */
1152
+
1153
+ ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => generateDerivedData(document), { appendNewLineIfNotEmpty: true })}
1154
+
1155
+ /* -------------------------------------------- */
1156
+
1157
+ ${generateDeltas(entry.documents)}
1158
+
1159
+ /* -------------------------------------------- */
1160
+
1161
+ reapplyActiveEffectsForName(name) {
1162
+ if (this.documentName !== "Actor") return;
1163
+ for (const effect of this.appliedEffects) {
1164
+ for (const change of effect.changes) {
1165
+ if (change.key == name) {
1166
+ const changes = effect.apply(this, change);
1167
+ Object.assign(this.overrides, changes);
1168
+ }
1169
+ }
1170
+ }
1171
+ }
1172
+
1173
+ /* -------------------------------------------- */
1174
+
1175
+ /** @override */
1176
+ _initialize(options = {}) {
1177
+ super._initialize(options);
1178
+
1179
+ switch ( this.type ) {
1180
+ ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => `case "${document.name.toLowerCase()}": return this._register${document.name}Hooks(this);`, { appendNewLineIfNotEmpty: true })}
1181
+ }
1182
+ }
1183
+
1184
+ /* -------------------------------------------- */
1185
+
1186
+ ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), generateDocumentHooks, { appendNewLineIfNotEmpty: true })}
1187
+
1188
+ /* -------------------------------------------- */
1189
+
1190
+ // In order to support per-document type effects, we need to override the allApplicableEffects method to yield virtualized effects with only changes that match the document type
1191
+ /** @override */
1192
+ *allApplicableEffects() {
1193
+ const systemFlags = this.flags["${id}"] ?? {};
1194
+ const edit = systemFlags["edit-mode"] ?? true;
1195
+
1196
+ function getTypedEffect(type, edit, effect, source) {
1197
+ // Pre-build data to avoid mutating ActiveEffect getters (changes is getter-only in v14).
1198
+ const data = foundry.utils.duplicate(effect);
1199
+ data.changes = (data.changes ?? []).filter(c => c.key.startsWith(type));
1200
+ for ( const change of data.changes ) {
1201
+ if (change.mode == 0) continue;
1202
+ change.key = change.key.replace(type + ".", "");
1203
+ }
1204
+ if ( edit ) data.disabled = true;
1205
+ data.flags ??= {};
1206
+ data.flags["${id}"] ??= {};
1207
+ data.flags["${id}"].source = source;
1208
+ return new ActiveEffect(data, {parent: effect.parent});
1209
+ }
1210
+
1211
+ for ( const effect of this.effects ) {
1212
+ yield getTypedEffect(this.type, edit, effect, game.i18n.localize("Self"));
1213
+ }
1214
+ ${type == "Actor" ? expandToNode `
1215
+ for ( const item of this.items ) {
1216
+ for ( const effect of item.effects ) {
1217
+ if ( effect.transfer ) yield getTypedEffect(this.type, edit, effect, item.name);
1218
+ }
1219
+ }
1220
+ `.appendNewLine() : ""}
1221
+ }
1222
+
1223
+
1224
+ /* -------------------------------------------- */
1225
+
1226
+ _onCreateDescendantDocuments(parent, collection, documents, data, options, userId) {
1227
+ super._onCreateDescendantDocuments(parent, collection, documents, data, options, userId);
1228
+
1229
+ for (const document of documents) {
1230
+ if (document.documentName !== "ActiveEffect") continue;
1231
+
1232
+ for (const change of document.changes) {
1233
+ if (change.mode != 0) continue;
1234
+ const customMode = foundry.utils.getProperty(document.flags["${id}"], change.key + "-custommode");
1235
+ switch (customMode) {
1236
+ case 1: // Add Once
1237
+ this._effectAddOnce(parent, document, change);
1238
+ break;
1239
+ default:
1240
+ console.error("Unknown custom mode", customMode);
1241
+ break;
1242
+ }
1243
+ }
1244
+ }
1245
+ }
1246
+
1247
+ /* -------------------------------------------- */
1248
+
1249
+ _effectAddOnce(parent, ae, change) {
1250
+ console.dir("AddOnce", parent, ae, change);
1251
+
1252
+ const key = change.key.replace(parent.type + ".", "");
1253
+ const currentValue = foundry.utils.getProperty(parent, key);
1254
+
1255
+ // Create an update for the parent
1256
+ const update = {
1257
+ [key]: currentValue + parseInt(change.value)
1258
+ };
1259
+ parent.update(update);
1260
+
1261
+ // Create a chat card
1262
+ const chatData = {
1263
+ user: game.user._id,
1264
+ speaker: ChatMessage.getSpeaker({ actor: parent }),
1265
+ content: \`<p>Added "\${ae.name}" once</p>\`
1266
+ };
1267
+ ChatMessage.create(chatData);
1268
+ }
1269
+
1270
+ /* -------------------------------------------- */
1271
+
1272
+ static async createDialog(data = {}, { parent = null, pack = null, types = null, ...options } = {}) {
1273
+ types ??= game.documentTypes[this.documentName].filter(t => (t !== CONST.BASE_DOCUMENT_TYPE) && (CONFIG[this.documentName].typeCreatables[t] !== false));
1274
+ if (!types.length) return null;
1275
+
1276
+ const collection = parent ? null : pack ? game.packs.get(pack) : game.collections.get(this.documentName);
1277
+ const folders = collection?._formatFolderSelectOptions() ?? [];
1278
+
1279
+ const label = game.i18n.localize(this.metadata.label);
1280
+ const title = game.i18n.format("DOCUMENT.Create", { type: label });
1281
+ const name = data.name || game.i18n.format("DOCUMENT.New", { type: label });
1282
+
1283
+ let type = data.type || CONFIG[this.documentName]?.defaultType;
1284
+ if (!types.includes(type)) type = types[0];
1285
+
1286
+ // If there's only one type, no need to prompt
1287
+ if (types.length === 1) {
1288
+ let createName = this.defaultName();
1289
+ const createData = {
1290
+ name: createName,
1291
+ type
1292
+ };
1293
+ return this.create(createData, { parent, pack, renderSheet: true });
1294
+ }
1295
+
1296
+ const createResponse = await game.system.documentCreateDialog.prompt({
1297
+ type,
1298
+ types: types.reduce((arr, typer) => {
1299
+ arr.push({
1300
+ type: typer,
1301
+ label: game.i18n.has(typer) ? game.i18n.localize(typer) : typer,
1302
+ icon: this.getDefaultArtwork({ type: typer })?.img ?? "icons/svg/item-bag.svg",
1303
+ description: CONFIG[this.documentName]?.typeDescriptions?.[typer] ?? "",
1304
+ selected: type === typer
1305
+ });
1306
+ return arr;
1307
+ }, []).sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang)),
1308
+ name,
1309
+ title,
1310
+ label,
1311
+ folders,
1312
+ folder: data.folder
1313
+ });
1314
+
1315
+ const createData = foundry.utils.mergeObject(data, createResponse, { inplace: false });
1316
+ createData.type = createData.type || type;
1317
+ // The type field can come back as the autocomplete's display label (e.g. the
1318
+ // localized "Basic Hero") rather than the machine type ("basichero"). Lower-casing
1319
+ // alone doesn't recover a multi-word machine type, so map the response back against
1320
+ // the known types -- matching either the raw machine type or its displayed label --
1321
+ // and fall back to the resolved default type if nothing matches.
1322
+ if (!types.includes(createData.type)) {
1323
+ createData.type = types.find(t => t === createData.type || (game.i18n.has(t) ? game.i18n.localize(t) : t) === createData.type) ?? type;
1324
+ }
1325
+ if (!createData.folder) delete createData.folder;
1326
+ if (!createData.name?.trim()) createData.name = this.defaultName();
1327
+ return this.create(createData, { parent, pack, renderSheet: true });
1328
+
1329
+ const content = await renderTemplate("systems/${id}/system/templates/document-create.hbs", {
1330
+ folders, name, type,
1331
+ folder: data.folder,
1332
+ hasFolders: folders.length > 0,
1333
+ types: types.reduce((arr, typer) => {
1334
+ arr.push({
1335
+ type: typer,
1336
+ label: game.i18n.has(typer) ? game.i18n.localize(typer) : typer,
1337
+ icon: this.getDefaultArtwork({ type: typer })?.img ?? "icons/svg/item-bag.svg",
1338
+ description: CONFIG[this.documentName]?.typeDescriptions?.[typer] ?? "",
1339
+ selected: type === typer
1340
+ });
1341
+ return arr;
1342
+ }, []).sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang))
1343
+ });
1344
+ return Dialog.prompt({
1345
+ title, content,
1346
+ label: title,
1347
+ render: html => {
1348
+ const app = html.closest(".app");
1349
+ const folder = app.querySelector("select");
1350
+ if (folder) app.querySelector(".dialog-buttons").insertAdjacentElement("afterbegin", folder);
1351
+ app.querySelectorAll(".window-header .header-button").forEach(btn => {
1352
+ const label = btn.innerText;
1353
+ const icon = btn.querySelector("i");
1354
+ btn.innerHTML = icon.outerHTML;
1355
+ btn.dataset.tooltip = label;
1356
+ btn.setAttribute("aria-label", label);
1357
+ });
1358
+ app.querySelector(".document-name").select();
1359
+ },
1360
+ callback: html => {
1361
+ const form = html.querySelector("form");
1362
+ const fd = new FormDataExtended(form);
1363
+ const createData = foundry.utils.mergeObject(data, fd.object, { inplace: false });
1364
+
1365
+ },
1366
+ rejectClose: false,
1367
+ options: { ...options, jQuery: false, width: 700, height: 'auto', classes: ["${id}", "create-document", "dialog"] }
1368
+ });
1369
+ }
1370
+
1371
+ /* -------------------------------------------- */
1372
+
1373
+ static getDefaultArtwork(itemData = {}) {
1374
+ const { type } = itemData;
1375
+ const { img, texture } = super.getDefaultArtwork(itemData);
1376
+ return { img: CONFIG[this.documentName]?.typeArtworks?.[type] ?? img, texture: texture };
1377
+ }
1378
+
1379
+ /* -------------------------------------------- */
1380
+
1381
+ getRollData() {
1382
+ const data = super.getRollData();
1383
+ const rollData = foundry.utils.duplicate(data);
1384
+ rollData.system = this.system;
1385
+ return rollData;
1386
+ }
1387
+
1388
+ /* -------------------------------------------- */
1389
+
1390
+ /** @override */
1391
+ async modifyTokenAttribute(attribute, value, isDelta, isBar) {
1392
+ const resource = foundry.utils.getProperty(this.system, attribute);
1393
+
1394
+ if (isDelta && value < 0) {
1395
+ // Apply to temp first
1396
+ resource.temp += value;
1397
+
1398
+ // If temp is negative, apply to value
1399
+ if (resource.temp < 0) {
1400
+ resource.value += resource.temp;
1401
+ resource.temp = 0;
1402
+ }
1403
+ await this.update({ ["system." + attribute]: resource });
1404
+ return;
1405
+ }
1406
+
1407
+ return super.modifyTokenAttribute(attribute, value, isDelta, isBar);
1408
+ }
1409
+
1410
+ ${type == "Actor" ? expandToNode `
1411
+ /* -------------------------------------------- */
1412
+
1413
+ getInitiativeFormula() {
1414
+ switch ( this.type ) {
1415
+ ${joinToNode(entry.documents.filter(d => isActor(d)), document => generateInitiativeFormula(document), { appendNewLineIfNotEmpty: true })}
1416
+ }
1417
+ }
1418
+ `.appendNewLine() : ""}
1419
+ }
1420
1420
  `.appendNewLineIfNotEmpty();
1421
1421
  fs.writeFileSync(generatedFilePath, toString(fileNode));
1422
1422
  }