From e9743ed99e0e3670cca00026f59f2639c87f0037 Mon Sep 17 00:00:00 2001 From: raeleus Date: Sat, 12 Oct 2024 16:36:40 -0700 Subject: [PATCH] #attack and #cast can target enemies by enemy number --- Input.js | 112 +++++++++++++++++++++++++++++++++-------------------- Library.js | 1 - Output.js | 4 +- 3 files changed, 72 insertions(+), 45 deletions(-) diff --git a/Input.js b/Input.js index 60e846f..532e3f8 100644 --- a/Input.js +++ b/Input.js @@ -1527,8 +1527,26 @@ function doAttack(command) { if (targetIndex >= 0 && targetIndex < difficultyNames.length) targetRoll = difficultyScores[targetIndex] } - var enemyString + var enemyString = "" if (state.initiativeOrder.length > 0) { + var foundEnemy + + for (var enemy of state.enemies) { + if (targetText.toLowerCase().includes(enemy.name.toLowerCase())) { + foundEnemy = enemy + break + } + } + + if (foundEnemy == null) { + var indexMatches = targetText.match(/(?<=enemy\s*)\d+/gi) + if (indexMatches != null) { + foundEnemy = state.enemies[parseInt(indexMatches[0]) - 1] + log(`foundEnemy:${foundEnemy}`) + targetText = targetText.replace(/enemy\s*d+/gi, foundEnemy.name) + } + } + var damage if (/^\d*d\d+((\+|-)d+)?$/gi.test(character.damage)) damage = score == 20 ? calculateRoll(character.damage) + calculateRoll(character.damage) : calculateRoll(character.damage) else damage = parseInt(character.damage) @@ -1540,17 +1558,14 @@ function doAttack(command) { if (damageMatches != null) damage = score == 20 ? parseInt(damageMatches[damageMatches.length - 1]) * 2 : parseInt(damageMatches[damageMatches.length - 1]) } - for (var enemy of state.enemies) { - if (targetText.toLowerCase().includes(enemy.name.toLowerCase())) { - if (usingDefaultDifficulty) targetRoll = enemy.ac - if (score == 20 || score + modifier >= targetRoll) { - if (score == 20) enemyString += ` Critical Damage: ${damage}` - else enemyString += ` Damage: ${damage}` - enemy.health = Math.max(0, enemy.health - damage) - if (enemy.health == 0) enemyString = ` ${toTitleCase(enemy.name)} has been defeated!` - else enemyString = ` ${toTitleCase(enemy.name)} has ${enemy.health} health remaining!` - } - break + if (foundEnemy != null) { + if (usingDefaultDifficulty) targetRoll = foundEnemy.ac + if (score == 20 || score + modifier >= targetRoll) { + if (score == 20) enemyString += `\nCritical Damage: ${damage}\n` + else enemyString += `\nDamage: ${damage}\n` + foundEnemy.health = Math.max(0, foundEnemy.health - damage) + if (foundEnemy.health == 0) enemyString += ` ${toTitleCase(foundEnemy.name)} has been defeated!` + else enemyString += ` ${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!` } } } @@ -1565,8 +1580,8 @@ function doAttack(command) { else state.prefix = `\n[Enemy AC: ${targetRoll} Attack roll: ${dieText}. ${score >= targetRoll ? "Success!" : "Failure!"}]\n` var text - if (score + modifier >= targetRoll) text = `\n${character.name} successfully hit ${targetText}!` - else text = `\n${character.name} ${tryWord} to hit ${targetText} ${character.name} ${missWord}!` + if (score + modifier >= targetRoll) text = `\n${toTitleCase(character.name)} successfully hit ${targetText}!` + else text = `\n${toTitleCase(character.name)} ${tryWord} to hit ${targetText} ${toTitleCase(character.name)} ${missWord}!` if (score == 20) text += " Critical success! Your attack is exceptionally damaging!" else if (score == 1) text += " Critical failure! Your attack missed in a spectacular way!" @@ -2676,21 +2691,21 @@ function doCastSpell(command) { state.show = "none" return "\n[Error: Not enough parameters. See #help]\n" } - var target = null + var targetText = null var atWord = null var found = character.spells.find(x => x.toLowerCase() == spell.toLowerCase()) if (found != null) { - target = getArgumentRemainder(command, spellIndex + 1) - if (target != null) { - target = target.trim() - if (!/^((at)|(on))\s+.*/.test(target)) target = "at " + target + targetText = getArgumentRemainder(command, spellIndex + 1) + if (targetText != null) { + targetText = targetText.trim() + if (!/^((at)|(on))\s+.*/.test(targetText)) targetText = "at " + targetText } } else { var remainder = getArgumentRemainder(command, spellIndex) if (/.*\s((at)|(on))\s.*/i.test(remainder)) { spell = remainder.replace(/\s+((at)|(on)).*/i, "").trim() - target = remainder.replace(/^.*\s+(?=(at)|(on))/i, "").trim() + targetText = remainder.replace(/^.*\s+(?=(at)|(on))/i, "").trim() } else { spell = getArgumentRemainder(command, spellIndex).trim() } @@ -2700,10 +2715,10 @@ function doCastSpell(command) { if (found == null) { state.show = "none" - return `\n[${character.name} ${tryWord} to cast the spell ${spell}, but ${character.name == "You" ? "you" : character.name} ${dontWord} know it.]\n` + return `\n[${toTitleCase(character.name)} ${tryWord} to cast the spell ${spell}, but ${character.name == "You" ? "you" : toTitleCase(character.name)} ${dontWord} know it.]\n` } - var text = `${character.name} cast the spell ${spell}${advantage != "normal" ? " with " + advantage : ""}${target == null ? "" : " " + target}.` + var text = `${character.name} cast the spell ${spell}${advantage != "normal" ? " with " + advantage : ""}${targetText == null ? "" : " " + targetText}.` var modifier = 0 if (character.spellStat != null) { @@ -2715,45 +2730,58 @@ function doCastSpell(command) { var roll2 = calculateRoll("d20") var roll = advantage == "advantage" ? Math.max(roll1, roll2) : advantage == "disadvantage" ? Math.min(roll1, roll2) : roll1 - //add enemy damage here - var enemyString - if (target != null && state.initiativeOrder.length > 0) { + var enemyString = "" + if (targetText != null && state.initiativeOrder.length > 0) { + var foundEnemy + + for (var enemy of state.enemies) { + if (targetText.toLowerCase().includes(enemy.name.toLowerCase())) { + foundEnemy = enemy + break + } + } + + if (foundEnemy == null) { + var indexMatches = targetText.match(/(?<=enemy\s*)\d+/gi) + if (indexMatches != null) { + foundEnemy = state.enemies[parseInt(indexMatches[0]) - 1] + targetText = targetText.replace(/enemy\s*d+/gi, foundEnemy.name) + } + } + var damage = roll == 20 ? calculateRoll("2d6") + calculateRoll("2d6") : calculateRoll("2d6") - var damageMatches = target.match(/\d*d\d+((\+|-)d+)?/gi) + var damageMatches = targetText.match(/\d*d\d+((\+|-)d+)?/gi) if (damageMatches != null) damage = roll == 20 ? calculateRoll(damageMatches[0]) + calculateRoll(damageMatches[0]) : calculateRoll(damageMatches[0]) else { - damageMatches = target.match(/\d+/g) + damageMatches = targetText.match(/\d+/g) if (damageMatches != null) damage = roll == 20 ? parseInt(damageMatches[damageMatches.length - 1]) * 2 : parseInt(damageMatches[damageMatches.length - 1]) } - for (var enemy of state.enemies) { - if (target.toLowerCase().includes(enemy.name.toLowerCase())) { - if (usingDefaultDifficulty) difficulty = enemy.ac - if (roll + modifier >= difficulty) { - if (roll == 20) enemyString += ` Critical Damage: ${damage}` - else enemyString += ` Damage: ${damage}` - enemy.health = Math.max(0, enemy.health - damage) - if (enemy.health == 0) enemyString = ` ${toTitleCase(enemy.name)} has been defeated!` - else enemyString = ` ${toTitleCase(enemy.name)} has ${enemy.health} health remaining!` - } - break + if (foundEnemy != null) { + if (usingDefaultDifficulty) difficulty = foundEnemy.ac + if (roll == 20 || roll + modifier >= difficulty) { + if (roll == 20) enemyString += `\nCritical Damage: ${damage}\n` + else enemyString += `\nDamage: ${damage}\n` + foundEnemy.health = Math.max(0, foundEnemy.health - damage) + if (foundEnemy.health == 0) enemyString += `${toTitleCase(foundEnemy.name)} has been defeated!\n` + else enemyString = `${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!\n` } } } state.show = "prefix" var dieText = advantage == "advantage" || advantage == "disadvantage" ? `${advantage}(${roll1},${roll2})` : roll1 - var difficultyWord = target == null ? "Difficulty" : "Armor" + var difficultyWord = targetText == null ? "Difficulty" : "Armor" if (roll == 20) state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}. Critcal Success!]\n` else if (roll == 1) state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}. Critcal Failure!]\n` else if (modifier != 0) state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}${modifier > 0 ? "+" + modifier : modifier}=${roll + modifier}. ${roll + modifier >= difficulty ? "Success!" : "Failure!"}]\n` else state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}. ${roll + modifier >= difficulty ? "Success!" : "Failure!"}]\n` if (roll == 20) text += ` Critical success!` - else if (roll == 1) text += ` Critical failure! The spell ${target != null ? "misses" : "fails"} in a spectacular way.` - else if (roll + modifier >= difficulty) text += ` The spell ${target != null ? "hits the target" : "is successful"}!` - else text += ` The spell ${target != null ? "misses" : "fails"}!` + else if (roll == 1) text += ` Critical failure! The spell ${targetText != null ? "misses" : "fails"} in a spectacular way.` + else if (roll + modifier >= difficulty) text += ` The spell ${targetText != null ? "hits the target" : "is successful"}!` + else text += ` The spell ${targetText != null ? "misses" : "fails"}!` if (enemyString != null) text += enemyString diff --git a/Library.js b/Library.js index a517880..7e4b504 100644 --- a/Library.js +++ b/Library.js @@ -1390,7 +1390,6 @@ function createEncounter(listName) { initiativeSuffix += Math.floor(3 * (multiplier - 1)) initiativeSuffix = `${initiativeSuffix > 0 ? "+" : ""}${initiativeSuffix}` enemy.initiative = `${initiativePrefix}${initiativeSuffix == 0 ? "" : initiativeSuffix}` - log(enemy) } return encounter diff --git a/Output.js b/Output.js index 2ea03d0..fe6128f 100644 --- a/Output.js +++ b/Output.js @@ -375,9 +375,9 @@ const modifier = (text) => { text += "\n#try (ability|skill) (advantage|disadvantage) (difficulty_class or automatic|effortless|easy|medium|hard|impossible) task" text += "\n Attempts to do the task based on the character's ability/skill against the specified difficulty. Quotes are not necessary." text += "\n#attack (ranged) (advantage|disadvantage) (ac or effortless|easy|medium|hard|impossible) target" - text += "\n Attacks the specified target with a melee (the default) or ranged attack. The roll is compared against the specified AC which will determine if the attack succeeds or misses. If the AC is not specified, the default AC or the AC of the opponent in combat will be used. The parameters can be listed in any order, except the target must be listed last. Quotes are not necessary." + text += "\n Attacks the specified target with a melee (the default) or ranged attack. The roll is compared against the specified AC which will determine if the attack succeeds or misses. If the AC is not specified, the default AC or the AC of the opponent in combat will be used. The parameters can be listed in any order, except the target must be listed last. The target can include the name of the enemy or the word \"enemy\" and the number of the enemy as listed in #enemies. The target can also include a damage amount. If the damage is not specified, the character's default damage is used. Quotes are not necessary.\nExample:\nAstri #attack advantage The Evil Knight for 2d12+2 damage" text += "\n#cast (advantage|disadvantage) (difficulty_class or effortless|easy|medium|hard|impossible) spell(target)" - text += "\n Character will cast the indicated spell if the spell is in their spellbook. It will be a targeted spell if a target is indicated. The roll is modified by the spell casting ability of the character. You may type a phrase without quotes for spell such as \"cast fire bolt at the giant chicken\". If the difficulty is not specified, the default difficulty or the AC of the opponent in combat will be used. The parameters can be listed in any order, except the target must be listed last. Quotes are not necessary." + text += "\n Character will cast the indicated spell if the spell is in their spellbook. It will be a targeted spell if a target is indicated. The roll is modified by the spell casting ability of the character. You may type a phrase without quotes for spell such as \"cast fire bolt at the giant chicken\". If the difficulty is not specified, the default difficulty or the AC of the opponent in combat will be used. The parameters can be listed in any order, except the target must be listed last. The target can include the name of the enemy or the word \"enemy\" and the number of the enemy as listed in #enemies. The target can also include a damage amount. If the damage is not specified, the character's default damage is used. Quotes are not necessary.\nExample:\nAstri #attack advantage The Evil Knight for 2d12+2 damage" text += "\n\n--Abilities--" text += "\n#setability ability value"