import { TVA_CONFIG } from '../settings.js'; import { decodeURISafely } from '../utils.js'; import { registerWrapper } from './wrappers.js'; const feature_id = 'UserMappings'; export function registerUserMappingWrappers() { registerWrapper(feature_id, 'Tile.prototype.draw', _draw); registerWrapper(feature_id, 'Token.prototype.draw', _draw); } async function _draw(wrapped, ...args) { let result; // If the Token/Tile has a UserToImage mappings momentarily set document.texture.src to it // so that it's texture gets loaded instead of the actual Token image const mappings = this.document.getFlag('token-variants', 'userMappings') || {}; const img = mappings[game.userId]; let previous; if (img) { previous = this.document.texture.src; this.document.texture.src = img; this.tva_iconOverride = img; result = await wrapped(...args); this.document.texture.src = previous; overrideVisibility(this, img); } else { overrideVisibility(this); result = await wrapped(...args); } return result; } /** * If the img is the same as TVA_CONFIG.invisibleImage then we'll override the isVisible * getter to return false of this client if it's not a GM. Reset it to default if not. * @param {*} obj object whose isVisible is to be overriden * @param {*} img UserToImage mapping */ function overrideVisibility(obj, img) { if (img && decodeURISafely(img) === TVA_CONFIG.invisibleImage && !obj.tva_customVisibility) { const originalIsVisible = _getIsVisibleDescriptor(obj).get; Object.defineProperty(obj, 'isVisible', { get: function () { const isVisible = originalIsVisible.call(this); if (isVisible && !game.user.isGM) return false; return isVisible; }, configurable: true, }); obj.tva_customVisibility = true; } else if (!img && obj.tva_customVisibility) { Object.defineProperty(obj, 'isVisible', _getIsVisibleDescriptor(obj)); delete obj.tva_customVisibility; } } function _getIsVisibleDescriptor(obj) { let iObj = Object.getPrototypeOf(obj); let descriptor = null; while (iObj) { descriptor = Object.getOwnPropertyDescriptor(iObj, 'isVisible'); if (descriptor) break; iObj = Object.getPrototypeOf(iObj); } return descriptor; } /** * Assign an image to be displayed to only that user. * @param {*} token token the image is to be applied to * @param {*} img image to be displayed, if no image is provided unassignUserSpecificImage(...) will be called * @param {*} opts.userName name of the user that the image is to be displayed to * @param {*} opts.id id of the user that the image is to be displayed to * @returns */ export function assignUserSpecificImage(token, img, { userName = null, userId = null } = {}) { if (!img) return unassignUserSpecificImage(token, { userName, userId }); if (userName instanceof Array) { for (const name of userName) assignUserSpecificImage(token, img, { userName: name }); return; } if (userId instanceof Array) { for (const id of userId) assignUserSpecificImage(token, img, { userId: id }); return; } let id = userId; if (!id && userName) { id = game.users.find((u) => u.name === userName)?.id; } if (!id) return; const doc = token.document ?? token; const mappings = doc.getFlag('token-variants', 'userMappings') || {}; mappings[id] = img; doc.setFlag('token-variants', 'userMappings', mappings); } /** * Calls assignUserSpecificImage passing in all currently selected tokens. * @param {*} img image to be displayed * @param {*} opts id or name of the user as per assignUserSpecificImage(...) */ export function assignUserSpecificImageToSelected(img, opts = {}) { const selected = [...canvas.tokens.controlled]; for (const t of selected) assignUserSpecificImage(t, img, opts); } /** * Un-assign image if one has been set to be displayed to a user. * @param {*} token token the image is to be removed from * @param {*} opts.userName name of the user that the image is to be removed for * @param {*} opts.id id of the user that the image is to be removed for */ export function unassignUserSpecificImage(token, { userName = null, userId = null } = {}) { if (userName instanceof Array) { for (const name of userName) unassignUserSpecificImage(token, { userName: name }); return; } if (userId instanceof Array) { for (const id of userId) unassignUserSpecificImage(token, { userId: id }); return; } let id = userId; if (!id && userName) { id = game.users.find((u) => u.name === userName)?.id; } if (!id) { if (!userName && !userId) (token.document ?? token).unsetFlag('token-variants', 'userMappings'); } else { const update = {}; update['flags.token-variants.userMappings.-=' + id] = null; (token.document ?? token).update(update); } } /** * Calls unassignUserSpecificImage passing in all currently selected tokens. * @param {*} opts id or name of the user as per unassignUserSpecificImage(...) */ export function unassignUserSpecificImageFromSelected(opts = {}) { const selected = [...canvas.tokens.controlled]; for (const t of selected) unassignUserSpecificImage(t, opts); }