import { showArtSelect } from '../token-variants.mjs'; import { BASE_IMAGE_CATEGORIES, SEARCH_TYPE, updateActorImage, updateTokenImage, userRequiresImageCache, } from '../scripts/utils.js'; import { addToQueue, ArtSelect, renderFromQueue } from './artSelect.js'; import { getSearchOptions, TVA_CONFIG, updateSettings } from '../scripts/settings.js'; import ConfigureSettings from './configureSettings.js'; import MissingImageConfig from './missingImageConfig.js'; import { cacheImages, doImageSearch } from '../scripts/search.js'; async function autoApply(actor, image1, image2, formData, typeOverride) { let portraitFound = formData.ignorePortrait; let tokenFound = formData.ignoreToken; if (formData.diffImages) { let results = []; if (!formData.ignorePortrait) { results = await doImageSearch(actor.name, { searchType: typeOverride ?? SEARCH_TYPE.PORTRAIT, simpleResults: true, searchOptions: formData.searchOptions, }); if ((results ?? []).length != 0) { portraitFound = true; await updateActorImage(actor, results[0], false, formData.compendium); } } if (!formData.ignoreToken) { results = await doImageSearch(actor.prototypeToken.name, { searchType: SEARCH_TYPE.TOKEN, simpleResults: true, searchOptions: formData.searchOptions, }); if ((results ?? []).length != 0) { tokenFound = true; updateTokenImage(results[0], { actor: actor, pack: formData.compendium, applyDefaultConfig: false, }); } } } else { let results = await doImageSearch(actor.name, { searchType: typeOverride ?? SEARCH_TYPE.PORTRAIT_AND_TOKEN, simpleResults: true, searchOptions: formData.searchOptions, }); if ((results ?? []).length != 0) { portraitFound = tokenFound = true; updateTokenImage(results[0], { actor: actor, actorUpdate: { img: results[0] }, pack: formData.compendium, applyDefaultConfig: false, }); } } if (!(tokenFound && portraitFound) && formData.autoDisplayArtSelect) { addToArtSelectQueue(actor, image1, image2, formData, typeOverride); } } function addToArtSelectQueue(actor, image1, image2, formData, typeOverride) { if (formData.diffImages) { if (!formData.ignorePortrait && !formData.ignoreToken) { addToQueue(actor.name, { searchType: typeOverride ?? SEARCH_TYPE.PORTRAIT, object: actor, preventClose: true, image1: image1, image2: image2, displayMode: ArtSelect.IMAGE_DISPLAY.PORTRAIT, searchOptions: formData.searchOptions, callback: async function (imgSrc, _) { await updateActorImage(actor, imgSrc); showArtSelect(actor.prototypeToken.name, { searchType: typeOverride ?? SEARCH_TYPE.TOKEN, object: actor, force: true, image1: imgSrc, image2: image2, displayMode: ArtSelect.IMAGE_DISPLAY.TOKEN, searchOptions: formData.searchOptions, callback: (imgSrc, name) => updateTokenImage(imgSrc, { actor: actor, imgName: name, applyDefaultConfig: false, }), }); }, }); } else if (formData.ignorePortrait) { addToQueue(actor.name, { searchType: typeOverride ?? SEARCH_TYPE.TOKEN, object: actor, image1: image1, image2: image2, displayMode: ArtSelect.IMAGE_DISPLAY.TOKEN, searchOptions: formData.searchOptions, callback: async function (imgSrc, name) { updateTokenImage(imgSrc, { actor: actor, imgName: name, applyDefaultConfig: false, }); }, }); } else if (formData.ignoreToken) { addToQueue(actor.name, { searchType: typeOverride ?? SEARCH_TYPE.PORTRAIT, object: actor, image1: image1, image2: image2, displayMode: ArtSelect.IMAGE_DISPLAY.PORTRAIT, searchOptions: formData.searchOptions, callback: async function (imgSrc, name) { await updateActorImage(actor, imgSrc); }, }); } } else { addToQueue(actor.name, { searchType: typeOverride ?? SEARCH_TYPE.PORTRAIT_AND_TOKEN, object: actor, image1: image1, image2: image2, displayMode: ArtSelect.IMAGE_DISPLAY.PORTRAIT_TOKEN, searchOptions: formData.searchOptions, callback: async function (imgSrc, name) { await updateActorImage(actor, imgSrc); updateTokenImage(imgSrc, { actor: actor, imgName: name, applyDefaultConfig: false, }); }, }); } } export default class CompendiumMapConfig extends FormApplication { constructor() { super({}, {}); this.searchOptions = deepClone(getSearchOptions()); mergeObject(this.searchOptions, deepClone(TVA_CONFIG.compendiumMapper.searchOptions)); this._fixSearchPaths(); } static get defaultOptions() { return mergeObject(super.defaultOptions, { id: 'token-variants-compendium-map-config', classes: ['sheet'], template: 'modules/token-variants/templates/compendiumMap.html', resizable: false, minimizable: false, title: game.i18n.localize('token-variants.settings.compendium-mapper.Name'), width: 500, }); } async getData(options) { let data = super.getData(options); data = mergeObject(data, TVA_CONFIG.compendiumMapper); const supportedPacks = ['Actor', 'Cards', 'Item', 'Macro', 'RollTable']; data.supportedPacks = supportedPacks.join(', '); const packs = []; game.packs.forEach((pack) => { if (!pack.locked && supportedPacks.includes(pack.documentName)) { packs.push({ title: pack.title, id: pack.collection, type: pack.documentName }); } }); data.compendiums = packs; data.compendium = TVA_CONFIG.compendiumMapper.compendium; data.categories = BASE_IMAGE_CATEGORIES.concat(TVA_CONFIG.customImageCategories); data.category = TVA_CONFIG.compendiumMapper.category; return data; } /** * @param {JQuery} html */ activateListeners(html) { super.activateListeners(html); html.find('.token-variants-override-category').change(this._onCategoryOverride).trigger('change'); html.find('.token-variants-auto-apply').change(this._onAutoApply); html.find('.token-variants-diff-images').change(this._onDiffImages); html.find(`.token-variants-search-options`).on('click', this._onSearchOptions.bind(this)); html.find(`.token-variants-missing-images`).on('click', this._onMissingImages.bind(this)); $(html).find('[name="compendium"]').change(this._onCompendiumSelect.bind(this)).trigger('change'); } async _onAutoApply(event) { $(event.target).closest('form').find('.token-variants-auto-art-select').prop('disabled', !event.target.checked); } async _onCategoryOverride(event) { $(event.target).closest('form').find('.token-variants-category').prop('disabled', !event.target.checked); } async _onDiffImages(event) { $(event.target).closest('form').find('.token-variants-tp-ignore').prop('disabled', !event.target.checked); } async _onCompendiumSelect(event) { const compendium = game.packs.get($(event.target).val()); if (compendium) { $(event.target) .closest('form') .find('.token-specific') .css('visibility', compendium.documentName === 'Actor' ? 'visible' : 'hidden'); } } _fixSearchPaths() { if (!this.searchOptions.searchPaths?.length) { this.searchOptions.searchPaths = deepClone(TVA_CONFIG.searchPaths); } } async _onSearchOptions(event) { this._fixSearchPaths(); new ConfigureSettings(this.searchOptions, { searchPaths: true, searchFilters: true, searchAlgorithm: true, randomizer: false, features: false, popup: false, permissions: false, worldHud: false, misc: false, activeEffects: false, }).render(true); } async _onMissingImages(event) { new MissingImageConfig().render(true); } async startMapping(formData) { if (formData.diffImages && formData.ignoreToken && formData.ignorePortrait) { return; } const originalSearchPaths = TVA_CONFIG.searchPaths; if (formData.searchOptions.searchPaths?.length) { TVA_CONFIG.searchPaths = formData.searchOptions.searchPaths; } if (formData.cache || !userRequiresImageCache() || formData.searchOptions.searchPaths?.length) { console.log('TVA-Mapper: Starting Image caching.'); await cacheImages(); console.log('TVA-Mapper: Caching finished.'); } const endMapping = function () { if (formData.searchOptions.searchPaths?.length) { TVA_CONFIG.searchPaths = originalSearchPaths; cacheImages(); } }; const compendium = game.packs.get(formData.compendium); let missingImageList = TVA_CONFIG.compendiumMapper.missingImages .filter((mi) => mi.document === 'all' || mi.document === compendium.documentName) .map((mi) => mi.image); const typeOverride = formData.overrideCategory ? formData.category : null; let artSelectDisplayed = false; let processItem; let consoleProcessedTracking = 0; if (compendium.documentName === 'Actor') { processItem = async function (item) { const actor = item; if (actor.name === '#[CF_tempEntity]') return; // Compendium Folders module's control entity let hasPortrait = actor.img !== CONST.DEFAULT_TOKEN && !missingImageList.includes(actor.img); let hasToken = actor.prototypeToken.texture.src !== CONST.DEFAULT_TOKEN && !missingImageList.includes(actor.prototypeToken.texture.src); if (formData.syncImages && hasPortrait !== hasToken) { if (hasPortrait) { await updateTokenImage(actor.img, { actor: actor, applyDefaultConfig: false }); } else { await updateActorImage(actor, actor.prototypeToken.texture.src); } hasPortrait = hasToken = true; } let includeThisActor = !(formData.missingOnly && hasPortrait) && !formData.ignorePortrait; let includeThisToken = !(formData.missingOnly && hasToken) && !formData.ignoreToken; const image1 = formData.showImages ? actor.img : ''; const image2 = formData.showImages ? actor.prototypeToken.texture.src : ''; if (includeThisActor || includeThisToken) { if (formData.autoApply) { await autoApply(actor, image1, image2, formData, typeOverride); } else { artSelectDisplayed = true; addToArtSelectQueue(actor, image1, image2, formData, typeOverride); } } consoleProcessedTracking++; if (consoleProcessedTracking % 100 === 0) console.log(`TVA-Mapper: Processed ${consoleProcessedTracking} ${compendium.documentName}s`); }; } else { processItem = async function (item) { const doc = item; if (doc.name === '#[CF_tempEntity]') return; // Compendium Folders module's control entity let defaultImg = ''; if (doc.schema.fields.img || doc.schema.fields.texture) { defaultImg = (doc.schema.fields.img ?? doc.schema.fields.texture).initial(); } const hasImage = doc.img != null && doc.img !== defaultImg && !missingImageList.includes(doc.img); let imageFound = false; if (formData.missingOnly && hasImage) return; if (formData.autoApply) { let results = await doImageSearch(doc.name, { searchType: typeOverride ?? compendium.documentName, simpleResults: true, searchOptions: formData.searchOptions, }); if ((results ?? []).length != 0) { imageFound = true; await updateActorImage(doc, results[0], false, formData.compendium); } } if (!formData.autoApply || (formData.autoDisplayArtSelect && !imageFound)) { artSelectDisplayed = true; addToQueue(doc.name, { searchType: typeOverride ?? compendium.documentName, object: doc, image1: formData.showImages ? doc.img : '', displayMode: ArtSelect.IMAGE_DISPLAY.IMAGE, searchOptions: formData.searchOptions, callback: async function (imgSrc, name) { await updateActorImage(doc, imgSrc); }, }); } consoleProcessedTracking++; if (consoleProcessedTracking % 100 === 0) console.log(`TVA-Mapper: Processed ${consoleProcessedTracking} ${compendium.documentName}s`); }; } console.log(`TVA-Mapper: Starting Batch ${compendium.documentName} load.`); const documents = await compendium.getDocuments(); console.log(`TVA-Mapper: Load finished. Beginning processing.`); if (formData.autoApply) { let processing = true; let stopProcessing = false; let processed = 0; let counter = $(`

CACHING 0/${documents.length}

`); let d; const startProcessing = async function () { while (processing && processed < documents.length) { await new Promise((resolve, reject) => { setTimeout(async () => { await processItem(documents[processed]); resolve(); }, 10); }); processed++; counter.html(`${processed}/${documents.length}`); } if (stopProcessing || processed === documents.length) { d?.close(true); addToQueue('DUMMY', { execute: endMapping }); renderFromQueue(); } }; d = new Dialog({ title: `Mapping: ${compendium.title}`, content: `
`, buttons: { cancel: { icon: '', label: 'Cancel', }, }, default: 'cancel', render: (html) => { html.find('.counter').append(counter); const spinner = html.find('.fa-spinner'); html.find('.pause').on('click', () => { if (processing) { processing = false; spinner.removeClass('fa-pulse'); } else { processing = true; startProcessing(); spinner.addClass('fa-pulse'); } }); setTimeout(async () => startProcessing(), 1000); }, close: () => { if (!stopProcessing) { stopProcessing = true; if (!processing) startProcessing(); else processing = false; } }, }); d.render(true); } else { const tasks = documents.map(processItem); Promise.all(tasks).then(() => { addToQueue('DUMMY', { execute: endMapping }); renderFromQueue(); if (formData.missingOnly && !artSelectDisplayed) { ui.notifications.warn('Token Variant Art: No documents found containing missing images.'); } }); } } /** * @param {Event} event * @param {Object} formData */ async _updateObject(event, formData) { // If search paths are the same, remove them from searchOptions if ( !this.searchOptions.searchPaths?.length || isEmpty(diffObject(this.searchOptions.searchPaths, TVA_CONFIG.searchPaths)) ) { this.searchOptions.searchPaths = []; } formData.searchOptions = this.searchOptions; await updateSettings({ compendiumMapper: formData }); if (formData.compendium) { this.startMapping(formData); } } }