import * as MODULE from "../MaterialDeck.js"; import {streamDeck, macroControl, otherControls, tokenHelper} from "../MaterialDeck.js"; export class TokenControl{ constructor(){ this.active = false; this.wildcardOffset = 0; } async update(tokenId=null){ if (this.active == false) return; for (let device of streamDeck.buttonContext) { if (device?.buttons == undefined) continue; for (let i=0; i e.label === condition).icon; if (tokenHelper.getConditionActive(token,condition)){ ring = 2; ringColor = "#FF7B00"; } } } else if (settings.onClick == 'wildcard') { //wildcard images if (MODULE.getPermission('TOKEN','WILDCARD') == false ) { streamDeck.noPermission(context,device); return; } if (icon != 'stats') return; const method = settings.wildcardMethod ? settings.wildcardMethod : 'iterate'; let value = parseInt(settings.wildcardValue); if (isNaN(value)) value = 1; const images = await token.actor.getTokenImages(); let currentImgNr = 0 let imgNr; for (let i=0; i= images.length) imgNr -= images.length; while (imgNr < 0) imgNr += images.length; iconSrc = images[imgNr]; } else if (method == 'set'){ imgNr = value - 1 + this.wildcardOffset; if (value >= images.length) iconSrc = "modules/MaterialDeck/img/black.png"; else iconSrc = images[imgNr]; ring = 1; if (currentImgNr == imgNr) { ring = 2; ringColor = "#FF7B00"; } } else return; } else if (settings.onClick == 'initiative') //Initiative iconSrc = "modules/MaterialDeck/img/token/init.png"; } //Items else { txt += prependTitle; const allItems = token.actor.items; const itemNr = settings.itemNr ? settings.itemNr - 1 : 0; const displayUses = settings.displayUses ? settings.displayUses : false; const displayName = settings.displayInventoryName ? settings.displayInventoryName : false; const displayIcon = settings.displayInventoryIcon ? settings.displayInventoryIcon : false; let items = allItems; let item; if (mode == 'inventory') { items = tokenHelper.getItems(token,settings.inventoryType); items = this.sortItems(items); item = items[itemNr]; if (item != undefined && displayUses) uses = tokenHelper.getItemUses(item); } else if (mode == 'features') { items = tokenHelper.getFeatures(token,settings.featureType); items = this.sortItems(items); item = items[itemNr]; if (item != undefined && displayUses) uses = tokenHelper.getFeatureUses(item); } else if (mode == 'spellbook') { items = tokenHelper.getSpells(token,settings.spellType); items = this.sortItems(items); item = items[itemNr]; if (displayUses && item != undefined) uses = tokenHelper.getSpellUses(token,settings.spellType,item); } if (item != undefined) { if (displayIcon) iconSrc = item.img; if (displayName) txt = item.name; } } } //No valid token found: else { if (mode == 'token') { iconSrc += ""; if (settings.onClick == 'visibility') { //toggle visibility if (MODULE.getPermission('TOKEN','VISIBILITY') == false ) { streamDeck.noPermission(context,device); return; } if (icon == 'stats') { iconSrc = window.CONFIG.controlIcons.visibility; ring = 2; overlay = true; } } else if (settings.onClick == 'combatState') { //toggle combat state if (MODULE.getPermission('TOKEN','COMBAT') == false ) { streamDeck.noPermission(context,device); return; } if (icon == 'stats') { iconSrc = window.CONFIG.controlIcons.combat; ring = 2; overlay = true; } } else if (settings.onClick == 'target') { //target token if (icon == 'stats') { iconSrc = "fas fa-bullseye"; ring = 2; overlay = true; } } else if (settings.onClick == 'condition') { //toggle condition if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) { streamDeck.noPermission(context,device); return; } ring = 1; overlay = true; if (icon == 'stats') iconSrc = tokenHelper.getConditionIcon(settings.condition); } else if (settings.onClick == 'cubCondition') { //Combat Utility Belt conditions if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) { streamDeck.noPermission(context,device); return; } const condition = settings.cubConditionName; if (condition == undefined || condition == '') return; if (icon == 'stats') { iconSrc = CONFIG.statusEffects.find(e => e.label === condition).icon; } ring = 1; overlay = true; } } } if (icon == 'stats'){ if (MODULE.getPermission('TOKEN','STATS') == false) stats = statsOld; if (stats == 'HP') //HP iconSrc = "modules/MaterialDeck/img/token/hp_empty.png"; if (stats == 'TempHP') //Temp HP iconSrc = "modules/MaterialDeck/img/token/temp_hp_empty.png"; else if (stats == 'AC' || stats == 'ShieldHP') //AC iconSrc = "modules/MaterialDeck/img/token/ac.webp"; else if (stats == 'Speed') //Speed iconSrc = "modules/MaterialDeck/img/token/speed.webp"; else if (stats == 'Init' || settings.onClick == 'initiative') //Initiative iconSrc = "modules/MaterialDeck/img/token/init.png"; else if (stats == 'PassivePerception') { iconSrc = "modules/MaterialDeck/img/token/skills/prc.png"; overlay = true; ring = 1; } else if (stats == 'PassiveInvestigation') { iconSrc = "modules/MaterialDeck/img/token/skills/inv.png"; overlay = true; ring = 1; } else if (stats == 'Ability' || stats == 'AbilityMod' || stats == 'Save') { overlay = true; ring = 1; let ability = (stats == 'Save') ? settings.save : settings.ability; if (ability == undefined) ability = 'str'; if (ability == 'con') iconSrc = "modules/MaterialDeck/img/token/abilities/cons.png"; else iconSrc = "modules/MaterialDeck/img/token/abilities/" + ability + ".png"; } else if (stats == 'Skill') { overlay = true; ring = 1; let skill = settings.skill; if (skill == undefined) skill = 'acr'; else iconSrc = "modules/MaterialDeck/img/token/skills/" + skill + ".png"; } else if (settings.onClick == 'center' || settings.onClick == 'centerSelect') { overlay = true; iconSrc = "modules/MaterialDeck/img/move/center.png"; } else if (settings.onClick == 'move') { overlay = true; const dir = settings.dir ? settings.dir : 'center'; if (dir == 'up') //up iconSrc = "modules/MaterialDeck/img/move/up.png"; else if (dir == 'down') //down iconSrc = "modules/MaterialDeck/img/move/down.png"; else if (dir == 'right') //right iconSrc = "modules/MaterialDeck/img/move/right.png"; else if (dir == 'left') //left iconSrc = "modules/MaterialDeck/img/move/left.png"; else if (dir == 'upRight') iconSrc = "modules/MaterialDeck/img/move/upright.png"; else if (dir == 'upLeft') iconSrc = "modules/MaterialDeck/img/move/upleft.png"; else if (dir == 'downRight') iconSrc = "modules/MaterialDeck/img/move/downright.png"; else if (dir == 'downLeft') iconSrc = "modules/MaterialDeck/img/move/downleft.png"; } else if (settings.onClick == 'rotate') { overlay = true; const value = isNaN(parseInt(settings.rotValue)) ? 0 : parseInt(settings.rotValue); if (value >= 0) iconSrc = "modules/MaterialDeck/img/move/rotatecw.png"; else iconSrc = "modules/MaterialDeck/img/move/rotateccw.png"; } } streamDeck.setIcon(context,device,iconSrc,{background:background,ring:ring,ringColor:ringColor,overlay:overlay,uses:uses,hp:hp}); streamDeck.setTitle(txt,context); } sortItems(items) { let sorted = Object.values(items); sorted.sort((a,b) => a.data.sort - b.data.sort); return sorted; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// async keyPress(settings){ const tokenId = canvas.tokens.controlled[0]?.id; const selection = settings.selection ? settings.selection : 'selected'; const tokenIdentifier = settings.tokenName ? settings.tokenName : ''; const mode = settings.tokenMode ? settings.tokenMode : 'token'; let token; if (selection == 'selected') token = canvas.tokens.controlled[0]; else token = tokenHelper.getToken(selection,tokenIdentifier); if (token == undefined) return; if (token.owner == false && token.observer == true && MODULE.getPermission('TOKEN','OBSERVER') == false ) return; if (token.owner == false && token.observer == false && MODULE.getPermission('TOKEN','NON_OWNED') == false ) return; if (mode == 'token') { const onClick = settings.onClick ? settings.onClick : 'doNothing'; if (onClick == 'doNothing') //Do nothing return; else if (onClick == 'select'){ //select token token.control(); } else if (onClick == 'center'){ //center on token let location = token.getCenter(token.x,token.y); canvas.animatePan(location); } else if (onClick == 'centerSelect'){ //center on token and select const location = token.getCenter(token.x,token.y); canvas.animatePan(location); token.control(); } else if (onClick == 'move') { //move token tokenHelper.moveToken(token,settings.dir); } else if (onClick == 'rotate') { //rotate token tokenHelper.rotateToken(token,settings.rot,settings.rotValue); } else if (onClick == 'charSheet'){ //Open character sheet const element = document.getElementById(token.actor.sheet.id); if (element == null) token.actor.sheet.render(true); else token.actor.sheet.close(); } else if (onClick == 'tokenConfig') { //Open token config const element = document.getElementById(token.sheet.id); if (element == null) token.sheet.render(true); else token.sheet.close(); } else if (onClick == 'visibility') { //Toggle visibility if (MODULE.getPermission('TOKEN','VISIBILITY') == false ) return; token.toggleVisibility(); } else if (onClick == 'combatState') { //Toggle combat state if (MODULE.getPermission('TOKEN','COMBAT') == false ) return; token.toggleCombat(); } else if (onClick == 'target') { //Target token token.setTarget(!token.isTargeted,{releaseOthers:false}); } else if (onClick == 'condition') { //Handle condition if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) return; const func = settings.conditionFunction ? settings.conditionFunction : 'toggle'; if (func == 'toggle'){ //toggle await tokenHelper.toggleCondition(token,settings.condition); this.update(tokenId); } else if (func == 'increase'){ //increase await tokenHelper.modifyConditionValue(token, settings.condition, +1) this.update(tokenId); } else if (func == 'decrease'){ //decrease await tokenHelper.modifyConditionValue(token, settings.condition, -1) this.update(tokenId); } } else if (onClick == 'cubCondition') { //Combat Utility Belt conditions if (MODULE.getPermission('TOKEN','CONDITIONS') == false ) return; const condition = settings.cubConditionName; if (condition == undefined || condition == '') return; const effect = CONFIG.statusEffects.find(e => e.label === condition); await token.toggleEffect(effect); this.update(tokenId); } else if (onClick == 'vision'){ if (MODULE.getPermission('TOKEN','VISION') == false ) return; const token = canvas.tokens.children[0].children.find(p => p.id == tokenId); if (token == undefined) return; let tokenData = token.data; const dimVision = parseInt(settings.dimVision); const brightVision = parseInt(settings.brightVision); const sightAngle = parseInt(settings.sightAngle); const dimRadius = parseInt(settings.dimRadius); const brightRadius = parseInt(settings.brightRadius); const emissionAngle = parseInt(settings.emissionAngle); const lightColor = settings.lightColor ? settings.lightColor : '#000000'; const colorIntensity = isNaN(parseInt(settings.colorIntensity)) ? 0 : parseInt(settings.colorIntensity)/100; const animationType = settings.animationType ? settings.animationType : 'none'; const animationSpeed = isNaN(parseInt(settings.animationSpeed)) ? 1 : parseInt(settings.animationSpeed); const animationIntensity = isNaN(parseInt(settings.animationIntensity)) ? 1 : parseInt(settings.animationIntensity); let data = {}; if (isNaN(dimVision)==false) data.dimSight = dimVision; if (isNaN(brightVision)==false) data.brightSight = brightVision; if (isNaN(sightAngle)==false) data.sightAngle = sightAngle; if (isNaN(dimRadius)==false) data.dimLight = dimRadius; if (isNaN(brightRadius)==false) data.brightLight = brightRadius; if (isNaN(emissionAngle)==false) data.lightAngle = emissionAngle; data.lightColor = lightColor; data.lightAlpha = Math.sqrt(colorIntensity).toNearest(0.05) let animation = { type: '', speed: tokenData.lightAnimation.speed, intensity: tokenData.lightAnimation.intensity }; if (animationType != 'none'){ animation.type = animationType; animation.intensity = animationIntensity; animation.speed = animationSpeed; } data.lightAnimation = animation; token.update(data); } else if (onClick == 'initiative'){ tokenHelper.toggleInitiative(token); } else if (onClick == 'wildcard') { //wildcard images if (MODULE.getPermission('TOKEN','WILDCARD') == false ) return; const method = settings.wildcardMethod ? settings.wildcardMethod : 'iterate'; let value = parseInt(settings.wildcardValue); if (isNaN(value)) value = 1; const images = await token.actor.getTokenImages(); let imgNr; let iconSrc; if (method == 'iterate'){ let currentImgNr = 0 for (let i=0; i= images.length) imgNr -= images.length; while (imgNr < 0) imgNr += images.length; } else if (method == 'set'){ imgNr = value - 1 + this.wildcardOffset; if (value >= images.length || value < 1) return; } else if (method == 'offset'){ this.wildcardOffset = value; this.update(canvas.tokens.controlled[0]?.id); } else return; iconSrc = images[imgNr]; token.update({img: iconSrc}) } else if (onClick == 'macro') { //call a macro const settingsNew = { target: token, macroMode: settings.macroMode, macroNumber: settings.macroId, macroArgs: settings.macroArgs } macroControl.keyPress(settingsNew); } else if (onClick == 'roll') { //roll skill/save/ability const rollMode = settings.rollMode ? settings.rollMode : 'default'; let options; if (rollMode == 'default') options = { fastForward: (otherControls.rollOption != 'dialog'), advantage: (otherControls.rollOption == 'advantage'), disadvantage: (otherControls.rollOption == 'disadvantage') } else if (rollMode == 'normal') options = {fastForward:true} else if (rollMode == 'advantage') options = {fastForward:true,advantage:true} else if (rollMode == 'disadvantage') options = {fastForward:true,disadvantage:true} tokenHelper.roll(token,settings.roll,options,settings.rollAbility,settings.rollSkill,settings.rollSave) if (otherControls.rollOption != 'dialog') otherControls.setRollOption('normal'); } else if (onClick == 'custom') {//custom onClick function if (MODULE.getPermission('TOKEN','CUSTOM') == false ) return; const formula = settings.customOnClickFormula ? settings.customOnClickFormula : ''; if (formula == '') return; let targetArrayTemp; let formulaArrayTemp; let split1 = formula.split(';'); for (let i=0; i 1) targetArray[i] = data[1]; if (i > 1) { if (furnaceArguments != "") furnaceArguments += " "; furnaceArguments += "\"" + targetArray[i] + "\""; } } } if (macro) { const settingsNew = { target: token, macroMode: 'name', macroNumber: targetArray[1], macroArgs: furnaceArguments } macroControl.keyPress(settingsNew); continue; } let formulaArray = this.splitCustom(formulaArrayTemp); let value = 0; let previousOperation = '+'; if (formulaArray.length == 1 && formulaArray[0][0] == '[') value = formulaArray[0].split('[')[1]; else if (formulaArray.length == 1) value = formulaArray[0]; else { for (let i=0; i= val) {value = val-1;} else if (previousOperation == '>' && value <= val) {value = val+1;} else if (previousOperation == '<=' && value > val) {value = val;} else if (previousOperation == '>=' && value < val) {value = val;} } } for (let i=0; i0 && split[i][0] != '@' && split[i] != "" && isNaN(split[i])) split[i] = '['+split[i] const split2 = split[i].split(']'); for (let j=0; j