buttons and descriptions for VAR module, added elemental manipulation
This commit is contained in:
parent
2870b6b587
commit
0d41527a5e
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [3.0.0] UNRELEASED
|
## [3.0.0] UNRELEASED
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Optional Visual Active Effect integration for power descriptions
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Refactor and redo of powers handling
|
- Refactor and redo of powers handling
|
||||||
|
|||||||
@ -23,6 +23,10 @@ export class moduleHelpers {
|
|||||||
return 'system';
|
return 'system';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get useVAE() {
|
||||||
|
return !!game.modules.get('visual-active-effects')?.active;
|
||||||
|
}
|
||||||
|
|
||||||
static getActorFolderByPath(path) {
|
static getActorFolderByPath(path) {
|
||||||
const names = path.split('/');
|
const names = path.split('/');
|
||||||
if (names[0] === '') {
|
if (names[0] === '') {
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { PowerEffect } from './basePowers.js';
|
import { PowerEffect } from './basePowers.js';
|
||||||
import { requestRollFromTokens } from '../helpers.js';
|
import { requestRollFromTokens } from '../helpers.js';
|
||||||
import { getPowerModifiers } from '../rollHelpers.js';
|
|
||||||
|
|
||||||
export class BanishEffect extends PowerEffect {
|
export class BanishEffect extends PowerEffect {
|
||||||
get name() {
|
get name() {
|
||||||
|
|||||||
@ -426,11 +426,40 @@ export class PowerEffect {
|
|||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get primaryEffectButtons() {
|
||||||
|
// button objects should have a label and a type.
|
||||||
|
// type should have one of the following, with the associated additional
|
||||||
|
// fields:
|
||||||
|
// roll:
|
||||||
|
// formula: dice formula eg '3d6 + 3'
|
||||||
|
// flavor: flavor text (optional)
|
||||||
|
// trait:
|
||||||
|
// rollType: 'attribute' or 'skill
|
||||||
|
// rollDesc: name or swid of the attribute or skill
|
||||||
|
// flavor: flavor text (optional)
|
||||||
|
// mods: list of mods { label, value, ignore }
|
||||||
|
// damage:
|
||||||
|
// formula: dice formula for example '1d4x[Blades]'
|
||||||
|
// ap: optional, a positive integer or 0, armor piercing
|
||||||
|
// flavor: flavor text (optional)
|
||||||
|
// callback:
|
||||||
|
// callback: the function callback to run, takes a token as an argument
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
async createPrimaryEffect(maintId) {
|
async createPrimaryEffect(maintId) {
|
||||||
const doc = this.createEffectDocument(this.icon, this.effectName, this.getPrimaryEffectChanges());
|
const doc = this.createEffectDocument(this.icon, this.effectName, this.getPrimaryEffectChanges());
|
||||||
|
if (moduleHelpers.useVAE) {
|
||||||
|
doc.flags['visual-active-effects'] = { data: { content: this.description } };
|
||||||
|
} else {
|
||||||
doc.description += this.description;
|
doc.description += this.description;
|
||||||
|
}
|
||||||
doc.flags[moduleName].maintId = maintId;
|
doc.flags[moduleName].maintId = maintId;
|
||||||
doc.duration.seconds = 594;
|
doc.duration.seconds = 594;
|
||||||
|
const effectButtons = this.primaryEffectButtons;
|
||||||
|
if (effectButtons.length > 0) {
|
||||||
|
doc.flags[moduleName].buttons = effectButtons;
|
||||||
|
}
|
||||||
return doc;
|
return doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -441,7 +470,11 @@ export class PowerEffect {
|
|||||||
}
|
}
|
||||||
const doc = this.createEffectDocument(icon, `Maintaining ${this.effectName}`, []);
|
const doc = this.createEffectDocument(icon, `Maintaining ${this.effectName}`, []);
|
||||||
doc.duration.rounds = this.duration;
|
doc.duration.rounds = this.duration;
|
||||||
|
if (moduleHelpers.useVAE) {
|
||||||
|
doc.flags['visual-active-effects'] = { data: { content: this.description } };
|
||||||
|
} else {
|
||||||
doc.description += this.description;
|
doc.description += this.description;
|
||||||
|
}
|
||||||
doc.flags.swade.expiration = CONFIG.SWADE.CONST.STATUS_EFFECT_EXPIRATION.EndOfTurnPrompt;
|
doc.flags.swade.expiration = CONFIG.SWADE.CONST.STATUS_EFFECT_EXPIRATION.EndOfTurnPrompt;
|
||||||
doc.flags.swade.loseTurnOnHold = true;
|
doc.flags.swade.loseTurnOnHold = true;
|
||||||
doc.flags[moduleName].maintainingId = maintId;
|
doc.flags[moduleName].maintainingId = maintId;
|
||||||
@ -541,7 +574,7 @@ export class PowerEffect {
|
|||||||
if (this.isDamaging && this.data.ap > 0) {
|
if (this.isDamaging && this.data.ap > 0) {
|
||||||
list.push(`AP ${this.data.ap}`);
|
list.push(`AP ${this.data.ap}`);
|
||||||
}
|
}
|
||||||
if (this.data.range != 'none') {
|
if (this.data.range ?? 'none' != 'none') {
|
||||||
list.push(`Range ${this.data.range}`);
|
list.push(`Range ${this.data.range}`);
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
97
src/module/powers/elementalManipulation.js
Normal file
97
src/module/powers/elementalManipulation.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { PowerEffect } from './basePowers.js';
|
||||||
|
|
||||||
|
export class ElementalManipulationEffect extends PowerEffect {
|
||||||
|
get name() {
|
||||||
|
return 'Elemental Manipulation';
|
||||||
|
}
|
||||||
|
|
||||||
|
get icon() {
|
||||||
|
return 'icons/magic/earth/projectiles-stone-salvo-gray.webp';
|
||||||
|
}
|
||||||
|
|
||||||
|
get duration() {
|
||||||
|
return this.data?.weather ? 0 : 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isTargeted() {
|
||||||
|
return this.data?.weather ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get isRaisable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get usePrimaryEffect() {
|
||||||
|
return this.data?.weather ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get oneTarget() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
get basePowerPoints() {
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
get modifiers() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'Power',
|
||||||
|
type: 'checkbox',
|
||||||
|
value: 3,
|
||||||
|
id: 'power',
|
||||||
|
epic: true,
|
||||||
|
effect: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Weather',
|
||||||
|
type: 'checkbox',
|
||||||
|
value: 5,
|
||||||
|
id: 'weather',
|
||||||
|
epic: true,
|
||||||
|
effect: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get primaryEffectButtons() {
|
||||||
|
const dmg = `${this.data.raise ? 3 : 2}d${this.data.power ? 6 : 4}`;
|
||||||
|
return [
|
||||||
|
...super.primaryEffectButtons,
|
||||||
|
{
|
||||||
|
type: 'damage',
|
||||||
|
label: `Damage (${dmg})`,
|
||||||
|
formula: `${dmg}x`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
if (this.data.weather) {
|
||||||
|
return (
|
||||||
|
super.description +
|
||||||
|
`Bring or disperse rain, snow, sun, and wind in about a five mile radius.
|
||||||
|
This takes 10 minutes and lasts an hour.
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let damage = `${this.data.raise ? 3 : 2}d${this.data.power ? 6 : 4}x`;
|
||||||
|
return (
|
||||||
|
super.description +
|
||||||
|
`
|
||||||
|
<p>Use the activation roll for:</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Attack:</strong> activation roll is the attack roll,
|
||||||
|
[[/r ${damage}]] damage within Range.</li>
|
||||||
|
<li><strong>Move:</strong> move a cubic foot of air, earth, fire or water
|
||||||
|
(half that of stone) up to the caster's Smarts as a limited action.</li>
|
||||||
|
<li><strong>Push:</strong> Use the activation roll in place of Strength
|
||||||
|
for a Push.</li>
|
||||||
|
<li><strong>Special Effects:</strong> eg. purify a gallon of water,
|
||||||
|
or conjure a quart, fix breaks in stone, conjure a flame or spread
|
||||||
|
existing flame.</li>
|
||||||
|
</ul>
|
||||||
|
`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { moduleName, moduleHelpers } from '../globals.js';
|
import { moduleName, moduleHelpers, log } from '../globals.js';
|
||||||
import { firstOwner, deleteActiveEffectsFromToken } from '../helpers.js';
|
import { firstOwner, deleteActiveEffectsFromToken } from '../helpers.js';
|
||||||
import { ArcaneProtectionEffect } from './arcaneProtection.js';
|
import { ArcaneProtectionEffect } from './arcaneProtection.js';
|
||||||
import { BanishEffect } from './banish.js';
|
import { BanishEffect } from './banish.js';
|
||||||
@ -20,6 +20,7 @@ import { DisguiseEffect } from './disguise.js';
|
|||||||
import { DispelEffect } from './dispel.js';
|
import { DispelEffect } from './dispel.js';
|
||||||
import { DivinationEffect } from './divination.js';
|
import { DivinationEffect } from './divination.js';
|
||||||
import { DrainPowerPointsEffect } from './drainPowerPoints.js';
|
import { DrainPowerPointsEffect } from './drainPowerPoints.js';
|
||||||
|
import { ElementalManipulationEffect } from './elementalManipulation.js';
|
||||||
|
|
||||||
const PowerClasses = {
|
const PowerClasses = {
|
||||||
'arcane-protection': ArcaneProtectionEffect,
|
'arcane-protection': ArcaneProtectionEffect,
|
||||||
@ -47,11 +48,75 @@ const PowerClasses = {
|
|||||||
dispel: DispelEffect,
|
dispel: DispelEffect,
|
||||||
divination: DivinationEffect,
|
divination: DivinationEffect,
|
||||||
'drain-power-points': DrainPowerPointsEffect,
|
'drain-power-points': DrainPowerPointsEffect,
|
||||||
|
'elemental-manipulation': ElementalManipulationEffect,
|
||||||
'lower-trait': BoostLowerTraitEffect,
|
'lower-trait': BoostLowerTraitEffect,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* ---------------------------------------------------------------- */
|
/* ---------------------------------------------------------------- */
|
||||||
|
|
||||||
|
export function visualActiveEffectPowerButtons(effect, buttons) {
|
||||||
|
if (!effect.flags?.[moduleName]?.buttons) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const button of effect.flags[moduleName].buttons) {
|
||||||
|
let callback = null;
|
||||||
|
switch (button.type) {
|
||||||
|
case 'roll':
|
||||||
|
callback = function () {
|
||||||
|
new CONFIG.Dice.SwadeRoll(button.formula ?? '3d6', null, {}).toMessage({
|
||||||
|
flavor: button.flavor ?? `${effect.name} roll`,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'trait':
|
||||||
|
callback = function () {
|
||||||
|
let rollFunc = 'rollAttribute';
|
||||||
|
const rollType = button?.rollType ?? 'attribute';
|
||||||
|
const rollDesc = button?.rollDesc ?? 'agility';
|
||||||
|
let rollId = rollDesc.toLowerCase();
|
||||||
|
if (rollType === 'skill') {
|
||||||
|
rollFunc = 'rollSkill';
|
||||||
|
rollId = effect.parent.items
|
||||||
|
.filter((i) => i.type === 'skill')
|
||||||
|
.find(
|
||||||
|
(i) => i.system.swid === rollDesc.toLowerCase() || i.name.toLowerCase() === rollDesc.toLowerCase(),
|
||||||
|
)?.id;
|
||||||
|
}
|
||||||
|
const options = {
|
||||||
|
flavor: button?.flavor ?? `${effect.name} ${rollDesc} roll`,
|
||||||
|
};
|
||||||
|
if (button?.mods) {
|
||||||
|
options.mods = button.mods;
|
||||||
|
}
|
||||||
|
effect.parent[rollFunc](rollId, options);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'damage':
|
||||||
|
callback = function () {
|
||||||
|
const formula = button?.formula ?? '2d6x';
|
||||||
|
const ap = button?.ap ?? 0;
|
||||||
|
const flavor = button?.flavor ?? `${effect.name} damage roll`;
|
||||||
|
const options = {};
|
||||||
|
if (ap > 0) {
|
||||||
|
options.ap = ap;
|
||||||
|
}
|
||||||
|
new CONFIG.Dice.DamageRoll(formula, null, options).toMessage({ flavor });
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 'callback':
|
||||||
|
callback =
|
||||||
|
button?.callback ||
|
||||||
|
function () {
|
||||||
|
console.log('not implemented');
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (button?.label && callback) {
|
||||||
|
buttons.push({ label: button.label, callback });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export async function powerEffectManagementHook(effect, data, userId) {
|
export async function powerEffectManagementHook(effect, data, userId) {
|
||||||
if (game.user.id !== userId) {
|
if (game.user.id !== userId) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import { initVisionModes } from './visionModes.js';
|
|||||||
import { requestTokenRoll, addActiveEffectsToToken, deleteActiveEffectsFromToken } from './helpers.js';
|
import { requestTokenRoll, addActiveEffectsToToken, deleteActiveEffectsFromToken } from './helpers.js';
|
||||||
import { preDamageRollModifiers, preTraitRollModifiers } from './rollHelpers.js';
|
import { preDamageRollModifiers, preTraitRollModifiers } from './rollHelpers.js';
|
||||||
import { log, moduleHelpers } from './globals.js';
|
import { log, moduleHelpers } from './globals.js';
|
||||||
import { powerEffectManagementHook } from './powers/powers.js';
|
import { powerEffectManagementHook, visualActiveEffectPowerButtons } from './powers/powers.js';
|
||||||
|
|
||||||
// Initialize module
|
// Initialize module
|
||||||
Hooks.once('init', async () => {
|
Hooks.once('init', async () => {
|
||||||
@ -44,6 +44,7 @@ Hooks.on('swadePreRollAttribute', preTraitRollModifiers);
|
|||||||
Hooks.on('swadePreRollSkill', preTraitRollModifiers);
|
Hooks.on('swadePreRollSkill', preTraitRollModifiers);
|
||||||
Hooks.on('swadeRollDamage', preDamageRollModifiers);
|
Hooks.on('swadeRollDamage', preDamageRollModifiers);
|
||||||
Hooks.on('deleteActiveEffect', powerEffectManagementHook);
|
Hooks.on('deleteActiveEffect', powerEffectManagementHook);
|
||||||
|
Hooks.on('visual-active-effects.createEffectButtons', visualActiveEffectPowerButtons);
|
||||||
|
|
||||||
Hooks.once('socketlib.ready', () => {
|
Hooks.once('socketlib.ready', () => {
|
||||||
const _socket = socketlib.registerModule('swade-mb-helpers');
|
const _socket = socketlib.registerModule('swade-mb-helpers');
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user