diff --git a/module.json b/module.json
index 437fb6c..253fa7a 100644
--- a/module.json
+++ b/module.json
@@ -4,7 +4,7 @@
"description": "A module for dnd5e that adds support for Mob Attacks.",
"author": "Stendarpaval (Lupusmalus)",
"url": "https://github.com/Stendarpaval/mob-attack-tool",
- "version": "0.1.23",
+ "version": "0.1.24",
"minimumCoreVersion": "0.7.7",
"compatibleCoreVersion": "0.7.9",
"system": "dnd5e",
@@ -12,5 +12,5 @@
"scripts/mobAttack.js"
],
"manifest": "https://raw.githubusercontent.com/Stendarpaval/mob-attack-tool/main/module.json",
- "download": "https://github.com/Stendarpaval/mob-attack-tool/releases/download/v0.1.23/module.zip"
+ "download": "https://github.com/Stendarpaval/mob-attack-tool/releases/download/v0.1.24/module.zip"
}
diff --git a/scripts/mobAttack.js b/scripts/mobAttack.js
index f7b910c..6d5c2b6 100644
--- a/scripts/mobAttack.js
+++ b/scripts/mobAttack.js
@@ -37,6 +37,15 @@ Hooks.once("init", () => {
type: Boolean
})
+ game.settings.register(MODULE, "showMultiattackDescription", {
+ name: "Show multiattack description",
+ hint: "If enabled, then a multiattack description will be shown for any mob attacker that has a multiattack feature on their actor sheet. (Client specific setting)",
+ config: true,
+ scope: "client",
+ default: true,
+ type: Boolean
+ })
+
game.settings.register(MODULE, "autoDetectMultiattacks", {
name: "Autodetect multiattack",
hint: `Attempt to automatically detect the multiattack weapon options of mob attackers. If set to "Autodetect + autoselect", then the multiattack weapon options should already be selected when you open the Mob Attack dialog. (Client specific setting)`,
diff --git a/scripts/mobAttackTool.js b/scripts/mobAttackTool.js
index 8341eca..19ad5ed 100644
--- a/scripts/mobAttackTool.js
+++ b/scripts/mobAttackTool.js
@@ -60,6 +60,13 @@ async function mobAttackTool() {
if (!monsters[token.actor.id].optionVisible) {
content += `
` + await formatMonsterLabel(monsters[token.actor.id]);
monsters[token.actor.id].optionVisible = true;
+ if (game.settings.get(MODULE, "showMultiattackDescription")) {
+ if (token.actor.items.entries.filter(i => i.name.startsWith("Multiattack")).length > 0) {
+ content += `${token.actor.items.filter(i => i.name.startsWith("Multiattack"))[0].data.data.description.value}
`;
+ } else if (token.actor.items.entries.filter(i => i.name.startsWith("Extra Attack")).length > 0) {
+ content += `${token.actor.items.filter(i => i.name.startsWith("Extra Attack"))[0].data.data.description.value}
`;
+ }
+ }
}
}
@@ -459,13 +466,11 @@ async function processIndividualDamageRolls(data, weaponData, finalAttackBonus,
let diceFormula = diceFormulas.join(" + ");
let damageType = damageTypes.join(", ");
let damageRoll = new Roll(diceFormula, {mod: weaponData.actor.data.data.abilities[weaponData.abilityMod].mod});
-
- //TODO: use better crit formula
await damageRoll.alter(numHitAttacks, numCrits, {multiplyNumeric: true}).roll();
+ // Roll Dice so Nice dice
if (game.modules.get("dice-so-nice")?.active) game.dice3d.showForRoll(damageRoll);
- //TODO: find out how to properly tell MidiQOL about multiple damage types
new MidiQOL.DamageOnlyWorkflow(
weaponData.options.actor,
data.targetToken,
@@ -728,7 +733,7 @@ async function formatMonsterLabel(monsterData) {
async function formatWeaponLabel(weapons, itemData) {
let weaponLabel = ``;
let checkVersatile = itemData.data.data.damage.versatile != "";
- for (let j = 0; j < ((checkVersatile) ? 2 : 1); j++) {
+ for (let j = 0; j < 1 + ((checkVersatile) ? 1 : 0); j++) {
let isVersatile = (j < 1) ? false : itemData.data.data.damage.versatile != "";
let damageData = getDamageFormulaAndType(itemData, isVersatile);
let weaponDamageText = ``;
@@ -738,14 +743,14 @@ async function formatWeaponLabel(weapons, itemData) {
let numAttacksTotal = 1, preChecked = false;
let autoDetect = game.settings.get("mob-attack-tool","autoDetectMultiattacks");
if (autoDetect > 0) [numAttacksTotal, preChecked] = getMultiattackFromActor(itemData.name, itemData.actor, weapons);
- if (autoDetect === 1) preChecked = false;
+ if (autoDetect === 1 || isVersatile) preChecked = false;
let labelData = {
numAttacksName: `numAttacks${(itemData.id + ((isVersatile) ? ` (Versatile)` : ``)).replace(" ","-")}`,
numAttack: numAttacksTotal,
weaponImg: itemData.img,
weaponNameImg: itemData.name.replace(" ","-"),
- weaponName: itemData.name + ((isVersatile) ? ` (Versatile)` : ``),
+ weaponName: `${itemData.name}${((isVersatile) ? ` (Versatile)` : ``)}`,
weaponAttackBonus: getAttackBonus(itemData),
weaponDamageText: weaponDamageText,
useButtonName: `use${(itemData.id + ((isVersatile) ? ` (Versatile)` : ``)).replace(" ","-")}`,
@@ -821,7 +826,7 @@ function getAttackBonus(weaponData) {
const actorName = weaponData.actor.name;
let weaponAbility = weaponData.abilityMod;
if (weaponAbility === "" || typeof weaponAbility === "undefined" || weaponAbility == null) {
- if (!weaponData.type == "spell") {
+ if (!weaponData.type === "spell") {
weaponAbility = "str";
} else {
weaponAbility = weaponData.actor.data.data.attributes.spellcasting;
@@ -862,20 +867,23 @@ function getDamageFormulaAndType(weaponData, versatile) {
let diceFormulas = [];
let damageTypes = [];
let damageTypeLabels = []
+ let partsLength = weaponData.data.data.damage.parts.length;
+ let lengthIndex = 0;
for (let diceFormulaParts of weaponData.data.data.damage.parts) {
damageTypeLabels.push(diceFormulaParts[1]);
damageTypes.push(diceFormulaParts[1].capitalize());
if (weaponData.type == "spell") {
if (weaponData.data.data.scaling.mode == "cantrip") {
- let rollFormula = new Roll(((versatile) ? weaponData.data.data.damage.versatile : diceFormulaParts[0]),{mod: weaponData.actor.data.data.abilities[weaponData.abilityMod].mod});
+ let rollFormula = new Roll(((versatile && lengthIndex === 0) ? weaponData.data.data.damage.versatile : diceFormulaParts[0]),{mod: weaponData.actor.data.data.abilities[weaponData.abilityMod].mod});
rollFormula.alter(0,cantripScalingFactor,{multiplyNumeric: false})
diceFormulas.push(rollFormula.formula);
} else {
- diceFormulas.push(((versatile) ? weaponData.data.data.damage.versatile : diceFormulaParts[0]).replace("@mod",weaponData.actor.data.data.abilities[weaponData.abilityMod].mod));
+ diceFormulas.push(((versatile && lengthIndex === 0) ? weaponData.data.data.damage.versatile : diceFormulaParts[0]).replace("@mod",weaponData.actor.data.data.abilities[weaponData.abilityMod].mod));
}
} else {
- diceFormulas.push(((versatile) ? weaponData.data.data.damage.versatile : diceFormulaParts[0]).replace("@mod",weaponData.actor.data.data.abilities[weaponData.abilityMod].mod));
+ diceFormulas.push(((versatile && lengthIndex === 0) ? weaponData.data.data.damage.versatile : diceFormulaParts[0]).replace("@mod",weaponData.actor.data.data.abilities[weaponData.abilityMod].mod));
}
+ lengthIndex++;
}
return [diceFormulas, damageTypes, damageTypeLabels];
}
\ No newline at end of file
diff --git a/scripts/multiattack.js b/scripts/multiattack.js
index 38d7ca7..9d73353 100644
--- a/scripts/multiattack.js
+++ b/scripts/multiattack.js
@@ -61,13 +61,14 @@ export function getMultiattackFromActor(weaponName, actorData, weapons) {
if (attackType === `melee`) {
if (![`mwak`, `msak`].includes(weaponData.data.data.actionType)) {
attackType = `choose`;
+ numAttacksWeapon = 1;
}
} else if (attackType === `ranged`) {
if (![`rwak`, `rsak`].includes(weaponData.data.data.actionType)) {
attackType = `choose`;
+ numAttacksWeapon = 1;
}
}
- console.log(weaponName, attackType);
}
let weaponDetected = false;
@@ -76,8 +77,6 @@ export function getMultiattackFromActor(weaponName, actorData, weapons) {
// Step backwards through multiattack description
for (let word of remainingWords) {
-
-
// homogenize words to simplify detection
word = word.toLowerCase();
let interpunction = [",",".",":"];
@@ -96,7 +95,7 @@ export function getMultiattackFromActor(weaponName, actorData, weapons) {
}
// detect possibility of choosing what kind of multiattack to use
- const optionKeywordsSingle = [`or`,`alternatively`,` instead`];
+ const optionKeywordsSingle = [`or`, `alternatively`, `instead`, `while`];
if (weaponDetected) {
if (optionKeywordsSingle.includes(word)) {
attackType = `choose`;
@@ -129,7 +128,7 @@ export function getMultiattackFromActor(weaponName, actorData, weapons) {
// either return the specific or total number of multiattacks
if (numAttacksTotal !== 0) {
if (numAttacksWeapon !== 0) {
- multiattack = [(numWeaponsInventory === numAttacksWeapon) ? 1 : numAttacksWeapon, (attackType !== `choose`) ? true : false];
+ multiattack = [(numWeaponsInventory === numAttacksWeapon && numWeaponsInventory === numAttacksTotal) ? 1 : numAttacksWeapon, (attackType !== `choose`) ? true : false];
} else if (weaponDetected) {
multiattack = [(numWeaponsInventory === numAttacksTotal) ? 1 : numAttacksTotal, (attackType !== `choose`) ? true : false];
}