buttons and descriptions for VAR module, added elemental manipulation

This commit is contained in:
Mike Bloy 2024-05-18 22:34:27 -05:00
parent 2870b6b587
commit 0d41527a5e
7 changed files with 209 additions and 6 deletions

View File

@ -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

View File

@ -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] === '') {

View File

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

View File

@ -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;

View 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>
`
);
}
}

View File

@ -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;

View File

@ -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');