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) {