All user data for FoundryVTT. Includes worlds, systems, modules, and any asset in the "foundryuserdata" directory. Does NOT include the FoundryVTT installation itself.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

273 lines
8.9 KiB

  1. import { showArtSelect } from '../../token-variants.mjs';
  2. import { doRandomSearch, doSyncSearch } from '../search.js';
  3. import { FEATURE_CONTROL, TVA_CONFIG } from '../settings.js';
  4. import { keyPressed, nameForgeRandomize, SEARCH_TYPE, updateActorImage, updateTokenImage } from '../utils.js';
  5. import { registerHook, unregisterHook } from './hooks.js';
  6. const feature_id = 'PopUpAndRandomize';
  7. export function registerPopRandomizeHooks() {
  8. if (FEATURE_CONTROL[feature_id]) {
  9. registerHook(feature_id, 'createActor', _createActor);
  10. registerHook(feature_id, 'createToken', _createToken);
  11. } else {
  12. ['createActor', 'createToken'].forEach((name) => unregisterHook(feature_id, name));
  13. }
  14. }
  15. async function _createToken(token, options, userId) {
  16. if (userId && game.user.id != userId) return;
  17. // Check if random search is enabled and if so perform it
  18. const actorRandSettings = game.actors.get(token.actorId)?.getFlag('token-variants', 'randomizerSettings');
  19. const randSettings = mergeObject(TVA_CONFIG.randomizer, actorRandSettings ?? {}, {
  20. inplace: false,
  21. recursive: false,
  22. });
  23. let vDown = keyPressed('v');
  24. const flagTarget = token.actor ? game.actors.get(token.actor.id) : token.document ?? token;
  25. const popupFlag = flagTarget.getFlag('token-variants', 'popups');
  26. if ((vDown && randSettings.tokenCopyPaste) || (!vDown && randSettings.tokenCreate)) {
  27. let performRandomSearch = true;
  28. if (!actorRandSettings) {
  29. if (randSettings.representedActorDisable && token.actor) performRandomSearch = false;
  30. if (randSettings.linkedActorDisable && token.actorLink) performRandomSearch = false;
  31. if (_disableRandomSearchForType(randSettings, token.actor)) performRandomSearch = false;
  32. } else {
  33. performRandomSearch = Boolean(actorRandSettings);
  34. }
  35. if (performRandomSearch) {
  36. // Randomize Token Name if need be
  37. const randomName = await nameForgeRandomize(randSettings);
  38. if (randomName) {
  39. token.update({ name: randomName });
  40. }
  41. const img = await doRandomSearch(token.name, {
  42. searchType: SEARCH_TYPE.TOKEN,
  43. actor: token.actor,
  44. randomizerOptions: randSettings,
  45. });
  46. if (img) {
  47. await updateTokenImage(img[0], {
  48. token: token,
  49. actor: token.actor,
  50. imgName: img[1],
  51. });
  52. }
  53. if (randSettings.diffImages) {
  54. let imgPortrait;
  55. if (randSettings.syncImages) {
  56. if (!img) return;
  57. imgPortrait = await doSyncSearch(img[1], {
  58. searchType: SEARCH_TYPE.PORTRAIT,
  59. });
  60. } else {
  61. // Prevent searches via method intended purely for tokens
  62. if (!(randSettings.tokenName || randSettings.actorName)) randSettings.tokenName = true;
  63. randSettings.wildcard = false;
  64. randSettings.shared = false;
  65. imgPortrait = await doRandomSearch(token.name, {
  66. searchType: SEARCH_TYPE.PORTRAIT,
  67. actor: token.actor,
  68. randomizerOptions: randSettings,
  69. });
  70. }
  71. if (imgPortrait) {
  72. await updateActorImage(token.actor, imgPortrait[0]);
  73. }
  74. } else if (randSettings.tokenToPortrait) {
  75. if (!img) return;
  76. await updateActorImage(token.actor, img[0]);
  77. }
  78. return;
  79. }
  80. if (popupFlag == null && !randSettings.popupOnDisable) {
  81. return;
  82. }
  83. } else if (randSettings.tokenCreate || randSettings.tokenCopyPaste) {
  84. return;
  85. }
  86. // Check if pop-up is enabled and if so open it
  87. if (!TVA_CONFIG.permissions.popups[game.user.role]) {
  88. return;
  89. }
  90. let dirKeyDown = keyPressed('popupOverride');
  91. if (vDown && TVA_CONFIG.popup.disableAutoPopupOnTokenCopyPaste) {
  92. return;
  93. }
  94. if (!dirKeyDown || (dirKeyDown && vDown)) {
  95. if (TVA_CONFIG.popup.disableAutoPopupOnTokenCreate && !vDown) {
  96. return;
  97. } else if (popupFlag == null && _disablePopupForType(token.actor)) {
  98. return;
  99. } else if (popupFlag != null && !popupFlag) {
  100. return;
  101. }
  102. }
  103. showArtSelect(token.name, {
  104. callback: async function (imgSrc, imgName) {
  105. if (TVA_CONFIG.popup.twoPopups) {
  106. await updateActorImage(token.actor, imgSrc);
  107. _twoPopupPrompt(token.actor, imgSrc, imgName, token);
  108. } else {
  109. updateTokenImage(imgSrc, {
  110. actor: token.actor,
  111. imgName: imgName,
  112. token: token,
  113. });
  114. }
  115. },
  116. searchType: TVA_CONFIG.popup.twoPopups ? SEARCH_TYPE.PORTRAIT : SEARCH_TYPE.TOKEN,
  117. object: token,
  118. preventClose: TVA_CONFIG.popup.twoPopups && TVA_CONFIG.popup.twoPopupsNoDialog,
  119. });
  120. }
  121. async function _createActor(actor, options, userId) {
  122. if (userId && game.user.id != userId) return;
  123. // Check if random search is enabled and if so perform it
  124. const randSettings = TVA_CONFIG.randomizer;
  125. if (randSettings.actorCreate) {
  126. let performRandomSearch = true;
  127. if (randSettings.linkedActorDisable && actor.prototypeToken.actorLink) performRandomSearch = false;
  128. if (_disableRandomSearchForType(randSettings, actor)) performRandomSearch = false;
  129. if (performRandomSearch) {
  130. const img = await doRandomSearch(actor.name, {
  131. searchType: SEARCH_TYPE.PORTRAIT,
  132. actor: actor,
  133. });
  134. if (img) {
  135. await updateActorImage(actor, img[0]);
  136. }
  137. if (!img) return;
  138. if (randSettings.diffImages) {
  139. let imgToken;
  140. if (randSettings.syncImages) {
  141. imgToken = await doSyncSearch(img[1], { searchType: SEARCH_TYPE.TOKEN });
  142. } else {
  143. imgToken = await doRandomSearch(actor.name, {
  144. searchType: SEARCH_TYPE.TOKEN,
  145. actor: actor,
  146. });
  147. }
  148. if (imgToken) {
  149. await updateTokenImage(imgToken[0], { actor: actor, imgName: imgToken[1] });
  150. }
  151. }
  152. return;
  153. }
  154. if (!randSettings.popupOnDisable) {
  155. return;
  156. }
  157. }
  158. // Check if pop-up is enabled and if so open it
  159. if (!TVA_CONFIG.permissions.popups[game.user.role]) {
  160. return;
  161. }
  162. if (TVA_CONFIG.popup.disableAutoPopupOnActorCreate && !keyPressed('popupOverride')) {
  163. return;
  164. } else if (_disablePopupForType(actor)) {
  165. return;
  166. }
  167. showArtSelect(actor.name, {
  168. callback: async function (imgSrc, name) {
  169. const actTokens = actor.getActiveTokens();
  170. const token = actTokens.length === 1 ? actTokens[0] : null;
  171. await updateActorImage(actor, imgSrc);
  172. if (TVA_CONFIG.popup.twoPopups) _twoPopupPrompt(actor, imgSrc, name, token);
  173. else {
  174. updateTokenImage(imgSrc, {
  175. actor: actor,
  176. imgName: name,
  177. token: token,
  178. });
  179. }
  180. },
  181. searchType: TVA_CONFIG.popup.twoPopups ? SEARCH_TYPE.PORTRAIT : SEARCH_TYPE.PORTRAIT_AND_TOKEN,
  182. object: actor,
  183. preventClose: TVA_CONFIG.popup.twoPopups && TVA_CONFIG.popup.twoPopupsNoDialog,
  184. });
  185. }
  186. function _disableRandomSearchForType(randSettings, actor) {
  187. if (!actor) return false;
  188. return randSettings[`${actor.type}Disable`] ?? false;
  189. }
  190. function _disablePopupForType(actor) {
  191. if (!actor) return false;
  192. return TVA_CONFIG.popup[`${actor.type}Disable`] ?? false;
  193. }
  194. function _twoPopupPrompt(actor, imgSrc, imgName, token) {
  195. if (TVA_CONFIG.popup.twoPopups && TVA_CONFIG.popup.twoPopupsNoDialog) {
  196. showArtSelect((token ?? actor.prototypeToken).name, {
  197. callback: (imgSrc, name) =>
  198. updateTokenImage(imgSrc, {
  199. actor: actor,
  200. imgName: name,
  201. token: token,
  202. }),
  203. searchType: SEARCH_TYPE.TOKEN,
  204. object: token ? token : actor,
  205. force: true,
  206. });
  207. } else if (TVA_CONFIG.popup.twoPopups) {
  208. let d = new Dialog({
  209. title: 'Portrait -> Token',
  210. content: `<p>${game.i18n.localize('token-variants.windows.art-select.apply-same-art')}</p>`,
  211. buttons: {
  212. one: {
  213. icon: '<i class="fas fa-check"></i>',
  214. callback: () => {
  215. updateTokenImage(imgSrc, {
  216. actor: actor,
  217. imgName: imgName,
  218. token: token,
  219. });
  220. const artSelects = Object.values(ui.windows).filter((app) => app instanceof ArtSelect);
  221. for (const app of artSelects) {
  222. app.close();
  223. }
  224. },
  225. },
  226. two: {
  227. icon: '<i class="fas fa-times"></i>',
  228. callback: () => {
  229. showArtSelect((token ?? actor.prototypeToken).name, {
  230. callback: (imgSrc, name) =>
  231. updateTokenImage(imgSrc, {
  232. actor: actor,
  233. imgName: name,
  234. token: token,
  235. }),
  236. searchType: SEARCH_TYPE.TOKEN,
  237. object: token ? token : actor,
  238. force: true,
  239. });
  240. },
  241. },
  242. },
  243. default: 'one',
  244. });
  245. d.render(true);
  246. }
  247. }