diff --git a/src/module.json b/src/module.json index 6faa777..1b6f207 100644 --- a/src/module.json +++ b/src/module.json @@ -142,7 +142,7 @@ "path": "lang/en.json" } ], - "socket": false, + "socket": true, "manifest": "https://git.bloy.org/foundryvtt/swade-mb-helpers/raw/branch/main/module.json", "download": "https://host/path/to/0.0.0.zip", "library": false diff --git a/src/module/api.js b/src/module/api.js index dde9932..f185e74 100644 --- a/src/module/api.js +++ b/src/module/api.js @@ -1,6 +1,5 @@ import { log, moduleHelpers } from './globals.js'; import { requestFearRollFromTokens, requestRollFromTokens } from './helpers.js'; -import { powerEffects } from './powerEffects.js'; import { powers } from './powers/powers.js'; export class api { @@ -14,8 +13,7 @@ export class api { game.modules.get(moduleName).api = { rulesVersion: moduleHelpers.rulesVersion, fearTable: moduleHelpers.fearTableHelper, - powerEffects, - powers, + powerEffects: powers, requestRollFromTokens, requestFearRollFromTokens, }; diff --git a/src/module/powerEffects.js b/src/module/powerEffects.js deleted file mode 100644 index 1b48eef..0000000 --- a/src/module/powerEffects.js +++ /dev/null @@ -1,1763 +0,0 @@ -/* globals warpgate */ -import { log, moduleHelpers } from './globals.js' -import { requestFearRollFromTokens, requestRollFromTokens } from './helpers.js' - -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 this.baseDurationRounds - } - - get baseDurationRounds () { - return 5 - } - - async powerEffect () { - try { - await this.prepMenu() - } catch (e) { - log('Error preparing menu for power effect: ' + e.toString()) - return - } - const { buttons, inputs } = await warpgate.menu( - 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 prepMenu () { - } - - async prepResult () { - } - - async applyResult () { - for (const target of this.targets) { - moduleHelpers.applyActiveEffects(target, this.effectDocs) - } - } - - static modEffectDoc (icon, name, key, value, durationRounds) { - return moduleHelpers.createEffectDocument(icon, name, durationRounds, [ - { - key, - mode: foundry.CONST.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) { - ui.notifications.error(`No target selected for ${this.name}`) - return - } - super.powerEffect() - } -} - -class LingeringDamagePowerEffect extends TargetedPowerEffect { - get baseDurationRounds () { - return 1 - } - - async prepMenu () { - this.menuData.inputs.splice(this.menuData.inputs.length - 1, 0, { - type: 'checkbox', label: 'Lingering Damage (+2)' - }) - } - - globalModifierEffects () { - super.globalModifierEffects() - this.inputIndex += 1 - if (this.inputs[7]) { // lingering damage - const doc = moduleHelpers.createEffectDocument( - 'icons/magic/death/skull-poison-green.webp', - `Lingering Damage (${this.name})`, - 1 - ) - doc.flags.swade.expiration = CONFIG.SWADE.CONST.STATUS_EFFECT_EXPIRATION.StartOfTurnPrompt - this.effectDocs.push(doc) - } - } -} - -class ArcaneProtectionEffect extends TargetedPowerEffect { - get name () { - return 'Arcane Protection' - } - - get baseDurationRounds () { - return 5 - } - - async prepMenu () { - this.menuData.inputs.push( - { type: 'checkbox', label: 'Greater', options: false }) - } - - async prepResult () { - const greater = !!this.inputs[this.inputIndex] - const raise = this.buttons === 'raise' - const amount = (raise ? -4 : -2) + (greater ? -2 : 0) - const icon = 'icons/magic/defensive/shield-barrier-flaming-pentagon-blue.webp' - const name = `${greater ? 'Greater ' : ''}Arcane Protection (${raise ? 'major, ' : ''}${amount})` - this.effectDocs.push( - moduleHelpers.createEffectDocument(icon, name, this.durationRounds, [])) - } -} - -class BlastEffect extends LingeringDamagePowerEffect { - get name () { - return 'Blast' - } -} - -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 baseDurationRounds () { - 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: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, - value: '-2', - priority: 0 - } - ] - this.effectDocs.push( - moduleHelpers.createEffectDocument( - icon, `minor Blindness (Vigor ${strong ? '-2 ' : ''}ends)`, - this.durationRounds, changes)) - if (raise) { - this.effectDocs.push( - moduleHelpers.createEffectDocument( - icon, `major Blindness (Vigor ${strong ? '-2 ' : ''}ends)`, - this.durationRounds, changes) - ) - } - } -} - -class BurrowEffect extends TargetedPowerEffect { - get name () { return 'Burrow' } - get baseDurationRounds () { return 5 } - - async prepResult () { - const raise = (this.buttons === 'raise') - const icon = 'icons/magic/earth/projectile-stone-landslide.webp' - this.effectDocs.push( - moduleHelpers.createEffectDocument( - icon, - `${raise ? 'major' : 'minor'} ${this.name}`, - this.durationRounds, - []) - ) - } -} - -class BoltEffect extends LingeringDamagePowerEffect { - get name () { - return 'Bolt' - } -} - -class BoostLowerTraitEffect extends TargetedPowerEffect { - get name () { - return 'Boost/Lower Trait' - } - - get baseDurationRounds () { - 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 = foundry.CONST.ACTIVE_EFFECT_MODES.ADD - const modValue = (direction === 'Boost' ? '+2' : '-2') - const minorEffect = moduleHelpers.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 = moduleHelpers.createEffectDocument( - icon, `major ${namePart}`, durationRounds, [ - { key: trait.diekey, mode, value: modValue, priority: 0 } - ]) - this.effectDocs.push(minorEffect) - if (raise) { this.effectDocs.push(majorEffect) } - } -} - -class BurstEffect extends LingeringDamagePowerEffect { - get name () { - return 'Burst' - } -} - -class ConfusionEffect extends TargetedPowerEffect { - get name () { - return 'Confusion' - } - - get baseDurationRounds () { - return 1 - } - - async 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' } - ] - } - - async prepResult () { - const greater = !!this.inputs[this.inputIndex] - if (this.buttons === 'distracted' || this.buttons === 'raise') { - this.effectDocs.push(moduleHelpers.getStatus('SWADE.Distr', 'Distracted')) - } - if (this.buttons === 'vulnerable' || this.buttons === 'raise') { - this.effectDocs.push(moduleHelpers.getStatus('SWADE.Vuln', 'Vulnerable')) - } - if (greater) { - this.effectDocs.push(moduleHelpers.getStatus('SWADE.Shaken', 'Shaken')) - } - } -} - -class DarksightEffect extends TargetedPowerEffect { - get name () { return 'Darksight' } - get baseDurationRounds () { return 600 } - async prepMenu () { - this.menuData.inputs.push( - { type: 'checkbox', label: '⭐ Greater (+2)', options: false }) - } - - async prepResult () { - const raise = this.buttons === 'raise' - const greater = !!this.inputs[this.inputIndex] - const icon = 'icons/magic/perception/eye-ringed-glow-angry-small-teal.webp' - this.effectDocs.push( - moduleHelpers.createEffectDocument( - icon, - `${raise ? 'major' : 'minor'} ${this.name}${greater ? ' (greater)' : ''}`, - this.durationRounds, - []) - ) - } -} - -class DisguiseEffect extends TargetedPowerEffect { - get name () { return 'Disguise' } - get baseDurationRounds () { return 100 } - - async prepResult () { - const raise = this.buttons === 'raise' - const icon = 'icons/skills/social/diplomacy-peace-alliance.webp' - this.effectDocs.push( - moduleHelpers.createEffectDocument( - icon, - `${raise ? 'major' : 'minor'} ${this.name}`, - this.durationRounds, - []) - ) - } -} - -class DeflectionEffect extends TargetedPowerEffect { - get name () { - return 'Deflection' - } - - get baseDurationRounds () { - return 5 - } - - async prepMenu () { - this.menuData.buttons = [ - { label: 'Melee', value: 'melee' }, - { label: 'Ranged', value: 'ranged' }, - { label: 'Raise (both)', value: 'raise' }, - { label: 'Cancel', value: 'cancel' } - ] - } - - async prepResult () { - const effectName = `Deflection (${this.buttons === 'raise' ? 'all' : this.buttons})` - const icon = 'icons/magic/defensive/shield-barrier-deflect-teal.webp' - this.effectDocs.push(moduleHelpers.createEffectDocument(icon, effectName, this.durationRounds)) - } -} - -class DetectConcealArcanaEffect extends TargetedPowerEffect { - get name () { return 'Detect/Conceal Arcana' } - - get baseDurationRounds () { - if (this.inputs?.[this.inputIndex + 2] === true) { - return 600 - } - return 5 - } - - async prepMenu () { - this.menuData.inputs = this.menuData.inputs.concat( - { type: 'info', label: 'Detect or Conceal?' }, - { type: 'radio', label: 'Detect', options: ['isDetect', true] }, - { type: 'radio', label: 'Conceal', options: ['isDetect', false] }, - { type: 'checkbox', label: 'Strong (+1, conceal only)', options: false } - ) - } - - async prepResult () { - const raise = (this.buttons === 'raise') - const isDetect = this.inputs[this.inputIndex + 1] === true - const strong = !isDetect && !!this.inputs[this.inputIndex + 3] - const icon = (isDetect - ? 'icons/magic/perception/third-eye-blue-red.webp' - : 'icons/magic/perception/silhouette-stealth-shadow.webp') - const name = `${raise ? 'major ' : ''}${isDetect ? 'Detect' : 'Conceal'} Arcana${strong ? ' (strong)' : ''}` - const effect = moduleHelpers.createEffectDocument(icon, name, this.durationRounds, []) - this.effectDocs.push(effect) - } -} - -class EntangleEffect extends TargetedPowerEffect { - get name () { - return 'Entangle' - } - - get baseDurationRounds () { - return 1 - } - - async 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' } - ] - } - - async 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 = moduleHelpers.getStatus(effectSearch, effectName) - const extraIcon = 'icons/magic/nature/root-vine-barrier-wall-brown.webp' - const extraEffect = moduleHelpers.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) - } - } -} - -class FearEffect extends TargetedPowerEffect { - get name () { - return 'Fear' - } - - get baseDurationRounds () { - return 1 - } - - async prepResult () { - this.raise = (this.buttons === 'raise') - } - - async applyResult () { - await super.applyResult() - await warpgate.wait(1000) - const options = { - title: 'Fear check!', - flavor: 'Failure: roll on the Fear Table if wildcard, Panicked if extra', - mods: [] - } - if (this.raise) { - options.fear = '-2' - } - await requestFearRollFromTokens(this.targets, options) - } -} - -class HavocEffect extends TargetedPowerEffect { - get name () { - return 'Havoc' - } - - get baseDurationRounds () { - return 1 - } - - async prepResult () { - this.raise = (this.buttons === 'raise') - this.effectDocs.unshift(moduleHelpers.getStatus('SWADE.Distr', 'Distracted')) - } - - async applyResult () { - await super.applyResult() - await warpgate.wait(1000) - - const resistMods = function (token) { - const mods = [] - if (token.actor.effects.find(e => e.name === 'Flying')) { - mods.push({ label: 'Flying', value: -2 }) - } - return mods - } - - const options = { - title: 'Resisting Havoc!', - flavour: 'Havoc!', - mods: [], - modCallback: resistMods - } - if (this.raise) { - options.mods.push({ label: 'vs. Raise', value: -2 }) - } - - await requestRollFromTokens(this.targets, 'attribute', 'strength', options) - } -} - -class IntangibilityEffect extends TargetedPowerEffect { - get name () { - return 'Intangility' - } - - get baseDurationRounds () { - if (!this.inputs) { - return 5 - } - if (this.inputs[this.inputs.length - 1]) { // Duration - return 50 - } - return 5 // no duration - } - - async prepMenu () { - this.menuData.inputs.push({ type: 'checkbox', label: 'Duration', options: false }) - this.menuData.buttons = [ - { label: 'Apply', value: 'apply' }, - { label: 'Cancel', value: 'cancel' } - ] - } - - async prepResult () { - const icon = 'icons/magic/control/debuff-energy-hold-levitate-blue-yellow.webp' - const effect = moduleHelpers.createEffectDocument(icon, this.name, this.durationRounds, []) - this.effectDocs.push(effect) - } -} - -class InvisibilityEffect extends TargetedPowerEffect { - get name () { - return 'Invisiblity' - } - - get baseDurationRounds () { - if (!this.inputs) { - return 5 - } - if (this.inputs[this.inputs.length - 1]) { // Duration - return 50 - } - return 5 // no duration - } - - async prepMenu () { - this.menuData.inputs.push({ type: 'checkbox', label: 'Duration', options: false }) - } - - async prepResult () { - const effect = moduleHelpers.getStatus('EFFECT.StatusInvisible', 'Invisible') - effect.duration = { rounds: this.durationRounds } - this.effectDocs.push(effect) - } -} - -class ProtectionEffect extends TargetedPowerEffect { - get name () { - return 'Protection' - } - - get baseDurationRounds () { - return 5 - } - - async prepMenu () { - this.menuData.buttons = [ - { label: 'Apply (+2 armor)', value: 'apply' }, - { label: 'Apply with raise (+2 toughness)', value: 'raise' }, - { label: 'Cancel', value: 'cancel' } - ] - } - - async prepResult () { - const effect = moduleHelpers.getStatus('SWADE.Protection', 'Protection') - effect.duration = { rounds: this.durationRounds } - const mode = foundry.CONST.ACTIVE_EFFECT_MODES.ADD - effect.changes = [ - { key: 'system.stats.toughness.armor', mode, value: 2, priority: 0 } - ] - if (this.buttons === 'raise') { - effect.changes[0].key = 'system.stats.toughness.value' - } - this.effectDocs.push(effect) - } -} - -class ShapeChangeEffect extends TargetedPowerEffect { - get actorFolderBase () { - return 'Morphables' - } - - get tempActorFolder () { - return `${this.actorFolderBase}/Changed` - } - - get actorFolder () { - return `${this.actorFolderBase}/${this.name}` - } - - get name () { - return 'Shape Change' - } - - get baseDurationRounds () { - if (this.increasedDuration ?? false) { - return 50 - } - return 5 - } - - async prepFolders () { - const folders = [] - const folderNames = [ - this.actorFolder, - `${this.actorFolder} - Default`, - `${this.actorFolder}/Default`, - `${this.actorFolder} - ${this.token.name}`, - `${this.actorFolder} - ${this.token.actor.name}`, - `${this.actorFolder}/${this.token.name}`, - `${this.actorFolder}/${this.token.actor.name}` - ] - for (const folderName of folderNames) { - const folder = moduleHelpers.getActorFolderByPath(folderName) - if (folder) { - log(`Found actor folder ${folderName}`) - folders.push(folder) - } - } - if (folders.length > 1) { - folders.shift() - } - return folders - } - - async prepActors () { - const folders = await this.prepFolders() - const actors = {} - for (const folder of folders) { - const folderActors = moduleHelpers.getActorsInFolder(folder) - for (const key in folderActors) { - actors[key] = folderActors[key] - } - } - return actors - } - - async prepMenu () { - const actors = await this.prepActors() - this.cancel = false - if (Object.keys(actors).length < 1) { - ui.notifications.error('No summonables found') - this.cancel = true - } - - function actorData (key) { - return { - value: actors[key].id, - html: key - } - } - - this.summonableActors = actors - - this.menuData.inputs = this.menuData.inputs.concat([ - { - type: 'select', - label: 'Turn into creature', - options: Object.keys(actors).filter( - k => !k.includes('_template')).sort().map(actorData) - }, { - type: 'checkbox', - label: 'Duration (+1, rounds to minutes)', - options: false - } - ]) - } - - async prepResult () { - this.raise = (this.buttons === 'raise') - this.actorId = (this.inputs[this.inputIndex]) - this.increasedDuration = (!!this.inputs[this.inputIndex + 1]) - this.actor = game.actors.get(this.actorId) - this.icon = this.targets[0].document.texture.src - const targetActor = this.targets[0].actor - this.protoDoc = await this.actor.getTokenDocument() - this.spawnOptions = { - controllingActor: this.targets[0].actor, - duplicates: 1, - updateOpts: { - embedded: { - Item: { - renderSheet: null - } - } - }, - crosshairs: { - rememberControlled: true - } - } - const effectChanges = [] - if (this.raise) { - for (const stat of ['vigor', 'strength']) { - effectChanges.push({ - key: `system.attributes.${stat}.die.sides`, - mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, - value: '+2', - priority: 0 - }) - } - } - this.effectDocs.push( - moduleHelpers.createEffectDocument( - this.icon, - `Shape Change into ${this.protoDoc.name}`, - this.durationRounds, effectChanges) - ) - - this.spawnMutation = { - actor: { - name: `${this.targets[0].actor.name} (${this.actor.name} form)`, - system: { - attributes: { - smarts: { die: targetActor.system.attributes.smarts.die }, - spirit: { die: targetActor.system.attributes.spirit.die } - }, - wildcard: targetActor.system.wildcard - } - }, - token: { - flags: { - 'swade-mb-helpers.shapeChange.srcTokenId': this.targets[0].id - }, - actorLink: false, - name: `${this.targets[0].name} (${this.protoDoc.name} form) `, - elevation: this.targets[0].document.elevation, - disposition: this.targets[0].document.disposition, - sight: { - enabled: true - } - }, - embedded: { ActiveEffect: {}, Item: {} } - } - for (const doc of this.effectDocs) { - this.spawnMutation.embedded.ActiveEffect[doc.name] = doc - } - for (const doc of this.targets[0].actor.effects) { - this.spawnMutation.embedded.ActiveEffect[doc.name] = this.targets[0].actor.getEmbeddedDocument('ActiveEffect', doc.id) - } - for (const item of targetActor.items) { - if (item.type === 'skill' && ['smarts', 'spirit'].includes(item.system.attribute)) { - const doc = await this.targets[0].actor.getEmbeddedDocument('Item', item.id) - this.spawnMutation.embedded.Item[item.name] = doc - } - if (['power', 'edge', 'hindrance', 'action'].includes(item.type)) { - const doc = await this.targets[0].actor.getEmbeddedDocument('Item', item.id) - this.spawnMutation.embedded.Item[item.name] = doc - } - } - } - - async applyResult () { - log('protoDoc', this.protoDoc) - log('spawnOptions', this.spawnOptions) - log('spawnMutation', this.spawnMutation) - const newTokenId = (await warpgate.spawnAt( - this.targets[0].center, - this.protoDoc, - this.spawnMutation, - {}, - this.spawnOptions - ))[0] - await this.targets[0].document.setFlag('swade-mb-helpers', 'shapeChange', { - toId: newTokenId, - saved: { - alpha: this.targets[0].document.alpha, - hidden: this.targets[0].document.hidden, - x: this.targets[0].document.x, - y: this.targets[0].document.y, - elevation: this.targets[0].document.elevation - } - }) - await this.targets[0].document.update({ - hidden: true, - alpha: 0.05 - }) - } -} - -class SmiteEffect extends TargetedPowerEffect { - get name () { - return 'Smite' - } - - get baseDurationRounds () { - return 5 - } - - async prepMenu () { - this.menuData.inputs.push({ - type: 'checkbox', label: 'Greater', options: false - }) - const tokenWeapons = {} - let index = this.menuData.inputs.length - 1 - for (const token of this.targets) { - index += 2 - tokenWeapons[token.id] = index - this.menuData.inputs.push({ type: 'info', label: `

${token.name}

` }) - const weapons = token.actor.items.filter(i => i.type === 'weapon').map( - i => { return { value: i.name, html: i.name } }) - weapons.unshift({ value: '', html: 'None' }) - this.menuData.inputs.push({ type: 'select', label: token.name, options: weapons }) - } - this.tokenWeapons = tokenWeapons - } - - async prepResult () { - this.baseEffect = moduleHelpers.getStatus('SWADE.Smite', 'Smite') - } - - async applyResult () { - const mode = foundry.CONST.ACTIVE_EFFECT_MODES.ADD - const raise = (this.buttons === 'raise') - const greater = !!this.inputs[this.inputIndex] - const changeValue = (greater ? (raise ? '+6' : '+4') : (raise ? '+4' : '+2')) - const changeKey = 'system.stats.globalMods.damage' - for (const token of this.targets) { - const weaponName = this.inputs[this.tokenWeapons[token.id]] - const effectName = `Smite (${weaponName})` - const changes = [ - { key: changeKey, mode, value: changeValue, priority: 0 } - ] - this.baseEffect.changes = changes - this.baseEffect.name = effectName - await moduleHelpers.applyActiveEffects(token, [this.baseEffect].concat(this.effectDocs)) - } - } -} - -class SlothSpeedEffect extends TargetedPowerEffect { - get name () { - return 'Sloth/Speed' - } - - get baseDurationRounds () { - if (this.inputs?.[this.inputIndex + 1] === true) { - return 1 - } - return 5 - } - - async prepMenu () { - this.menuData.inputs = this.menuData.inputs.concat( - { type: 'info', label: 'Sloth or Speed?' }, - { type: 'radio', label: 'Sloth', options: ['isSloth', true] }, - { type: 'radio', label: 'Speed', options: ['isSloth', false] }, - { type: 'checkbox', label: 'Dash (+2, speed only)', options: false }, - { type: 'checkbox', label: 'Quickness (+2, speed only)', options: false }, - { type: 'checkbox', label: 'Strong (+1, sloth only)', options: false } - ) - } - - async prepResult () { - const raise = (this.buttons === 'raise') - const isSloth = this.inputs[this.inputIndex + 1] === true - const icon = (isSloth - ? 'icons/magic/control/debuff-chains-shackles-movement-blue.webp' - : 'icons/skills/movement/feet-winged-sandals-tan.webp' - ) - const dash = !isSloth && !!this.inputs[this.inputIndex + 3] - const quickness = !isSloth && !!this.inputs[this.inputIndex + 4] - const strong = isSloth && !!this.inputs[this.inputIndex + 4] - const nameMods = [] - if (raise) { nameMods.push('Major') } - if (dash) { nameMods.push('Dash') } - if (quickness) { nameMods.push('Quickness') } - if (strong) { nameMods.push('Strong') } - const nameModifier = ( - `${nameMods.length > 0 ? ' (' : ''}` + - `${nameMods.join(', ')}${nameMods.length > 0 ? ')' : ''}`) - const name = `${isSloth ? 'Sloth' : 'Speed'}${nameModifier}` - const effect = moduleHelpers.createEffectDocument( - icon, name, this.durationRounds, [ - { - key: 'system.stats.speed.value', - mode: foundry.CONST.ACTIVE_EFFECT_MODES.MULTIPLY, - value: (isSloth ? 0.5 : 2), - priority: 0 - }]) - this.effectDocs.push(effect) - } -} - -class SummonEffect extends PowerEffect { - ICON = 'icons/magic/symbols/runes-triangle-blue.webp' - - get actorFolderBase () { - return 'Summonables' - } - - get actorFolder () { - return `${this.actorFolderBase}/${this.name}` - } - - get name () { - return 'Summon Creature' - } - - get baseDurationRounds () { - return 5 - } - - async prepFolders () { - const folders = [] - const folderNames = [ - this.actorFolder, - `${this.actorFolder} - Default`, - `${this.actorFolder}/Default`, - `${this.actorFolder} - ${this.token.name}`, - `${this.actorFolder} - ${this.token.actor.name}`, - `${this.actorFolder}/${this.token.name}`, - `${this.actorFolder}/${this.token.actor.name}` - ] - for (const folderName of folderNames) { - const folder = moduleHelpers.getActorFolderByPath(folderName) - if (folder) { - log(`Found actor folder ${folderName}`) - folders.push(folder) - } - } - if (folders.length > 1) { - folders.shift() - } - return folders - } - - async prepActors () { - const folders = await this.prepFolders() - const actors = {} - for (const folder of folders) { - const folderActors = moduleHelpers.getActorsInFolder(folder) - for (const key in folderActors) { - actors[key] = folderActors[key] - } - } - return actors - } - - async prepMenu () { - this.menuData.inputs[1].label = `${this.token.name} is summoning...` - this.menuData.buttons = this.menuData.buttons.filter(b => b.value !== 'raise') - const actors = await this.prepActors() - if (Object.keys(actors).length < 1) { - ui.notifications.error('No summonables found') - throw new Error('No summonables found') - } - - function actorData (key) { - return { - value: actors[key].id, - html: key - } - } - - this.summonableActors = actors - - this.menuData.inputs = this.menuData.inputs.concat([ - { - type: 'select', - label: 'Creature to summon', - options: Object.keys(actors).filter( - k => !k.includes('_template')).sort().map(actorData) - }, { - type: 'number', - label: 'Number to spawn (+half base cost per)', - options: 1 - }, { - type: 'checkbox', - label: 'Add Increased Trait(s)? (+1 per trait)', - options: false - } - ]) - } - - async prepResult () { - this.raise = (this.buttons === 'raise') - this.actorId = (this.inputs[this.inputIndex]) - this.number = (this.inputs[this.inputIndex + 1]) - this.actor = game.actors.get(this.actorId) - this.icon = this.actor.prototypeToken.texture.src - this.protoDoc = await this.actor.getTokenDocument() - this.increasedTrait = !!(this.inputs[this.inputIndex + 2]) - this.inputIndex += 3 - this.spawnOptions = { - controllingActor: this.token.actor, - duplicates: this.number, - updateOpts: { - embedded: { - Item: { - renderSheet: null - } - } - }, - crosshairs: { - icon: this.icon, - label: `Summon ${this.actor.name}`, - drawOutline: true, - rememberControlled: true - } - } - this.spawnMutation = { - actor: { - name: `${this.token.name}'s ${this.actor.name}` - }, - token: { - actorLink: false, - name: `${this.token.name}'s ${this.protoDoc.name}`, - disposition: this.token.document.disposition, - sight: { - enabled: true - } - }, - embedded: { ActiveEffect: {}, Item: {} } - } - if (this.raise && ('raise_template' in this.summonableActors)) { - const raiseTemplate = this.summonableActors.raise_template - for (const item of raiseTemplate.items) { - const raiseItemDoc = await raiseTemplate.getEmbeddedDocument('Item', item.id) - this.spawnMutation.embedded.Item[item.name] = raiseItemDoc - } - } - - for (const effectDocument of this.effectDocs) { - this.spawnMutation.embedded.ActiveEffect[effectDocument.name] = effectDocument - } - } - - async prepAdditional () { - if (!this.increasedTrait) { - return - } - const traitMenuOptions = { - title: `${this.name} Summon Trait Increase`, - defaultButton: 'Cancel', - options: {} - } - const skillSet = new Set() - for (const skill of this.actor.items.filter(i => i.type === 'skill')) { - skillSet.add(skill.name) - } - for (const item of Object.values(this.spawnMutation.embedded.Item).filter(i => i.type === 'skill')) { - skillSet.add(item.name) - } - const skillList = Array.from(skillSet) - const attrList = ['Agility', 'Smarts', 'Spirit', 'Strength', 'Vigor'] - skillList.sort() - const traitMenuData = { - inputs: [ - { type: 'header', label: 'Increase Attributes (+1 each)' } - ], - buttons: [ - { label: 'Apply', value: 'apply' }, - { label: 'Increase no traits', value: 'cancel' } - ] - } - traitMenuData.inputs = traitMenuData.inputs.concat( - attrList.map((x) => { return { type: 'checkbox', label: x, options: false } })) - traitMenuData.inputs.push({ type: 'header', label: 'Increase Skills (+1 each)' }) - traitMenuData.inputs = traitMenuData.inputs.concat( - skillList.map((x) => { return { type: 'checkbox', label: x, options: false } })) - const { buttons, inputs } = await warpgate.menu(traitMenuData, traitMenuOptions) - if (!buttons || buttons === 'cancel') { - return - } - const modKeys = [] - for (let i = 0; i < attrList.length; i++) { - if (inputs[i + 1]) { - modKeys.push(`system.attributes.${attrList[i].toLowerCase()}.die.sides`) - } - } - for (let i = 0; i < skillList.length; i++) { - if (inputs[i + 7]) { - modKeys.push(`@Skill{${skillList[i]}}[system.die.sides]`) - } - } - const effectDoc = moduleHelpers.createEffectDocument( - this.ICON, 'Increased Trait', this.durationRounds) - effectDoc.changes = modKeys.map(key => { - return { - key, mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, value: '+2', priority: 0 - } - }) - this.spawnMutation.embedded.ActiveEffect[effectDoc.name] = effectDoc - } - - async applyResult () { - await this.prepAdditional() - await warpgate.spawn(this.protoDoc, this.spawnMutation, {}, this.spawnOptions) - log('protoDoc', this.protoDoc) - log('spawnOptions', this.spawnOptions) - log('spawnMutation', this.spawnMutation) - } -} - -class SummonAllyEffect extends SummonEffect { - get name () { - return 'Summon Ally' - } - - async prepMenu () { - await super.prepMenu() - this.menuData.buttons = [ - this.menuData.buttons[0], - { label: 'Apply with Raise', value: 'raise' }, - this.menuData.buttons[1] - ] - this.menuData.inputs = this.menuData.inputs.concat([ - { - type: 'checkbox', - label: 'Bite/Claw (+1)', - options: false - }, { - type: 'checkbox', - label: 'Up to 3 Combat Edges (+1 per)', - options: false - }, { - type: 'checkbox', - label: 'Flight (+3)', - options: false - } - ]) - } - - async prepResult () { - await super.prepResult() - this.biteClaw = !!(this.inputs[this.inputIndex]) - this.combatEdge = !!(this.inputs[this.inputIndex + 1]) - this.flight = !!(this.inputs[this.inputIndex + 2]) - await this.prepMirrorSelf() - } - - async prepAdditional () { - await super.prepAdditional() - await this.prepBiteClaw() - await this.prepFlight() - await this.prepCombatEdge() - } - - async prepCombatEdge () { - if (!this.combatEdge || !('combat-edge_template' in this.summonableActors)) { - return - } - const template = this.summonableActors['combat-edge_template'] - const edges = template.items.filter(i => i.type === 'edge').map(i => i.name) - edges.sort() - edges.unshift('None') - const edgeMenuData = { - inputs: [ - { type: 'header', label: 'Choose Edges (+1 per choice)' }, - { type: 'select', label: 'Edge 1', options: edges }, - { type: 'select', label: 'Edge 2', options: edges }, - { type: 'select', label: 'Edge 3', options: edges } - ], - buttons: [ - { label: 'Apply', value: 'apply' }, - { label: 'Add no edges', value: 'cancel' } - ] - } - const edgeMenuOptions = { - title: `${this.name} Combat Edge Selection`, - defaultButton: 'Cancel', - options: {} - } - const { buttons, inputs } = await warpgate.menu(edgeMenuData, edgeMenuOptions) - if (!buttons || buttons === 'cancel') { - return - } - for (let i = 1; i <= 3; i++) { - if (inputs[i] === 'None') { - continue - } - const edge = template.items.getName(inputs[i]) - if (edge) { - const doc = await template.getEmbeddedDocument('Item', edge.id) - this.spawnMutation.embedded.Item[edge.name] = doc - } - } - } - - async prepBiteClaw () { - if (!this.biteClaw || !('bite-claw_template' in this.summonableActors)) { - return - } - const template = this.summonableActors['bite-claw_template'] - for (const item of template.items) { - const doc = await template.getEmbeddedDocument('Item', item.id) - this.spawnMutation.embedded.Item[item.name] = doc - } - } - - async prepFlight () { - if (!this.flight || !('flight_template' in this.summonableActors)) { - return - } - const template = this.summonableActors.flight_template - for (const item of template.items) { - const doc = await template.getEmbeddedDocument('Item', item.id) - this.spawnMutation.embedded.Item[item.name] = doc - } - for (const effect of template.effects.values()) { - const doc = ActiveEffect.fromSource(effect) - this.spawnMutation.embedded.ActiveEffect[effect.name] = doc - } - } - - async prepMirrorSelf () { - if (this.actor.name !== 'Mirror Self') { - return - } - const mirrorActor = this.token.actor - this.icon = mirrorActor.prototypeToken.texture.src - this.spawnMutation.actor.system = mirrorActor.system.clone({ - wildcard: false, - 'fatigue.value': 0, - 'wounds.value': 0, - 'wounds.max': 0, - 'bennies.max': 0, - 'bennies.value': 0 - }) - this.spawnMutation.actor.name = `Mirror ${mirrorActor.name}` - this.spawnMutation.actor.img = mirrorActor.img - this.spawnMutation.token.name = `Mirror ${this.token.name}` - this.spawnMutation.token.texture = { - src: this.token.document.texture.src, - scaleX: this.token.document.texture.scaleX * -1, - scaleY: this.token.document.texture.scaleY - } - this.spawnOptions.crosshairs.icon = this.icon - const effectChanges = [] - for (const mirrorItem of mirrorActor.items) { - if (mirrorItem.type === 'power' && - (mirrorItem.system?.swid === 'summon-ally' || mirrorItem.name === 'Summon Ally')) { - continue - } - if (['weapon', 'armor', 'consumable', 'gear'].includes(mirrorItem.type)) { - continue - } - this.spawnMutation.embedded.Item[mirrorItem.name] = await mirrorActor.getEmbeddedDocument( - 'Item', mirrorItem.id) - if (mirrorItem.type === 'skill') { - effectChanges.push({ - key: `@Skill{${mirrorItem.name}}[system.die.sides]`, - mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, - value: '-2', - priority: 0 - }) - } - } - this.spawnMutation.embedded.ActiveEffect['Mirror Self'] = - moduleHelpers.createEffectDocument(this.ICON, 'Mirror Self', - this.durationRounds, effectChanges) - } -} - -class SpiritualWeaponEffect extends SummonEffect { - get name () { - return 'Spiritual Weapon' - } - - async prepMenu () { - await super.prepMenu() - this.menuData.buttons = [ - this.menuData.buttons[0], - { label: 'Apply with Raise', value: 'raise' }, - this.menuData.buttons[1] - ] - this.menuData.inputs.pop() - this.menuData.inputs.pop() - this.menuData.inputs[this.menuData.inputs.length - 1].label = 'Weapon to summon' - const arcaneSkills = this.token.actor.items - .filter(i => i.type === 'skill').filter(s => ( - s.system.swid === 'faith' || - s.system.swid === 'spellcasting' || - s.system.swid === 'performance')).map(s => { - return { - value: s.system.swid, - html: s.name - } - }) - this.menuData.inputs.push( - { type: 'select', label: 'Arcane Skill', options: arcaneSkills } - ) - } - - async prepResult () { - this.raise = (this.buttons === 'raise') - this.actorId = this.inputs[this.inputIndex] - this.arcaneSkillSwid = this.inputs[this.inputIndex + 1] - this.actor = game.actors.get(this.actorId) - this.icon = 'icons/weapons/hammers/hammer-double-glowing-yellow.webp' - this.protoDoc = await this.actor.getTokenDocument() - this.spawnOptions = { - controllingActor: this.token.actor, - duplicates: 1, - updateOpts: { - embedded: { - Item: { renderSheet: null } - } - }, - crosshairs: { - icon: this.icon, - label: `Summon ${this.actor.name}`, - drawOutline: true, - rememberControlled: true - } - } - this.spawnMutation = { - actor: { - name: `${this.token.name}'s ${this.actor.name}`, - system: { - wildcard: this.token.actor.system.wildcard, - attributes: { - spirit: { - die: { - sides: this.token.actor.system.attributes.spirit.die.sides, - modifier: this.token.actor.system.attributes.spirit.die.modifier - }, - 'wild-die': { - sides: this.token.actor.system.attributes.spirit['wild-die'].sides - } - } - } - } - }, - token: { - actorLink: false, - name: `${this.token.name}'s ${this.protoDoc.name}`, - disposition: this.token.document.disposition, - sight: { enabled: false } - }, - embedded: { ActiveEffect: {}, Item: {} } - } - const weaponItemId = this.actor.items.find(i => - i.type === 'weapon' && i.system.swid === 'spiritual-weapon').id - const weaponDoc = this.actor.getEmbeddedDocument('Item', weaponItemId).clone() - const arcaneSkill = this.token.actor.items.find(i => i.type === 'skill' && i.system.swid === this.arcaneSkillSwid) - const arcaneDoc = this.token.actor.getEmbeddedDocument('Item', arcaneSkill.id).clone() - this.spawnMutation.embedded.Item[weaponDoc.name] = { - system: { - damage: '@spi+d4', - actions: { - trait: arcaneDoc.name - } - } - } - // weaponDoc.system.actions.trait = arcaneDoc.name - if (this.raise) { - this.spawnMutation.embedded.Item[weaponDoc.name].system.damage = '@spi+d6' - // weaponDoc.system.damage = '@spi+d6' - } - this.spawnMutation.embedded.Item[arcaneDoc.name] = arcaneDoc - // this.spawnMutation.embedded.Item[weaponDoc.name] = weaponDoc - for (const effectDocument of this.effectDocs) { - this.spawnMutation.embedded.ActiveEffect[effectDocument.name] = effectDocument - } - } -} - -class SummonAnimalEffect extends SummonEffect { - get name () { - return 'Summon Animal' - } -} - -class SummonMonsterEffect extends SummonEffect { - get name () { - return 'Summon Monster' - } -} - -class SummonNaturesAllyEffect extends SummonEffect { - get name () { - return "Summon Nature's Ally" - } -} - -class SummonPlanarAllyEffect extends SummonEffect { - get name () { - return 'Summon Planar Ally' - } -} - -class SummonUndeadEffect extends SummonEffect { - get name () { - return 'Summon Undead' - } -} - -class ZombieEffect extends SummonEffect { - get name () { - return 'Zombie' - } - - async prepMenu () { - await super.prepMenu() - this.menuData.buttons = [ - this.menuData.buttons[0], - { label: 'Apply with Raise', value: 'raise' }, - this.menuData.buttons[1] - ] - this.menuData.inputs.pop() - this.menuData.inputs = this.menuData.inputs.concat([ - { - type: 'checkbox', - label: 'Armed (Hand Weapon (Str+d6) or Ranged Weapon (2d6)', - options: false - }, { - type: 'checkbox', - label: '+2 Armor', - options: false - }, { - type: 'info', - label: 'Skeletal creatures +1 per zombie' - } - ]) - } - - async prepResult () { - this.raise = (this.buttons === 'raise') - this.actorId = this.inputs[this.inputIndex] - this.number = this.inputs[this.inputIndex + 1] - this.actor = game.actors.get(this.actorId) - this.icon = this.actor.prototypeToken.texture.src - this.protoDoc = await this.actor.getTokenDocument() - this.increasedTrait = this.raise - this.armed = this.inputs[this.inputIndex + 2] - this.armor = this.inputs[this.inputIndex + 3] - this.spawnOptions = { - controllingActor: this.token.actor, - duplicates: this.number, - updateOpts: { - embedded: { - Item: { renderSheet: null } - } - }, - crosshairs: { - icon: this.icon, - label: `Summon ${this.actor.name}`, - drawOutline: true, - rememberControlled: true - } - } - this.spawnMutation = { - actor: { - name: `${this.token.name}'s ${this.actor.name}` - }, - token: { - actorLink: false, - name: `${this.token.name}'s ${this.protoDoc.name}`, - disposition: this.token.document.disposition, - sight: { enabled: true } - }, - embedded: { ActiveEffect: {}, Item: {} } - } - if (this.armed && ('armed_template' in this.summonableActors)) { - const armedTemplate = this.summonableActors.armed_template - for (const item of armedTemplate.items) { - const itemDoc = await armedTemplate.getEmbeddedDocument('Item', item.id) - this.spawnMutation.embedded.Item[item.name] = itemDoc - } - } - if (this.armor) { - const effectDoc = moduleHelpers.createEffectDocument( - 'icons/equipment/chest/breastplate-layered-leather-stitched.webp', - 'Rotting Armor', - 0) - delete effectDoc.duration - delete effectDoc.flags.swade.expiration - effectDoc.changes = [{ - key: 'system.stats.toughness.armor', - mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, - value: '+2', - priority: 0 - }] - this.spawnMutation.embedded.ActiveEffect[effectDoc.name] = effectDoc - } - for (const effectDocument of this.effectDocs) { - this.spawnMutation.embedded.ActiveEffect[effectDocument.name] = effectDocument - } - } - - async prepAdditional () { - if (!this.increasedTrait) { - return - } - const traitMenuOptions = { - title: `${this.name} Raise Trait Increase`, - defaultButton: 'Cancel', - options: {} - } - const skillSet = new Set() - for (const skill of this.actor.items.filter(i => i.type === 'skill')) { - skillSet.add(skill.name) - } - for (const item of Object.values(this.spawnMutation.embedded.Item).filter(i => i.type === 'skill')) { - skillSet.add(item.name) - } - const skillList = Array.from(skillSet) - const attrList = ['Agility', 'Smarts', 'Spirit', 'Strength', 'Vigor'] - skillList.sort() - const traitMenuData = { - inputs: [ - { type: 'header', label: 'Raise! Increase an attribute' } - ], - buttons: [ - { label: 'Apply', value: 'apply' }, - { label: 'Increase no traits', value: 'cancel' } - ] - } - traitMenuData.inputs = traitMenuData.inputs.concat( - attrList.map((x) => { return { type: 'radio', label: x, options: ['trait', false] } })) - traitMenuData.inputs.push({ type: 'header', label: 'Increase Skills (+1 each)' }) - traitMenuData.inputs = traitMenuData.inputs.concat( - skillList.map((x) => { return { type: 'radio', label: x, options: ['trait', false] } })) - const { buttons, inputs } = await warpgate.menu(traitMenuData, traitMenuOptions) - if (!buttons || buttons === 'cancel') { - return - } - const modKeys = [] - for (let i = 0; i < attrList.length; i++) { - if (inputs[i + 1]) { - modKeys.push(`system.attributes.${attrList[i].toLowerCase()}.die.sides`) - } - } - for (let i = 0; i < skillList.length; i++) { - if (inputs[i + 7]) { - modKeys.push(`@Skill{${skillList[i]}}[system.die.sides]`) - } - } - const effectDoc = moduleHelpers.createEffectDocument( - this.ICON, 'Increased Trait', this.durationRounds) - effectDoc.changes = modKeys.map(key => { - return { - key, mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, value: '+2', priority: 0 - } - }) - this.spawnMutation.embedded.ActiveEffect[effectDoc.name] = effectDoc - } -} - -const PowerClasses = { - 'arcane protection': ArcaneProtectionEffect, - 'arcane-protection': ArcaneProtectionEffect, - blast: BlastEffect, - blind: BlindEffect, - bolt: BoltEffect, - 'boost/lower trait': BoostLowerTraitEffect, - 'boostlower-trait': BoostLowerTraitEffect, - 'boost trait': BoostLowerTraitEffect, - 'boost-trait': BoostLowerTraitEffect, - burrow: BurrowEffect, - burst: BurstEffect, - 'conceal arcana': DetectConcealArcanaEffect, - 'conceal-arcana': DetectConcealArcanaEffect, - confusion: ConfusionEffect, - darksight: DarksightEffect, - deflection: DeflectionEffect, - 'detect arcana': DetectConcealArcanaEffect, - 'detect-arcana': DetectConcealArcanaEffect, - 'detect/conceal aracana': DetectConcealArcanaEffect, - 'detectconceal-aracana': DetectConcealArcanaEffect, - disguise: DisguiseEffect, - entangle: EntangleEffect, - fear: FearEffect, - havoc: HavocEffect, - intangibility: IntangibilityEffect, - invisibility: InvisibilityEffect, - 'lower trait': BoostLowerTraitEffect, - 'lower-trait': BoostLowerTraitEffect, - protection: ProtectionEffect, - 'shape change': ShapeChangeEffect, - 'shape-change': ShapeChangeEffect, - sloth: SlothSpeedEffect, - 'sloth/speed': SlothSpeedEffect, - slothspeed: SlothSpeedEffect, - smite: SmiteEffect, - speed: SlothSpeedEffect, - 'spiritual-weapon': SpiritualWeaponEffect, - 'summon ally': SummonAllyEffect, - 'summon-ally': SummonAllyEffect, - 'summon animal': SummonAnimalEffect, - 'summon-animal': SummonAnimalEffect, - 'summon monster': SummonMonsterEffect, - 'summon-monster': SummonMonsterEffect, - "summon nature's ally": SummonNaturesAllyEffect, - 'summon-natures-ally': SummonNaturesAllyEffect, - 'summon planar ally': SummonPlanarAllyEffect, - 'summon-planar-ally': SummonPlanarAllyEffect, - 'summon undead': SummonUndeadEffect, - 'summon-undead': SummonUndeadEffect, - zombie: ZombieEffect -} - -export async function powerEffects (options = {}) { - const token = 'token' in options ? options.token : [] - if (token === undefined || token === null) { - ui.notifications.error('Please select one token') - return - } - - const targets = 'targets' in options ? Array.from(options.targets) : [] - const item = 'item' in options ? options.item : null - const name = options?.name || item?.system?.swid || 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 - } - } - ui.notifications.error(`No power effect found for ${name}`) -} - -export async function shapeChangeOnDismiss (data) { - if (game.user.id !== data.userId) { return } - const dismissedToken = data.actorData.prototypeToken - const flags = dismissedToken.flags['swade-mb-helpers']?.shapeChange - const srcTokenId = flags?.srcTokenId - if (!srcTokenId) { return } - const scene = game.scenes.get(data.sceneId) - const token = scene.tokens.get(srcTokenId) - if (!token) { return } - const saved = token.flags['swade-mb-helpers']?.shapeChange?.saved - if (saved) { - const update = { - alpha: saved.alpha, - hidden: saved.hidden, - x: dismissedToken.x, - y: dismissedToken.y, - elevation: dismissedToken.elevation - } - await token.update(update) - } -} diff --git a/src/module/swade-mb-helpers.js b/src/module/swade-mb-helpers.js index 8392539..e3dd9d0 100644 --- a/src/module/swade-mb-helpers.js +++ b/src/module/swade-mb-helpers.js @@ -9,7 +9,6 @@ import { api } from './api.js'; import { initVisionModes } from './visionModes.js'; import { requestTokenRoll } from './helpers.js'; import { preDamageRollModifiers, preTraitRollModifiers } from './rollHelpers.js'; -import { shapeChangeOnDismiss } from './powerEffects.js'; import { log, moduleHelpers } from './globals.js'; import { powerEffectManagementHook } from './powers/powers.js'; @@ -24,23 +23,17 @@ Hooks.once('init', async () => { // Preload Handlebars templates await preloadTemplates(); - - // Register custom sheets (if any) }); // Setup module Hooks.once('setup', async () => { api.registerFunctions(); - // Do anything after initialization but before - // ready }); -// When ready Hooks.once('ready', async () => { _checkModule('warpgate'); _checkModule('socketlib'); log('Initialized SWADE MB Helpers'); - warpgate.event.watch(warpgate.EVENT.DISMISS, shapeChangeOnDismiss); }); function _checkModule(name) {