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.

147 lines
5.0 KiB

  1. import { getAllEffectMappings } from '../hooks/effectMappingHooks.js';
  2. import { FEATURE_CONTROL, TVA_CONFIG } from '../settings.js';
  3. import { registerWrapper, unregisterWrapper } from './wrappers.js';
  4. const feature_id = 'EffectIcons';
  5. export function registerEffectIconWrappers() {
  6. unregisterWrapper(feature_id, 'Token.prototype.drawEffects');
  7. unregisterWrapper(feature_id, 'CombatTracker.prototype.getData');
  8. if (!FEATURE_CONTROL[feature_id]) return;
  9. if (
  10. !TVA_CONFIG.disableEffectIcons &&
  11. TVA_CONFIG.filterEffectIcons &&
  12. !['pf1e', 'pf2e'].includes(game.system.id)
  13. ) {
  14. registerWrapper(feature_id, 'Token.prototype.drawEffects', _drawEffects, 'OVERRIDE');
  15. } else if (TVA_CONFIG.disableEffectIcons) {
  16. registerWrapper(
  17. feature_id,
  18. 'Token.prototype.drawEffects',
  19. _drawEffects_fullReplace,
  20. 'OVERRIDE'
  21. );
  22. } else if (TVA_CONFIG.displayEffectIconsOnHover) {
  23. registerWrapper(feature_id, 'Token.prototype.drawEffects', _drawEffects_hoverOnly, 'WRAPPER');
  24. }
  25. if (TVA_CONFIG.disableEffectIcons || TVA_CONFIG.filterCustomEffectIcons) {
  26. registerWrapper(
  27. feature_id,
  28. 'CombatTracker.prototype.getData',
  29. _combatTrackerGetData,
  30. 'WRAPPER'
  31. );
  32. }
  33. }
  34. async function _drawEffects_hoverOnly(wrapped, ...args) {
  35. let result = await wrapped(...args);
  36. this.effects.visible = this.hover;
  37. return result;
  38. }
  39. async function _drawEffects_fullReplace(...args) {
  40. this.effects.removeChildren().forEach((c) => c.destroy());
  41. this.effects.bg = this.effects.addChild(new PIXI.Graphics());
  42. this.effects.overlay = null;
  43. }
  44. async function _combatTrackerGetData(wrapped, ...args) {
  45. let data = await wrapped(...args);
  46. if (data && data.combat && data.turns) {
  47. const combat = data.combat;
  48. for (const turn of data.turns) {
  49. const combatant = combat.combatants.find((c) => c.id === turn.id);
  50. if (combatant) {
  51. if (TVA_CONFIG.disableEffectIcons) {
  52. turn.effects = new Set();
  53. } else if (TVA_CONFIG.filterEffectIcons) {
  54. const restrictedEffects = _getRestrictedEffects(combatant.token);
  55. // Copied from CombatTracker.getData(...)
  56. turn.effects = new Set();
  57. if (combatant.token) {
  58. combatant.token.effects.forEach((e) => turn.effects.add(e));
  59. if (combatant.token.overlayEffect) turn.effects.add(combatant.token.overlayEffect);
  60. }
  61. // modified to filter restricted effects
  62. if (combatant.actor) {
  63. for (const effect of combatant.actor.temporaryEffects) {
  64. if (effect.statuses.has(CONFIG.specialStatusEffects.DEFEATED)) {
  65. } else if (effect.icon && !restrictedEffects.includes(effect.name ?? effect.label))
  66. turn.effects.add(effect.icon);
  67. }
  68. }
  69. // end of copy
  70. }
  71. }
  72. }
  73. }
  74. return data;
  75. }
  76. async function _drawEffects(...args) {
  77. this.effects.renderable = false;
  78. this.effects.removeChildren().forEach((c) => c.destroy());
  79. this.effects.bg = this.effects.addChild(new PIXI.Graphics());
  80. this.effects.overlay = null;
  81. // Categorize new effects
  82. let tokenEffects = this.document.effects;
  83. let actorEffects = this.actor?.temporaryEffects || [];
  84. let overlay = {
  85. src: this.document.overlayEffect,
  86. tint: null,
  87. };
  88. // Modified from the original token.drawEffects
  89. if (TVA_CONFIG.displayEffectIconsOnHover) this.effects.visible = this.hover;
  90. if (tokenEffects.length || actorEffects.length) {
  91. const restrictedEffects = _getRestrictedEffects(this.document);
  92. actorEffects = actorEffects.filter((ef) => !restrictedEffects.includes(ef.name ?? ef.label));
  93. tokenEffects = tokenEffects.filter(
  94. // check if it's a string here
  95. // for tokens without representing actors effects are just stored as paths to icons
  96. (ef) => typeof ef === 'string' || !restrictedEffects.includes(ef.name ?? ef.label)
  97. );
  98. }
  99. // End of modifications
  100. // Draw status effects
  101. if (tokenEffects.length || actorEffects.length) {
  102. const promises = [];
  103. // Draw actor effects first
  104. for (let f of actorEffects) {
  105. if (!f.icon) continue;
  106. const tint = Color.from(f.tint ?? null);
  107. if (f.getFlag('core', 'overlay')) {
  108. overlay = { src: f.icon, tint };
  109. continue;
  110. }
  111. promises.push(this._drawEffect(f.icon, tint));
  112. }
  113. // Next draw token effects
  114. for (let f of tokenEffects) promises.push(this._drawEffect(f, null));
  115. await Promise.all(promises);
  116. }
  117. // Draw overlay effect
  118. this.effects.overlay = await this._drawOverlay(overlay.src, overlay.tint);
  119. this._refreshEffects();
  120. this.effects.renderable = true;
  121. }
  122. function _getRestrictedEffects(tokenDoc) {
  123. let restrictedEffects = TVA_CONFIG.filterIconList;
  124. if (TVA_CONFIG.filterCustomEffectIcons) {
  125. const mappings = getAllEffectMappings(tokenDoc);
  126. if (mappings) restrictedEffects = restrictedEffects.concat(mappings.map((m) => m.expression));
  127. }
  128. return restrictedEffects;
  129. }