add resist roll to banish
This commit is contained in:
parent
ae7295028d
commit
8f015bc05c
@ -1,27 +1,25 @@
|
|||||||
/* global warpgate */
|
/* global warpgate */
|
||||||
import { moduleHelpers } from './globals.js'
|
import { moduleHelpers } from './globals.js';
|
||||||
|
|
||||||
export async function requestFearRollFromTokens (tokens, options = {}) {
|
export async function requestFearRollFromTokens(tokens, options = {}) {
|
||||||
// tokens: list of tokens to request the roll from
|
// tokens: list of tokens to request the roll from
|
||||||
// options:
|
// options:
|
||||||
// title: tile for the roll dialog. Will have "- {{ token name }}" appended
|
// title: tile for the roll dialog. Will have "- {{ token name }}" appended
|
||||||
// flavour: flavor text for the roll card. Defaults to title
|
// flavour: flavor text for the roll card. Defaults to title
|
||||||
// fear: value of the fear modifier. Defaults to 0. Positive number.
|
// fear: value of the fear modifier. Defaults to 0. Positive number.
|
||||||
const requestingUser = game.user
|
const requestingUser = game.user;
|
||||||
const title = options?.title || `${requestingUser.name} requests a Fear check`
|
const title = options?.title || `${requestingUser.name} requests a Fear check`;
|
||||||
const flavour = options?.flavour || options?.flavor || title
|
const flavour = options?.flavour || options?.flavor || title;
|
||||||
const fear = options.fear || 0
|
const fear = options.fear || 0;
|
||||||
const rollOpts = {
|
const rollOpts = {
|
||||||
title,
|
title,
|
||||||
flavour,
|
flavour,
|
||||||
mods: [
|
mods: [{ label: 'Fear Penalty', value: Math.abs(fear) * -1, ignore: false }],
|
||||||
{ label: 'Fear Penalty', value: Math.abs(fear) * -1, ignore: false }
|
};
|
||||||
]
|
return requestRollFromTokens(tokens, 'attribute', 'spirit', rollOpts);
|
||||||
}
|
|
||||||
return requestRollFromTokens(tokens, 'attribute', 'spirit', rollOpts)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function requestRollFromTokens (tokens, rollType, rollDesc, options = {}) {
|
export async function requestRollFromTokens(tokens, rollType, rollDesc, options = {}) {
|
||||||
// tokens: list of tokens to request a roll from
|
// tokens: list of tokens to request a roll from
|
||||||
// rollType: 'attribute' or 'skill
|
// rollType: 'attribute' or 'skill
|
||||||
// rollDesc: name of attribute or skill
|
// rollDesc: name of attribute or skill
|
||||||
@ -33,91 +31,98 @@ export async function requestRollFromTokens (tokens, rollType, rollDesc, options
|
|||||||
// mods: list of modifiers {label: "", value: 0, ignore: false}
|
// mods: list of modifiers {label: "", value: 0, ignore: false}
|
||||||
// modCallback: callback function that takes a token and returns a list of
|
// modCallback: callback function that takes a token and returns a list of
|
||||||
// modifiers in the same format as modifiers, above
|
// modifiers in the same format as modifiers, above
|
||||||
const requestingUser = game.user
|
const requestingUser = game.user;
|
||||||
const title = options?.title || `${requestingUser.name} requests a ${rollDesc} roll`
|
const title = options?.title || `${requestingUser.name} requests a ${rollDesc} roll`;
|
||||||
const flavour = options?.flavour || options?.flavor || title
|
const flavour = options?.flavour || options?.flavor || title;
|
||||||
const targetNumber = options?.targetNumber || 4
|
const targetNumber = options?.targetNumber || 4;
|
||||||
const promises = []
|
const promises = [];
|
||||||
for (const token of tokens) {
|
for (const token of tokens) {
|
||||||
const owner = warpgate.util.firstOwner(token.document)
|
const owner = warpgate.util.firstOwner(token.document);
|
||||||
const rollOpts = {
|
const rollOpts = {
|
||||||
title: `${title} - ${token.name}`,
|
title: `${title} - ${token.name}`,
|
||||||
targetNumber,
|
targetNumber,
|
||||||
flavour
|
flavour,
|
||||||
}
|
};
|
||||||
const additionalMods = []
|
const additionalMods = [];
|
||||||
if ('mods' in options) {
|
if ('mods' in options) {
|
||||||
for (const mod of options.mods) {
|
for (const mod of options.mods) {
|
||||||
additionalMods.push(mod)
|
additionalMods.push(mod);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ('modCallback' in options) {
|
if ('modCallback' in options) {
|
||||||
const tokenMods = await options.modCallback(token)
|
const tokenMods = await options.modCallback(token);
|
||||||
for (const tm of tokenMods) {
|
for (const tm of tokenMods) {
|
||||||
additionalMods.push(tm)
|
additionalMods.push(tm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (additionalMods.length > 0) {
|
if (additionalMods.length > 0) {
|
||||||
rollOpts.additionalMods = additionalMods
|
rollOpts.additionalMods = additionalMods;
|
||||||
}
|
}
|
||||||
promises.push(moduleHelpers.socket.executeAsUser(requestTokenRoll, owner.id,
|
promises.push(
|
||||||
token.scene.id, token.id, rollType, rollDesc, rollOpts))
|
moduleHelpers.socket.executeAsUser(
|
||||||
|
requestTokenRoll,
|
||||||
|
owner.id,
|
||||||
|
token.scene.id,
|
||||||
|
token.id,
|
||||||
|
rollType,
|
||||||
|
rollDesc,
|
||||||
|
rollOpts,
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const results = (await Promise.allSettled(promises)).map(r => r.value)
|
const results = (await Promise.allSettled(promises)).map((r) => r.value);
|
||||||
const contentExtra = targetNumber === 4 ? '' : ` vs TN: ${targetNumber}`
|
const contentExtra = targetNumber === 4 ? '' : ` vs TN: ${targetNumber}`;
|
||||||
const messageData = {
|
const messageData = {
|
||||||
flavor: flavour,
|
flavor: flavour,
|
||||||
speaker: { alias: 'Requested Roll Results' },
|
speaker: { alias: 'Requested Roll Results' },
|
||||||
whisper: [...ChatMessage.getWhisperRecipients('GM'), requestingUser],
|
whisper: [...ChatMessage.getWhisperRecipients('GM'), requestingUser],
|
||||||
content: `<p>Results of ${rollDesc[0].toUpperCase()}${rollDesc.slice(1)} roll${contentExtra}:</p>
|
content: `<p>Results of ${rollDesc[0].toUpperCase()}${rollDesc.slice(1)} roll${contentExtra}:</p>
|
||||||
<table><thead><tr><th>Token</th><th>Roll</th><th>Result</th></tr></thead><tbody>`,
|
<table><thead><tr><th>Token</th><th>Roll</th><th>Result</th></tr></thead><tbody>`,
|
||||||
rolls: []
|
rolls: [],
|
||||||
}
|
};
|
||||||
for (const result of results) {
|
for (const result of results) {
|
||||||
const token = game.scenes.get(result.sceneId).tokens.get(result.tokenId)
|
const token = game.scenes.get(result.sceneId).tokens.get(result.tokenId);
|
||||||
const roll = (
|
const roll =
|
||||||
result.result instanceof CONFIG.Dice.SwadeRoll
|
result.result instanceof CONFIG.Dice.SwadeRoll
|
||||||
? result.result
|
? result.result
|
||||||
: CONFIG.Dice[result.result.class].fromData(result.result)
|
: CONFIG.Dice[result.result.class].fromData(result.result);
|
||||||
)
|
roll.targetNumber = targetNumber;
|
||||||
roll.targetNumber = targetNumber
|
let textResult = '';
|
||||||
let textResult = ''
|
|
||||||
if (roll.successes === -1) {
|
if (roll.successes === -1) {
|
||||||
textResult = 'CRITICAL FAILURE'
|
textResult = 'CRITICAL FAILURE';
|
||||||
} else if (roll.successes === 0) {
|
} else if (roll.successes === 0) {
|
||||||
textResult = 'failed'
|
textResult = 'failed';
|
||||||
} else if (roll.successes === 1) {
|
} else if (roll.successes === 1) {
|
||||||
textResult = 'success'
|
textResult = 'success';
|
||||||
} else {
|
} else {
|
||||||
textResult = `success and ${roll.successes - 1} raise${roll.successes > 2 ? 's' : ''}`
|
textResult = `success and ${roll.successes - 1} raise${roll.successes > 2 ? 's' : ''}`;
|
||||||
}
|
}
|
||||||
messageData.content += ('<tr>' +
|
messageData.content +=
|
||||||
|
'<tr>' +
|
||||||
`<th>${token.name}</th>` +
|
`<th>${token.name}</th>` +
|
||||||
`<td>${roll ? roll.total : '<i>Canceled</i>'}</td>` +
|
`<td>${roll ? roll.total : '<i>Canceled</i>'}</td>` +
|
||||||
`<td>${textResult}</td>` +
|
`<td>${textResult}</td>` +
|
||||||
'</tr>')
|
'</tr>';
|
||||||
if (roll) {
|
if (roll) {
|
||||||
messageData.rolls.unshift(roll)
|
messageData.rolls.unshift(roll);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
messageData.content += '</tbody></table>'
|
messageData.content += '</tbody></table>';
|
||||||
ChatMessage.create(messageData, {})
|
ChatMessage.create(messageData, {});
|
||||||
return results
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function requestTokenRoll (
|
export async function requestTokenRoll(sceneId, tokenId, rollType, rollDesc, options) {
|
||||||
sceneId, tokenId, rollType, rollDesc, options
|
const scene = game.scenes.get(sceneId);
|
||||||
) {
|
const token = scene.tokens.get(tokenId);
|
||||||
const scene = game.scenes.get(sceneId)
|
let rollFunc = 'rollAttribute';
|
||||||
const token = scene.tokens.get(tokenId)
|
let rollId = rollDesc.toLowerCase();
|
||||||
let rollFunc = 'rollAttribute'
|
|
||||||
let rollId = rollDesc.toLowerCase()
|
|
||||||
if (rollType === 'skill') {
|
if (rollType === 'skill') {
|
||||||
rollFunc = 'rollSkill'
|
rollFunc = 'rollSkill';
|
||||||
rollId = token.actor.items.filter(i => i.type === 'skill').find(i => (
|
rollId = token.actor.items
|
||||||
i.system.swid === rollDesc.toLowerCase() ||
|
.filter((i) => i.type === 'skill')
|
||||||
i.name.toLowerCase() === rollDesc.toLowerCase()))?.id
|
.find((i) => i.system.swid === rollDesc.toLowerCase() || i.name.toLowerCase() === rollDesc.toLowerCase())?.id;
|
||||||
}
|
}
|
||||||
const result = await token.actor[rollFunc](rollId, options)
|
const result = await token.actor[rollFunc](rollId, options);
|
||||||
return { sceneId, tokenId, result }
|
return { sceneId, tokenId, result };
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,6 @@
|
|||||||
import { PowerEffect } from './basePowers.js';
|
import { PowerEffect } from './basePowers.js';
|
||||||
|
import { requestRollFromTokens } from '../helpers.js';
|
||||||
|
import { getPowerModifiers } from '../rollHelpers.js';
|
||||||
|
|
||||||
export class BanishEffect extends PowerEffect {
|
export class BanishEffect extends PowerEffect {
|
||||||
get name() {
|
get name() {
|
||||||
@ -31,6 +33,15 @@ export class BanishEffect extends PowerEffect {
|
|||||||
|
|
||||||
get modifiers() {
|
get modifiers() {
|
||||||
const mods = super.modifiers;
|
const mods = super.modifiers;
|
||||||
|
mods.push({
|
||||||
|
type: 'number',
|
||||||
|
default: 4,
|
||||||
|
name: 'Opposed Target Number',
|
||||||
|
id: 'tn',
|
||||||
|
epic: false,
|
||||||
|
effect: false,
|
||||||
|
value: 0,
|
||||||
|
});
|
||||||
mods.push({
|
mods.push({
|
||||||
type: 'select',
|
type: 'select',
|
||||||
default: 'none',
|
default: 'none',
|
||||||
@ -49,6 +60,17 @@ export class BanishEffect extends PowerEffect {
|
|||||||
return mods;
|
return mods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sideEffects() {
|
||||||
|
await super.sideEffects();
|
||||||
|
const rollOpts = {
|
||||||
|
title: 'Spirit roll to resist banishment',
|
||||||
|
flavor: 'Roll Spirit to resist banishment!',
|
||||||
|
modCallback: getPowerModifiers,
|
||||||
|
targetNumber: this.data.tn,
|
||||||
|
};
|
||||||
|
await requestRollFromTokens(this.targets, 'ability', 'spirit', rollOpts);
|
||||||
|
}
|
||||||
|
|
||||||
get description() {
|
get description() {
|
||||||
return (
|
return (
|
||||||
super.description +
|
super.description +
|
||||||
|
|||||||
@ -1,60 +1,64 @@
|
|||||||
import { log } from './globals.js'
|
import { log } from './globals.js';
|
||||||
|
|
||||||
export async function preTraitRollModifiers (actor, trait, roll, modifiers, options) {
|
export async function preTraitRollModifiers(actor, trait, roll, modifiers, options) {
|
||||||
const targets = Array.from(game.user.targets)
|
const targets = Array.from(game.user.targets);
|
||||||
const token = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0] : null
|
const token = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0] : null;
|
||||||
if (targets.some(target => target.actor.system.status.isVulnerable)) {
|
if (targets.some((target) => target.actor.system.status.isVulnerable)) {
|
||||||
modifiers.push({ label: 'Target is Vulnerable', value: '+2', ignore: false })
|
modifiers.push({ label: 'Target is Vulnerable', value: '+2', ignore: false });
|
||||||
}
|
}
|
||||||
if (targets.some(
|
if (
|
||||||
target => target.actor.effects.filter(
|
targets.some(
|
||||||
e => !e.disabled && e.name.toLowerCase().includes('deflection')).length > 0)
|
(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 })
|
modifiers.push({ label: 'Target has Deflection', value: '-2', ignore: false });
|
||||||
}
|
}
|
||||||
if (targets.some(
|
if (
|
||||||
target => target.actor.effects.filter(
|
targets.some(
|
||||||
e => !e.disabled && e.name.toLowerCase().includes('glow')).length > 0)
|
(target) => target.actor.effects.filter((e) => !e.disabled && e.name.toLowerCase().includes('glow')).length > 0,
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
modifiers.push({
|
modifiers.push({
|
||||||
label: 'Glowing target (negate 1 point of illumination penalty)',
|
label: 'Glowing target (negate 1 point of illumination penalty)',
|
||||||
value: '+1',
|
value: '+1',
|
||||||
ignore: true
|
ignore: true,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (targets.some(
|
if (
|
||||||
target => target.actor.effects.filter(
|
targets.some(
|
||||||
e => !e.disabled && e.name.toLowerCase().includes('shroud')).length > 0)
|
(target) => target.actor.effects.filter((e) => !e.disabled && e.name.toLowerCase().includes('shroud')).length > 0,
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
modifiers.push({
|
modifiers.push({
|
||||||
label: 'Shrouded target',
|
label: 'Shrouded target',
|
||||||
value: '-1',
|
value: '-1',
|
||||||
ignore: false
|
ignore: false,
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if (targets.length === 1 && token) {
|
if (targets.length === 1 && token) {
|
||||||
const target = targets[0]
|
const target = targets[0];
|
||||||
_addArcaneModifiers(target, modifiers)
|
_addArcaneModifiers(target, modifiers);
|
||||||
_addRangeModifiers(token, target, options, modifiers)
|
_addRangeModifiers(token, target, options, modifiers);
|
||||||
const scaleMod = calcScaleMod(token, target)
|
const scaleMod = calcScaleMod(token, target);
|
||||||
if (scaleMod !== 0) {
|
if (scaleMod !== 0) {
|
||||||
modifiers.push({ label: 'Scale', value: scaleMod, ignore: false })
|
modifiers.push({ label: 'Scale', value: scaleMod, ignore: false });
|
||||||
}
|
}
|
||||||
if (target.actor.items.find(e => e.type === 'edge' && e.system.swid === 'dodge')) {
|
if (target.actor.items.find((e) => e.type === 'edge' && e.system.swid === 'dodge')) {
|
||||||
modifiers.push({ label: 'Dodge', value: -2, ignore: true })
|
modifiers.push({ label: 'Dodge', value: -2, ignore: true });
|
||||||
}
|
}
|
||||||
if (trait?.type === 'skill' && trait?.system?.swid === 'fighting') {
|
if (trait?.type === 'skill' && trait?.system?.swid === 'fighting') {
|
||||||
const gangUpBonus = calcGangup(token, target)
|
const gangUpBonus = calcGangup(token, target);
|
||||||
if (gangUpBonus > 0) {
|
if (gangUpBonus > 0) {
|
||||||
modifiers.push({ label: 'Gang Up', value: gangUpBonus, ignore: false })
|
modifiers.push({ label: 'Gang Up', value: gangUpBonus, ignore: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function preDamageRollModifiers (actor, item, roll, modifiers, options) {
|
export async function preDamageRollModifiers(actor, item, roll, modifiers, options) {
|
||||||
const targets = Array.from(game.user.targets)
|
const targets = Array.from(game.user.targets);
|
||||||
const token = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0] : null
|
const token = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0] : null;
|
||||||
// log('ACTOR', actor)
|
// log('ACTOR', actor)
|
||||||
// log('TOKEN', token)
|
// log('TOKEN', token)
|
||||||
// log('ITEM', item)
|
// log('ITEM', item)
|
||||||
@ -63,148 +67,150 @@ export async function preDamageRollModifiers (actor, item, roll, modifiers, opti
|
|||||||
// log('OPTIONS', options)
|
// log('OPTIONS', options)
|
||||||
// log('TARGET', targets)
|
// log('TARGET', targets)
|
||||||
if (targets.length === 1 && token) {
|
if (targets.length === 1 && token) {
|
||||||
const target = targets[0]
|
const target = targets[0];
|
||||||
_addArcaneModifiers(target, modifiers)
|
_addArcaneModifiers(target, modifiers);
|
||||||
const weaknesses = target.actor.items.filter(
|
const weaknesses = target.actor.items.filter(
|
||||||
i => i.type === 'ability' && i.system.swid.toLowerCase().includes('environmental-weakness'))
|
(i) => i.type === 'ability' && i.system.swid.toLowerCase().includes('environmental-weakness'),
|
||||||
|
);
|
||||||
if (weaknesses.length > 0) {
|
if (weaknesses.length > 0) {
|
||||||
modifiers.push(...weaknesses.map(i => { return { label: i.name, value: '+4', ignore: true } }))
|
modifiers.push(
|
||||||
|
...weaknesses.map((i) => {
|
||||||
|
return { label: i.name, value: '+4', ignore: true };
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
const resistances = target.actor.items.filter(
|
const resistances = target.actor.items.filter(
|
||||||
i => i.type === 'ability' && i.system.swid.toLowerCase().includes('environmental-resistance'))
|
(i) => i.type === 'ability' && i.system.swid.toLowerCase().includes('environmental-resistance'),
|
||||||
|
);
|
||||||
if (resistances.length > 0) {
|
if (resistances.length > 0) {
|
||||||
modifiers.push(...resistances.map(i => { return { label: i.name, value: '-4', ignore: true } }))
|
modifiers.push(
|
||||||
|
...resistances.map((i) => {
|
||||||
|
return { label: i.name, value: '-4', ignore: true };
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (_findItem(token.actor, 'ability', 'pack-tactics')) {
|
if (_findItem(token.actor, 'ability', 'pack-tactics')) {
|
||||||
const gangupBonus = calcGangup(token, target)
|
const gangupBonus = calcGangup(token, target);
|
||||||
if (gangupBonus > 0) {
|
if (gangupBonus > 0) {
|
||||||
modifiers.push({ label: 'Gang Up (Pack Tactics)', value: gangupBonus, ignore: false })
|
modifiers.push({ label: 'Gang Up (Pack Tactics)', value: gangupBonus, ignore: false });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _addRangeModifiers (token, target, options, modifiers) {
|
export async function getPowerModifiers(token) {
|
||||||
|
const modifiers = [];
|
||||||
|
_addArcaneModifiers(token, modifiers);
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _addRangeModifiers(token, target, options, modifiers) {
|
||||||
if (options?.item?.type !== 'weapon' || !options?.item?.system?.range.includes('/')) {
|
if (options?.item?.type !== 'weapon' || !options?.item?.system?.range.includes('/')) {
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
const ranges = options.item.system.range.split('/').map(x => parseInt(x))
|
const ranges = options.item.system.range.split('/').map((x) => parseInt(x));
|
||||||
const distance = getDistance(token, target)
|
const distance = getDistance(token, target);
|
||||||
const rollmods = CONFIG.SWADE.prototypeRollGroups.find(g => g.name === 'Range').modifiers
|
const rollmods = CONFIG.SWADE.prototypeRollGroups.find((g) => g.name === 'Range').modifiers;
|
||||||
log('ITEM RANGES:', ranges)
|
log('ITEM RANGES:', ranges);
|
||||||
if (distance <= ranges[0]) {
|
if (distance <= ranges[0]) {
|
||||||
// nothing here
|
// nothing here
|
||||||
} else if (ranges.length >= 2 && distance <= ranges[1]) {
|
} else if (ranges.length >= 2 && distance <= ranges[1]) {
|
||||||
modifiers.push(rollmods[0])
|
modifiers.push(rollmods[0]);
|
||||||
} else if (ranges.length >= 3 && distance <= ranges[2]) {
|
} else if (ranges.length >= 3 && distance <= ranges[2]) {
|
||||||
modifiers.push(rollmods[1])
|
modifiers.push(rollmods[1]);
|
||||||
} else {
|
} else {
|
||||||
modifiers.push(rollmods[2]) // extreme range
|
modifiers.push(rollmods[2]); // extreme range
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _addArcaneModifiers (target, modifiers) {
|
function _addArcaneModifiers(target, modifiers) {
|
||||||
if (_findItem(target.actor, 'edge', 'improved-arcane-resistance')) {
|
if (_findItem(target.actor, 'edge', 'improved-arcane-resistance')) {
|
||||||
modifiers.push({ label: 'Arcane Resistance', value: '-4', ignore: true })
|
modifiers.push({ label: 'Arcane Resistance', value: '-4', ignore: true });
|
||||||
} else if (_findItem(target.actor, 'edge', 'arcane-resistance')) {
|
} else if (_findItem(target.actor, 'edge', 'arcane-resistance')) {
|
||||||
modifiers.push({ label: 'Arcane Resistance', value: '-2', ignore: true })
|
modifiers.push({ label: 'Arcane Resistance', value: '-2', ignore: true });
|
||||||
}
|
}
|
||||||
const effect = target.actor.effects.find(
|
const effect = target.actor.effects.find((e) => !e.disabled && e.name.toLowerCase().includes('arcane protection'));
|
||||||
e => !e.disabled && e.name.toLowerCase().includes('arcane protection'))
|
|
||||||
if (effect) {
|
if (effect) {
|
||||||
const effectName = effect.name.toLowerCase()
|
const effectName = effect.name.toLowerCase();
|
||||||
const effectMod = (
|
const effectMod = -2 + (effectName.includes('major') ? -2 : 0) + (effectName.includes('greater') ? -2 : 0);
|
||||||
-2 +
|
modifiers.push({ label: 'Target Arcane Protection', value: effectMod, ignore: true });
|
||||||
(effectName.includes('major') ? -2 : 0) +
|
|
||||||
(effectName.includes('greater') ? -2 : 0)
|
|
||||||
)
|
|
||||||
modifiers.push({ label: 'Target Arcane Protection', value: effectMod, ignore: true })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getScaleDistanceMod (token) {
|
function getScaleDistanceMod(token) {
|
||||||
const scale = token.actor.system.stats.scale
|
const scale = token.actor.system.stats.scale;
|
||||||
return (scale > 0 ? (scale / 2) : 0)
|
return scale > 0 ? scale / 2 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDistance (origin, target) {
|
function getDistance(origin, target) {
|
||||||
const ray = new Ray(origin, target)
|
const ray = new Ray(origin, target);
|
||||||
const originScale = getScaleDistanceMod(origin)
|
const originScale = getScaleDistanceMod(origin);
|
||||||
const targetScale = getScaleDistanceMod(target)
|
const targetScale = getScaleDistanceMod(target);
|
||||||
const flatDistance = game.canvas.grid.measureDistances(
|
const flatDistance = game.canvas.grid.measureDistances([{ ray }], { gridSpaces: true })[0];
|
||||||
[{ ray }], { gridSpaces: true })[0]
|
const elevation = Math.abs(origin.document.elevation - target.document.elevation);
|
||||||
const elevation = Math.abs(origin.document.elevation - target.document.elevation)
|
const distance = Math.sqrt(elevation * elevation + flatDistance * flatDistance);
|
||||||
const distance = Math.sqrt(elevation * elevation + flatDistance * flatDistance)
|
return distance - (originScale + targetScale);
|
||||||
return distance - (originScale + targetScale)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function withinRange (origin, target, range) {
|
function withinRange(origin, target, range) {
|
||||||
const distance = getDistance(origin, target)
|
const distance = getDistance(origin, target);
|
||||||
return range >= distance
|
return range >= distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _findItem (actor, type, swid) {
|
function _findItem(actor, type, swid) {
|
||||||
return actor.items.find(i => i.type === type && i.system.swid === swid)
|
return actor.items.find((i) => i.type === type && i.system.swid === swid);
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcScaleMod (attacker, target) {
|
function calcScaleMod(attacker, target) {
|
||||||
const attackerScale = attacker.actor.system.stats.scale
|
const attackerScale = attacker.actor.system.stats.scale;
|
||||||
const targetScale = target.actor.system.stats.scale
|
const targetScale = target.actor.system.stats.scale;
|
||||||
const attackerHasSwat = !!_findItem(attacker.actor, 'ability', 'swat')
|
const attackerHasSwat = !!_findItem(attacker.actor, 'ability', 'swat');
|
||||||
let modifier = targetScale - attackerScale
|
let modifier = targetScale - attackerScale;
|
||||||
if (attackerHasSwat && modifier < 0) {
|
if (attackerHasSwat && modifier < 0) {
|
||||||
modifier = Math.min(modifier + 4, 0)
|
modifier = Math.min(modifier + 4, 0);
|
||||||
}
|
}
|
||||||
return modifier
|
return modifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcGangup (attacker, target, debug) {
|
function calcGangup(attacker, target, debug) {
|
||||||
debug = (typeof debug === 'undefined') ? false : debug
|
debug = typeof debug === 'undefined' ? false : debug;
|
||||||
const range = 1.2
|
const range = 1.2;
|
||||||
let modifier = 0
|
let modifier = 0;
|
||||||
if (_findItem(target.actor, 'edge', 'improved-block')) {
|
if (_findItem(target.actor, 'edge', 'improved-block')) {
|
||||||
modifier = -2
|
modifier = -2;
|
||||||
} else if (_findItem(target.actor, 'edge', 'block')) {
|
} else if (_findItem(target.actor, 'edge', 'block')) {
|
||||||
modifier = -1
|
modifier = -1;
|
||||||
}
|
}
|
||||||
const attackerHasFormationFighter = !!(_findItem(attacker.actor, 'edge', 'formation-fighter'))
|
const attackerHasFormationFighter = !!_findItem(attacker.actor, 'edge', 'formation-fighter');
|
||||||
|
|
||||||
const withinRangeOfToken = game.canvas.tokens.placeables.filter(t =>
|
const withinRangeOfToken = game.canvas.tokens.placeables.filter(
|
||||||
t.id !== attacker.id &&
|
(t) =>
|
||||||
t.id !== target.id &&
|
t.id !== attacker.id &&
|
||||||
t.actor.system.status.isStunned === false &&
|
t.id !== target.id &&
|
||||||
t.visible &&
|
t.actor.system.status.isStunned === false &&
|
||||||
withinRange(target, t, range) &&
|
t.visible &&
|
||||||
!(t.actor.effects.find(c => c.name === 'Incapacitated' || c.name === 'Defeated'))
|
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 attackerAllies = withinRangeOfToken.filter((t) => t.document.disposition === attacker.document.disposition);
|
||||||
const targetAllies = withinRangeOfToken.filter(
|
const targetAllies = withinRangeOfToken.filter(
|
||||||
t => t.document.disposition === target.document.disposition &&
|
(t) => t.document.disposition === target.document.disposition && withinRange(attacker, t, range),
|
||||||
withinRange(attacker, t, range)
|
);
|
||||||
)
|
const attackersWithFormationFighter = attackerAllies.filter((t) => !!_findItem(t.actor, 'edge', 'formation-fighter'));
|
||||||
const attackersWithFormationFighter = attackerAllies.filter(
|
const attackerCount = attackerAllies.length;
|
||||||
t => !!_findItem(t.actor, 'edge', 'formation-fighter'))
|
const attackerFormationBonus =
|
||||||
const attackerCount = attackerAllies.length
|
(attackerCount > 0 && attackerHasFormationFighter ? 1 : 0) + attackersWithFormationFighter.length;
|
||||||
const attackerFormationBonus = (
|
const defenderCount = targetAllies.length;
|
||||||
(attackerCount > 0 && attackerHasFormationFighter ? 1 : 0) +
|
const gangUp = Math.max(0, Math.min(4, attackerCount + attackerFormationBonus - defenderCount + modifier));
|
||||||
attackersWithFormationFighter.length
|
|
||||||
)
|
|
||||||
const defenderCount = targetAllies.length
|
|
||||||
const gangUp = Math.max(
|
|
||||||
0,
|
|
||||||
Math.min(
|
|
||||||
4,
|
|
||||||
attackerCount + attackerFormationBonus - defenderCount + modifier))
|
|
||||||
if (debug) {
|
if (debug) {
|
||||||
log('GANG UP | Attacker:', attacker)
|
log('GANG UP | Attacker:', attacker);
|
||||||
log('GANG UP | Target:', target)
|
log('GANG UP | Target:', target);
|
||||||
log('GANG UP | Others within range:', withinRangeOfToken)
|
log('GANG UP | Others within range:', withinRangeOfToken);
|
||||||
log('GANG UP | Attacker Allies:', attackerCount)
|
log('GANG UP | Attacker Allies:', attackerCount);
|
||||||
log('GANG UP | Attacker Formation Bonus:', attackerFormationBonus)
|
log('GANG UP | Attacker Formation Bonus:', attackerFormationBonus);
|
||||||
log('GANG UP | Effective Defender Allies:', defenderCount)
|
log('GANG UP | Effective Defender Allies:', defenderCount);
|
||||||
log('GANG UP | Target Block Modifier:', modifier)
|
log('GANG UP | Target Block Modifier:', modifier);
|
||||||
log('GANG UP | Total Bonus:', gangUp)
|
log('GANG UP | Total Bonus:', gangUp);
|
||||||
}
|
}
|
||||||
return gangUp
|
return gangUp;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,12 +26,20 @@
|
|||||||
{{#if epic}}⭐ {{/if}}{{name}} ({{numberFormat value decimals=0 sign=true}})
|
{{#if epic}}⭐ {{/if}}{{name}} ({{numberFormat value decimals=0 sign=true}})
|
||||||
</label>
|
</label>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
{{#if isText}}
|
||||||
|
<label for="{{id}}">{{#if epic}}⭐ {{/if}}{{name}}:</label>
|
||||||
|
<input name="{{id}}" type="text" value="{{default}}">
|
||||||
|
{{/if}}
|
||||||
|
{{#if isNumber}}
|
||||||
|
<label for="{{id}}">{{#if epic}}⭐ {{/if}}{{name}}:</label>
|
||||||
|
<input name="{{id}}" type="number" value="{{default}}" step="1" min="0">
|
||||||
|
{{/if}}
|
||||||
{{#if isSelect}}
|
{{#if isSelect}}
|
||||||
<label>{{#if epic}}⭐ {{/if}}{{name}}:</label>
|
<label>{{#if epic}}⭐ {{/if}}{{name}}:</label>
|
||||||
<select name="{{id}}">{{selectOptions choices selected=default}}</select>
|
<select name="{{id}}">{{selectOptions choices selected=default}}</select>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if isRadio}}
|
{{#if isRadio}}
|
||||||
<label>{{#if epic}}⭐ {{/if}}{{name}}:</label> {{radioBoxes id choices checked=default}}
|
{{#if epic}}⭐ {{/if}}{{name}}:</label> {{radioBoxes id choices checked=default}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user