add resist roll to banish

This commit is contained in:
Mike Bloy 2024-05-13 19:56:38 -05:00
parent ae7295028d
commit 8f015bc05c
4 changed files with 229 additions and 188 deletions

View File

@ -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 };
} }

View File

@ -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 +

View File

@ -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) =>
t.id !== attacker.id && t.id !== attacker.id &&
t.id !== target.id && t.id !== target.id &&
t.actor.system.status.isStunned === false && t.actor.system.status.isStunned === false &&
t.visible && t.visible &&
withinRange(target, t, range) && withinRange(target, t, range) &&
!(t.actor.effects.find(c => c.name === 'Incapacitated' || c.name === 'Defeated')) !t.actor.effects.find((c) => c.name === 'Incapacitated' || c.name === 'Defeated'),
) );
const attackerAllies = withinRangeOfToken.filter( const attackerAllies = withinRangeOfToken.filter((t) => t.document.disposition === attacker.document.disposition);
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;
} }

View File

@ -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}}