import {compatibleCore} from "../misc.js"; import {otherControls} from "../../MaterialDeck.js"; export class pf2e{ constructor(){ } getHP(token) { const hp = token.actor.data.data.attributes.hp; return { value: hp.value, max: hp.max } } getTempHP(token) { const hp = token.actor.data.data.attributes.hp; return { value: (hp.temp == null) ? 0 : hp.temp, max: (hp.tempmax == null) ? 0 : hp.tempmax } } getAC(token) { return token.actor.data.data.attributes.ac.value; } getShieldHP(token) { return token.actor.data.data.attributes.shield.value; } getSpeed(token) { let speed = `${token.actor.data.data.attributes.speed.total}'`; const otherSpeeds = token.actor.data.data.attributes.speed.otherSpeeds; if (otherSpeeds.length > 0) for (let os of otherSpeeds) speed += `\n${os.type} ${os.total}'`; return speed; } getInitiative(token) { let initiativeModifier = token.actor.data.data.attributes?.initiative.totalModifier; let initiativeAbility = token.actor.data.data.attributes?.initiative.ability; if (initiativeModifier > 0) { initiativeModifier = `+${initiativeModifier}`; } else { initiativeModifier = this.getPerception(token); //NPCs won't have a valid Initiative value, so default to use Perception } return (initiativeAbility != '') ? `(${initiativeAbility}): ${initiativeModifier}` : `(perception): ${initiativeModifier}`; } toggleInitiative(token) { return; } getPassivePerception(token) { return; } getPassiveInvestigation(token) { return; } getPerception(token) { let perception = token.actor.data.data.attributes?.perception.totalModifier; return (perception >= 0) ? `+${perception}` : perception; } getAbility(token, ability) { if (ability == undefined) ability = 'str'; return token.actor.data.data.abilities?.[ability].value; } getAbilityModifier(token, ability) { if (ability == undefined) ability = 'str'; let val = token.actor.data.data.abilities?.[ability].mod; return (val >= 0) ? `+${val}` : val; } getAbilitySave(token, ability) { if (ability == undefined) ability = 'fortitude'; else if (ability == 'fort') ability = 'fortitude'; else if (ability == 'ref') ability = 'reflex'; else if (ability == 'will') ability = 'will'; let val = token.actor.data.data.saves?.[ability].value; return (val >= 0) ? `+${val}` : val; } getSkill(token, skill) { if (skill == undefined) skill = 'acr'; if (skill.startsWith('lor')) { const index = parseInt(skill.split('_')[1])-1; const loreSkills = this.getLoreSkills(token); if (loreSkills.length > index) { return `${loreSkills[index].name}: +${loreSkills[index].totalModifier}`; } else { return ''; } } const val = token.actor.data.data.skills?.[skill].totalModifier; return (val >= 0) ? `+${val}` : val; } getLoreSkills(token) { const skills = token.actor.data.data.skills; return Object.keys(skills).map(key => skills[key]).filter(s => s.lore == true); } getProficiency(token) { return; } getCondition(token,condition) { if (condition == undefined || condition == 'removeAll') return undefined; const Condition = this.getConditionName(condition); const effects = token.actor.items.filter(i => i.type == 'condition'); return effects.find(e => e.name === Condition); } getConditionIcon(condition) { if (condition == undefined) condition = 'removeAll'; if (condition == 'removeAll') return window.CONFIG.controlIcons.effects; else return `${CONFIG.PF2E.statusEffects.effectsIconFolder}${condition}.webp`; } getConditionActive(token,condition) { return this.getCondition(token,condition) != undefined; } getConditionValue(token,condition) { const effect = this.getCondition(token, condition); if (effect != undefined && effect?.value != null) return effect; } async modifyConditionValue(token,condition,delta) { if (condition == undefined) condition = 'removeAll'; if (condition == 'removeAll'){ for( let effect of token.actor.items.filter(i => i.type == 'condition')) await effect.delete(); } else { const effect = this.getConditionValue(token,condition); if (effect == undefined) { if (delta > 0) { const Condition = this.getConditionName(condition); const newCondition = game.pf2e.ConditionManager.getCondition(Condition); await game.pf2e.ConditionManager.addConditionToToken(newCondition, token); } } else { try { await game.pf2e.ConditionManager.updateConditionValue(effect.id, token, effect.value+delta); } catch (error) { //Do nothing. updateConditionValue will have an error about 'documentData is not iterable' when called from an NPC token. } } } return true; } getConditionName(condition) { if ("flatFooted" == condition) { return 'Flat-Footed'; //An inconsistency has been introduced on the PF2E system. The icon is still using 'flatFooted' as the name, but the condition in the manager has been renamed to 'Flat-Footed' } else return condition.charAt(0).toUpperCase() + condition.slice(1); } async toggleCondition(token,condition) { if (condition == undefined) condition = 'removeAll'; if (condition == 'removeAll'){ for( let effect of token.actor.items.filter(i => i.type == 'condition')) await effect.delete(); } else { const effect = this.getCondition(token,condition); if (effect == undefined) { const Condition = this.getConditionName(condition); const newCondition = game.pf2e.ConditionManager.getCondition(Condition); newCondition.data.sources.hud = !0, await game.pf2e.ConditionManager.addConditionToToken(newCondition, token); } else { effect.delete(); } } return true; } /** * Roll */ roll(token,roll,options,ability,skill,save) { if (roll == undefined) roll = 'skill'; if (ability == undefined) ability = 'str'; if (skill == undefined) skill = 'acr'; if (save == undefined) save = 'fort'; if (roll == 'perception') token.actor.data.data.attributes.perception.roll(options); if (roll == 'initiative') token.actor.rollInitiative(options); if (roll == 'ability') token.actor.rollAbility(options, ability); else if (roll == 'save') { let ability = save; if (ability == 'fort') ability = 'fortitude'; else if (ability == 'ref') ability = 'reflex'; else if (ability == 'will') ability = 'will'; token.actor.rollSave(options, ability); } else if (roll == 'skill') { if (skill.startsWith('lor')) { const index = parseInt(skill.split('_')[1])-1; const loreSkills = this.getLoreSkills(token); if (loreSkills.length > index) { let loreSkill = loreSkills[index]; skill = loreSkill.shortform == undefined? loreSkills[index].expanded : loreSkills[index].shortform; } else { return; } } token.actor.data.data.skills?.[skill].roll(options); } } /** * Items */ getItems(token,itemType) { if (itemType == undefined) itemType = 'any'; const allItems = token.actor.items; if (itemType == 'any') return allItems.filter(i => i.type == 'weapon' || i.type == 'equipment' || i.type == 'consumable' || i.type == 'loot' || i.type == 'container'); if (itemType == 'weapon') return allItems.filter(i => i.type == 'weapon' || i.type == 'melee') //Include melee actions for NPCs without equipment else return allItems.filter(i => i.type == itemType); } getItemUses(item) { return {available: item.data.data.quantity.value}; } /** * Features */ getFeatures(token,featureType) { if (featureType == undefined) featureType = 'any'; const allItems = token.actor.items; if (featureType == 'any') return allItems.filter(i => i.type == 'ancestry' || i.type == 'background' || i.type == 'class' || i.type == 'feat' || i.type == 'action'); if (featureType == 'action-any') return allItems.filter(i => i.type == 'action'); if (featureType == 'action-def') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'defensive'); if (featureType == 'action-int') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'interaction'); if (featureType == 'action-off') return allItems.filter(i => i.type == 'action' && i.data.data.actionCategory?.value == 'offensive'); if (featureType == 'strike') { //Strikes are not in the actor.items collection let actions = token.actor.data.data.actions.filter(a=>a.type == 'strike'); for (let a of actions) { a.img = a.imageUrl; a.data = { sort: 1 }; } return actions; } else return allItems.filter(i => i.type == featureType) } getFeatureUses(item) { if (item.data.type == 'class') return {available: item.actor.data.data.details.level.value}; else return; } /** * Spells */ getSpells(token,level) { if (level == undefined) level = 'any'; const allItems = token.actor.items; if (level == 'any') return allItems.filter(i => i.type == 'spell') if (level == '0') return allItems.filter(i => i.type == 'spell' && i.isCantrip == true) else return allItems.filter(i => i.type == 'spell' && i.level == level && i.isCantrip == false) } getSpellUses(token,level,item) { if (level == undefined || level == 'any') level = item.level; if (item.isCantrip == true) return; const spellbook = token.actor.items.filter(i => i.data.type === 'spellcastingEntry')[0]; if (spellbook == undefined) return; return { available: spellbook.data.data.slots?.[`slot${level}`].value, maximum: spellbook.data.data.slots?.[`slot${level}`].max } } rollItem(item) { let variant = 0; if (otherControls.rollOption == 'map1') variant = 1; if (otherControls.rollOption == 'map2') variant = 2; if (item.type==='strike') return item.variants[variant].roll({event}); if (item.type==='weapon' || item.type==='melee') return item.parent.data.data.actions.find(a=>a.name===item.name).variants[variant].roll({event}); return game.pf2e.rollItemMacro(item.id); } }