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.

151 lines
5.2 KiB

  1. import { TVA_CONFIG } from '../settings.js';
  2. import { decodeURISafely } from '../utils.js';
  3. import { registerWrapper } from './wrappers.js';
  4. const feature_id = 'UserMappings';
  5. export function registerUserMappingWrappers() {
  6. registerWrapper(feature_id, 'Tile.prototype.draw', _draw);
  7. registerWrapper(feature_id, 'Token.prototype.draw', _draw);
  8. }
  9. async function _draw(wrapped, ...args) {
  10. let result;
  11. // If the Token/Tile has a UserToImage mappings momentarily set document.texture.src to it
  12. // so that it's texture gets loaded instead of the actual Token image
  13. const mappings = this.document.getFlag('token-variants', 'userMappings') || {};
  14. const img = mappings[game.userId];
  15. let previous;
  16. if (img) {
  17. previous = this.document.texture.src;
  18. this.document.texture.src = img;
  19. this.tva_iconOverride = img;
  20. result = await wrapped(...args);
  21. this.document.texture.src = previous;
  22. overrideVisibility(this, img);
  23. } else {
  24. overrideVisibility(this);
  25. result = await wrapped(...args);
  26. }
  27. return result;
  28. }
  29. /**
  30. * If the img is the same as TVA_CONFIG.invisibleImage then we'll override the isVisible
  31. * getter to return false of this client if it's not a GM. Reset it to default if not.
  32. * @param {*} obj object whose isVisible is to be overriden
  33. * @param {*} img UserToImage mapping
  34. */
  35. function overrideVisibility(obj, img) {
  36. if (img && decodeURISafely(img) === TVA_CONFIG.invisibleImage && !obj.tva_customVisibility) {
  37. const originalIsVisible = _getIsVisibleDescriptor(obj).get;
  38. Object.defineProperty(obj, 'isVisible', {
  39. get: function () {
  40. const isVisible = originalIsVisible.call(this);
  41. if (isVisible && !game.user.isGM) return false;
  42. return isVisible;
  43. },
  44. configurable: true,
  45. });
  46. obj.tva_customVisibility = true;
  47. } else if (!img && obj.tva_customVisibility) {
  48. Object.defineProperty(obj, 'isVisible', _getIsVisibleDescriptor(obj));
  49. delete obj.tva_customVisibility;
  50. }
  51. }
  52. function _getIsVisibleDescriptor(obj) {
  53. let iObj = Object.getPrototypeOf(obj);
  54. let descriptor = null;
  55. while (iObj) {
  56. descriptor = Object.getOwnPropertyDescriptor(iObj, 'isVisible');
  57. if (descriptor) break;
  58. iObj = Object.getPrototypeOf(iObj);
  59. }
  60. return descriptor;
  61. }
  62. /**
  63. * Assign an image to be displayed to only that user.
  64. * @param {*} token token the image is to be applied to
  65. * @param {*} img image to be displayed, if no image is provided unassignUserSpecificImage(...) will be called
  66. * @param {*} opts.userName name of the user that the image is to be displayed to
  67. * @param {*} opts.id id of the user that the image is to be displayed to
  68. * @returns
  69. */
  70. export function assignUserSpecificImage(token, img, { userName = null, userId = null } = {}) {
  71. if (!img) return unassignUserSpecificImage(token, { userName, userId });
  72. if (userName instanceof Array) {
  73. for (const name of userName) assignUserSpecificImage(token, img, { userName: name });
  74. return;
  75. }
  76. if (userId instanceof Array) {
  77. for (const id of userId) assignUserSpecificImage(token, img, { userId: id });
  78. return;
  79. }
  80. let id = userId;
  81. if (!id && userName) {
  82. id = game.users.find((u) => u.name === userName)?.id;
  83. }
  84. if (!id) return;
  85. const doc = token.document ?? token;
  86. const mappings = doc.getFlag('token-variants', 'userMappings') || {};
  87. mappings[id] = img;
  88. doc.setFlag('token-variants', 'userMappings', mappings);
  89. }
  90. /**
  91. * Calls assignUserSpecificImage passing in all currently selected tokens.
  92. * @param {*} img image to be displayed
  93. * @param {*} opts id or name of the user as per assignUserSpecificImage(...)
  94. */
  95. export function assignUserSpecificImageToSelected(img, opts = {}) {
  96. const selected = [...canvas.tokens.controlled];
  97. for (const t of selected) assignUserSpecificImage(t, img, opts);
  98. }
  99. /**
  100. * Un-assign image if one has been set to be displayed to a user.
  101. * @param {*} token token the image is to be removed from
  102. * @param {*} opts.userName name of the user that the image is to be removed for
  103. * @param {*} opts.id id of the user that the image is to be removed for
  104. */
  105. export function unassignUserSpecificImage(token, { userName = null, userId = null } = {}) {
  106. if (userName instanceof Array) {
  107. for (const name of userName) unassignUserSpecificImage(token, { userName: name });
  108. return;
  109. }
  110. if (userId instanceof Array) {
  111. for (const id of userId) unassignUserSpecificImage(token, { userId: id });
  112. return;
  113. }
  114. let id = userId;
  115. if (!id && userName) {
  116. id = game.users.find((u) => u.name === userName)?.id;
  117. }
  118. if (!id) {
  119. if (!userName && !userId) (token.document ?? token).unsetFlag('token-variants', 'userMappings');
  120. } else {
  121. const update = {};
  122. update['flags.token-variants.userMappings.-=' + id] = null;
  123. (token.document ?? token).update(update);
  124. }
  125. }
  126. /**
  127. * Calls unassignUserSpecificImage passing in all currently selected tokens.
  128. * @param {*} opts id or name of the user as per unassignUserSpecificImage(...)
  129. */
  130. export function unassignUserSpecificImageFromSelected(opts = {}) {
  131. const selected = [...canvas.tokens.controlled];
  132. for (const t of selected) unassignUserSpecificImage(t, opts);
  133. }