From 3d6a56192930ea056c902999876f0caf6c86ae70 Mon Sep 17 00:00:00 2001 From: Mike Bloy Date: Tue, 21 May 2024 23:49:40 -0500 Subject: [PATCH] add illusion, invisibility, intangibility --- src/module/powers/basePowers.js | 5 ++ src/module/powers/illusion.js | 113 +++++++++++++++++++++++++++++ src/module/powers/intangibility.js | 70 ++++++++++++++++++ src/module/powers/invisibility.js | 80 ++++++++++++++++++++ src/module/powers/powers.js | 6 ++ src/templates/powerDialog.html | 3 +- 6 files changed, 276 insertions(+), 1 deletion(-) create mode 100644 src/module/powers/illusion.js create mode 100644 src/module/powers/intangibility.js create mode 100644 src/module/powers/invisibility.js diff --git a/src/module/powers/basePowers.js b/src/module/powers/basePowers.js index 1d59308..597e3a5 100644 --- a/src/module/powers/basePowers.js +++ b/src/module/powers/basePowers.js @@ -81,6 +81,7 @@ export class PowerFormApplication extends FormApplication { data.recipients.cost = this.powerEffect.additionalRecipientCost; data.recipients.count = this.powerEffect.targets.length - 1; data.recipients.total = data.recipients.cost * data.recipients.count; + data.recipients.epic = this.powerEffect.additionalRecipientsIsEpic; } return data; } @@ -186,6 +187,10 @@ export class PowerEffect { return false; } + get additionalRecipientsIsEpic() { + return false; + } + get additionalRecipientCost() { return 0; } diff --git a/src/module/powers/illusion.js b/src/module/powers/illusion.js new file mode 100644 index 0000000..1b4f19d --- /dev/null +++ b/src/module/powers/illusion.js @@ -0,0 +1,113 @@ +import { PowerEffect } from './basePowers.js'; + +export class IllusionEffect extends PowerEffect { + get name() { + return 'Illusion'; + } + + get icon() { + return 'icons/magic/defensive/illusion-evasion-echo-purple.webp'; + } + + get duration() { + return this.data.duration ? 50 : 5; + } + + get isTargeted() { + return false; + } + + get isRaisable() { + return true; + } + + get usePrimaryEffect() { + return false; + } + + get basePowerPoints() { + return 5; + } + + get modifiers() { + return [ + { + name: 'Area of Effect', + type: 'checkbox', + value: 1, + id: 'aoe', + epic: false, + effect: false, + }, + { + name: 'Deadly Illusion', + type: 'checkbox', + value: 3, + id: 'deadly', + epic: true, + effect: false, + }, + { + name: 'Duration', + type: 'checkbox', + value: 2, + id: 'duration', + epic: true, + effect: false, + }, + { + name: 'Mobility', + type: 'select', + default: 'none', + id: 'mobility', + choices: { + none: 'None', + 12: 'Move and fly at Pace 12', + 24: 'Move and fly at Pace 24', + }, + values: { none: 0, 12: 1, 24: 2 }, + effects: { none: null, 12: null, 24: null }, + epic: false, + }, + { + name: 'Sound', + id: 'sound', + type: 'checkbox', + epic: false, + effect: false, + value: 1, + }, + { + name: 'Strong', + id: 'strong', + type: 'checkbox', + epic: false, + effect: false, + value: 2, + }, + ]; + } + + get description() { + const size = this.data.aoe ? 'LBT' : 'MBT'; + const penalty = (this.data.raise ? -2 : 0) + (this.data.strong ? -2 : 0); + const motion = this.data.mobility === 'none' ? '(stationary)' : `that can move at pace ${this.data.mobility}`; + let text = + super.description + + `

+ Create an illusion ${motion} ${this.data.sound ? 'with sound' : ''}. + Its effects must remain within a sphere the size of a ${size}.

+

Creatures can actively disbelieve in the illusion with a Smarts roll + ${penalty === 0 ? '' : `at ${penalty}`}.

+ `; + if (this.data.deadly) { + text += `

The caster can 'attack' targets while the illusion is active + with an opposed roll of the caster's arcane skill versus the target's + Smarts${penalty === 0 ? '' : ` at ${penalty}`}. If the caster wins the + target is Shaken (cannot incapacitate). With a raise, the target suffers + a Wound.

. + `; + } + return text; + } +} diff --git a/src/module/powers/intangibility.js b/src/module/powers/intangibility.js new file mode 100644 index 0000000..2b18c23 --- /dev/null +++ b/src/module/powers/intangibility.js @@ -0,0 +1,70 @@ +import { moduleName } from '../globals.js'; +import { PowerEffect } from './basePowers.js'; + +export class IntangibilityEffect extends PowerEffect { + get name() { + return 'Intangility'; + } + + get duration() { + return 5; + } + + get icon() { + return 'icons/magic/control/debuff-energy-hold-levitate-blue-yellow.webp'; + } + + get hasAdditionalRecipients() { + return true; + } + + get additionalRecipientCost() { + return 3; + } + + get additionalRecipientsIsEpic() { + return true; + } + + get basePowerPoints() { + return 5; + } + + get isTargeted() { + return true; + } + + get isRaisable() { + return true; + } + + get modifiers() { + const mods = super.modifiers; + mods.push({ + name: 'Duration', + type: 'checkbox', + id: 'Duration', + value: 3, + epic: true, + effect: false, + }); + return mods; + } + + get description() { + let text = + super.description + + `

+ The subject becomes incorporeal. Non magical weapons and walls can't + affect him. The character may affect other incorporeal beings, and is still + susceptible to supernatural attacks. Unwilling targets resist with Spirit, + and shake off the effect with a Spirit roll at the end of following turns.

+

The being is Stunned and shunted to an open space if it is within + something solid when the power ends.

+ `; + if (this.data.raise) { + text += '

Reduce damage taken from supernatural effects and magic weapons by 4.

'; + } + return text; + } +} diff --git a/src/module/powers/invisibility.js b/src/module/powers/invisibility.js new file mode 100644 index 0000000..5bccea5 --- /dev/null +++ b/src/module/powers/invisibility.js @@ -0,0 +1,80 @@ +import { moduleName } from '../globals.js'; +import { PowerEffect } from './basePowers.js'; + +export class InvisibliltyEffect extends PowerEffect { + get name() { + return 'Invisibility'; + } + + get duration() { + return 5; + } + + get icon() { + return 'icons/svg/invisible.svg'; + } + + get hasAdditionalRecipients() { + return true; + } + + get additionalRecipientCost() { + return 3; + } + + get basePowerPoints() { + return 5; + } + + get isTargeted() { + return true; + } + + get isRaisable() { + return true; + } + + get modifiers() { + const mods = super.modifiers; + mods.push({ + name: 'Duration', + type: 'checkbox', + id: 'Duration', + value: 2, + epic: true, + effect: false, + }); + return mods; + } + + get description() { + let text = + super.description + + ` +

The subject is invisible, except for a vague blur or outline. + Any action taken against it that requires sight is made at + ${this.data.raise ? -6 : -4}, including Notice rolls. This penalty is + reduced by 2 if the invisible character's position is given away by some + environmental circumstance.

+ `; + if (this.data.duration) { + text += `

This long-duration invisibility ends immediately if the subject + attempts a damage-causing attack or targets an unwilling character with a + power.

`; + } + return text; + } + + async parseValues() { + await super.parseValues(); + const doc = await PowerEffect.getStatus('EFFECT.StatusInvisible', 'Invisible'); + doc.description = `

From ${this.source.name} casting ${this.name}

`; + doc.flags = mergeObject(doc.flags ?? {}, { [moduleName]: { powerEffect: true } }); + (doc.duration = { rounds: 99 }), (this.baseEffectDoc = doc); + this.basePrimaryEffectDoc = doc; + } + + get basePrimaryEffect() { + return this.basePrimaryEffectDoc; + } +} diff --git a/src/module/powers/powers.js b/src/module/powers/powers.js index 8ffa365..2b0a554 100644 --- a/src/module/powers/powers.js +++ b/src/module/powers/powers.js @@ -31,6 +31,9 @@ import { FlyEffect } from './fly.js'; import { GrowthShrinkEffect } from './growthShrink.js'; import { HavocEffect } from './havoc.js'; import { HealingEffect } from './healing.js'; +import { IllusionEffect } from './illusion.js'; +import { IntangibilityEffect } from './intangibility.js'; +import { InvisibliltyEffect } from './invisibility.js'; const PowerClasses = { 'arcane-protection': ArcaneProtectionEffect, @@ -71,6 +74,9 @@ const PowerClasses = { growthshrink: GrowthShrinkEffect, havoc: HavocEffect, healing: HealingEffect, + illusion: IllusionEffect, + intangibility: IntangibilityEffect, + invisibility: InvisibliltyEffect, 'lower-trait': BoostLowerTraitEffect, shrink: GrowthShrinkEffect, }; diff --git a/src/templates/powerDialog.html b/src/templates/powerDialog.html index 79e1e39..7f62474 100644 --- a/src/templates/powerDialog.html +++ b/src/templates/powerDialog.html @@ -19,7 +19,8 @@ Targets: {{#each targets}}{{#if @index}}, {{/if}}{{this}}{{/each}} {{#if recipients.cost}} -
(Additional Recipients {{recipients.cost}}pp each × {{recipients.count}} = {{recipients.total}}) +
({{#if recipients.epic}}⭐ {{/if}}Additional Recipients + {{recipients.cost}}pp each × {{recipients.count}} = {{recipients.total}}) {{/if}}

{{/if}}