intelligent-system-design-language 0.3.13

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 (209) hide show
  1. package/.claude/agents/langium-language-designer.md +38 -0
  2. package/.claude/agents/typescript-vscode-expert.md +29 -0
  3. package/.claude/agents/ui-ux-designer.md +36 -0
  4. package/.claude/settings.local.json +33 -0
  5. package/.idea/inspectionProfiles/Project_Default.xml +7 -0
  6. package/.idea/isdl.iml +14 -0
  7. package/.idea/modules.xml +9 -0
  8. package/.idea/vcs.xml +7 -0
  9. package/.idea/watcherTasks.xml +4 -0
  10. package/.vscodeignore +18 -0
  11. package/LICENSE +674 -0
  12. package/README.md +86 -0
  13. package/bin/cli.js +4 -0
  14. package/bin/lsp.js +8 -0
  15. package/isdl.png +0 -0
  16. package/out/_backgrounds.scss +91 -0
  17. package/out/_handlebars.scss +505 -0
  18. package/out/_isdlStyles.scss +1357 -0
  19. package/out/_vuetifyOverrides.scss +425 -0
  20. package/out/_vuetifyStyles.scss +31957 -0
  21. package/out/cli/cli-util.js +39 -0
  22. package/out/cli/cli-util.js.map +1 -0
  23. package/out/cli/components/_backgrounds.scss +91 -0
  24. package/out/cli/components/_handlebars.scss +505 -0
  25. package/out/cli/components/_isdlStyles.scss +1357 -0
  26. package/out/cli/components/_vuetifyOverrides.scss +425 -0
  27. package/out/cli/components/_vuetifyStyles.scss +31957 -0
  28. package/out/cli/components/active-effect-sheet-generator.js +643 -0
  29. package/out/cli/components/active-effect-sheet-generator.js.map +1 -0
  30. package/out/cli/components/base-actor-sheet-generator.js +125 -0
  31. package/out/cli/components/base-actor-sheet-generator.js.map +1 -0
  32. package/out/cli/components/base-sheet-generator.js +525 -0
  33. package/out/cli/components/base-sheet-generator.js.map +1 -0
  34. package/out/cli/components/chat-card-generator.js +683 -0
  35. package/out/cli/components/chat-card-generator.js.map +1 -0
  36. package/out/cli/components/css-generator.js +58 -0
  37. package/out/cli/components/css-generator.js.map +1 -0
  38. package/out/cli/components/damage-roll-generator.js +173 -0
  39. package/out/cli/components/damage-roll-generator.js.map +1 -0
  40. package/out/cli/components/datamodel-generator.js +672 -0
  41. package/out/cli/components/datamodel-generator.js.map +1 -0
  42. package/out/cli/components/derived-data-generator.js +1340 -0
  43. package/out/cli/components/derived-data-generator.js.map +1 -0
  44. package/out/cli/components/hotbar-drop-hook-generator.js +95 -0
  45. package/out/cli/components/hotbar-drop-hook-generator.js.map +1 -0
  46. package/out/cli/components/init-hook-generator.js +597 -0
  47. package/out/cli/components/init-hook-generator.js.map +1 -0
  48. package/out/cli/components/keywords-generator.js +220 -0
  49. package/out/cli/components/keywords-generator.js.map +1 -0
  50. package/out/cli/components/language-generator.js +110 -0
  51. package/out/cli/components/language-generator.js.map +1 -0
  52. package/out/cli/components/measured-template-preview.js +234 -0
  53. package/out/cli/components/measured-template-preview.js.map +1 -0
  54. package/out/cli/components/method-generator.js +1812 -0
  55. package/out/cli/components/method-generator.js.map +1 -0
  56. package/out/cli/components/ready-hook-generator.js +448 -0
  57. package/out/cli/components/ready-hook-generator.js.map +1 -0
  58. package/out/cli/components/token-generator.js +138 -0
  59. package/out/cli/components/token-generator.js.map +1 -0
  60. package/out/cli/components/utils.js +176 -0
  61. package/out/cli/components/utils.js.map +1 -0
  62. package/out/cli/components/vue/base-components/vue-attribute.js +148 -0
  63. package/out/cli/components/vue/base-components/vue-attribute.js.map +1 -0
  64. package/out/cli/components/vue/base-components/vue-boolean.js +77 -0
  65. package/out/cli/components/vue/base-components/vue-boolean.js.map +1 -0
  66. package/out/cli/components/vue/base-components/vue-calculator.js +106 -0
  67. package/out/cli/components/vue/base-components/vue-calculator.js.map +1 -0
  68. package/out/cli/components/vue/base-components/vue-damage-application.js +369 -0
  69. package/out/cli/components/vue/base-components/vue-damage-application.js.map +1 -0
  70. package/out/cli/components/vue/base-components/vue-damage-bonuses.js +225 -0
  71. package/out/cli/components/vue/base-components/vue-damage-bonuses.js.map +1 -0
  72. package/out/cli/components/vue/base-components/vue-damage-resistances.js +256 -0
  73. package/out/cli/components/vue/base-components/vue-damage-resistances.js.map +1 -0
  74. package/out/cli/components/vue/base-components/vue-damage-track.js +134 -0
  75. package/out/cli/components/vue/base-components/vue-damage-track.js.map +1 -0
  76. package/out/cli/components/vue/base-components/vue-date-time.js +55 -0
  77. package/out/cli/components/vue/base-components/vue-date-time.js.map +1 -0
  78. package/out/cli/components/vue/base-components/vue-dice.js +111 -0
  79. package/out/cli/components/vue/base-components/vue-dice.js.map +1 -0
  80. package/out/cli/components/vue/base-components/vue-die.js +86 -0
  81. package/out/cli/components/vue/base-components/vue-die.js.map +1 -0
  82. package/out/cli/components/vue/base-components/vue-document-choice.js +172 -0
  83. package/out/cli/components/vue/base-components/vue-document-choice.js.map +1 -0
  84. package/out/cli/components/vue/base-components/vue-document-choices.js +203 -0
  85. package/out/cli/components/vue/base-components/vue-document-choices.js.map +1 -0
  86. package/out/cli/components/vue/base-components/vue-document-link.js +73 -0
  87. package/out/cli/components/vue/base-components/vue-document-link.js.map +1 -0
  88. package/out/cli/components/vue/base-components/vue-extended-choice.js +101 -0
  89. package/out/cli/components/vue/base-components/vue-extended-choice.js.map +1 -0
  90. package/out/cli/components/vue/base-components/vue-inventory.js +532 -0
  91. package/out/cli/components/vue/base-components/vue-inventory.js.map +1 -0
  92. package/out/cli/components/vue/base-components/vue-macro-choice.js +150 -0
  93. package/out/cli/components/vue/base-components/vue-macro-choice.js.map +1 -0
  94. package/out/cli/components/vue/base-components/vue-measured-template.js +543 -0
  95. package/out/cli/components/vue/base-components/vue-measured-template.js.map +1 -0
  96. package/out/cli/components/vue/base-components/vue-money.js +496 -0
  97. package/out/cli/components/vue/base-components/vue-money.js.map +1 -0
  98. package/out/cli/components/vue/base-components/vue-number.js +184 -0
  99. package/out/cli/components/vue/base-components/vue-number.js.map +1 -0
  100. package/out/cli/components/vue/base-components/vue-paperdoll.js +56 -0
  101. package/out/cli/components/vue/base-components/vue-paperdoll.js.map +1 -0
  102. package/out/cli/components/vue/base-components/vue-parent-property-reference.js +89 -0
  103. package/out/cli/components/vue/base-components/vue-parent-property-reference.js.map +1 -0
  104. package/out/cli/components/vue/base-components/vue-prosemirror.js +31 -0
  105. package/out/cli/components/vue/base-components/vue-prosemirror.js.map +1 -0
  106. package/out/cli/components/vue/base-components/vue-resource.js +149 -0
  107. package/out/cli/components/vue/base-components/vue-resource.js.map +1 -0
  108. package/out/cli/components/vue/base-components/vue-roll-visualizer.js +121 -0
  109. package/out/cli/components/vue/base-components/vue-roll-visualizer.js.map +1 -0
  110. package/out/cli/components/vue/base-components/vue-self-property-reference.js +75 -0
  111. package/out/cli/components/vue/base-components/vue-self-property-reference.js.map +1 -0
  112. package/out/cli/components/vue/base-components/vue-string-choice.js +111 -0
  113. package/out/cli/components/vue/base-components/vue-string-choice.js.map +1 -0
  114. package/out/cli/components/vue/base-components/vue-string-choices.js +216 -0
  115. package/out/cli/components/vue/base-components/vue-string-choices.js.map +1 -0
  116. package/out/cli/components/vue/base-components/vue-string.js +73 -0
  117. package/out/cli/components/vue/base-components/vue-string.js.map +1 -0
  118. package/out/cli/components/vue/base-components/vue-text-field.js +66 -0
  119. package/out/cli/components/vue/base-components/vue-text-field.js.map +1 -0
  120. package/out/cli/components/vue/base-components/vue-tracker.js +444 -0
  121. package/out/cli/components/vue/base-components/vue-tracker.js.map +1 -0
  122. package/out/cli/components/vue/vue-action-component-generator.js +88 -0
  123. package/out/cli/components/vue/vue-action-component-generator.js.map +1 -0
  124. package/out/cli/components/vue/vue-active-effect-sheet-generator.js +1016 -0
  125. package/out/cli/components/vue/vue-active-effect-sheet-generator.js.map +1 -0
  126. package/out/cli/components/vue/vue-base-components-generator.js +59 -0
  127. package/out/cli/components/vue/vue-base-components-generator.js.map +1 -0
  128. package/out/cli/components/vue/vue-datatable-component-generator.js +307 -0
  129. package/out/cli/components/vue/vue-datatable-component-generator.js.map +1 -0
  130. package/out/cli/components/vue/vue-datatable-sheet-class-generator.js +342 -0
  131. package/out/cli/components/vue/vue-datatable-sheet-class-generator.js.map +1 -0
  132. package/out/cli/components/vue/vue-datatable2-component-generator.js +939 -0
  133. package/out/cli/components/vue/vue-datatable2-component-generator.js.map +1 -0
  134. package/out/cli/components/vue/vue-document-creation-app.js +140 -0
  135. package/out/cli/components/vue/vue-document-creation-app.js.map +1 -0
  136. package/out/cli/components/vue/vue-document-creation-sheet.js +105 -0
  137. package/out/cli/components/vue/vue-document-creation-sheet.js.map +1 -0
  138. package/out/cli/components/vue/vue-generator.js +240 -0
  139. package/out/cli/components/vue/vue-generator.js.map +1 -0
  140. package/out/cli/components/vue/vue-mixin.js +338 -0
  141. package/out/cli/components/vue/vue-mixin.js.map +1 -0
  142. package/out/cli/components/vue/vue-pinned-datatable-component-generator.js +306 -0
  143. package/out/cli/components/vue/vue-pinned-datatable-component-generator.js.map +1 -0
  144. package/out/cli/components/vue/vue-prompt-generator.js +201 -0
  145. package/out/cli/components/vue/vue-prompt-generator.js.map +1 -0
  146. package/out/cli/components/vue/vue-prompt-sheet-class-generator.js +252 -0
  147. package/out/cli/components/vue/vue-prompt-sheet-class-generator.js.map +1 -0
  148. package/out/cli/components/vue/vue-sheet-application-generator.js +2008 -0
  149. package/out/cli/components/vue/vue-sheet-application-generator.js.map +1 -0
  150. package/out/cli/components/vue/vue-sheet-class-generator.js +484 -0
  151. package/out/cli/components/vue/vue-sheet-class-generator.js.map +1 -0
  152. package/out/cli/generator.js +659 -0
  153. package/out/cli/generator.js.map +1 -0
  154. package/out/cli/main.js +43 -0
  155. package/out/cli/main.js.map +1 -0
  156. package/out/datatables.min.css +54 -0
  157. package/out/datatables.min.js +178 -0
  158. package/out/extension/github/githubAuthProvider.js +345 -0
  159. package/out/extension/github/githubAuthProvider.js.map +1 -0
  160. package/out/extension/github/githubConfig.js +132 -0
  161. package/out/extension/github/githubConfig.js.map +1 -0
  162. package/out/extension/github/githubGistActions.js +251 -0
  163. package/out/extension/github/githubGistActions.js.map +1 -0
  164. package/out/extension/github/githubGistManager.js +255 -0
  165. package/out/extension/github/githubGistManager.js.map +1 -0
  166. package/out/extension/github/githubManager.js +1735 -0
  167. package/out/extension/github/githubManager.js.map +1 -0
  168. package/out/extension/github/githubQuickActions.js +659 -0
  169. package/out/extension/github/githubQuickActions.js.map +1 -0
  170. package/out/extension/github/githubTreeProvider.js +181 -0
  171. package/out/extension/github/githubTreeProvider.js.map +1 -0
  172. package/out/extension/github/system-workflow.yml +48 -0
  173. package/out/extension/main.cjs +70315 -0
  174. package/out/extension/main.cjs.map +7 -0
  175. package/out/extension/main.js +237 -0
  176. package/out/extension/main.js.map +1 -0
  177. package/out/extension/package.json +426 -0
  178. package/out/isdl.png +0 -0
  179. package/out/language/generated/ast.js +2992 -0
  180. package/out/language/generated/ast.js.map +1 -0
  181. package/out/language/generated/grammar.js +13970 -0
  182. package/out/language/generated/grammar.js.map +1 -0
  183. package/out/language/generated/module.js +20 -0
  184. package/out/language/generated/module.js.map +1 -0
  185. package/out/language/intelligent-system-design-language-formatter.js +85 -0
  186. package/out/language/intelligent-system-design-language-formatter.js.map +1 -0
  187. package/out/language/intelligent-system-design-language-module.js +69 -0
  188. package/out/language/intelligent-system-design-language-module.js.map +1 -0
  189. package/out/language/intelligent-system-design-language-quickfixes.js +37 -0
  190. package/out/language/intelligent-system-design-language-quickfixes.js.map +1 -0
  191. package/out/language/intelligent-system-design-language-validator.js +515 -0
  192. package/out/language/intelligent-system-design-language-validator.js.map +1 -0
  193. package/out/language/isdl-hover-provider.js +77 -0
  194. package/out/language/isdl-hover-provider.js.map +1 -0
  195. package/out/language/isdl-scope-provider.js +149 -0
  196. package/out/language/isdl-scope-provider.js.map +1 -0
  197. package/out/language/main.cjs +47655 -0
  198. package/out/language/main.cjs.map +7 -0
  199. package/out/language/main.js +11 -0
  200. package/out/language/main.js.map +1 -0
  201. package/out/missing-character.png +0 -0
  202. package/out/package.json +426 -0
  203. package/out/paperdoll_default.png +0 -0
  204. package/out/progressbar.min.js +7 -0
  205. package/out/styles.scss +722 -0
  206. package/out/test/formatting/formatter.test.js +46 -0
  207. package/out/test/formatting/formatter.test.js.map +1 -0
  208. package/out/vuetify.esm.js +30279 -0
  209. package/package.json +426 -0
@@ -0,0 +1,1340 @@
1
+ import { isStringExp, isStringParamValue, isInitiativeProperty, isAttributeParamMod, isHookHandler, isTrackerExp, isLayout, isMeasuredTemplateField, isTableField, isAccess, isDiceField, isMoneyField } from '../../language/generated/ast.js';
2
+ import { isActor, isItem, isResourceExp, isAttributeExp, isMethodBlock, isNumberExp, isNumberParamMax, isNumberParamValue, isNumberParamMin, isWhereParam, isDocument, } from "../../language/generated/ast.js";
3
+ import { expandToNode, joinToNode, toString } from 'langium/generate';
4
+ import * as fs from 'node:fs';
5
+ import * as path from 'node:path';
6
+ import { translateBodyExpressionToJavascript, translateExpression } from './method-generator.js';
7
+ import { getAllOfType } from './utils.js';
8
+ export function generateExtendedDocumentClasses(entry, id, destination) {
9
+ const generatedFileDir = path.join(destination, "system", "documents");
10
+ if (!fs.existsSync(generatedFileDir)) {
11
+ fs.mkdirSync(generatedFileDir, { recursive: true });
12
+ }
13
+ function generateExtendedDocumentClass(type, entry) {
14
+ function translateMethodOrValueOrStored(property, param) {
15
+ if (param == undefined) {
16
+ return expandToNode `
17
+ return system.${property.name.toLowerCase()} ?? 0
18
+ `;
19
+ }
20
+ if (isMethodBlock(param.value)) {
21
+ if (isNumberParamValue(param)) {
22
+ toBeReapplied.add("system." + property.name.toLowerCase());
23
+ }
24
+ return expandToNode `
25
+ ${translateExpression(entry, id, param.value, true, property)}
26
+ `;
27
+ }
28
+ return expandToNode `
29
+ return ${param.value}
30
+ `;
31
+ }
32
+ const generatedFilePath = path.join(generatedFileDir, `${type.toLowerCase()}.mjs`);
33
+ const toBeReapplied = new Set();
34
+ function generateDerivedAttribute(property) {
35
+ var _a, _b;
36
+ if (isLayout(property)) {
37
+ return joinToNode(property.body, property => generateDerivedAttribute(property), { appendNewLineIfNotEmpty: true });
38
+ }
39
+ if (isStringExp(property)) {
40
+ let stringValue = property.params.find(p => isStringParamValue(p));
41
+ if (stringValue != undefined) {
42
+ if (stringValue.value == "")
43
+ return;
44
+ if (typeof stringValue.value == "string") {
45
+ return expandToNode `
46
+ // ${property.name} String Derived Data
47
+ this.system.${property.name.toLowerCase()} = "${stringValue.value}";
48
+ `.appendNewLineIfNotEmpty();
49
+ }
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);
60
+ `.appendNewLineIfNotEmpty();
61
+ }
62
+ }
63
+ }
64
+ if (isNumberExp(property)) {
65
+ const valueParam = property.params.find(p => isNumberParamValue(p));
66
+ const minParam = property.params.find(p => isNumberParamMin(p));
67
+ const maxParam = property.params.find(p => isNumberParamMax(p));
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
+
128
+ `.appendNewLineIfNotEmpty();
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() : ""}
165
+ `.appendNewLineIfNotEmpty();
166
+ }
167
+ if (isMoneyField(property)) {
168
+ const valueParam = property.params.find(p => isNumberParamValue(p));
169
+ if (valueParam) {
170
+ // Check if this money field has denominations
171
+ if (property.denominations && property.denominations.length > 0) {
172
+ // Cannot use value param with multi-denomination money
173
+ console.warn(`Money field ${property.name} has both denominations and value parameter. Value parameter will be ignored for multi-denomination money.`);
174
+ return;
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
+
191
+ `.appendNewLineIfNotEmpty();
192
+ }
193
+ }
194
+ if (isAttributeExp(property)) {
195
+ console.log("Processing Derived Attribute: " + property.name);
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
+ }
221
+ `.appendNewLineIfNotEmpty();
222
+ }
223
+ ;
224
+ function generateValueOrMethod(value) {
225
+ if (isMethodBlock(value)) {
226
+ return expandToNode `
227
+ const context = {
228
+ object: this
229
+ };
230
+ ${translateExpression(entry, id, value, true, undefined)}
231
+ `;
232
+ }
233
+ return expandToNode `
234
+ return ${value};
235
+ `;
236
+ }
237
+ if (isTrackerExp(property)) {
238
+ console.log("Processing Derived Tracker: " + property.name);
239
+ const maxParam = property.params.find(x => isNumberParamMax(x));
240
+ const minParam = property.params.find(x => isNumberParamMin(x));
241
+ const valueParam = property.params.find(x => isNumberParamValue(x));
242
+ if (maxParam == undefined && minParam == undefined && valueParam == undefined)
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
+ }
281
+ `.appendNewLineIfNotEmpty();
282
+ }
283
+ ;
284
+ if (isResourceExp(property)) {
285
+ console.log("Processing Derived Resource: " + property.name);
286
+ const maxParam = property.params.find(x => isNumberParamMax(x));
287
+ const minParam = property.params.find(x => isNumberParamMin(x));
288
+ const valueParam = property.params.find(x => isNumberParamValue(x));
289
+ if (maxParam == undefined && minParam == undefined && valueParam == undefined)
290
+ return;
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
+ }
325
+ `.appendNewLineIfNotEmpty();
326
+ }
327
+ if (isTableField(property)) {
328
+ console.log("Processing Derived Document Array: " + property.name);
329
+ const whereParam = property.params.find(p => isWhereParam(p));
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
+ });
337
+ `.appendNewLineIfNotEmpty();
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()}");
342
+ `.appendNewLineIfNotEmpty();
343
+ }
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();
355
+ `.appendNewLineIfNotEmpty();
356
+ }
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();
366
+ `.appendNewLineIfNotEmpty();
367
+ }
368
+ // if (isParentPropertyRefExp(property)) {
369
+ // console.log("Processing Derived Parent Property: " + property.name);
370
+ // return expandToNode`
371
+ // // Parent ${property.name} Property Derived Data
372
+ // if ( !this.parent || this.system.${property.name.toLowerCase()}Ref == "" ) {
373
+ // this.system.${property.name.toLowerCase()} = 0;
374
+ // }
375
+ // else {
376
+ // this.system.${property.name.toLowerCase()} = foundry.utils.getProperty(this.parent, this.system.${property.name.toLowerCase()}Ref) ?? 0;
377
+ // }
378
+ // `.appendNewLineIfNotEmpty().appendNewLine();
379
+ // }
380
+ return;
381
+ }
382
+ function extractPropertyDependencies(methodBlock) {
383
+ const dependencies = new Set();
384
+ function traverseExpression(node) {
385
+ var _a, _b;
386
+ if (!node)
387
+ return;
388
+ if (isAccess(node)) {
389
+ // For access expressions like self.PropertyName, check if it's referencing another property
390
+ if ((_b = (_a = node.property) === null || _a === void 0 ? void 0 : _a.ref) === null || _b === void 0 ? void 0 : _b.name) {
391
+ dependencies.add(node.property.ref.name.toLowerCase());
392
+ }
393
+ }
394
+ if (isAccess(node)) {
395
+ // Self-reference expressions don't create dependencies on specific properties
396
+ // since they're resolved at runtime based on user selection
397
+ return;
398
+ }
399
+ // Recursively traverse child nodes
400
+ if (node.$children) {
401
+ for (const child of node.$children) {
402
+ traverseExpression(child);
403
+ }
404
+ }
405
+ // Handle common expression properties
406
+ if (node.left)
407
+ traverseExpression(node.left);
408
+ if (node.right)
409
+ traverseExpression(node.right);
410
+ if (node.value)
411
+ traverseExpression(node.value);
412
+ if (node.expression)
413
+ traverseExpression(node.expression);
414
+ if (node.body && Array.isArray(node.body)) {
415
+ for (const expr of node.body) {
416
+ traverseExpression(expr);
417
+ }
418
+ }
419
+ // Handle binary expression operands (e1, e2)
420
+ if (node.e1)
421
+ traverseExpression(node.e1);
422
+ if (node.e2)
423
+ traverseExpression(node.e2);
424
+ }
425
+ traverseExpression(methodBlock);
426
+ return dependencies;
427
+ }
428
+ function isPropertyComputed(property) {
429
+ if (isLayout(property)) {
430
+ return false; // Layouts don't have computed values themselves
431
+ }
432
+ // Check if any parameter contains a method block
433
+ if (isNumberExp(property)) {
434
+ const valueParam = property.params.find((p) => isNumberParamValue(p));
435
+ const minParam = property.params.find((p) => isNumberParamMin(p));
436
+ const maxParam = property.params.find((p) => isNumberParamMax(p));
437
+ return !!(valueParam && isMethodBlock(valueParam.value)) ||
438
+ !!(minParam && isMethodBlock(minParam.value)) ||
439
+ !!(maxParam && isMethodBlock(maxParam.value));
440
+ }
441
+ if (isStringExp(property)) {
442
+ const stringValue = property.params.find((p) => isStringParamValue(p));
443
+ return !!(stringValue && isMethodBlock(stringValue.value));
444
+ }
445
+ if (isAttributeExp(property)) {
446
+ const modParam = property.params.find((p) => isAttributeParamMod(p));
447
+ return !!(modParam && isMethodBlock(modParam.method));
448
+ }
449
+ if (isTrackerExp(property) || isResourceExp(property)) {
450
+ const params = property.params;
451
+ const valueParam = params.find((p) => isNumberParamValue(p));
452
+ const minParam = params.find((p) => isNumberParamMin(p));
453
+ const maxParam = params.find((p) => isNumberParamMax(p));
454
+ return !!(valueParam && isMethodBlock(valueParam.value)) ||
455
+ !!(minParam && isMethodBlock(minParam.value)) ||
456
+ !!(maxParam && isMethodBlock(maxParam.value));
457
+ }
458
+ if (isMoneyField(property)) {
459
+ const valueParam = property.params.find((p) => isNumberParamValue(p));
460
+ return !!(valueParam && isMethodBlock(valueParam.value));
461
+ }
462
+ return false;
463
+ }
464
+ function buildDependencyGraph(properties) {
465
+ const dependencies = [];
466
+ function processProperty(property) {
467
+ if (isLayout(property)) {
468
+ // Recursively process layout children
469
+ const childDeps = new Set();
470
+ for (const child of property.body) {
471
+ const childDep = processProperty(child);
472
+ childDep.dependencies.forEach(dep => childDeps.add(dep));
473
+ }
474
+ return {
475
+ property,
476
+ dependencies: childDeps,
477
+ isComputed: false
478
+ };
479
+ }
480
+ const deps = new Set();
481
+ const computed = isPropertyComputed(property);
482
+ if (computed) {
483
+ // Extract dependencies from method blocks
484
+ if (isNumberExp(property)) {
485
+ const valueParam = property.params.find((p) => isNumberParamValue(p));
486
+ const minParam = property.params.find((p) => isNumberParamMin(p));
487
+ const maxParam = property.params.find((p) => isNumberParamMax(p));
488
+ if (valueParam && isMethodBlock(valueParam.value)) {
489
+ extractPropertyDependencies(valueParam.value).forEach(dep => deps.add(dep));
490
+ }
491
+ if (minParam && isMethodBlock(minParam.value)) {
492
+ extractPropertyDependencies(minParam.value).forEach(dep => deps.add(dep));
493
+ }
494
+ if (maxParam && isMethodBlock(maxParam.value)) {
495
+ extractPropertyDependencies(maxParam.value).forEach(dep => deps.add(dep));
496
+ }
497
+ }
498
+ else if (isStringExp(property)) {
499
+ const stringValue = property.params.find((p) => isStringParamValue(p));
500
+ if (stringValue && isMethodBlock(stringValue.value)) {
501
+ extractPropertyDependencies(stringValue.value).forEach(dep => deps.add(dep));
502
+ }
503
+ }
504
+ else if (isAttributeExp(property)) {
505
+ const modParam = property.params.find((p) => isAttributeParamMod(p));
506
+ if (modParam && isMethodBlock(modParam.method)) {
507
+ extractPropertyDependencies(modParam.method).forEach(dep => deps.add(dep));
508
+ }
509
+ }
510
+ else if (isTrackerExp(property) || isResourceExp(property)) {
511
+ const params = property.params;
512
+ const valueParam = params.find((p) => isNumberParamValue(p));
513
+ const minParam = params.find((p) => isNumberParamMin(p));
514
+ const maxParam = params.find((p) => isNumberParamMax(p));
515
+ if (valueParam && isMethodBlock(valueParam.value)) {
516
+ extractPropertyDependencies(valueParam.value).forEach(dep => deps.add(dep));
517
+ }
518
+ if (minParam && isMethodBlock(minParam.value)) {
519
+ extractPropertyDependencies(minParam.value).forEach(dep => deps.add(dep));
520
+ }
521
+ if (maxParam && isMethodBlock(maxParam.value)) {
522
+ extractPropertyDependencies(maxParam.value).forEach(dep => deps.add(dep));
523
+ }
524
+ }
525
+ else if (isMoneyField(property)) {
526
+ const valueParam = property.params.find((p) => isNumberParamValue(p));
527
+ if (valueParam && isMethodBlock(valueParam.value)) {
528
+ extractPropertyDependencies(valueParam.value).forEach(dep => deps.add(dep));
529
+ }
530
+ }
531
+ // Remove self-references (only for properties that have names)
532
+ if ('name' in property && property.name) {
533
+ deps.delete(property.name.toLowerCase());
534
+ }
535
+ }
536
+ return {
537
+ property,
538
+ dependencies: deps,
539
+ isComputed: computed
540
+ };
541
+ }
542
+ for (const property of properties) {
543
+ dependencies.push(processProperty(property));
544
+ }
545
+ return dependencies;
546
+ }
547
+ function topologicalSort(dependencies) {
548
+ const sorted = [];
549
+ const visiting = new Set();
550
+ const visited = new Set();
551
+ const cycles = [];
552
+ function getPropertyName(property) {
553
+ return ('name' in property && property.name) ? property.name.toLowerCase() : 'unknown';
554
+ }
555
+ function visit(depItem, path = []) {
556
+ const name = getPropertyName(depItem.property);
557
+ if (visiting.has(name)) {
558
+ // Found a cycle
559
+ const cycleStart = path.indexOf(name);
560
+ if (cycleStart >= 0) {
561
+ cycles.push([...path.slice(cycleStart), name]);
562
+ }
563
+ return;
564
+ }
565
+ if (visited.has(name)) {
566
+ return;
567
+ }
568
+ visiting.add(name);
569
+ path.push(name);
570
+ // Visit dependencies first
571
+ for (const depName of depItem.dependencies) {
572
+ const depItem = dependencies.find(d => getPropertyName(d.property) === depName);
573
+ if (depItem) {
574
+ visit(depItem, [...path]);
575
+ }
576
+ }
577
+ visiting.delete(name);
578
+ visited.add(name);
579
+ path.pop();
580
+ sorted.push(depItem);
581
+ }
582
+ // Visit all non-computed properties first
583
+ for (const depItem of dependencies) {
584
+ if (!depItem.isComputed && !visited.has(getPropertyName(depItem.property))) {
585
+ visit(depItem);
586
+ }
587
+ }
588
+ // Then visit computed properties
589
+ for (const depItem of dependencies) {
590
+ if (depItem.isComputed && !visited.has(getPropertyName(depItem.property))) {
591
+ visit(depItem);
592
+ }
593
+ }
594
+ return { sorted, cycles };
595
+ }
596
+ function getTopLevelProperties(body) {
597
+ const result = [];
598
+ for (const item of body) {
599
+ if (isDocument(item)) {
600
+ // Recursively process document bodies
601
+ result.push(...getTopLevelProperties(item.body));
602
+ }
603
+ else if (isLayout(item)) {
604
+ // Flatten ALL layouts (pages, sections, rows, columns, tabs) down to
605
+ // their leaf properties. Layout nodes must NOT be added to the
606
+ // dependency graph: anonymous layouts (row/column have no name) all
607
+ // resolve to the same 'unknown' key in topologicalSort's visited set,
608
+ // so every anonymous layout after the first is skipped and the derived
609
+ // data for its children (e.g. a resource's calculated max) is never
610
+ // emitted — leaving it at the datamodel default. Flattening to named
611
+ // leaf properties makes section/row/column/page/root all behave the same.
612
+ result.push(...getTopLevelProperties(item.body));
613
+ }
614
+ else {
615
+ // For regular properties (ClassExpression), add them directly
616
+ result.push(item);
617
+ }
618
+ }
619
+ return result;
620
+ }
621
+ function generateDerivedData(document) {
622
+ // Build dependency graph for top-level properties only (no duplicates from nested layouts)
623
+ const allProperties = getTopLevelProperties(document.body);
624
+ const dependencies = buildDependencyGraph(allProperties);
625
+ const { sorted, cycles } = topologicalSort(dependencies);
626
+ // Debug: Log dependency information (can be removed in production)
627
+ // console.log(`Dependencies for ${document.name}:`, dependencies.map(d => ({
628
+ // name: ('name' in d.property && d.property.name) ? d.property.name : 'unknown',
629
+ // deps: Array.from(d.dependencies),
630
+ // computed: d.isComputed
631
+ // })));
632
+ // Log dependency cycles for debugging
633
+ if (cycles.length > 0) {
634
+ console.warn(`Dependency cycles detected in ${document.name}:`, cycles);
635
+ }
636
+ return expandToNode `
637
+ async _prepare${document.name}DerivedData() {
638
+ const editMode = this.flags["${id}"]?.["edit-mode"] ?? true;
639
+
640
+ ${cycles.length > 0 ? expandToNode `
641
+ // WARNING: Dependency cycles detected: ${cycles.map(cycle => cycle.join(' -> ')).join(', ')}
642
+ // Properties will be processed in original order to prevent infinite loops.
643
+ `.appendNewLine() : ""}
644
+
645
+ ${joinToNode(sorted.map(dep => dep.property), property => generateDerivedAttribute(property), { appendNewLineIfNotEmpty: true })}
646
+
647
+ ${isActor(document) ? expandToNode `
648
+ // Reapply Active Effects for calculated values
649
+ ${joinToNode(toBeReapplied, name => expandToNode `this.reapplyActiveEffectsForName("${name}");`, { appendNewLineIfNotEmpty: true })}
650
+
651
+ // Bridge fighter.system.* active effect overrides into system.* for flat (non-nested) fields.
652
+ // allApplicableEffects cannot strip the type prefix in V14 (changes is getter-only),
653
+ // so Foundry applies flat fields (e.g. bonusdamage, resistanceflat) to actor.fighter.system.*
654
+ // Nested fields (e.g. fight.value) are handled by reapplyActiveEffectsForName above.
655
+ const _typeOverrides = this.overrides?.${document.name.toLowerCase()}?.system ?? {};
656
+ for (const [_k, _v] of Object.entries(_typeOverrides)) {
657
+ if (typeof _v !== 'object' && _k in this.system) {
658
+ this.system[_k] = _v;
659
+ }
660
+ }
661
+ ` : ""}
662
+ }
663
+ `.appendNewLineIfNotEmpty().appendNewLine();
664
+ }
665
+ function generateActorPreUpdate(document) {
666
+ const allResources = getAllOfType(document.body, isResourceExp);
667
+ const healthResource = allResources.find(x => x.tag == "health");
668
+ if (healthResource == undefined)
669
+ return expandToNode `
670
+ _handlePreUpdate${document.name}Delta(changes, deltas) {
671
+ // No health resource defined
672
+ }
673
+ `.appendNewLine();
674
+ return expandToNode `
675
+ _handlePreUpdate${document.name}Delta(changes, deltas) {
676
+ // Health resource updates
677
+ if (changes.system.${healthResource.name.toLowerCase()} === undefined) return;
678
+
679
+ // Store value and temp changes
680
+ const valueChange = changes.system.${healthResource.name.toLowerCase()}.value;
681
+ const tempChange = changes.system.${healthResource.name.toLowerCase()}.temp;
682
+
683
+ // Calculate delta
684
+ if (valueChange !== undefined) {
685
+ const delta = valueChange - this.system.${healthResource.name.toLowerCase()}.value;
686
+ if (delta !== 0) {
687
+ deltas.${healthResource.name.toLowerCase()} = delta;
688
+ }
689
+ }
690
+
691
+ // Calculate temp delta
692
+ if (tempChange !== undefined) {
693
+ const tempDelta = tempChange - this.system.${healthResource.name.toLowerCase()}.temp;
694
+ if (tempDelta !== 0) {
695
+ deltas.${healthResource.name.toLowerCase()}Temp = tempDelta;
696
+ }
697
+ }
698
+ }
699
+ `.appendNewLine();
700
+ }
701
+ function generateDeltas(documents) {
702
+ if (type != "Actor")
703
+ return;
704
+ function generateHealthResourceAssignments(documents) {
705
+ function documentHealthResource(document) {
706
+ const healthResource = getAllOfType(document.body, isResourceExp).find(x => x.tag == "health");
707
+ if (healthResource == undefined)
708
+ return;
709
+ return expandToNode `
710
+ case "${document.name.toLowerCase()}": {
711
+ // ${healthResource.name} health resource
712
+
713
+ if ( !data.prototypeToken.bar1.attribute ) data.prototypeToken.bar1.attribute = "${healthResource.name.toLowerCase()}";
714
+ if ( !data.prototypeToken.displayBars ) data.prototypeToken.displayBars = CONST.TOKEN_DISPLAY_MODES.ALWAYS;
715
+ }
716
+ `.appendNewLineIfNotEmpty();
717
+ }
718
+ return expandToNode `
719
+ switch ( data.type ) {
720
+ ${joinToNode(documents.filter(x => isActor(x)), document => documentHealthResource(document), { appendNewLineIfNotEmpty: true })}
721
+ }
722
+ `.appendNewLineIfNotEmpty();
723
+ }
724
+ return expandToNode `
725
+ async _preUpdate(data, options, userId) {
726
+ await super._preUpdate(data, options, userId);
727
+ if (!options.diff || data === undefined) return;
728
+ let changes = {};
729
+
730
+ // Foundry v12 no longer has diffed data during _preUpdate, so we need to compute it ourselves.
731
+ if (game.release.version >= 12) {
732
+ // Retrieve a copy of the existing actor data.
733
+ let newData = game.system.utils.flattenObject(data);
734
+ let oldData = game.system.utils.flattenObject(this);
735
+
736
+ // Limit data to just the new data.
737
+ const diffData = foundry.utils.diffObject(oldData, newData);
738
+ changes = foundry.utils.expandObject(diffData);
739
+ }
740
+ else {
741
+ changes = foundry.utils.duplicate(data);
742
+ }
743
+
744
+ // Handle name changes
745
+ if (changes.name) {
746
+ let tokenData = {};
747
+
748
+ // Propagate name update to prototype token if same as actor
749
+ if (changes.name && this.name == this.prototypeToken.name) {
750
+ data.prototypeToken = {name: data.name};
751
+ }
752
+
753
+ // Update tokens.
754
+ let tokens = this.getActiveTokens();
755
+ tokens.forEach(token => {
756
+ let updateData = foundry.utils.duplicate(tokenData);
757
+
758
+ // Propagate name update to token if same as actor
759
+ if (data.name && this.name == token.name) {
760
+ updateData.name = data.name;
761
+ }
762
+ token.document.update(updateData);
763
+ });
764
+ }
765
+
766
+ if (changes.system === undefined) return; // Nothing more to do
767
+
768
+ const deltas = {};
769
+
770
+ ${joinToNode(documents.filter(x => isActor(x)), document => expandToNode `
771
+ if (this.type == "${document.name.toLowerCase()}") this._handlePreUpdate${document.name}Delta(changes, deltas);
772
+ `, { appendNewLineIfNotEmpty: true })}
773
+
774
+ options.fromPreUpdate = deltas;
775
+ }
776
+
777
+ /* -------------------------------------------- */
778
+
779
+ ${joinToNode(documents.filter(x => isActor(x)), document => generateActorPreUpdate(document), { appendNewLineIfNotEmpty: true })}
780
+
781
+ /* -------------------------------------------- */
782
+
783
+ async _onUpdate(data, options, userId) {
784
+ await super._onUpdate(data, options, userId);
785
+
786
+ // Iterate over all objects in fromPreUpdate, showing scrolling text for each.
787
+ if (options.fromPreUpdate) {
788
+ for (const [key, delta] of Object.entries(options.fromPreUpdate)) {
789
+ this._showScrollingText(delta, key);
790
+ }
791
+ }
792
+
793
+ // Add / remove status effects
794
+ const calculatedStatusEffects = CONFIG.statusEffects.filter(effect => effect.calculated);
795
+ for (const effect of calculatedStatusEffects) {
796
+ const key = effect.id;
797
+ const active = this.system[key] ?? false;
798
+ const existing = this.effects.find(e => e.statuses.has(key));
799
+
800
+ if ((active && existing) || (!active && !existing)) continue;
801
+
802
+ // If the effect is active the AE doesn't exist, add it
803
+ if (active && !existing) {
804
+ const cls = getDocumentClass("ActiveEffect");
805
+ const createData = foundry.utils.deepClone(effect);
806
+ createData.statuses = [key];
807
+ delete createData.id;
808
+ createData.name = game.i18n.localize(createData.name);
809
+ await cls.create(createData, {parent: this});
810
+ if (key == "dead") Hooks.callAll("death", this);
811
+ }
812
+
813
+ // If the effect is active the AE doesn't exist, add it
814
+ if (!active && existing) {
815
+ this.deleteEmbeddedDocuments("ActiveEffect", [existing.id]);
816
+ }
817
+ }
818
+ }
819
+
820
+ /* -------------------------------------------- */
821
+
822
+ async _onCreate(data, options, userId) {
823
+ await super._onCreate(data, options, userId);
824
+
825
+ console.log("onCreate", data, options, userId);
826
+
827
+ ${generateHealthResourceAssignments(documents)}
828
+ }
829
+
830
+ /* -------------------------------------------- */
831
+
832
+ _showScrollingText(delta, suffix="", overrideOptions={}) {
833
+ // Show scrolling text of hp update
834
+ const tokens = this.isToken ? [this.token?.object] : this.getActiveTokens(true);
835
+ if (delta != 0 && tokens.length > 0) {
836
+ let color = delta < 0 ? 0xcc0000 : 0x00cc00;
837
+ for ( let token of tokens ) {
838
+ let textOptions = {
839
+ anchor: CONST.TEXT_ANCHOR_POINTS.CENTER,
840
+ direction: CONST.TEXT_ANCHOR_POINTS.TOP,
841
+ fontSize: 32,
842
+ fill: color,
843
+ stroke: 0x000000,
844
+ strokeThickness: 4,
845
+ duration: 3000
846
+ };
847
+ canvas.interface.createScrollingText(
848
+ token.center,
849
+ delta.signedString()+" "+suffix,
850
+ foundry.utils.mergeObject(textOptions, overrideOptions)
851
+ );
852
+ // Flash dynamic token rings.
853
+ if (token?.ring) {
854
+ const flashColor = delta < 0 ? Color.fromString('#ff0000') : Color.fromString('#00ff00');
855
+ token.ring.flashColor(flashColor, {
856
+ duration: 600,
857
+ easing: foundry.canvas.tokens.TokenRing.easeTwoPeaks,
858
+ });
859
+ }
860
+ }
861
+ }
862
+ }
863
+ `.appendNewLineIfNotEmpty();
864
+ }
865
+ function generateInitiativeFormula(document) {
866
+ var _a;
867
+ const initiativeAttribute = getAllOfType(document.body, isInitiativeProperty);
868
+ if (initiativeAttribute.length == 0) {
869
+ return expandToNode `
870
+ case "${document.name.toLowerCase()}": return "0";
871
+ `.appendNewLineIfNotEmpty();
872
+ }
873
+ let initiative = (_a = initiativeAttribute[0]) === null || _a === void 0 ? void 0 : _a.value;
874
+ if (initiative == undefined) {
875
+ return expandToNode `
876
+ case "${document.name.toLowerCase()}": return "0";
877
+ `.appendNewLineIfNotEmpty();
878
+ }
879
+ console.log("Initiative Formula");
880
+ return expandToNode `
881
+ case "${document.name.toLowerCase()}": return "${translateExpression(entry, id, initiative, true, initiativeAttribute[0])}";
882
+ `.appendNewLineIfNotEmpty();
883
+ }
884
+ function generateDocumentHooks(document) {
885
+ const hooks = getAllOfType(document.body, isHookHandler);
886
+ function generateHook(hook) {
887
+ var _a;
888
+ let name = hook.name;
889
+ function generateBody() {
890
+ return expandToNode `
891
+ const ${entry.config.name}Roll = game.system.rollClass;
892
+ const ${entry.config.name}DamageRoll = game.system.damageRollClass;
893
+ const context = {
894
+ object: document,
895
+ target: game.user.getTargetOrNothing()
896
+ };
897
+ const system = document.system;
898
+ let update = {};
899
+ let embeddedUpdate = {};
900
+ let parentUpdate = {};
901
+ let parentEmbeddedUpdate = {};
902
+ let targetUpdate = {};
903
+ let targetEmbeddedUpdate = {};
904
+ let selfDeleted = false;
905
+
906
+ ${translateBodyExpressionToJavascript(entry, id, hook.body, false, hook)}
907
+
908
+ if (!selfDeleted && Object.keys(update).length > 0) {
909
+ await document.update(update);
910
+ }
911
+ if (!selfDeleted && Object.keys(embeddedUpdate).length > 0) {
912
+ for (let key of Object.keys(embeddedUpdate)) {
913
+ await document.updateEmbeddedDocuments("Item", embeddedUpdate[key]);
914
+ }
915
+ }
916
+ if (Object.keys(parentUpdate).length > 0) {
917
+ await document.parent.update(parentUpdate);
918
+ }
919
+ if (Object.keys(parentEmbeddedUpdate).length > 0) {
920
+ for (let key of Object.keys(parentEmbeddedUpdate)) {
921
+ await document.parent.updateEmbeddedDocuments("Item", parentEmbeddedUpdate[key]);
922
+ }
923
+ }
924
+ if (Object.keys(targetUpdate).length > 0) {
925
+ await context.target.update(targetUpdate);
926
+ }
927
+ if (Object.keys(targetEmbeddedUpdate).length > 0) {
928
+ for (let key of Object.keys(targetEmbeddedUpdate)) {
929
+ await context.target.updateEmbeddedDocuments("Item", targetEmbeddedUpdate[key]);
930
+ }
931
+ }
932
+ `;
933
+ }
934
+ switch (name) {
935
+ case "combatStart":
936
+ return expandToNode `
937
+ Hooks.on("combatStart", async (${hook.params.map(p => p.name).join(", ")}) => {
938
+ ${generateBody()}
939
+ });
940
+ `.appendNewLineIfNotEmpty();
941
+ case "combatEnd":
942
+ return expandToNode `
943
+ Hooks.on("deleteCombat", async (${hook.params.map(p => p.name).join(", ")}) => {
944
+ ${generateBody()}
945
+ });
946
+ `.appendNewLineIfNotEmpty();
947
+ case "roundStart":
948
+ return expandToNode `
949
+ Hooks.on("combatRound", async (combat, updateData, updateOptions) => {
950
+ const roundStart = async (${hook.params.map(p => p.name).join(", ")}) => {
951
+ ${generateBody()}
952
+ }
953
+
954
+ if (updateData.turn == 0) {
955
+ await roundStart(updateData.round);
956
+ }
957
+ });
958
+ `.appendNewLineIfNotEmpty();
959
+ case "roundEnd":
960
+ return expandToNode `
961
+ Hooks.on("combatRound", async (combat, updateData, updateOptions) => {
962
+ const roundEnd = async (${hook.params.map(p => p.name).join(", ")}) => {
963
+ ${generateBody()}
964
+ }
965
+
966
+ if (updateData.round > 0) {
967
+ roundEnd(updateData.round - 1);
968
+ }
969
+ });
970
+ `.appendNewLineIfNotEmpty();
971
+ case "turnStart":
972
+ return expandToNode `
973
+ Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
974
+ const turnStart = async () => {
975
+ ${generateBody()}
976
+ }
977
+ if (combat.combatant.actor.uuid == document.uuid) {
978
+ await turnStart();
979
+ }
980
+ });
981
+ `.appendNewLineIfNotEmpty();
982
+ case "turnEnd":
983
+ return expandToNode `
984
+ Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
985
+ const turnEnd = async () => {
986
+ ${generateBody()}
987
+ }
988
+ const previousCombatant = combat.combatants.get(combat.previous?.combatantId);
989
+ if (previousCombatant?.actor?.uuid == document.uuid) {
990
+ await turnEnd();
991
+ }
992
+ });
993
+ `.appendNewLineIfNotEmpty();
994
+ case "turnIsNext":
995
+ return expandToNode `
996
+ Hooks.on("combatTurnChange", async (combat, updateData, updateOptions) => {
997
+ const turnIsNext = async () => {
998
+ ${generateBody()}
999
+ };
1000
+ if (combat.nextCombatant.actor.uuid == document.uuid) {
1001
+ await turnIsNext();
1002
+ }
1003
+ });
1004
+ `.appendNewLineIfNotEmpty();
1005
+ case "death":
1006
+ return expandToNode `
1007
+ Hooks.on("death", async (deadDocument) => {
1008
+ const onDeath = async () => {
1009
+ ${generateBody()}
1010
+ };
1011
+ if ( deadDocument.uuid == document.uuid ) {
1012
+ await onDeath();
1013
+ }
1014
+ });
1015
+ `.appendNewLineIfNotEmpty();
1016
+ case "preApplyDamage":
1017
+ case "preApplyHealing":
1018
+ case "preApplyTemp":
1019
+ return expandToNode `
1020
+ if (game.system.documentHooks.has("${name}-" + this.uuid)) return;
1021
+ const on${name} = async (document, context) => {
1022
+ const preApply = async (${hook.params.map(p => p.name).join(", ")}) => {
1023
+ ${generateBody()}
1024
+ return ${(_a = hook.params.shift()) === null || _a === void 0 ? void 0 : _a.name};
1025
+ }
1026
+ if (document.uuid == this.uuid) {
1027
+ context.amount = await preApply(context.amount, context.damageType, context.damageMetadata);
1028
+ }
1029
+ }
1030
+ game.system.documentHooks.set("${name}-" + this.uuid, on${name});
1031
+ Hooks.on("${name}", on${name});
1032
+ `.appendNewLineIfNotEmpty();
1033
+ case "appliedDamage":
1034
+ case "appliedHealing":
1035
+ case "appliedTemp":
1036
+ return expandToNode `
1037
+ if (game.system.documentHooks.has("${name}-" + this.uuid)) return;
1038
+ const on${name} = async (document, context) => {
1039
+ const applied = async (${hook.params.map(p => p.name).join(", ")}) => {
1040
+ ${generateBody()}
1041
+ }
1042
+ if (document.uuid == this.uuid) {
1043
+ await applied(context.amount, context.damageType, context.damageMetadata);
1044
+ }
1045
+ };
1046
+ game.system.documentHooks.set("${name}-" + this.uuid, on${name});
1047
+ Hooks.on("${name}", on${name});
1048
+ `.appendNewLineIfNotEmpty();
1049
+ default:
1050
+ return expandToNode `
1051
+ Hooks.on("${name}", async (${hook.params.map(p => p.name).join(", ")}) => {
1052
+ ${generateBody()}
1053
+ });
1054
+ `.appendNewLineIfNotEmpty();
1055
+ }
1056
+ }
1057
+ return expandToNode `
1058
+ _register${document.name}Hooks(document) {
1059
+ ${joinToNode(hooks, generateHook, { appendNewLineIfNotEmpty: true })}
1060
+ }
1061
+ `.appendNewLineIfNotEmpty().appendNewLine();
1062
+ }
1063
+ const fileNode = expandToNode `
1064
+ export default class ${entry.config.name}${type} extends ${type} {
1065
+ /** @override */
1066
+ prepareDerivedData() {
1067
+ switch ( this.type ) {
1068
+ ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => `case "${document.name.toLowerCase()}": return this._prepare${document.name}DerivedData();`, { appendNewLineIfNotEmpty: true })}
1069
+ }
1070
+ }
1071
+
1072
+ /* -------------------------------------------- */
1073
+
1074
+ ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), document => generateDerivedData(document), { appendNewLineIfNotEmpty: true })}
1075
+
1076
+ /* -------------------------------------------- */
1077
+
1078
+ ${generateDeltas(entry.documents)}
1079
+
1080
+ /* -------------------------------------------- */
1081
+
1082
+ reapplyActiveEffectsForName(name) {
1083
+ if (this.documentName !== "Actor") return;
1084
+ for (const effect of this.appliedEffects) {
1085
+ for (const change of effect.changes) {
1086
+ if (change.key == name) {
1087
+ const changes = effect.apply(this, change);
1088
+ Object.assign(this.overrides, changes);
1089
+ }
1090
+ }
1091
+ }
1092
+ }
1093
+
1094
+ /* -------------------------------------------- */
1095
+
1096
+ /** @override */
1097
+ _initialize(options = {}) {
1098
+ super._initialize(options);
1099
+
1100
+ switch ( this.type ) {
1101
+ ${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 })}
1102
+ }
1103
+ }
1104
+
1105
+ /* -------------------------------------------- */
1106
+
1107
+ ${joinToNode(entry.documents.filter(d => type == "Actor" ? isActor(d) : isItem(d)), generateDocumentHooks, { appendNewLineIfNotEmpty: true })}
1108
+
1109
+ /* -------------------------------------------- */
1110
+
1111
+ // 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
1112
+ /** @override */
1113
+ *allApplicableEffects() {
1114
+ const systemFlags = this.flags["${id}"] ?? {};
1115
+ const edit = systemFlags["edit-mode"] ?? true;
1116
+
1117
+ function getTypedEffect(type, edit, effect, source) {
1118
+ // Pre-build data to avoid mutating ActiveEffect getters (changes is getter-only in v14).
1119
+ const data = foundry.utils.duplicate(effect);
1120
+ data.changes = (data.changes ?? []).filter(c => c.key.startsWith(type));
1121
+ for ( const change of data.changes ) {
1122
+ if (change.mode == 0) continue;
1123
+ change.key = change.key.replace(type + ".", "");
1124
+ }
1125
+ if ( edit ) data.disabled = true;
1126
+ data.flags ??= {};
1127
+ data.flags["${id}"] ??= {};
1128
+ data.flags["${id}"].source = source;
1129
+ return new ActiveEffect(data, {parent: effect.parent});
1130
+ }
1131
+
1132
+ for ( const effect of this.effects ) {
1133
+ yield getTypedEffect(this.type, edit, effect, game.i18n.localize("Self"));
1134
+ }
1135
+ ${type == "Actor" ? expandToNode `
1136
+ for ( const item of this.items ) {
1137
+ for ( const effect of item.effects ) {
1138
+ if ( effect.transfer ) yield getTypedEffect(this.type, edit, effect, item.name);
1139
+ }
1140
+ }
1141
+ `.appendNewLine() : ""}
1142
+ }
1143
+
1144
+
1145
+ /* -------------------------------------------- */
1146
+
1147
+ _onCreateDescendantDocuments(parent, collection, documents, data, options, userId) {
1148
+ super._onCreateDescendantDocuments(parent, collection, documents, data, options, userId);
1149
+
1150
+ for (const document of documents) {
1151
+ if (document.documentName !== "ActiveEffect") continue;
1152
+
1153
+ for (const change of document.changes) {
1154
+ if (change.mode != 0) continue;
1155
+ const customMode = foundry.utils.getProperty(document.flags["${id}"], change.key + "-custommode");
1156
+ switch (customMode) {
1157
+ case 1: // Add Once
1158
+ this._effectAddOnce(parent, document, change);
1159
+ break;
1160
+ default:
1161
+ console.error("Unknown custom mode", customMode);
1162
+ break;
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+
1168
+ /* -------------------------------------------- */
1169
+
1170
+ _effectAddOnce(parent, ae, change) {
1171
+ console.dir("AddOnce", parent, ae, change);
1172
+
1173
+ const key = change.key.replace(parent.type + ".", "");
1174
+ const currentValue = foundry.utils.getProperty(parent, key);
1175
+
1176
+ // Create an update for the parent
1177
+ const update = {
1178
+ [key]: currentValue + parseInt(change.value)
1179
+ };
1180
+ parent.update(update);
1181
+
1182
+ // Create a chat card
1183
+ const chatData = {
1184
+ user: game.user._id,
1185
+ speaker: ChatMessage.getSpeaker({ actor: parent }),
1186
+ content: \`<p>Added "\${ae.name}" once</p>\`
1187
+ };
1188
+ ChatMessage.create(chatData);
1189
+ }
1190
+
1191
+ /* -------------------------------------------- */
1192
+
1193
+ static async createDialog(data = {}, { parent = null, pack = null, types = null, ...options } = {}) {
1194
+ types ??= game.documentTypes[this.documentName].filter(t => (t !== CONST.BASE_DOCUMENT_TYPE) && (CONFIG[this.documentName].typeCreatables[t] !== false));
1195
+ if (!types.length) return null;
1196
+
1197
+ const collection = parent ? null : pack ? game.packs.get(pack) : game.collections.get(this.documentName);
1198
+ const folders = collection?._formatFolderSelectOptions() ?? [];
1199
+
1200
+ const label = game.i18n.localize(this.metadata.label);
1201
+ const title = game.i18n.format("DOCUMENT.Create", { type: label });
1202
+ const name = data.name || game.i18n.format("DOCUMENT.New", { type: label });
1203
+
1204
+ let type = data.type || CONFIG[this.documentName]?.defaultType;
1205
+ if (!types.includes(type)) type = types[0];
1206
+
1207
+ // If there's only one type, no need to prompt
1208
+ if (types.length === 1) {
1209
+ let createName = this.defaultName();
1210
+ const createData = {
1211
+ name: createName,
1212
+ type
1213
+ };
1214
+ return this.create(createData, { parent, pack, renderSheet: true });
1215
+ }
1216
+
1217
+ const createResponse = await game.system.documentCreateDialog.prompt({
1218
+ type,
1219
+ types: types.reduce((arr, typer) => {
1220
+ arr.push({
1221
+ type: typer,
1222
+ label: game.i18n.has(typer) ? game.i18n.localize(typer) : typer,
1223
+ icon: this.getDefaultArtwork({ type: typer })?.img ?? "icons/svg/item-bag.svg",
1224
+ description: CONFIG[this.documentName]?.typeDescriptions?.[typer] ?? "",
1225
+ selected: type === typer
1226
+ });
1227
+ return arr;
1228
+ }, []).sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang)),
1229
+ name,
1230
+ title,
1231
+ label,
1232
+ folders,
1233
+ folder: data.folder
1234
+ });
1235
+
1236
+ const createData = foundry.utils.mergeObject(data, createResponse, { inplace: false });
1237
+ createData.type = createData.type || type;
1238
+ createData.type = createData.type.toLowerCase();
1239
+ if (!createData.folder) delete createData.folder;
1240
+ if (!createData.name?.trim()) createData.name = this.defaultName();
1241
+ return this.create(createData, { parent, pack, renderSheet: true });
1242
+
1243
+ const content = await renderTemplate("systems/${id}/system/templates/document-create.hbs", {
1244
+ folders, name, type,
1245
+ folder: data.folder,
1246
+ hasFolders: folders.length > 0,
1247
+ types: types.reduce((arr, typer) => {
1248
+ arr.push({
1249
+ type: typer,
1250
+ label: game.i18n.has(typer) ? game.i18n.localize(typer) : typer,
1251
+ icon: this.getDefaultArtwork({ type: typer })?.img ?? "icons/svg/item-bag.svg",
1252
+ description: CONFIG[this.documentName]?.typeDescriptions?.[typer] ?? "",
1253
+ selected: type === typer
1254
+ });
1255
+ return arr;
1256
+ }, []).sort((a, b) => a.label.localeCompare(b.label, game.i18n.lang))
1257
+ });
1258
+ return Dialog.prompt({
1259
+ title, content,
1260
+ label: title,
1261
+ render: html => {
1262
+ const app = html.closest(".app");
1263
+ const folder = app.querySelector("select");
1264
+ if (folder) app.querySelector(".dialog-buttons").insertAdjacentElement("afterbegin", folder);
1265
+ app.querySelectorAll(".window-header .header-button").forEach(btn => {
1266
+ const label = btn.innerText;
1267
+ const icon = btn.querySelector("i");
1268
+ btn.innerHTML = icon.outerHTML;
1269
+ btn.dataset.tooltip = label;
1270
+ btn.setAttribute("aria-label", label);
1271
+ });
1272
+ app.querySelector(".document-name").select();
1273
+ },
1274
+ callback: html => {
1275
+ const form = html.querySelector("form");
1276
+ const fd = new FormDataExtended(form);
1277
+ const createData = foundry.utils.mergeObject(data, fd.object, { inplace: false });
1278
+
1279
+ },
1280
+ rejectClose: false,
1281
+ options: { ...options, jQuery: false, width: 700, height: 'auto', classes: ["${id}", "create-document", "dialog"] }
1282
+ });
1283
+ }
1284
+
1285
+ /* -------------------------------------------- */
1286
+
1287
+ static getDefaultArtwork(itemData = {}) {
1288
+ const { type } = itemData;
1289
+ const { img, texture } = super.getDefaultArtwork(itemData);
1290
+ return { img: CONFIG[this.documentName]?.typeArtworks?.[type] ?? img, texture: texture };
1291
+ }
1292
+
1293
+ /* -------------------------------------------- */
1294
+
1295
+ getRollData() {
1296
+ const data = super.getRollData();
1297
+ const rollData = foundry.utils.duplicate(data);
1298
+ rollData.system = this.system;
1299
+ return rollData;
1300
+ }
1301
+
1302
+ /* -------------------------------------------- */
1303
+
1304
+ /** @override */
1305
+ async modifyTokenAttribute(attribute, value, isDelta, isBar) {
1306
+ const resource = foundry.utils.getProperty(this.system, attribute);
1307
+
1308
+ if (isDelta && value < 0) {
1309
+ // Apply to temp first
1310
+ resource.temp += value;
1311
+
1312
+ // If temp is negative, apply to value
1313
+ if (resource.temp < 0) {
1314
+ resource.value += resource.temp;
1315
+ resource.temp = 0;
1316
+ }
1317
+ await this.update({ ["system." + attribute]: resource });
1318
+ return;
1319
+ }
1320
+
1321
+ return super.modifyTokenAttribute(attribute, value, isDelta, isBar);
1322
+ }
1323
+
1324
+ ${type == "Actor" ? expandToNode `
1325
+ /* -------------------------------------------- */
1326
+
1327
+ getInitiativeFormula() {
1328
+ switch ( this.type ) {
1329
+ ${joinToNode(entry.documents.filter(d => isActor(d)), document => generateInitiativeFormula(document), { appendNewLineIfNotEmpty: true })}
1330
+ }
1331
+ }
1332
+ `.appendNewLine() : ""}
1333
+ }
1334
+ `.appendNewLineIfNotEmpty();
1335
+ fs.writeFileSync(generatedFilePath, toString(fileNode));
1336
+ }
1337
+ generateExtendedDocumentClass("Actor", entry);
1338
+ generateExtendedDocumentClass("Item", entry);
1339
+ }
1340
+ //# sourceMappingURL=derived-data-generator.js.map