From a97beea0af1503d92fba4bcaab29c3e2baf6fadc Mon Sep 17 00:00:00 2001 From: napolitanod Date: Sat, 13 Jul 2024 22:00:23 -0700 Subject: [PATCH 1/4] 1.2.7 --- languages/en.json | 1 + module.json | 4 ++-- scripts/constants.js | 9 +++++++++ scripts/index.js | 7 +++++++ scripts/napolitano-scripts.js | 5 ++--- scripts/workflow.js | 14 ++++++++++++++ 6 files changed, 35 insertions(+), 5 deletions(-) diff --git a/languages/en.json b/languages/en.json index c95da15..167cdc7 100644 --- a/languages/en.json +++ b/languages/en.json @@ -35,6 +35,7 @@ "relentless-endurance.label":"Relentless Endurance", "short-rest.label": "Short Rest", "taste-of-the-stones.label": "Taste of the Stones", + "voices-from-beyond.label": "Voices from Beyond", "wild-surge.label": "Wild Surge (Barbarian)", "witch-bolt.label": "Witch Bolt" }, diff --git a/module.json b/module.json index aa2ef80..2990286 100644 --- a/module.json +++ b/module.json @@ -30,8 +30,8 @@ "styles/napolitano-scripts.css" ], "title": "_Napolitano Scripts", - "version": "1.2.6", + "version": "1.2.7", "url": "https://github.com/napolitanod/napolitano-scripts", "manifest": "https://raw.githubusercontent.com/napolitanod/napolitano-scripts/master/module.json", - "download": "https://github.com/napolitanod/napolitano-scripts/releases/download/1.2.6/module.zip" + "download": "https://github.com/napolitanod/napolitano-scripts/releases/download/1.2.7/module.zip" } \ No newline at end of file diff --git a/scripts/constants.js b/scripts/constants.js index 1772a45..c2ee711 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -762,6 +762,14 @@ export const NAPOLITANOCONFIG = { } } }, + voicesFromBeyond: { + name: "Voices from Beyond", + table: { + compendium: "Napolitano Roll Table", + compendiumBackup: "DDB Roll Table", + name: "Voices from Beyond" + } + }, wardingFlare: { effects:{pre: {file: 'modules/jb2a_patreon/Library/Generic/Energy/Shimmer01_01_Regular_Orange_400x400.webm', duration: 2000, scale: 2}}, name: "Warding Flare" @@ -901,6 +909,7 @@ export const CONFIGS = [ {id:"shield", name:"Shield (spell)"}, {id:"silvery-barbs", name:"Silvery Barbs"}, {id:"taste-of-the-stones", name:"Taste of the Stones"}, + {id:"voices-from-beyond", name:"Voices from Beyond"}, {id:"warding-flare", name:"Warding Flare"}, {id:"wild-surge", name:"Wild Surge"}, {id:"witch-bolt", name:"Witch Bolt"} diff --git a/scripts/index.js b/scripts/index.js index e5e0dee..6a53d9b 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -46,6 +46,13 @@ Hooks.once('init', async function() { type: Boolean, requiresReload: true }); + game.settings.register(module, "logging", { + name: "Debug", + scope: "world", + config: true, + default: true, + type: Boolean + }); game.settings.register(module, "marker", { name: game.i18n.localize("Turn Marker"), hint: game.i18n.localize("Choose turn marker."), diff --git a/scripts/napolitano-scripts.js b/scripts/napolitano-scripts.js index 32c10e4..e93c733 100644 --- a/scripts/napolitano-scripts.js +++ b/scripts/napolitano-scripts.js @@ -14,9 +14,8 @@ export class napolitano { * @param {...any} args - what to log */ static log(force, ...args) { - const shouldLog = true//force || game.modules.get('_dev-mode')?.api?.getPackageDebugValue(this.ID); - - if (shouldLog) { + + if (force || (game.user.isGM && game.settings.get(napolitano.ID, 'logging'))) { console.log(this.ID, '|', ...args); } } diff --git a/scripts/workflow.js b/scripts/workflow.js index 73124bd..e854cc0 100644 --- a/scripts/workflow.js +++ b/scripts/workflow.js @@ -1653,6 +1653,7 @@ export class workflow extends framework { case 'torch': flow._torch(); break; case 'totemSpiritBear': flow._totemSpiritBear(); break; case 'varallasClawsOfDarkness': flow._varallasClawsOfDarkness(); break; + case 'voicesFromBeyond': flow._voicesFromBeyond(); break; case 'whisperingAura': flow._whisperingAura(); break; case 'wildSurgeRetribution': flow._wildSurgeRetribution(); break; case 'witchBolt': flow._witchBolt(); break; @@ -1941,6 +1942,7 @@ export class workflow extends framework { break; case 1: if(game.settings.get("napolitano-scripts", "intrusive-echoes")) workflow.play('intrusiveEchoes', this.data, {hook: this.hook}) + if(game.settings.get("napolitano-scripts", "voices-from-beyond")) workflow.play('voicesFromBeyond', this.data, {hook: this.hook}) break; case 13: if(game.settings.get("napolitano-scripts", "echoing-mind")) workflow.play('echoingMind', this.data, {hook: this.hook}) @@ -3356,6 +3358,18 @@ export class workflow extends framework { await this.summon(); } + /** + * Tested: v12 + * Rolls Voices from Beyond table (on 1) + * @returns + */ + async _voicesFromBeyond(){ + if(!this.setItem()) return + await this.rollTable() + await this.updateItemUses(-1) + this.message(`Voices from Beyond: ${this.tableRollText}`, {title: 'Voices from Beyond'}) + } + async _wardingFlare(){ if(!this.hasTargets || !this.itemData.isAttack) return const results = [] From 86c2dc93a56ebc28b4cb4e0d8959aa8c91f441c5 Mon Sep 17 00:00:00 2001 From: napolitanod Date: Mon, 15 Jul 2024 19:50:30 -0700 Subject: [PATCH 2/4] 1.2.7 --- scripts/constants.js | 1 + scripts/macros.js | 11 ----------- scripts/workflow.js | 21 ++++++++++++++++----- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/scripts/constants.js b/scripts/constants.js index c2ee711..92eb27d 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -845,6 +845,7 @@ export const HOOKEDUSEITEMITEMS = { 'Feather of Diatryma Summoning': 'featherOfDiatrymaSummoning', 'Figurine of Wondrous Power (Golden Lions)': 'figurineOfWonderousPowerLions', 'Figurine of Wondrous Power (Obsidian Steed)': 'figurineOfWonderousPowerObsidianSteed', + 'Fog Cloud': 'fogCloud', 'Grease': 'grease', 'Halo of Spores': 'haloOfSpores', "Maximilian's Earthen Grasp": 'maximiliansEarthenGrasp', diff --git a/scripts/macros.js b/scripts/macros.js index 4a34c55..5d2814a 100644 --- a/scripts/macros.js +++ b/scripts/macros.js @@ -61,7 +61,6 @@ game.napolitano.macros(args, 'createBonfire', _napOps) case 'falseLife': await macro._falseLife(); break; case 'findFamiliar': await macro._findFamiliar(); break; case 'fireShield': await macro._fireShield(); break; - case 'fogCloud': await macro._fogCloud(); break; case 'formOfDread': await macro._formOfDread(); break; case 'gazerEyeRays': await macro._gazerEyeRays(); break; case 'genericContest': await macro._genericContest(); break; @@ -765,16 +764,6 @@ game.napolitano.macros(args, 'createBonfire', _napOps) const choice = await this.choose(['Warm', 'Chill'], 'Choose shield type.', 'Fire Shield', {owner: this.sourceData.owner, img: this.item.img}) if(choice) await this.addActiveEffect({effectName: `Fire Shield - ${choice}`, uuid: this.source.actor.uuid, origin: this.source.actor.uuid}) } - - async _fogCloud(){ - this.summonData.updates = { - token: { - height: 8 * this.spellLevel, - width: 8 * this.spellLevel - } - } - await this.summon(); - } async _formOfDread(){ const tempHp = 10 + this.sourceData.warlockLevel diff --git a/scripts/workflow.js b/scripts/workflow.js index e854cc0..69be810 100644 --- a/scripts/workflow.js +++ b/scripts/workflow.js @@ -144,7 +144,7 @@ export class framework { get currentCombatant(){ if(!this.hasCombat || !this.combat.current?.tokenId) return - return this.combat.scene.getEmbeddedDocument("Token", this.combat.current.tokenId) + return game.scenes.get(this.combat.combatant.sceneId).getEmbeddedDocument("Token", this.combat.current.tokenId) } get currentCombatantPlaceable(){ @@ -2392,11 +2392,22 @@ export class workflow extends framework { } async _fogCloud(){ - if(this.hook === 'createToken') { - await this.buildBoundaryWall(this.source.token) - if(game.modules.get('token-attacher')?.active) await tokenAttacher.attachElementsToToken(this.walls, this.source.token.id) + if(this.hook === 'dnd5e.useItem') { + this.summonData.updates = { + token: { + height: 8 * this.spellLevel, + width: 8 * this.spellLevel + } + } + await this.summon(); + } + else { + if(this.hook === 'createToken') { + await this.buildBoundaryWall(this.source.token) + if(game.modules.get('token-attacher')?.active) await tokenAttacher.attachElementsToToken(this.walls, this.source.token.id) + } + await this.heavilyObscure() } - await this.heavilyObscure() } async _formOfDread(){ From 96238bca94c8f972a58515ef0ba2a60cd676652a Mon Sep 17 00:00:00 2001 From: napolitanod Date: Sat, 27 Jul 2024 08:59:43 -0700 Subject: [PATCH 3/4] 1.2.7 --- scripts/constants.js | 9 ++++ scripts/index.js | 6 ++- scripts/macros.js | 25 +-------- scripts/tooltip.js | 1 + scripts/workflow.js | 117 +++++++++++++++++++++++++++++++------------ 5 files changed, 101 insertions(+), 57 deletions(-) diff --git a/scripts/constants.js b/scripts/constants.js index 92eb27d..8766d27 100644 --- a/scripts/constants.js +++ b/scripts/constants.js @@ -668,6 +668,11 @@ export const NAPOLITANOCONFIG = { effects:{pre: {file: 'modules/jb2a_patreon/Library/1st_Level/Sleep/Cloud01_02_Regular_Pink_400x400.webm', scale: 1}}, name: "Sleep" }, + sneakAttack: { + effects: {pre: {file: 'modules/jb2a_patreon/Library/1st_Level/Sneak_Attack/Sneak_Attack_Regular_Blue_300x300.webm', scale: 1, wait: 100}}, + name: "Sneak Attack", + prompt: "Use Sneak Attack?" + }, spikeGrowth: { name: "Spike Growth", killIn: {minutes: 10} @@ -841,6 +846,7 @@ const LINKDATACATEGORIES = { export const HOOKEDUSEITEMITEMS = { 'Accursed Specter': 'accursedSpecter', + 'Booming Blade': 'boomingBlade', 'Divine Smite': 'divineSmite', 'Feather of Diatryma Summoning': 'featherOfDiatrymaSummoning', 'Figurine of Wondrous Power (Golden Lions)': 'figurineOfWonderousPowerLions', @@ -848,8 +854,10 @@ export const HOOKEDUSEITEMITEMS = { 'Fog Cloud': 'fogCloud', 'Grease': 'grease', 'Halo of Spores': 'haloOfSpores', + 'Hand Grenade': 'handGrenade', "Maximilian's Earthen Grasp": 'maximiliansEarthenGrasp', 'Polymorph': 'polymorph', + 'Silvery Barbs': 'silveryBarbs', 'Spare the Dying': 'spareTheDying', 'Spike Growth': 'spikeGrowth', 'Symbiotic Entity': 'symbioticEntity', @@ -909,6 +917,7 @@ export const CONFIGS = [ {id:"relentless-endurance", name:"Relentless Endurence"}, {id:"shield", name:"Shield (spell)"}, {id:"silvery-barbs", name:"Silvery Barbs"}, + {id:"sneak-attack", name:"Sneak Attack"}, {id:"taste-of-the-stones", name:"Taste of the Stones"}, {id:"voices-from-beyond", name:"Voices from Beyond"}, {id:"warding-flare", name:"Warding Flare"}, diff --git a/scripts/index.js b/scripts/index.js index 6a53d9b..a0435d1 100644 --- a/scripts/index.js +++ b/scripts/index.js @@ -243,7 +243,7 @@ Hooks.once("midi-qol.midiReady", () => { }); HOOKIDS['midi-qol.DamageRollComplete'] = Hooks.on("midi-qol.DamageRollComplete", async (data) => { - const hook = "midi-qol.DamageRollComplete"; + const hook = "midi-qol.DamageRollComplete", results = [];; if(data.item?.name !== "Armor of Agathys" && game.settings.get("napolitano-scripts", "armor-of-agathys")){ workflow.play('armorOfAgathys', data, {hook: hook}) } @@ -271,10 +271,12 @@ Hooks.once("midi-qol.midiReady", () => { if(game.settings.get("napolitano-scripts", "disarming-attack")){ workflow.play('disarmingAttack', data, {hook: hook}); } + if(game.settings.get("napolitano-scripts", "sneak-attack")) results.push(workflow.playAsync('sneakAttack', data, {hook: hook})) switch(data.item?.name){ case 'Hungry Jaws': if(game.settings.get("napolitano-scripts", "hungry-jaws")) workflow.play('hungryJaws', data, {hook: hook}); break; - case 'Magic Missile': if(data.options?.notCast) await workflow.playAsync('magicMissile', data, {hook: hook}); break; + case 'Magic Missile': if(data.options?.notCast) results.push(workflow.playAsync('magicMissile', data, {hook: hook})); break; } + await Promise.all(results) }); HOOKIDS['midi-qol.preApplyDynamicEffects'] = Hooks.on('midi-qol.preApplyDynamicEffects', async function(data){ diff --git a/scripts/macros.js b/scripts/macros.js index 5d2814a..0f43623 100644 --- a/scripts/macros.js +++ b/scripts/macros.js @@ -37,7 +37,6 @@ game.napolitano.macros(args, 'createBonfire', _napOps) case 'bigbysHand': await macro._bigbysHand(); break; case 'blight': await macro._blight(); break; case 'blindnessDeafness': await macro._blindnessDeafness(); break; - case 'boomingBlade': await macro._boomingBlade(); break; case 'brazierOfCommandingFireElementals': await macro._brazierOfCommandingFireElementals(); break; case 'channelDivinityInvokeDuplicity': await macro._channelDivinityInvokeDuplicity(); break; case 'chromaticOrb': await macro._chromaticOrb(); break; @@ -95,7 +94,6 @@ game.napolitano.macros(args, 'createBonfire', _napOps) case 'shoveToSide': await macro._shove(); break; case 'shoveBack': await macro._shove(); break; case 'shoveProne': await macro._shove(); break; - case 'silveryBarbs': await macro._silveryBarbs(); break; case 'spiritualWeapon': await macro._spiritualWeapon(); break; case 'stormSphere': await macro._stormSphere(); break; case 'summonAberration': await macro._summonAberration(); break; @@ -267,7 +265,7 @@ game.napolitano.macros(args, 'createBonfire', _napOps) if(!this.hasHitTargets) return this.generateEffect(this.firstTarget, {source: this.source.token}) const type = this.getActorType(this.firstTarget) - this.roll = await new Roll(`${type==='plant' ? `${((this.spellLevel+4) * 8)}d1` : `${this.spellLevel+4}d8`}`).evaluate({async: true}); + this.roll = await new Roll(`${type==='plant' ? `${((this.spellLevel+4) * 8)}d1` : `${this.spellLevel+4}d8`}`).evaluate(); const saveRoll = await this.firstTarget.actor.rollAbilitySave("con", {flavor: `${CONFIG.DND5E.abilities["con"].label} DC ${this.sourceData.spelldc} Blight`, fastforward: true, disadvantage: type==='plant' ? true : false }) if (["undead", "construct"].includes(type)) return this.message(`${this.firstTarget.name} resists the blight because they are a ${type}`, {title: 'Blight Resisted', whisper: "GM"}); await this.damage({type: "necrotic", targets: [this.firstTarget], half: saveRoll.total >= this.sourceData.spelldc ? true : false}) @@ -279,19 +277,6 @@ game.napolitano.macros(args, 'createBonfire', _napOps) if(choice) await this.addActiveEffect({effectName: `${choice}ed Spell`, uuid: this.firstTarget.actor.uuid, origin: this.source.actor.uuid}) } - async _boomingBlade(){ - const weapons = this.source.actor.items.filter(i => i.type === 'weapon' && i.system.actionType === 'mwak') - const choice = await this.choose(weapons.map(w => [w.id, w.name]), 'Choose the weapon to make the booming blade attack with.', `${this.name} choose weapon`) - const item = weapons.find(w => w.id === choice) - let result - if(item) result = await this.useItem(item) - if(result.hitTargets.size){ - const target = result.hitTargets.values().next().value - if(target && this.cantripScale > 1) await this.damage({targets: [target], dice: `${this.cantripScale - 1}d8`, type: 'thunder'}) - await this.addActiveEffect({effectName: 'Booming Blade', uuid: target.actor.uuid, origin: this.source.actor.uuid}) - } - } - async _brazierOfCommandingFireElementals(){ await this.summon(); this.message( `${this.name} speaks the brazier’s command word and summons a fire elemental.`, {title: this.item.name}) @@ -1304,12 +1289,6 @@ game.napolitano.macros(args, 'createBonfire', _napOps) } } - async _silveryBarbs(){ - const response = await this.promptTarget({title: 'Silvery Barbs Advantage', origin: this.source.actor.uuid, owner: this.sourceData.owner, event: 'Grant Silvery Barbs Advantage', prompt: `Select a target to grant advantage to on their next attack roll, saving throw or ability check.`}) - const target = this.scene.tokens.find(t => t.id === response.targets[0]) - if(target) this.addActiveEffect({effectName: 'Silvery Barbs Advantage', origin: response.origin, uuid: target.actor.uuid}) - } - async _spiritualWeapon(){//tested v10 const weapon = await this.choose([['sword', 'Sword'], ['mace', 'Mace'], ['scythe', 'Scythe'], ['maul', 'Maul']] , 'Choose the form the spectral weapon takes:', 'Weapon Type') const color = await this.choose([['blue', 'Blue'], ['green', 'Green'], ['orange', 'Orange'], ['purple', 'Purple'], ['red', 'Red']], 'Choose the color of the weapon', 'Weapon Color') @@ -1589,7 +1568,7 @@ game.napolitano.macros(args, 'createBonfire', _napOps) async _tollTheDead(){ if (!this.failedSaves.size) return - this.roll = await new Roll(`${this.cantripScale}d${this.hasMaxHP(this.firstFailedSave) ? 8 : 12}[${"necrotic"}]`).evaluate({async: true}); + this.roll = await new Roll(`${this.cantripScale}d${this.hasMaxHP(this.firstFailedSave) ? 8 : 12}[${"necrotic"}]`).evaluate(); await this.damage({type: "necrotic", critical: this.isCritical}) } diff --git a/scripts/tooltip.js b/scripts/tooltip.js index 9366e08..1cb4e03 100644 --- a/scripts/tooltip.js +++ b/scripts/tooltip.js @@ -204,6 +204,7 @@ const OVERRIDES = { ,'Form of Dread: Transform': 'Roll this item to apply the effect to your token. You will be prompted automatically when saving throw opportunity arises during an attack.' ,'Green-Flame Blade': 'Roll this item before making a melee weapon attack. On your next melee attack you will then gain the benefits of this spell and you will be prompted for any spreading damage.' ,'Halo of Spores': "No roll required. You will be prompted to use this ability when the opportunity arises and if you haven't yet used this reaction since your last turn." + ,"Hand Grenade": "Roll this item to place template within 30 feet range." ,"Hexblade's Curse": "Target a creature and then roll this item. The curse will be automatically accounted for when you roll attacks and damage." ,'Improved Flare': 'For each attack made against a friendly create not you, you will be prompted as to whether or not you wish to use your warding flare. Confirming that you wish to use will apply the disadvantage to the attack roll. Disable or enable the prompt by toggling the active effect for this item.' ,'Maneuvers: Disarming Attack': 'Roll item to indicate your desire to use disarming attack on your next hit. This will mark your token. On your next damage roll disarming attack will be invoked and at that time your superiority die will be used.' ,'Magic Missile': 'Target a creature then roll item. You will then be prompted to select the remainder of the available targets (based on spell level) one at a time.' diff --git a/scripts/workflow.js b/scripts/workflow.js index 69be810..9a4f420 100644 --- a/scripts/workflow.js +++ b/scripts/workflow.js @@ -16,7 +16,8 @@ export class framework { }, this.damageData = { roll: {}, - original: {} + original: {}, + workflow: {} }, this.data = data ?? {}, this.failedSaves = new Set, @@ -152,6 +153,10 @@ export class framework { return canvas.tokens.get(this.combat.current.tokenId) ?? {} } + get damageRollCount(){ + return this.data.damageRollCount ?? 0 + } + get damageTotal(){ return this.data?.damageTotal ?? 0 } @@ -160,6 +165,10 @@ export class framework { return Array.from(this.failedSaves) } + get firstDamageRoll(){ + return this.data.damageRolls[0] + } + get gridDistance(){ return this.scene.dimensions.distance } @@ -224,9 +233,11 @@ export class framework { dc: this.item.system.save.dc, isAttack: ["mwak","rwak","msak","rsak"].includes(this.item.system.actionType) ? true : false, isDamage: game.dnd5e.config.damageTypes[this.item.system.damage?.parts?.[0]?.[1]] ? true : false, + isFinesse: this.item.system.properties.has("fin"), isMeleeAttack: ["mwak","msak"].includes(this.item.system.actionType) ? true : false, isMeleeWeaponAttack: this.item.system.actionType ==="mwak" ? true : false, isRangedAttack: ["rwak","rsak"].includes(this.item.system.actionType) ? true : false, + isRangedWeaponAttack: ["rwak"].includes(this.item.system.actionType) ? true : false, isSpell: this.item.type === 'spell' ? true : false, isSpellAttack: ["msak","rsak"].includes(this.item.system.actionType) ? true : false, isWeaponAttack: ["mwak","rwak"].includes(this.item.system.actionType) ? true : false, @@ -284,6 +295,7 @@ export class framework { owner: getActorOwner(this.source.actor), prof: this.source.actor.system.attributes.prof ?? 0, race: this.source.actor.system.details.race, + rogueLevel: this.source.actor.classes.rogue?.system?.levels ?? 0, size: this.getSize(this.source.actor), spellAbility: this.source.actor.system.attributes.spellcasting, spellAttack: this.source.actor.system.attributes.spelldc - 8, @@ -541,18 +553,17 @@ export class framework { half = false, show = true }={}){ - this.damageData.original = dice ? await new Roll(dice).evaluate({async: true}) : roll - if(critical) this.damageData.original = MidiQOL.doCritModify(this.damageData.original) + this.damageData.original = dice ? await new Roll(dice).evaluate() : roll if(show) await this.showRoll(this.damageData.original) this.damageData.roll = half ? await this.halfRoll(this.damageData.original) : this.damageData.original - await new MidiQOL.DamageOnlyWorkflow( + this.damageData.workflow = await new MidiQOL.DamageOnlyWorkflow( actor, token, this.damageData.roll.total, type, targets, this.damageData.roll, - {flavor: flavor ? flavor : `${this.config.name ?? 'Damage'} from ${actor.name} adds ${this.damageData.roll.result} ${type} damage${half ? ' (half ' + this.damageData.original.result + ' damage due to save)' : ''}.`, itemData: itemData, itemCardId: itemCardId } + {flavor: flavor ? flavor : `${this.config.name ?? 'Damage'} from ${actor.name} adds ${this.damageData.roll.result} ${type} damage${half ? ' (half ' + this.damageData.original.result + ' damage due to save)' : ''}.`, itemData: itemData, itemCardId: itemCardId, isCritical: this.isCritical } ); } @@ -808,7 +819,7 @@ export class framework { } async halfRoll(roll = this.roll){ - roll = await new Roll(`${Math.floor(roll.total/2)}`).evaluate({async: true}) + roll = await new Roll(`${Math.floor(roll.total/2)}`).evaluate() return roll } @@ -840,7 +851,7 @@ export class framework { const actor = document.actor ?? document if(!this.isActor(actor)) return const last = this.getFlag(actor, flag)?.[`${id}`] - return (last.time > this.now || (last.time === this.now && last.turn >= this.combat.current.turn)) ? true : false + return (last && (last.time > this.now || (last.time === this.now && last.turn >= this.combat.current.turn))) ? true : false } async heavilyObscure({target = this.firstTarget, origin = this.source.actor.uuid, source = this.source.token}={}){ @@ -936,7 +947,7 @@ export class framework { } async randomDirection(){ - const directionRoll = await new Roll(`1d8`).evaluate({async: true}) + const directionRoll = await new Roll(`1d8`).evaluate() switch(directionRoll.result){ case '1': return 'north'; case '2': return 'northeast'; @@ -1018,7 +1029,7 @@ export class framework { const actor = document.actor ?? document if(!this.isActor(actor)) return const dice = `1${actor.system.scale.bard.inspiration?.formula}` - const result = await new Roll(dice).evaluate({async: true}) + const result = await new Roll(dice).evaluate() if(show) this.showRoll(result) return result } @@ -1035,7 +1046,7 @@ export class framework { } async rollDice(dice, {show=true, toWorkflow=true}={}){ - const roll = await new Roll(dice).evaluate({async: true}) + const roll = await new Roll(dice).evaluate() if(toWorkflow) this.roll = roll if(show) this.showRoll(roll) return roll @@ -1073,7 +1084,7 @@ export class framework { } async rollSuperiorityDie({show=true}={}){ - const result = await new Roll(this.sourceData.superiorityDie).evaluate({async: true}) + const result = await new Roll(this.sourceData.superiorityDie).evaluate() if(show) this.showRoll(result) return result } @@ -1567,6 +1578,7 @@ export class workflow extends framework { case 'scorchingRay': await flow._scorchingRay(); break; case 'shield': await flow._shield(); break; case 'silveryBarbs': await flow._silveryBarbs(); break; + case 'sneakAttack': await flow._sneakAttack(); break; case 'wardingFlare': await flow._wardingFlare(); break; } } @@ -1615,6 +1627,7 @@ export class workflow extends framework { case 'grease': flow._grease(); break; case 'greenFlameBlade': flow._greenFlameBlade(); break; case 'haloOfSpores': flow._haloOfSpores(); break; + case 'handGrenade': flow._handGrenade(); break; case 'healingSpirit': flow._healingSpirit(); break; case 'heatedBody': flow._heatedBody(); break; case 'hex': flow._hex(); break; @@ -1736,7 +1749,7 @@ export class workflow extends framework { return } const roll = await this.rollDice('1d8') - await this.appendRoll(this.data.damageRoll, roll, {sign: 1, isDamage: true}) + await this.appendRoll(this.firstDamageRoll, roll, {sign: 1, isDamage: true}) this.appendMessageMQ(`+${roll.total} ${this.itemData.damageType} damage due to Arcane Firearm.`) } } else { @@ -1780,7 +1793,7 @@ export class workflow extends framework { } }); if(damage) { - this.roll = await new Roll(`${damage}d1`).evaluate({async: true}) + this.roll = await new Roll(`${damage}d1`).evaluate() await this.damage({type: "cold", targets: [this.source.token], itemData: this.getItem(), itemCardId: "new"}) this.message(`The Armor of Agathys from the targets that were attacked deals ${damage} cold damage to ${this.source.actor.name}.`, {title: 'Armor of Agathys'}) //no itemcard so flavor is messaged in chat } @@ -1892,7 +1905,19 @@ export class workflow extends framework { } async _boomingBlade(){ - if(this.hasEffect(this.source.actor, 'Booming Blade')){ + if(this.hook === 'dnd5e.useItem'){ + const weapons = this.source.actor.items.filter(i => i.type === 'weapon' && i.system.actionType === 'mwak') + const choice = await this.choose(weapons.map(w => [w.id, w.name]), 'Choose the weapon to make the booming blade attack with.', `${this.name} choose weapon`) + const item = weapons.find(w => w.id === choice) + let result + if(item) result = await this.useItem(item) + if(result.hitTargets.size){ + const target = result.hitTargets.values().next().value + if(target && this.cantripScale > 1) await this.damage({targets: [target], dice: `${this.cantripScale - 1}d8`, type: 'thunder'}) + await this.addActiveEffect({effectName: 'Booming Blade', uuid: target.actor.uuid, origin: this.source.actor.uuid}) + } + } + else if(this.hasEffect(this.source.actor, 'Booming Blade')){ let eff = this.getEffect(this.source.actor, "Booming Blade") const source = fromUuidSync(eff.origin) if(source){ @@ -2096,8 +2121,8 @@ export class workflow extends framework { break; case 'midi-qol.preDamageRollComplete': type = 'damage' - originalRollTotal = this.data.damageRoll?.terms[0]?.total ?? 0 - rollSource = this.data.damageRoll + originalRollTotal = this.firstDamageRoll?.terms[0]?.total ?? 0 + rollSource = this.firstDamageRoll break; default: type = 'ability' @@ -2194,14 +2219,14 @@ export class workflow extends framework { confirmed: { label: "Yes", callback: async () => { - this.roll = await new Roll(`${numDice * 2}d8`).evaluate({async: true}); + this.roll = await new Roll(`${numDice * 2}d8`).evaluate(); this.damage({type: 'radiant', targets: [this.firstTarget]}) } }, cancel: { label: "No", callback: async () => { - this.roll = await new Roll(`${numDice}d8`).evaluate({async: true}); + this.roll = await new Roll(`${numDice}d8`).evaluate(); this.damage({type: 'radiant', targets: [this.firstTarget]}) } } @@ -2265,7 +2290,7 @@ export class workflow extends framework { if(this.hook === 'midi-qol.preDamageRollComplete'){ if(!this.hasHitTargets) return if(this.hasItem({itemName: 'Eldritch Invocations: Agonizing Blast'})){ - await this.appendRoll(this.data.damageRoll, false, {sign: 1, isDamage: true, mod: this.sourceData.charismaMod}) + await this.appendRoll(this.firstDamageRoll, false, {sign: 1, isDamage: true, mod: this.sourceData.charismaMod}) await this.appendMessageMQ(`+${this.sourceData.charismaMod} force damage from Agonizing Blast.`) } } @@ -2530,6 +2555,11 @@ export class workflow extends framework { } } + async _handGrenade(){ + await dangerZone.triggerZone ("Hand Grenade", this.scene.id, {sources: [this.source.token], location: {x: this.template?._object?.bounds?.x ?? this.template.x, y: this.template?._object?.bounds?.y ?? this.template.y, z: this.template.elevation}}) + await this.deleteTemplates(); + } + async _healingSpirit(){//tested v10 this.summonData.updates = { @@ -2581,7 +2611,7 @@ export class workflow extends framework { } }); if(hitTargets.length){ - this.roll = await new Roll(`1d6`).evaluate({async: true}) + this.roll = await new Roll(`1d6`).evaluate() await this.damage({flavor: `The lasting hex from ${this.source.actor.name} adds ${this.roll.result} necrotic damage.`, type: "necrotic", targets: hitTargets, itemData: this.getItem(), itemCardId: "new"}) } } @@ -2605,9 +2635,9 @@ export class workflow extends framework { } }); if(hitTargets.length){ - this.roll = await new Roll(`${this.sourceData.prof}d1`).evaluate({async: true}) + this.roll = await new Roll(`${this.sourceData.prof}d1`).evaluate() await this.damage({flavor: `The hexblade curse from ${this.source.actor.name} adds ${this.roll.result} damage.`, type: "none", targets: hitTargets, itemData: this.getItem(), itemCardId: "new"}) - const roll = await new Roll(`${this.sourceData.charismaMod + this.sourceData.warlockLevel}d1`).evaluate({async: true}) + const roll = await new Roll(`${this.sourceData.charismaMod + this.sourceData.warlockLevel}d1`).evaluate() if(hitTargets.find(t => this.getHP(t) <= 0)) await this.damage({roll: roll, flavor: `The hexblade curse heals ${this.source.actor.name} ${roll.result} due to the death of their target.`, type: "healing", targets: [this.source.token], itemData: this.getItem(), itemCardId: "new"}) } } @@ -2648,7 +2678,7 @@ export class workflow extends framework { } async _interception(){ - const originalRollTotal = this.data.damageRoll?.total ?? 0 + const originalRollTotal = this.data.damageTotal ?? 0 if(!this.itemData.isAttack || !originalRollTotal) return const results = [] for(const pl of canvas.tokens.placeables) { @@ -2678,7 +2708,7 @@ export class workflow extends framework { if(value.yes){ const roll = await this.rollDice(`1d10`) const mod = value.document.actor.system.attributes.prof - await this.appendRoll(this.data.damageRoll, roll, {sign: -1, isDamage: true, mod: mod}) + await this.appendRoll(this.firstDamageRoll, roll, {sign: -1, isDamage: true, mod: mod}) this.message(`${value.document.name} uses Interception, reducing the damage dealt of ${originalRollTotal} by ${roll.total + mod}.`, {title: "Interception"}) this.appendMessageMQ(`-${roll.total + mod} to damage due to Interception.`) this.addActiveEffect({effectName: 'Reaction', uuid: value.document.actor.uuid, origin: value.document.actor.uuid}) @@ -2796,7 +2826,7 @@ export class workflow extends framework { } async _melfsAcidArrow(){ - if(!this.DamageOnlyWorkflow && this.hasTargets && !this.hasHitTargets) await this.damage({type: 'acid', half: true, roll: this.data.damageRoll, targets: [this.firstTarget], flavor: `${this.firstTarget.name} sustains acid damage from a missed acid arrow attack.`}) + if(!this.DamageOnlyWorkflow && this.hasTargets && !this.hasHitTargets) await this.damage({type: 'acid', half: true, roll: this.firstDamageRoll, targets: [this.firstTarget], flavor: `${this.firstTarget.name} sustains acid damage from a missed acid arrow attack.`}) } async _message(){ @@ -2915,7 +2945,7 @@ export class workflow extends framework { } async _parry(){ - const originalRollTotal = this.data.damageRoll?.total ?? 0 + const originalRollTotal = this.data.damageTotal ?? 0 if(!this.itemData.isWeaponAttack || !originalRollTotal) return const results = [] const avail = this.hitTargetsArray.filter(t => @@ -2934,7 +2964,7 @@ export class workflow extends framework { if(value.yes){ const roll = await this.rollSuperiorityDie({document: value.document}) const mod = value.document.actor.system.abilities.dex.mod - await this.appendRoll(this.data.damageRoll, roll, {sign: -1, isDamage: true, mod: mod}) + await this.appendRoll(this.firstDamageRoll, roll, {sign: -1, isDamage: true, mod: mod}) const supItem = this.getItem('Superiority Dice', value.document); this.message(`${value.document.name} uses Parry, reducing the damage dealt of ${originalRollTotal} by ${roll.total + mod}.`, {title: "Parry"}) this.appendMessageMQ(`-${roll.total + mod} to damage due to Parry.`) @@ -2972,7 +3002,7 @@ export class workflow extends framework { async _potentSpellcasting(){ if(!this.hasItem() || !this.hasHitTargets || !this.damageTotal || !this.source.actor.id || !this.item.id || !(this.itemData.isSpell && this.itemData.baseSpellLevel === 0)) return - await this.appendRoll(this.data.damageRoll, false, {sign: 1, isDamage: true, mod: this.sourceData.wisdomMod}) + await this.appendRoll(this.firstDamageRoll, false, {sign: 1, isDamage: true, mod: this.sourceData.wisdomMod}) await this.appendMessageMQ(`+${this.sourceData.wisdomMod} additional damage from Potent Spellcasting.`) this.generateEffect(this.firstTarget) } @@ -3031,9 +3061,27 @@ export class workflow extends framework { } } + async _sneakAttack(){ + if(this.damageRollCount && this.hasHitTargets && this.hasItem("Sneak Attack") && (this.itemData.isRangedWeaponAttack || (this.itemData.isMeleeWeaponAttack && this.itemData.isFinesse) ) && this.sourceData.rogueLevel ){ + if(this.hasOccurredOnce()) return console.log('Sneak attack already used during this time', this) + let valid = this.data.advantage; + if(!valid) { + this.tokensInProximity(this.firstTarget) + if(!this.proximityTokens.find(t => t.id !== this.source.token.id && t.id !== this.firstTarget.id && !t.actor?.effects?.find(e => !e.disabled && INCAPACITATEDCONDITIONS.includes(e.name)) && t.disposition === this.tokenData.disposition)) return + } + this.setItem() + const useForm = await this.yesNo(); + if (useForm) { + await this.damage({targets: [this.firstHitTarget], dice: this.source.actor.system.scale.rogue['sneak-attack'].formula, type: '', critical: this.isCritical, show: false}) + this.message(`${this.firstHitTarget.name} sustains ${this.damageData.roll.result} damage from a sneak attack on a ${this.damageData.roll.formula} roll!`, {title: 'Sneak Attack'}) + await this.setOccurredOnce() + } + } + } + async _rayOfEnfeeblement(){ - if (this.data.damageRoll?.formula && this.hasEffect() && this.itemData.isWeaponAttack && this.itemModifier() === 'str'){ - await this.wrapRoll(this.data.damageRoll, {mod: 2, div: true}) + if (this.damageRollCount && this.hasEffect() && this.itemData.isWeaponAttack && this.itemModifier() === 'str'){ + await this.wrapRoll(this.firstDamageRoll, {mod: 2, div: true}) this.appendMessageMQ(`Damage halved due to Ray of Enfeeblement.`) } } @@ -3123,6 +3171,11 @@ export class workflow extends framework { async _silveryBarbs(){ let type, originalRollTotal, rollSource, success = true switch(this.hook){ + case 'dnd5e.useItem': + const response = await this.promptTarget({title: 'Silvery Barbs Advantage', origin: this.source.actor.uuid, owner: this.sourceData.owner, event: 'Grant Silvery Barbs Advantage', prompt: `Select a target to grant advantage to on their next attack roll, saving throw or ability check.`}) + const target = this.scene.tokens.find(t => t.id === response.targets[0]) + if(target) this.addActiveEffect({effectName: 'Silvery Barbs Advantage', origin: response.origin, uuid: target.actor.uuid}) + return case 'midi-qol.AttackRollComplete': type = 'attack' rollSource = this.data.attackRoll @@ -3443,7 +3496,7 @@ export class workflow extends framework { } }); if(hit) { - this.roll = await new Roll(`1d6`).evaluate({async: true}) + this.roll = await new Roll(`1d6`).evaluate() await this.damage({type: "force", targets: [this.source.token], itemData: this.getItem(), itemCardId: "new"}) this.message(`The Wild Magic Surge from the targets that were attacked deals ${this.roll.total} force damage to ${this.source.actor.name}.`, {title: 'Wild Magic Surge'}) } @@ -3458,7 +3511,7 @@ export class workflow extends framework { const doIt = await this.yesNo({title: 'Witch Bolt', prompt: `${this.name}, Continue concentrating?`, owner: this.sourceData.owner}) if(doIt){ this.generateEffect(this.firstHitTarget) - this.roll = await new Roll(`${this.spellLevel}d12`).evaluate({async: true}) + this.roll = await new Roll(`${this.spellLevel}d12`).evaluate() await this.damage({type: 'lightning'}) } else{ await this.removeConcentration() From 6ce02cb453be09adf03212e3834b30ca48b53f8f Mon Sep 17 00:00:00 2001 From: napolitanod Date: Sat, 27 Jul 2024 09:00:07 -0700 Subject: [PATCH 4/4] k --- module.json | 1 - 1 file changed, 1 deletion(-) diff --git a/module.json b/module.json index 2990286..5af63c4 100644 --- a/module.json +++ b/module.json @@ -1,5 +1,4 @@ { - "author": "napolitanod", "authors": [ { "name": "napolitanod"