diff --git a/src/module/powers/basePowers.js b/src/module/powers/basePowers.js index f2b5596..efd8a3e 100644 --- a/src/module/powers/basePowers.js +++ b/src/module/powers/basePowers.js @@ -25,12 +25,12 @@ export class PowerFormApplication extends FormApplication { if (a.isGlobal !== b.isGlobal) { return a.isGlobal ? -1 : 1; } - if (a.type !== b.type) { - return a.type === 'checkbox' ? -1 : 1; - } if ((a.sortOrder ?? 0) !== (b.sortOrder ?? 0)) { return (a.sortOrder ?? 0) < (b.sortOrder ?? 0) ? -1 : 1; } + if (a.type !== b.type) { + return a.type === 'checkbox' ? -1 : 1; + } return a.name === b.name ? 0 : a.name < b.name ? -1 : 1; } diff --git a/src/module/powers/boostLowerTrait.js b/src/module/powers/boostLowerTrait.js new file mode 100644 index 0000000..33a4cb9 --- /dev/null +++ b/src/module/powers/boostLowerTrait.js @@ -0,0 +1,204 @@ +import { moduleName } from '../globals.js'; +import { PowerEffect } from './basePowers.js'; + +export class BoostLowerTraitEffect extends PowerEffect { + get name() { + return 'Boost/Lower Trait'; + } + + get hasAdditionalRecipients() { + return true; + } + + get additionalRecipientCost() { + return 2; + } + + get icon() { + return this?.data?.direction === 'Boost' + ? 'icons/magic/life/cross-embers-glow-yellow-purple.webp' + : 'icons/magic/movement/chevrons-down-yellow.webp'; + } + + get duration() { + return this?.data?.direction === 'Boost' ? 5 : 0; + } + + get isTargeted() { + return true; + } + + get basePowerPoints() { + return 3; + } + + getPrimaryEffectChanges() { + let modValue = '2'; + if (this.data.raise) { + modValue = '4'; + } + modValue = (this.data.direction === 'Boost' ? '+' : '-') + modValue; + const changes = [ + { + key: this.data.trait.diekey, + value: modValue, + priority: 0, + mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, + }, + ]; + if (this.data.direction === 'Lower' && this.data.greater) { + changes.push({ + key: this.data.trait.modkey, + mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, + value: -2, + priority: 0, + }); + } + return changes; + } + + async createSecondaryEffects(maintId) { + const docs = await super.createSecondaryEffects(maintId); + if (this.data.raise && this.data.direction === 'Lower') { + const name = 'major ' + this.effectName; + const modValue = this.data.direction === 'Boost' ? '+2' : '-2'; + const changes = [ + { + key: this.data.trait.diekey, + value: modValue, + priority: 0, + mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, + }, + ]; + const doc = this.createEffectDocument(this.icon, name, changes); + doc.duration.seconds = 594; + doc.description = this.description + '
This is the raise effect which can be shaken off separately.
'; + doc.flags[moduleName].maintId = maintId; + docs.push(doc); + } + return docs; + } + + get effectName() { + let name = `${this.data.direction} ${this.data.trait.name}`; + const nameMods = []; + if (this.data.greater) { + nameMods.push('Greater'); + } + if (this.data.direction === 'Lower' && this.data.strong) { + nameMods.push('Strong'); + } + if (nameMods.length > 0) { + name += ` (${nameMods.join(', ')})`; + } + return name; + } + + get description() { + let desc = super.description; + const amount = `${this.data.raise ? 2 : 1} die type${this.data.raise ? 's' : ''}`; + desc += `${this.data.direction === 'Boost' ? 'Raise' : 'Lower'} the + target's ${this.data.trait.name} die type ${amount}.`; + if (this.data.greater) { + if (this.data.direction === 'Boost') { + desc += ` Additionally, the target gains a free ${this.data.trait.name} + reroll once per ${this.data.raise ? 'action' : 'round'}.`; + } else { + desc += ` Additionally, the target suffers a -2 penalty to their + ${this.data.trait.name} rolls.`; + } + } + desc += '
'; + if (this.data.direction === 'Lower') { + desc += `At the end of the target's following turns, they attempt to shake off + the affect with a Spirit${this.data.strong ? ' -2' : ''} + roll as a free action. Success reduces the effect one die type. A raise + completely shakes off the effect.
`; + } + return desc; + } + + get modifiers() { + const mods = super.modifiers; + let traitOptions = ['Agility', 'Smarts', 'Spirit', 'Strength', 'Vigor']; + const allSkills = new Set(); + 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.add(name); + } + } + } + traitOptions = traitOptions.concat(Array.from(allSkills).sort()); + const traitChoices = {}; + const traitValues = {}; + const traitEffects = {}; + for (const trait of traitOptions) { + traitChoices[trait] = trait; + traitValues[trait] = 0; + traitEffects[trait] = null; + } + this.data.traits = traits; + mods.push({ + sortOrder: -2, + name: 'Boost or Lower?', + id: 'direction', + type: 'radio', + default: 'Boost', + epic: false, + choices: { Boost: 'Boost', Lower: 'Lower' }, + effects: { Boost: null, Lower: null }, + values: { Boost: 0, Lower: 0 }, + }); + mods.push({ + sortOrder: -1, + name: 'Trait', + id: 'traitName', + type: 'select', + epic: false, + choices: traitChoices, + values: traitValues, + effects: traitEffects, + }); + mods.push({ + name: 'Greater Boost/Lower Trailt', + type: 'checkbox', + value: 2, + id: 'greater', + epic: true, + effect: false, + }); + mods.push({ + name: 'Strong (lower only)', + type: 'checkbox', + value: 1, + id: 'strong', + epic: false, + effect: false, + }); + return mods; + } + + async parseValues() { + await super.parseValues(); + this.data.trait = this.data.traits[this.data.traitName]; + } +} diff --git a/src/module/powers/burrow.js b/src/module/powers/burrow.js new file mode 100644 index 0000000..762402f --- /dev/null +++ b/src/module/powers/burrow.js @@ -0,0 +1,58 @@ +import { PowerEffect } from './basePowers.js'; + +export class BurrowEffect extends PowerEffect { + get name() { + return 'Burrow'; + } + + get duration() { + return 5; + } + + get icon() { + return 'icons/magic/earth/projectile-stone-landslide.webp'; + } + + get hasAdditionalRecipients() { + return true; + } + + get additionalRecipientCost() { + return 1; + } + + get basePowerPoints() { + return 1; + } + + get isTargeted() { + return true; + } + + get modifiers() { + const mods = super.modifiers; + mods.push({ + name: 'Power', + type: 'checkbox', + id: 'power', + value: 1, + epic: false, + effect: false, + }); + return mods; + } + + get effectName() { + return `${this.name} ${this.data.power ? '[Power] ' : ''}` + `(${this.data.raise ? 'full' : 'half'} pace)`; + } + + get description() { + let text = + super.description + + `Meld into the ground. Move at ${this.data.raise ? 'full' : 'half'} pace. May not run.
`; + if (this.data.power) { + text += 'Can burrow through solid stone, concrete, etc
'; + } + return text; + } +} diff --git a/src/module/powers/powers.js b/src/module/powers/powers.js index c402c15..fd346f5 100644 --- a/src/module/powers/powers.js +++ b/src/module/powers/powers.js @@ -8,251 +8,8 @@ import { BeastFriendEffect } from './beastFriend.js'; import { BlastEffect } from './blast.js'; import { BlindEffect } from './blind.js'; import { BoltEffect } from './bolt.js'; - -class BurrowEffect extends PowerEffect { - get name() { - return 'Burrow'; - } - - get duration() { - return 5; - } - - get icon() { - return 'icons/magic/earth/projectile-stone-landslide.webp'; - } - - get hasAdditionalRecipients() { - return true; - } - - get additionalRecipientCost() { - return 1; - } - - get basePowerPoints() { - return 1; - } - - get isTargeted() { - return true; - } - - get modifiers() { - const mods = super.modifiers; - mods.push({ - name: 'Power', - id: 'power', - value: 1, - epic: false, - effect: false, - }); - return mods; - } - - get effectName() { - return ( - `${this.name} ${this.data.mods.has('power') ? '[Power] ' : ''}` + `(${this.data.raise ? 'full' : 'half'} pace)` - ); - } - - get description() { - let text = - super.description + - `Meld into the ground. Move at ${this.data.raise ? 'full' : 'half'} pace. May not run.
`; - if (this.data.mods.has('power')) { - text += 'Can burrow through solid stone, concrete, etc
'; - } - return text; - } -} - -class BoostLowerTraitEffect extends PowerEffect { - get name() { - return 'Boost/Lower Trait'; - } - - get hasAdditionalRecipients() { - return true; - } - - get additionalRecipientCost() { - return 2; - } - - get icon() { - return this?.data?.direction === 'Boost' - ? 'icons/magic/life/cross-embers-glow-yellow-purple.webp' - : 'icons/magic/movement/chevrons-down-yellow.webp'; - } - - get duration() { - return this?.data?.direction === 'Boost' ? 5 : 0; - } - - get isTargeted() { - return true; - } - - get basePowerPoints() { - return 3; - } - - getPrimaryEffectChanges() { - let modValue = '2'; - if (this.data.raise && this.data.direction === 'Boost') { - modValue = '4'; - } - modValue = (this.data.direction === 'Boost' ? '+' : '-') + modValue; - const changes = [ - { - key: this.data.trait.diekey, - value: modValue, - priority: 0, - mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, - }, - ]; - if (this.data.direction === 'Lower' && this.data.mods.has('greater')) { - changes.push({ - key: this.data.trait.modkey, - mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, - value: -2, - priority: 0, - }); - } - return changes; - } - - async createSecondaryEffects(maintId) { - const docs = await super.createSecondaryEffects(maintId); - if (this.data.raise && this.data.direction === 'Lower') { - const name = 'major ' + this.effectName; - const modValue = this.data.direction === 'Boost' ? '+2' : '-2'; - const changes = [ - { - key: this.data.trait.diekey, - value: modValue, - priority: 0, - mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD, - }, - ]; - const doc = this.createEffectDocument(this.icon, name, changes); - doc.duration.seconds = 594; - doc.description = this.description + 'This is the raise effect which can be shaken off separately.
'; - doc.flags[moduleName].maintId = maintId; - docs.push(doc); - } - return docs; - } - - get effectName() { - let name = `${this.data.direction} ${this.data.trait.name}`; - const nameMods = []; - if (this.data.mods.has('greater')) { - nameMods.push('Greater'); - } - if (this.data.direction === 'Lower' && this.data.mods.has('strong')) { - nameMods.push('Strong'); - } - if (nameMods.length > 0) { - name += ` (${nameMods.join(', ')})`; - } - return name; - } - - get description() { - let desc = super.description; - const amount = `${this.data.raise ? 2 : 1} die type${this.data.raise ? 's' : ''}`; - desc += `${this.data.direction === 'Boost' ? 'Raise' : 'Lower'} the - target's ${this.data.trait.name} die type ${amount}.`; - if (this.data.mods.has('greater')) { - if (this.data.direction === 'Boost') { - desc += ` Additionally, the target gains a free ${this.data.trait.name} - reroll once per ${this.data.raise ? 'action' : 'round'}.`; - } else { - desc += ` Additionally, the target suffers a -2 penalty to their - ${this.data.trait.name} rolls.`; - } - } - desc += '
'; - if (this.data.direction === 'Lower') { - desc += `At the end of the target's following turns, they attempt to shake off - the affect with a Spirit${this.data.mods.has('strong') ? ' -2' : ''} - roll as a free action. Success reduces the effect one die type. A raise - completely shakes off the effect.
`; - } - return desc; - } - - get modifiers() { - const mods = super.modifiers; - mods.push({ - name: 'Greater Boost/Lower Trailt', - value: 2, - id: 'greater', - epic: true, - effect: false, - }); - mods.push({ - name: 'Strong (lower only)', - value: 1, - id: 'strong', - epic: false, - effect: false, - }); - return mods; - } - - get menuInputs() { - const inputs = super.menuInputs; - let traitOptions = ['Agility', 'Smarts', 'Spirit', 'Strength', 'Vigor']; - const allSkills = new Set(); - 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.add(name); - } - } - } - traitOptions = traitOptions.concat(Array.from(allSkills).sort()); - this.data.traits = traits; - inputs.push({ type: 'select', label: 'Trait', options: traitOptions }); - inputs.push({ type: 'info', label: 'Boost or Lower?' }); - inputs.push({ type: 'radio', label: 'Boost', options: ['isBoost', true] }); - inputs.push({ type: 'radio', label: 'Lower', options: ['isBoost', false] }); - return inputs; - } - - async parseValues() { - await super.parseValues(); - this.data.trait = this.data.traits[this.data.values.shift()]; - this.data.values.shift(); - this.data.direction = this.data.values.shift() ? 'Boost' : 'Lower'; - } - - get powerPoints() { - const total = super.powerPoints; - return total; - } -} +import { BurrowEffect } from './burrow.js'; +import { BoostLowerTraitEffect } from './boostLowerTrait.js'; class BurstEffect extends PowerEffect { get name() {