218 lines
8.0 KiB
JavaScript
218 lines
8.0 KiB
JavaScript
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('environmental-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('environmental-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 flatDistance = shim.canvas.grid.measureDistances(
|
|
[{ ray }], { gridSpaces: true })[0]
|
|
const elevation = Math.abs(origin.document.elevation - target.document.elevation)
|
|
const distance = Math.sqrt(elevation * elevation + flatDistance * flatDistance)
|
|
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) &&
|
|
!(t.actor.effects.find(c => c.name === 'Incapacitated' || c.name === 'Defeated'))
|
|
)
|
|
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
|
|
}
|