swade-mb-helpers/scripts/rollHelpers.js
Mike Bloy 6ed989c4bc several changes that were not properly split up
- Macro: Request fear check specialization macro
- Macro: Fear Table to call the new fearTable api endpoint
- API: rulesVersion property
- API: fearTable(actor) calls the relevant premium core rules module's fear
  table
- API: added requestFearRollFromTokens special helper
- Trait roll hooks for:
    - Glow/Shroud
    - Range modifiers
- added a summary chat message for the roll results to requested rolls.
- added a target number option to requested rolls.
2023-12-23 22:50:15 -06:00

211 lines
7.5 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 (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
}