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.some( target => target.actor.effects.filter( e => !e.disabled && e.name.toLowerCase().includes('glow')).length > 0) ) { modifiers.push({ label: 'Glowing target (negate 1 point of illumination penalty)', value: '+1', ignore: true }) } if (targets.some( target => target.actor.effects.filter( e => !e.disabled && e.name.toLowerCase().includes('shroud')).length > 0) ) { modifiers.push({ label: 'Shrouded target', value: '-1', ignore: false }) } if (targets.length === 1 && token) { const target = targets[0] _addArcaneModifiers(target, modifiers) _addRangeModifiers(token, target, options, modifiers) const scaleMod = calcScaleMod(token, target) if (scaleMod !== 0) { modifiers.push({ label: 'Scale', value: scaleMod, ignore: false }) } if (target.actor.items.find(e => e.type === 'edge' && e.system.swid === 'dodge')) { modifiers.push({ label: 'Dodge', value: -2, ignore: true }) } 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 _addRangeModifiers (token, target, options, modifiers) { if (options?.item?.type !== 'weapon' || !options?.item?.system?.range.includes('/')) { return } const ranges = options.item.system.range.split('/').map(x => parseInt(x)) const distance = getDistance(token, target) const rollmods = shim.CONFIG.SWADE.prototypeRollGroups.find(g => g.name === 'Range').modifiers log('ITEM RANGES:', ranges) if (distance <= ranges[0]) { // nothing here } else if (ranges.length >= 2 && distance <= ranges[1]) { modifiers.push(rollmods[0]) } else if (ranges.length >= 3 && distance <= ranges[2]) { modifiers.push(rollmods[1]) } else { modifiers.push(rollmods[2]) // extreme range } } 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 getScaleDistanceMod (token) { const scale = token.actor.system.stats.scale return (scale > 0 ? (scale / 2) : 0) } function getDistance (origin, target) { const ray = new Ray(origin, target) const originScale = getScaleDistanceMod(origin) const targetScale = getScaleDistanceMod(target) const distance = shim.canvas.grid.measureDistances([{ ray }], { gridSpaces: true })[0] return distance - (originScale + targetScale) } function withinRange (origin, target, range) { const distance = getDistance(origin, target) 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 }