class SmartTarget { static handleTargeting(token,shift) { const isTargeted = token.isTargeted; const release = shift ? !SmartTarget.settings().release : SmartTarget.settings().release; token.setTarget(!isTargeted, { releaseOthers: release }); } static getBorderColor({hover}={}) { const colors = CONFIG.Canvas.dispositionColors; if ( this.controlled ) return colors.CONTROLLED; else if ( (hover ?? this.hover) || canvas.tokens._highlight ) { let d = this.document.disposition; if ( !game.user.isGM && this.isOwner ) return colors.CONTROLLED; else if ( this.actor?.hasPlayerOwner ) return colors.PARTY; else if ( d === CONST.TOKEN_DISPOSITIONS.FRIENDLY ) return colors.FRIENDLY; else if ( d === CONST.TOKEN_DISPOSITIONS.NEUTRAL ) return colors.NEUTRAL; else if ( d === CONST.TOKEN_DISPOSITIONS.HOSTILE ) return colors.HOSTILE; else if ( d === CONST.TOKEN_DISPOSITIONS.SECRET ) return this.isOwner ? colors.SECRET : null; } return null; } static _tokenOnClickLeft(wrapped, ...args) { const mode = SmartTarget.settings().mode; switch (mode) { case 0: return wrapped(...args); break; case 1: if (game.smartTarget.altModifier) { SmartTarget.handleTargeting(this,game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.SHIFT)); return }else{ return wrapped(...args); } break; case 2: if ((!game.user.isGM && !this.isOwner) || (this.isOwner && game.smartTarget.altModifier)) { SmartTarget.handleTargeting(this,game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.SHIFT)); return } else { return wrapped(...args); } break; } super._onClickLeft(...args); } static canvasOnClickLeft(wrapped, ...args) { const canvasMousePos = args[0].interactionData.origin if (game.smartTarget.altModifier){ let distance = Infinity let closestTemplate = null for(let template of canvas.templates.placeables){ if(!template.owner) continue const inTemplate = template.shape.contains(canvasMousePos.x-template.x,canvasMousePos.y-template.y) const d = Math.sqrt(Math.pow(template.x-canvasMousePos.x,2)+Math.pow(template.y-canvasMousePos.y,2)) if(inTemplate && d= 1 ? (16 + circleR / 12) * Math.log2(scaleMulti) : 0; portraitCenterOffset += game.settings.get(SMARTTARGET_MODULE_NAME, "pipOffsetManualY") || 0; let portraitXoffset = game.settings.get(SMARTTARGET_MODULE_NAME, "pipOffsetManualX") || 0; let matrix = new PIXI.Matrix( (scaleMulti * (2 * circleR + 2)) / texture.width, 0, 0, (scaleMulti * (2 * circleR + 2)) / texture.height, newTexW / 2 + 4 + i * circleOffsetMult + portraitXoffset + insidePip + totalOffset.x, newTexH / 2 + portraitCenterOffset + insidePip + totalOffset.y ); token.target .beginFill(color) .drawCircle(2 + i * circleOffsetMult + insidePip + totalOffset.x, 0 + insidePip + totalOffset.y, circleR) .beginTextureFill({ texture: texture, alpha: 1, matrix: matrix, }) .lineStyle(borderThic, 0x0000000) .drawCircle(2 + i * circleOffsetMult + insidePip + totalOffset.x, 0 + insidePip + totalOffset.y, circleR) .endFill() .lineStyle(borderThic / 2, color) .drawCircle(2 + i * circleOffsetMult + insidePip + totalOffset.x, 0 + insidePip + totalOffset.y, circleR) } // Draw custom crosshair and pips static async _refreshTarget(wrapped, ...args) { const usePips = game.settings.get(SMARTTARGET_MODULE_NAME, "portraitPips"); const selectedIndicator = game.settings.get(SMARTTARGET_MODULE_NAME, "target-indicator"); if(!usePips && selectedIndicator == "0") return wrapped(...args); if (!this.target) return; let reticule = args[0] ?? {}; this.target.clear(); // We don't show the target arrows for a secret token disposition and non-GM users const isSecret = this.document.disposition === CONST.TOKEN_DISPOSITIONS.SECRET && !this.isOwner; if (!this.targeted.size || isSecret) return; // Determine whether the current user has target and any other users const [others, user] = Array.from(this.targeted).partition( (u) => u === game.user ); const userTarget = user.length; // For the current user, draw the target arrows if (userTarget) { let textColor = game.settings.get( SMARTTARGET_MODULE_NAME, "crossairColor" ) ? game.settings .get(SMARTTARGET_MODULE_NAME, "crossairColor") .replace("#", "0x") : SmartTarget.getBorderColor.bind(this)({hover: true}); if (game.settings.get(SMARTTARGET_MODULE_NAME, "use-player-color")) { textColor = Color.from(game.user["color"]); } let p = 4; let aw = 12; let h = this.h; let hh = h / 2; let w = this.w; let hw = w / 2; let ah = canvas.dimensions.size / 3; switch (selectedIndicator) { case "0": reticule.color = textColor; this._drawTarget(reticule)//{color: textColor})//drawDefault(this, textColor, p, aw, h, hh, w, hw, ah); break; case "1": drawCrossHairs1(this, textColor, p, aw, h, hh, w, hw, ah); break; case "2": drawCrossHairs2(this, textColor, p, aw, h, hh, w, hw, ah); break; case "3": drawBullsEye1(this, textColor, p, aw, h, hh, w, hw, ah); break; case "4": drawBullsEye2(this, textColor, p, aw, h, hh, w, hw, ah); break; case "5": drawBetterTarget(this, textColor, p, aw, h, hh, w, hw, ah); break; default: drawDefault(this, textColor, p, aw, h, hh, w, hw, ah); break; } } // For other users, draw offset pips if (usePips) { for (let [i, u] of others.entries()) { const offset = SmartTarget.getOffset(this, others.length); SmartTarget.buildCharacterPortrait(u, i, this.target,this, offset); } } else { const hw2 = this.w / 2 + (others.length % 2 === 0 ? 8 : 0); for (let [i, u] of others.entries()) { const offset = Math.floor((i + 1) / 2) * 16; const sign = i % 2 === 0 ? 1 : -1; const x = hw2 + sign * offset; this.target.beginFill(Color.from(u.color), 1.0).lineStyle(2, 0x0000000).drawCircle(x, 0, 6); } } } static settings() { const settings = { mode: game.settings.get(SMARTTARGET_MODULE_NAME, "targetingMode"), release: !game.settings.get(SMARTTARGET_MODULE_NAME, "release"), }; return settings; } } Hooks.on("targetToken", (user,token,targeted) =>{ const gmTexSetting = game.settings.get(SMARTTARGET_MODULE_NAME, "useTokenGm") if(!game.user.isGM || !targeted || !gmTexSetting) return let flag if(gmTexSetting == 1) flag = _token?.document.actor?.img || _token?.document.texture.src if(gmTexSetting == 2) flag = _token?.document.texture.src || _token?.document.actor?.img flag && flag != token.document.getFlag(SMARTTARGET_MODULE_NAME,"gmtargetimg") && token.document.setFlag(SMARTTARGET_MODULE_NAME,"gmtargetimg",flag) }) Hooks.on("updateToken", (token, update) => { if (update?.flags?.[SMARTTARGET_MODULE_NAME]?.gmtargetimg) { token.object._refreshTarget() } });