mirror of
https://github.com/raeleus/Hashtag-DnD.git
synced 2025-07-05 13:10:28 -04:00
#attack and #cast can target enemies by enemy number
This commit is contained in:
parent
5784b2328a
commit
e9743ed99e
3 changed files with 72 additions and 45 deletions
112
Input.js
112
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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue