import { log, shim } from './shim.js' export async function preTraitRollModifiers (actor, trait, roll, modifiers, options) { const targets = Array.from(shim.targets) const token = shim.canvas.tokens.controlled.length > 0 ? shim.canvas.tokens.controlled[0] : null // log('ACTOR', actor) // log('TOKEN', token) // log('TRAIT', trait) // log('ROLL', roll) // log('MODIFIERS', modifiers) // log('OPTIONS', options) // log('TARGET', targets) if (targets.some(target => target.actor.system.status.isVulnerable)) { modifiers.push({ label: 'Target is Vulnerable', value: '+2', ignore: false }) } if (targets.some( target => target.actor.effects.filter( e => !e.disabled && e.name.toLowerCase().includes('deflection')).length > 0) ) { modifiers.push({ label: 'Target has Deflection', value: '-2', ignore: false }) } if (targets.length === 1 && token) { const target = targets[0] _addArcaneModifiers(target, modifiers) const scaleMod = calcScaleMod(token, target) if (scaleMod !== 0) { modifiers.push({ label: 'Scale', value: scaleMod, ignore: false }) } if (trait?.type === 'skill' && trait?.system?.swid === 'fighting') { const gangUpBonus = calcGangup(token, target) if (gangUpBonus > 0) { modifiers.push({ label: 'Gang Up', value: gangUpBonus, ignore: false }) } } } } export async function preDamageRollModifiers (actor, item, roll, modifiers, options) { const targets = Array.from(shim.targets) const token = shim.canvas.tokens.controlled.length > 0 ? shim.canvas.tokens.controlled[0] : null // log('ACTOR', actor) // log('TOKEN', token) // log('ITEM', item) // log('ROLL', roll) // log('MODIFIERS', modifiers) // log('OPTIONS', options) // log('TARGET', targets) if (targets.length === 1 && token) { const target = targets[0] _addArcaneModifiers(target, modifiers) const weaknesses = target.actor.items.filter( i => i.type === 'ability' && i.system.swid.toLowerCase().includes('weakness')) if (weaknesses.length > 0) { modifiers.push(...weaknesses.map(i => { return { label: i.name, value: '+4', ignore: true } })) } const resistances = target.actor.items.filter( i => i.type === 'ability' && i.system.swid.toLowerCase().includes('resistance')) if (resistances.length > 0) { modifiers.push(...resistances.map(i => { return { label: i.name, value: '-4', ignore: true } })) } if (_findItem(token.actor, 'ability', 'pack-tactics')) { const gangupBonus = calcGangup(token, target) if (gangupBonus > 0) { modifiers.push({ label: 'Gang Up (Pack Tactics)', value: gangupBonus, ignore: false }) } } } } function _addArcaneModifiers (target, modifiers) { if (_findItem(target.actor, 'edge', 'improved-arcane-resistance')) { modifiers.push({ label: 'Arcane Resistance', value: '-4', ignore: true }) } else if (_findItem(target.actor, 'edge', 'arcane-resistance')) { modifiers.push({ label: 'Arcane Resistance', value: '-2', ignore: true }) } const effect = target.actor.effects.find( e => !e.disabled && e.name.toLowerCase().includes('arcane protection')) if (effect) { const effectName = effect.name.toLowerCase() const effectMod = ( -2 + (effectName.includes('major') ? -2 : 0) + (effectName.includes('greater') ? -2 : 0) ) modifiers.push({ label: 'Target Arcane Protection', value: effectMod, ignore: true }) } } function withinRange (origin, target, range) { const ray = new Ray(origin, target) const distance = shim.canvas.grid.measureDistances([{ ray }], { gridSpaces: true })[0] const originScale = origin.actor.system.stats.scale const targetScale = target.actor.system.stats.scale range += (originScale > 0 ? originScale / 2 : 0) + (targetScale > 0 ? targetScale / 2 : 0) return range >= distance } function _findItem (actor, type, swid) { return actor.items.find(i => i.type === type && i.system.swid === swid) } function calcScaleMod (attacker, target) { const attackerScale = attacker.actor.system.stats.scale const targetScale = target.actor.system.stats.scale const attackerHasSwat = !!_findItem(attacker.actor, 'ability', 'swat') let modifier = targetScale - attackerScale if (attackerHasSwat && modifier < 0) { modifier = Math.min(modifier + 4, 0) } return modifier } function calcGangup (attacker, target, debug) { debug = (typeof debug === 'undefined') ? false : debug const range = 1.2 let modifier = 0 if (_findItem(target.actor, 'edge', 'improved-block')) { modifier = -2 } else if (_findItem(target.actor, 'edge', 'block')) { modifier = -1 } const attackerHasFormationFighter = !!(_findItem(attacker.actor, 'edge', 'formation-fighter')) const withinRangeOfToken = shim.canvas.tokens.placeables.filter(t => t.id !== attacker.id && t.id !== target.id && t.actor.system.status.isStunned === false && t.visible && withinRange(target, t, range) ) const attackerAllies = withinRangeOfToken.filter( t => t.document.disposition === attacker.document.disposition) const targetAllies = withinRangeOfToken.filter( t => t.document.disposition === target.document.disposition && withinRange(attacker, t, range) ) const attackersWithFormationFighter = attackerAllies.filter( t => !!_findItem(t.actor, 'edge', 'formation-fighter')) const attackerCount = attackerAllies.length const attackerFormationBonus = ( (attackerCount > 0 && attackerHasFormationFighter ? 1 : 0) + attackersWithFormationFighter.length ) const defenderCount = targetAllies.length const gangUp = Math.max( 0, Math.min( 4, attackerCount + attackerFormationBonus - defenderCount + modifier)) if (debug) { log('GANG UP | Attacker:', attacker) log('GANG UP | Target:', target) log('GANG UP | Others within range:', withinRangeOfToken) log('GANG UP | Attacker Allies:', attackerCount) log('GANG UP | Attacker Formation Bonus:', attackerFormationBonus) log('GANG UP | Effective Defender Allies:', defenderCount) log('GANG UP | Target Block Modifier:', modifier) log('GANG UP | Total Bonus:', gangUp) } return gangUp }