769 lines
24 KiB
JavaScript
769 lines
24 KiB
JavaScript
import { CONST, shim } from './shim.js'
|
|
|
|
function baseMenu (title, targets) {
|
|
const targetList = targets.map(t => t.name).join(', ')
|
|
return {
|
|
menuOptions: {
|
|
title,
|
|
defaultButton: 'Cancel',
|
|
options: {}
|
|
},
|
|
menuData: {
|
|
inputs: [
|
|
{ type: 'header', label: title },
|
|
{ type: 'info', label: `Apply ${title} to ${targetList}` },
|
|
{ type: 'header', label: 'Global Modifiers' },
|
|
{ type: 'checkbox', label: 'Glow (+1)' },
|
|
{ type: 'checkbox', label: 'Shroud (+1)' },
|
|
{ type: 'checkbox', label: 'Hinder (+1)' },
|
|
{ type: 'checkbox', label: 'Hurry (+1)' },
|
|
{ type: 'header', label: 'Power Modifiers' }
|
|
],
|
|
buttons: [
|
|
{ label: 'Apply', value: 'apply' },
|
|
{ label: 'Apply with Raise', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
}
|
|
}
|
|
}
|
|
|
|
class ModifierEffects {
|
|
static glow (basename, durationRounds) {
|
|
const effectDoc = shim.createEffectDocument(
|
|
'icons/magic/light/orb-shadow-blue.webp',
|
|
'Glow',
|
|
durationRounds
|
|
)
|
|
effectDoc.changes = [
|
|
{
|
|
key: '@Skill{Stealth}[system.die.modifier]',
|
|
mode: CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD,
|
|
value: -2,
|
|
priority: 0
|
|
}
|
|
]
|
|
return effectDoc
|
|
}
|
|
|
|
static shroud (basename, durationRounds) {
|
|
const effectDoc = shim.createEffectDocument(
|
|
'icons/magic/perception/shadow-stealth-eyes-purple.webp',
|
|
'Shroud',
|
|
durationRounds
|
|
)
|
|
effectDoc.changes = [
|
|
{
|
|
key: '@Skill{Stealth}[system.die.modifier]',
|
|
mode: CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD,
|
|
value: 1,
|
|
priority: 0
|
|
}
|
|
]
|
|
return effectDoc
|
|
}
|
|
|
|
static hinder (basename, durationRounds) {
|
|
const effectDoc = shim.createEffectDocument(
|
|
'icons/magic/control/debuff-chains-shackle-movement-red.webp',
|
|
'Hinder',
|
|
durationRounds
|
|
)
|
|
effectDoc.changes = [
|
|
{
|
|
key: 'system.stats.speed.value',
|
|
mode: CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD,
|
|
value: -2,
|
|
priority: 0
|
|
}
|
|
]
|
|
return effectDoc
|
|
}
|
|
|
|
static hurry (basename, durationRounds) {
|
|
const effectDoc = shim.createEffectDocument(
|
|
'icons/skills/movement/feet-winged-sandals-tan.webp',
|
|
'Hurry',
|
|
durationRounds
|
|
)
|
|
effectDoc.changes = [
|
|
{
|
|
key: 'system.stats.speed.value',
|
|
mode: CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD,
|
|
value: 2,
|
|
priority: 0
|
|
}
|
|
]
|
|
return effectDoc
|
|
}
|
|
}
|
|
|
|
function globalModifierEffects (inputs, basename, durationRounds) {
|
|
const effectDocs = []
|
|
const inputIndex = 8
|
|
if (inputs[3]) { // glow
|
|
effectDocs.push(ModifierEffects.glow(basename, durationRounds))
|
|
}
|
|
if (inputs[4]) { // shroud
|
|
effectDocs.push(ModifierEffects.shroud(basename, durationRounds))
|
|
}
|
|
if (inputs[5]) { // hinder
|
|
effectDocs.push(ModifierEffects.hinder(basename, durationRounds))
|
|
}
|
|
if (inputs[6]) { // hurry
|
|
effectDocs.push(ModifierEffects.hurry(basename, durationRounds))
|
|
}
|
|
return { effectDocs, inputIndex }
|
|
}
|
|
|
|
const PowerMenus = {
|
|
blind: function (token, targets) {
|
|
if (targets.length < 1) {
|
|
shim.notifications.error('No target selected for Blind')
|
|
return null
|
|
}
|
|
const { menuOptions, menuData } = baseMenu('Blind', targets)
|
|
menuData.inputs.push({
|
|
type: 'checkbox',
|
|
label: 'Strong (+1 point)',
|
|
options: false
|
|
})
|
|
return { menuOptions, menuData, extra: {} }
|
|
},
|
|
|
|
'boost/lower trait': function (token, targets) {
|
|
if (targets.length < 1) {
|
|
shim.notifications.error('No target selected for Boost/Lower Trait')
|
|
return null
|
|
}
|
|
const { menuOptions, menuData } = baseMenu('Boost/Lower Trait', targets)
|
|
let traitOptions = ['Agility', 'Smarts', 'Spirit', 'Strength', 'Vigor']
|
|
const allSkills = []
|
|
const traits = {}
|
|
for (const traitName of traitOptions) {
|
|
const lower = traitName.toLowerCase()
|
|
traits[traitName] = {
|
|
name: traitName,
|
|
type: 'attribute',
|
|
modkey: `system.attributes.${lower}.die.modifier`,
|
|
diekey: `system.attributes.${lower}.die.sides`
|
|
}
|
|
}
|
|
for (const token of targets) {
|
|
const skills = token.actor.items.filter(item => item.type === 'skill')
|
|
for (const skill of skills) {
|
|
const name = skill.name
|
|
traits[name] = {
|
|
name,
|
|
type: 'skill',
|
|
modkey: `@Skill{${name}}[system.die.modifier]`,
|
|
diekey: `@Skill{${name}}[system.die.sides]`
|
|
}
|
|
if (name !== 'Unskilled' && !allSkills.find(v => v === name)) {
|
|
allSkills.push(name)
|
|
}
|
|
}
|
|
traitOptions = traitOptions.concat(allSkills.sort())
|
|
}
|
|
menuData.inputs.push({
|
|
type: 'select', label: 'Trait', options: traitOptions
|
|
})
|
|
menuData.inputs.push({ type: 'info', label: 'Boost or Lower?' })
|
|
menuData.inputs.push({ type: 'radio', label: 'Boost', options: ['isBoost', true] })
|
|
menuData.inputs.push({ type: 'radio', label: 'Lower', options: ['isBoost', false] })
|
|
menuData.inputs.push({ type: 'checkbox', label: 'Greater', options: false })
|
|
menuData.inputs.push({ type: 'checkbox', label: 'Strong (lower only)', options: false })
|
|
return { menuOptions, menuData, extra: { traits } }
|
|
},
|
|
|
|
confusion: function (token, targets) {
|
|
if (targets.length < 1) {
|
|
shim.notifications.error('No target selected for Confusion')
|
|
return null
|
|
}
|
|
const { menuOptions, menuData } = baseMenu('Confusion', targets)
|
|
menuData.inputs.push({ type: 'checkbox', label: 'Greater (adds Shaken)', options: false })
|
|
menuData.buttons = [
|
|
{ label: 'Distracted', value: 'distracted' },
|
|
{ label: 'Vulnerable', value: 'vulnerable' },
|
|
{ label: 'Raise (both)', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
return { menuOptions, menuData, extra: {} }
|
|
},
|
|
|
|
deflection: function (token, targets) {
|
|
if (targets.length < 1) {
|
|
shim.notifications.error('No target selected for Deflection')
|
|
return null
|
|
}
|
|
const { menuOptions, menuData } = baseMenu('Deflection', targets)
|
|
menuData.buttons = [
|
|
{ label: 'Melee', value: 'melee' },
|
|
{ label: 'Ranged', value: 'ranged' },
|
|
{ label: 'Raise (both)', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
return { menuOptions, menuData, extra: {} }
|
|
},
|
|
|
|
entangle: function (token, targets) {
|
|
if (targets.length < 1) {
|
|
shim.notifications.error('No target selected for Entangle')
|
|
return null
|
|
}
|
|
const { menuOptions, menuData } = baseMenu('Entangle', targets)
|
|
menuData.inputs = menuData.inputs.concat([
|
|
{ type: 'radio', label: 'Not Damaging', options: ['dmg', true] },
|
|
{ type: 'radio', label: 'Damaging', options: ['dmg', false] },
|
|
{ type: 'radio', label: 'Deadly', options: ['dmg', false] },
|
|
{ type: 'checkbox', label: 'Tough', options: false }
|
|
])
|
|
menuData.buttons = [
|
|
{ label: 'Entangled', value: 'apply' },
|
|
{ label: 'Bound (raise)', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
return { menuOptions, menuData, extra: {} }
|
|
},
|
|
|
|
intangibility: function (token, targets) {
|
|
if (targets.length < 1) {
|
|
shim.notifications.error('No target selected for Intangibility')
|
|
return null
|
|
}
|
|
const { menuOptions, menuData } = baseMenu('Intangibility', targets)
|
|
menuData.inputs.push({ type: 'checkbox', label: 'Duration', options: false })
|
|
menuData.buttons.splice(1, 1) // delete the raise button
|
|
return { menuOptions, menuData, extra: {} }
|
|
}
|
|
}
|
|
|
|
const PowerHandlers = {
|
|
blind: async function (token, targets, buttons, inputs, extra) {
|
|
const raise = (buttons === 'raise')
|
|
const { effectDocs, inputIndex } = globalModifierEffects(inputs, 'Blind', 1)
|
|
const strong = !!inputs[inputIndex]
|
|
const icon = 'icons/skills/wounds/injury-eyes-blood-red.webp'
|
|
const changes = [
|
|
{
|
|
key: 'system.stats.globalMods.trait',
|
|
mode: CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD,
|
|
value: '-2',
|
|
priority: 0
|
|
}
|
|
]
|
|
effectDocs.push(
|
|
shim.createEffectDocument(
|
|
icon, `minor Blindness (Vigor ${strong ? '-2 ' : ''}ends)`, 1, changes)
|
|
)
|
|
if (raise) {
|
|
effectDocs.push(
|
|
shim.createEffectDocument(
|
|
icon, `major Blindness (Vigor ${strong ? '-2 ' : ''}ends)`, 1, changes)
|
|
)
|
|
}
|
|
for (const target of targets) {
|
|
shim.applyActiveEffects(target, effectDocs)
|
|
}
|
|
},
|
|
|
|
'boost/lower trait': async function (token, targets, buttons, inputs, extra) {
|
|
const raise = (buttons === 'raise')
|
|
const direction = inputs[inputs.length - 4] ? 'Boost' : 'Lower'
|
|
const durationRounds = (direction === 'Boost' ? 5 : 1)
|
|
const { effectDocs, inputIndex } = globalModifierEffects(
|
|
inputs, 'Boost/Lower Trait', durationRounds)
|
|
const icon = (direction === 'Boost'
|
|
? 'icons/magic/life/cross-embers-glow-yellow-purple.webp'
|
|
: 'icons/magic/movement/chevrons-down-yellow.webp')
|
|
const trait = extra.traits[inputs[inputIndex]]
|
|
const greater = !!inputs[inputIndex + 4]
|
|
const strong = !!inputs[inputIndex + 5]
|
|
|
|
let namePart = `${direction} ${trait.name}`
|
|
const mods = []
|
|
if (direction === 'Lower') {
|
|
mods.push(`Spirit${strong ? '-2' : ''} ends`)
|
|
}
|
|
if (greater) {
|
|
mods.push('greater')
|
|
}
|
|
if (mods.length > 0) {
|
|
namePart = `${namePart} (${mods.join(', ')})`
|
|
}
|
|
const mode = CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD
|
|
const modValue = (direction === 'Boost' ? '+2' : '-2')
|
|
const minorEffect = shim.createEffectDocument(
|
|
icon, `minor ${namePart}`, durationRounds, [
|
|
{ key: trait.diekey, mode, value: modValue, priority: 0 }
|
|
])
|
|
if (direction === 'Lower' && greater) {
|
|
minorEffect.changes.push({ key: trait.modkey, mode, value: modValue, priority: 0 })
|
|
}
|
|
const majorEffect = shim.createEffectDocument(
|
|
icon, `major ${namePart}`, durationRounds, [
|
|
{ key: trait.diekey, mode, value: modValue, priority: 0 }
|
|
])
|
|
effectDocs.push(minorEffect)
|
|
if (raise) { effectDocs.push(majorEffect) }
|
|
for (const target of targets) {
|
|
shim.applyActiveEffects(target, effectDocs)
|
|
}
|
|
},
|
|
|
|
confusion: async function (token, targets, buttons, inputs, extra) {
|
|
const { effectDocs, inputIndex } = globalModifierEffects(inputs, 'Confusion', 1)
|
|
const greater = !!inputs[inputIndex]
|
|
if (buttons === 'distracted' || buttons === 'raise') {
|
|
effectDocs.push(shim.getStatus('SWADE.Distr', 'Distracted'))
|
|
}
|
|
if (buttons === 'vulnerable' || buttons === 'raise') {
|
|
effectDocs.push(shim.getStatus('SWADE.Vuln', 'Vulnerable'))
|
|
}
|
|
if (greater) {
|
|
effectDocs.push(shim.getStatus('SWADE.Shaken', 'Shaken'))
|
|
}
|
|
for (const target of targets) {
|
|
shim.applyActiveEffects(target, effectDocs)
|
|
}
|
|
},
|
|
|
|
deflection: async function (token, targets, buttons, inputs, extra) {
|
|
const { effectDocs } = globalModifierEffects(inputs, 'Confusion', 1)
|
|
const effectName = `Deflection (${buttons === 'raise' ? 'all' : buttons})`
|
|
const icon = 'icons/magic/defensive/shield-barrier-deflect-teal.webp'
|
|
effectDocs.push(shim.createEffectDocument(icon, effectName, 5))
|
|
for (const target of targets) {
|
|
shim.applyActiveEffects(target, effectDocs)
|
|
}
|
|
},
|
|
|
|
entangle: async function (token, targets, buttons, inputs, extra) {
|
|
const { effectDocs, inputIndex } = globalModifierEffects(inputs, 'Entangle', 1)
|
|
const damage = (inputs[inputIndex + 1] ? '2d4' : (inputs[inputIndex + 2] ? '2d6' : null))
|
|
const tough = !!inputs[inputIndex + 3]
|
|
const effectSearch = (buttons === 'raise' ? 'SWADE.Bound' : 'SWADE.Entangled')
|
|
const effectName = (buttons === 'raise' ? 'Bound' : 'Entangled')
|
|
const effect = shim.getStatus(effectSearch, effectName)
|
|
const extraIcon = 'icons/magic/nature/root-vine-barrier-wall-brown.webp'
|
|
const extraEffect = shim.createEffectDocument(extraIcon, 'Entangle Modifier', 1, [])
|
|
if (damage) {
|
|
extraEffect.name = `${extraEffect.name} - ${damage} dmg`
|
|
}
|
|
if (tough) {
|
|
extraEffect.name = `Tough ${extraEffect.name}`
|
|
}
|
|
effectDocs.push(effect)
|
|
if (damage || tough) {
|
|
effectDocs.push(extraEffect)
|
|
}
|
|
for (const target of targets) {
|
|
shim.applyActiveEffects(target, effectDocs)
|
|
}
|
|
}
|
|
}
|
|
|
|
class PowerEffect {
|
|
constructor (token, targets) {
|
|
this.token = token
|
|
this.targets = targets
|
|
this.effectDocs = []
|
|
this.menuData = {
|
|
inputs: [
|
|
{ type: 'header', label: `${this.name} Effect` },
|
|
{ type: 'info', label: `Apply ${this.name} Effect` },
|
|
{ type: 'header', label: 'Global Modifiers' },
|
|
{ type: 'checkbox', label: 'Glow (+1)' },
|
|
{ type: 'checkbox', label: 'Shroud (+1)' },
|
|
{ type: 'checkbox', label: 'Hinder (+1)' },
|
|
{ type: 'checkbox', label: 'Hurry (+1)' },
|
|
{ type: 'header', label: '---------------' }
|
|
],
|
|
buttons: [
|
|
{ label: 'Apply', value: 'apply' },
|
|
{ label: 'Apply with Raise', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
}
|
|
this.menuOptions = {
|
|
title: `${this.name} Effect`,
|
|
defaultButton: 'Cancel',
|
|
options: {}
|
|
}
|
|
this.inputs = []
|
|
this.buttons = null
|
|
}
|
|
|
|
get name () {
|
|
return 'Unknown Power'
|
|
}
|
|
|
|
get durationRounds () {
|
|
return 5
|
|
}
|
|
|
|
async powerEffect () {
|
|
this.prepMenu()
|
|
const { buttons, inputs } = await shim.warpgateMenu(
|
|
this.menuData, this.menuOptions)
|
|
this.buttons = buttons
|
|
this.inputs = inputs
|
|
if (this.buttons && this.buttons !== 'cancel') {
|
|
this.globalModifierEffects()
|
|
await this.prepResult()
|
|
await this.applyResult()
|
|
}
|
|
}
|
|
|
|
async prepResult () {
|
|
}
|
|
|
|
async applyResult () {
|
|
for (const target of this.targets) {
|
|
shim.applyActiveEffects(target, this.effectDocs)
|
|
}
|
|
}
|
|
|
|
static modEffectDoc (icon, name, key, value, durationRounds) {
|
|
return shim.createEffectDocument(icon, name, durationRounds, [
|
|
{
|
|
key,
|
|
mode: CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD,
|
|
value,
|
|
priority: 0
|
|
}
|
|
])
|
|
}
|
|
|
|
static glow (durationRounds) {
|
|
return PowerEffect.modEffectDoc(
|
|
'icons/magic/light/orb-shadow-blue.webp',
|
|
'Glow', '@Skill{Stealth}[system.die.modifier]', -2, durationRounds)
|
|
}
|
|
|
|
static shroud (durationRounds) {
|
|
return PowerEffect.modEffectDoc(
|
|
'icons/magic/perception/shadow-stealth-eyes-purple.webp',
|
|
'Shroud', '@Skill{Stealth}[system.die.modifier]', 1, durationRounds)
|
|
}
|
|
|
|
static hinder (durationRounds) {
|
|
return PowerEffect.modEffectDoc(
|
|
'icons/magic/control/debuff-chains-shackle-movement-red.webp',
|
|
'Hinder', 'system.stats.speed.value', -2, durationRounds)
|
|
}
|
|
|
|
static hurry (durationRounds) {
|
|
return PowerEffect.modEffectDoc(
|
|
'icons/skills/movement/feet-winged-sandals-tan.webp',
|
|
'Hurry', 'system.stats.speed.value', 2, durationRounds)
|
|
}
|
|
|
|
globalModifierEffects () {
|
|
this.inputIndex = 8
|
|
if (this.inputs[3]) { // glow
|
|
this.effectDocs.push(PowerEffect.glow(this.durationRounds))
|
|
}
|
|
if (this.inputs[4]) { // shroud
|
|
this.effectDocs.push(PowerEffect.shroud(this.durationRounds))
|
|
}
|
|
if (this.inputs[5]) { // hinder
|
|
this.effectDocs.push(PowerEffect.hinder(this.durationRounds))
|
|
}
|
|
if (this.inputs[6]) { // hurry
|
|
this.effectDocs.push(PowerEffect.hurry(this.durationRounds))
|
|
}
|
|
}
|
|
}
|
|
|
|
class TargetedPowerEffect extends PowerEffect {
|
|
constructor (token, targets) {
|
|
super(token, targets)
|
|
const targetList = this.targets.map(t => t.name).join(', ')
|
|
this.menuData.inputs[1] = {
|
|
type: 'info',
|
|
label: `Apply ${this.name} Effect to ${targetList}`
|
|
}
|
|
}
|
|
|
|
async powerEffect () {
|
|
if (this.targets.length < 1) {
|
|
shim.notifications.error(`No target selected for ${this.name}`)
|
|
return
|
|
}
|
|
super.powerEffect()
|
|
}
|
|
}
|
|
|
|
class BlindEffect extends TargetedPowerEffect {
|
|
async prepMenu (token, targets) {
|
|
this.menuData.inputs.push({
|
|
type: 'checkbox',
|
|
label: 'Strong (+1 point)',
|
|
options: false
|
|
})
|
|
}
|
|
|
|
get name () {
|
|
return 'Blind'
|
|
}
|
|
|
|
get durationRounds () {
|
|
return 1
|
|
}
|
|
|
|
async prepResult () {
|
|
const raise = (this.buttons === 'raise')
|
|
const strong = !!this.inputs[this.inputIndex]
|
|
const icon = 'icons/skills/wounds/injury-eyes-blood-red.webp'
|
|
const changes = [
|
|
{
|
|
key: 'system.stats.globalMods.trait',
|
|
mode: CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD,
|
|
value: '-2',
|
|
priority: 0
|
|
}
|
|
]
|
|
this.effectDocs.push(
|
|
shim.createEffectDocument(
|
|
icon, `minor Blindness (Vigor ${strong ? '-2 ' : ''}ends)`,
|
|
this.durationRounds, changes))
|
|
if (raise) {
|
|
this.effectDocs.push(
|
|
shim.createEffectDocument(
|
|
icon, `major Blindness (Vigor ${strong ? '-2 ' : ''}ends)`,
|
|
this.durationRounds, changes)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
class BoostLowerTraitEffect extends TargetedPowerEffect {
|
|
get name () {
|
|
return 'Boost/Lower Trait'
|
|
}
|
|
|
|
get durationRounds () {
|
|
if (!this.inputs) {
|
|
return 1
|
|
}
|
|
if (this.inputs[this.inputs.length - 4]) { // Boost
|
|
return 5
|
|
}
|
|
return 1 // Lower
|
|
}
|
|
|
|
async prepMenu () {
|
|
let traitOptions = ['Agility', 'Smarts', 'Spirit', 'Strength', 'Vigor']
|
|
const allSkills = []
|
|
const traits = {}
|
|
for (const traitName of traitOptions) {
|
|
const lower = traitName.toLowerCase()
|
|
traits[traitName] = {
|
|
name: traitName,
|
|
type: 'attribute',
|
|
modkey: `system.attributes.${lower}.die.modifier`,
|
|
diekey: `system.attributes.${lower}.die.sides`
|
|
}
|
|
}
|
|
for (const token of this.targets) {
|
|
const skills = token.actor.items.filter(item => item.type === 'skill')
|
|
for (const skill of skills) {
|
|
const name = skill.name
|
|
traits[name] = {
|
|
name,
|
|
type: 'skill',
|
|
modkey: `@Skill{${name}}[system.die.modifier]`,
|
|
diekey: `@Skill{${name}}[system.die.sides]`
|
|
}
|
|
if (name !== 'Unskilled' && !allSkills.find(v => v === name)) {
|
|
allSkills.push(name)
|
|
}
|
|
}
|
|
traitOptions = traitOptions.concat(allSkills.sort())
|
|
}
|
|
this.menuData.inputs = this.menuData.inputs.concat(
|
|
{ type: 'select', label: 'Trait', options: traitOptions },
|
|
{ type: 'info', label: 'Boost or Lower?' },
|
|
{ type: 'radio', label: 'Boost', options: ['isBoost', true] },
|
|
{ type: 'radio', label: 'Lower', options: ['isBoost', false] },
|
|
{ type: 'checkbox', label: 'Greater', options: false },
|
|
{ type: 'checkbox', label: 'Strong (lower only)', options: false }
|
|
)
|
|
this.traits = traits
|
|
}
|
|
|
|
async prepResult () {
|
|
const raise = (this.buttons === 'raise')
|
|
const direction = this.inputs[this.inputs.length - 4] ? 'Boost' : 'Lower'
|
|
const durationRounds = (direction === 'Boost' ? 5 : 1)
|
|
const icon = (direction === 'Boost'
|
|
? 'icons/magic/life/cross-embers-glow-yellow-purple.webp'
|
|
: 'icons/magic/movement/chevrons-down-yellow.webp')
|
|
const trait = this.traits[this.inputs[this.inputIndex]]
|
|
const greater = !!this.inputs[this.inputIndex + 4]
|
|
const strong = !!this.inputs[this.inputIndex + 5]
|
|
|
|
let namePart = `${direction} ${trait.name}`
|
|
const mods = []
|
|
if (direction === 'Lower') {
|
|
mods.push(`Spirit${strong ? '-2' : ''} ends`)
|
|
}
|
|
if (greater) {
|
|
mods.push('greater')
|
|
}
|
|
if (mods.length > 0) {
|
|
namePart = `${namePart} (${mods.join(', ')})`
|
|
}
|
|
const mode = CONST.FOUNDRY.ACTIVE_EFFECT_MODES.ADD
|
|
const modValue = (direction === 'Boost' ? '+2' : '-2')
|
|
const minorEffect = shim.createEffectDocument(
|
|
icon, `minor ${namePart}`, durationRounds, [
|
|
{ key: trait.diekey, mode, value: modValue, priority: 0 }
|
|
])
|
|
if (direction === 'Lower' && greater) {
|
|
minorEffect.changes.push({ key: trait.modkey, mode, value: modValue, priority: 0 })
|
|
}
|
|
const majorEffect = shim.createEffectDocument(
|
|
icon, `major ${namePart}`, durationRounds, [
|
|
{ key: trait.diekey, mode, value: modValue, priority: 0 }
|
|
])
|
|
this.effectDocs.push(minorEffect)
|
|
if (raise) { this.effectDocs.push(majorEffect) }
|
|
}
|
|
}
|
|
|
|
class ConfusionEffect extends TargetedPowerEffect {
|
|
get name () {
|
|
return 'Confusion'
|
|
}
|
|
|
|
get durationRounds () {
|
|
return 1
|
|
}
|
|
|
|
prepMenu () {
|
|
this.menuData.inputs.push(
|
|
{ type: 'checkbox', label: 'Greater (adds Shaken)', options: false })
|
|
this.menuData.buttons = [
|
|
{ label: 'Distracted', value: 'distracted' },
|
|
{ label: 'Vulnerable', value: 'vulnerable' },
|
|
{ label: 'Raise (both)', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
}
|
|
|
|
prepResult () {
|
|
const greater = !!this.inputs[this.inputIndex]
|
|
if (this.buttons === 'distracted' || this.buttons === 'raise') {
|
|
this.effectDocs.push(shim.getStatus('SWADE.Distr', 'Distracted'))
|
|
}
|
|
if (this.buttons === 'vulnerable' || this.buttons === 'raise') {
|
|
this.effectDocs.push(shim.getStatus('SWADE.Vuln', 'Vulnerable'))
|
|
}
|
|
if (greater) {
|
|
this.effectDocs.push(shim.getStatus('SWADE.Shaken', 'Shaken'))
|
|
}
|
|
}
|
|
}
|
|
|
|
class DeflectionEffect extends TargetedPowerEffect {
|
|
get name () {
|
|
return 'Deflection'
|
|
}
|
|
|
|
get durationRounds () {
|
|
return 5
|
|
}
|
|
|
|
prepMenu () {
|
|
this.menuData.buttons = [
|
|
{ label: 'Melee', value: 'melee' },
|
|
{ label: 'Ranged', value: 'ranged' },
|
|
{ label: 'Raise (both)', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
}
|
|
|
|
prepResult () {
|
|
const effectName = `Deflection (${this.buttons === 'raise' ? 'all' : this.buttons})`
|
|
const icon = 'icons/magic/defensive/shield-barrier-deflect-teal.webp'
|
|
this.effectDocs.push(shim.createEffectDocument(icon, effectName, this.durationRounds))
|
|
}
|
|
}
|
|
|
|
class EntangleEffect extends TargetedPowerEffect {
|
|
get name () {
|
|
return 'Entangle'
|
|
}
|
|
|
|
get durationRounds () {
|
|
return 1
|
|
}
|
|
|
|
prepMenu () {
|
|
this.menuData.inputs = this.menuData.inputs.concat([
|
|
{ type: 'radio', label: 'Not Damaging', options: ['dmg', true] },
|
|
{ type: 'radio', label: 'Damaging', options: ['dmg', false] },
|
|
{ type: 'radio', label: 'Deadly', options: ['dmg', false] },
|
|
{ type: 'checkbox', label: 'Tough', options: false }
|
|
])
|
|
this.menuData.buttons = [
|
|
{ label: 'Entangled', value: 'apply' },
|
|
{ label: 'Bound (raise)', value: 'raise' },
|
|
{ label: 'Cancel', value: 'cancel' }
|
|
]
|
|
}
|
|
|
|
prepResult () {
|
|
const damage = (this.inputs[this.inputIndex + 1]
|
|
? '2d4'
|
|
: (this.inputs[this.inputIndex + 2] ? '2d6' : null))
|
|
const tough = !!this.inputs[this.inputIndex + 3]
|
|
const effectSearch = (this.buttons === 'raise' ? 'SWADE.Bound' : 'SWADE.Entangled')
|
|
const effectName = (this.buttons === 'raise' ? 'Bound' : 'Entangled')
|
|
const effect = shim.getStatus(effectSearch, effectName)
|
|
const extraIcon = 'icons/magic/nature/root-vine-barrier-wall-brown.webp'
|
|
const extraEffect = shim.createEffectDocument(extraIcon,
|
|
'Entangle Modifier', this.durationRounds, [])
|
|
if (damage) {
|
|
extraEffect.name = `${extraEffect.name} - ${damage} dmg`
|
|
}
|
|
if (tough) {
|
|
extraEffect.name = `Tough ${extraEffect.name}`
|
|
}
|
|
this.effectDocs.push(effect)
|
|
if (damage || tough) {
|
|
this.effectDocs.push(extraEffect)
|
|
}
|
|
}
|
|
}
|
|
|
|
const PowerClasses = {
|
|
blind: BlindEffect,
|
|
'boost/lower trait': BoostLowerTraitEffect,
|
|
'boost trait': BoostLowerTraitEffect,
|
|
confusion: ConfusionEffect,
|
|
deflection: DeflectionEffect,
|
|
entangle: EntangleEffect,
|
|
'lower trait': BoostLowerTraitEffect
|
|
}
|
|
|
|
export async function powerEffects (options = {}) {
|
|
const token = 'token' in options ? options.token : []
|
|
const targets = 'targets' in options ? Array.from(options.targets) : []
|
|
const item = 'item' in options ? options.item : null
|
|
const name = 'name' in options ? options.name : (item !== null ? item.name : null)
|
|
|
|
const lcName = name.toLowerCase()
|
|
for (const name in PowerClasses) {
|
|
if (lcName.includes(name)) {
|
|
const runner = new PowerClasses[name](token, targets)
|
|
runner.powerEffect()
|
|
return
|
|
}
|
|
}
|
|
shim.notifications.error(`No power effect found for ${name}`)
|
|
}
|