let circularMaskTexture = null; function countEffects(token) { if (!token) { return 0; } let numEffects = token.document.effects?.length || 0; token.actor?.temporaryEffects?.forEach((actorEffect) => { if (!actorEffect.getFlag("core", "overlay")) { numEffects++; } }); return numEffects; } function sortIcons(e1, e2) { if (e1.position.x === e2.position.x) { return e1.position.y - e2.position.y; } return e1.position.x - e2.position.x; } function updateIconSize(effectIcon, size) { effectIcon.width = size; effectIcon.height = size; } function polar_to_cartesian(r, theta) { return { x: r * Math.cos(theta), y: r * Math.sin(theta), }; } function updateIconPosition(effectIcon, i, effectIcons, token) { const actorSize = token?.actor?.size; let max = 20; if (actorSize == "tiny") max = 10; if (actorSize == "sm") max = 14; if (actorSize == "med") max = 16; const ratio = i / max; // const angularOffset = i < max ? 0 : ratio / 2; const gridSize = token?.scene?.grid?.size ?? 100; const tokenTileFactor = token?.document?.width ?? 1; const sizeOffset = sizeToOffset(actorSize); const offset = sizeOffset * tokenTileFactor * gridSize; const initialRotation = (0.5 + (1 / max) * Math.PI) * Math.PI; const { x, y } = polar_to_cartesian(offset, (ratio + 0) * 2 * Math.PI + initialRotation); // debugger; effectIcon.position.x = x / 2 + (gridSize * tokenTileFactor) / 2; effectIcon.position.y = (-1 * y) / 2 + (gridSize * tokenTileFactor) / 2; } // Nudge icons to be on the token ring or slightly outside function sizeToOffset(size) { if (size == "tiny") { return 1.4; } else if (size == "sm") { return 1.0; } else if (size == "med") { return 1.2; } else if (size == "lg") { return 0.925; } else if (size == "huge") { return 0.925; } else if (size == "grg") { return 0.925; } return 1.0; } function sizeToIconScale(size) { if (size == "tiny") { return 1.4; } else if (size == "sm") { return 1.4; } else if (size == "med") { return 1.4; } else if (size == "lg") { return 1.25; } else if (size == "huge") { return 1.55; } else if (size == "grg") { return 2.2; } return 1.0; } function drawBG(effectIcon, background, gridScale) { const r = effectIcon.width / 2; const isDorakoUiActive = game.modules.get("pf2e-dorako-ui")?.active; const appTheme = isDorakoUiActive ? game.settings.get("pf2e-dorako-ui", "theme.app-theme") : false; if (appTheme && appTheme.includes("foundry2")) { background.lineStyle((1 * gridScale) / 2, 0x302831, 1, 0); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.beginFill(0x0b0a13); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.endFill(); return; } else if (appTheme && appTheme.includes("crb")) { background.lineStyle((1 * gridScale) / 2, 0x956d58, 1, 1); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.lineStyle((1 * gridScale) / 2, 0xe9d7a1, 1, 0); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.beginFill(0x956d58); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.endFill(); return; } else if (appTheme && appTheme.includes("bg3")) { background.lineStyle((1 * gridScale) / 2, 0x9a8860, 1, 1); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.lineStyle((1 * gridScale) / 2, 0xd3b87c, 1, 0); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.beginFill(0x000000); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.endFill(); return; } // background.lineStyle((1 * gridScale) / 2, 0x222222, 1, 1); // background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.lineStyle((1 * gridScale) / 2, 0x444444, 1, 0); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.beginFill(0x222222); background.drawCircle(effectIcon.position.x, effectIcon.position.y, r + 1 * gridScale); background.endFill(); } function updateEffectScales(token) { // if (token?.actor?.size == "sm") return; const numEffects = countEffects(token); if (numEffects > 0 && token.effects.children.length > 0) { const background = token.effects.children[0]; if (!(background instanceof PIXI.Graphics)) return; background.clear(); // Exclude the background and overlay const effectIcons = token.effects.children.slice(1, 1 + numEffects); const tokenSize = token?.actor?.size; const gridSize = token?.scene?.grid?.size ?? 100; // Reposition and scale them effectIcons.forEach((effectIcon, i, effectIcons) => { if (!(effectIcon instanceof PIXI.Sprite)) return; effectIcon.anchor.set(0.5); const iconScale = sizeToIconScale(tokenSize); const gridScale = gridSize / 100; const scaledSize = 12 * iconScale * gridScale; updateIconSize(effectIcon, scaledSize); updateIconPosition(effectIcon, i, effectIcons, token); drawBG(effectIcon, background, gridScale); }); } } Hooks.once("ready", () => { const enabled = game.settings.get("pf2e-dorako-ux", "moving.adjust-token-effects-hud"); if (!enabled) return; const origRefreshEffects = Token.prototype._refreshEffects; Token.prototype._refreshEffects = function (...args) { // const enabled = game.settings.get("pf2e-dorako-ux", "ux.adjust-token-effects-hud"); // if (!enabled) { // origRefreshEffects.apply(this, args); // return; // } if (this) { origRefreshEffects.apply(this, args); updateEffectScales(this); } }; const origDrawEffect = Token.prototype._drawEffect; Token.prototype._drawEffect = async function (...args) { // const enabled = game.settings.get("pf2e-dorako-ux", "ux.adjust-token-effects-hud"); // if (!enabled) { // origDrawEffect.apply(this, args); // return; // } if (this) { const src = args[0]; const tint = args[1]; // debugger; if (!src) return; let tex = await loadTexture(src, { fallback: "icons/svg/hazard.svg" }); let icon = new PIXI.Sprite(tex); if (src != game.settings.get("pf2e", "deathIcon")) { // If the circular mask hasn't been created yet // if (!circularMaskTexture) { // // Define a new render texture that is 110x110 // circularMaskTexture = PIXI.RenderTexture.create(110, 110); // // Define the mask sprite // const renderedMaskSprite = new PIXI.Graphics().beginFill(0xffffff).drawCircle(55, 55, 55).endFill(); // // Blur the mask sprite // const blurFilter = new PIXI.filters.BlurFilter(2); // renderedMaskSprite.filters = [blurFilter]; // // Render the result of the mask sprite to the texture // canvas.app.renderer.render(renderedMaskSprite, circularMaskTexture); // } const minDimension = Math.min(icon.width, icon.height); // Use the blurred pre-made texture and create a new mask sprite for the specific icon const myMask = new PIXI.Graphics().beginFill(0xffffff).drawCircle(55, 55, 55).endFill(); //const myMask = new PIXI.Sprite(circularMaskTexture); //myMask.anchor.set(0.5,0.5); myMask.width = minDimension; myMask.height = minDimension; myMask.x = -icon.width / 2; myMask.y = -icon.height / 2; icon.mask = myMask; icon.addChild(myMask); } // debugger; return this.effects.addChild(icon); } }; });