mirror of
https://github.com/raeleus/Hashtag-DnD.git
synced 2025-07-05 05:00:26 -04:00
Added allies.
This commit is contained in:
parent
1a6a605251
commit
8db353f486
4 changed files with 869 additions and 11 deletions
729
Input.js
729
Input.js
|
@ -1,4 +1,4 @@
|
|||
const version = "Hashtag DnD v0.6.0"
|
||||
const version = "Hashtag DnD v0.7.0"
|
||||
const rollSynonyms = ["roll"]
|
||||
const createSynonyms = ["create", "generate", "start", "begin", "setup", "party", "member", "new"]
|
||||
const renameCharacterSynonyms = ["renamecharacter", "renameperson"]
|
||||
|
@ -70,15 +70,20 @@ const showDaySynonyms = ["showday", "showdate", "day", "date"]
|
|||
const setDaySynonyms = ["setday", "setdate"]
|
||||
const encounterSynonyms = ["encounter", "startencounter"]
|
||||
const showEnemiesSynonyms = ["showenemies", "enemies"]
|
||||
const showAlliesSynonyms = ["showallies", "allies"]
|
||||
const addEnemySynonyms = ["addenemy"]
|
||||
const addAllySynonyms = ["addally"]
|
||||
const removeEnemySynonyms = ["removeenemy"]
|
||||
const removeAllySynonyms = ["removeally"]
|
||||
const clearEnemiesSynonyms = ["clearenemies", "resetenemies", "removeenemies"]
|
||||
const clearAlliesSynonyms = ["clearallies", "resetallies", "removeallies"]
|
||||
const initiativeSynonyms = ["initiative"]
|
||||
const setAcSynonyms = ["setac", "setarmorclass", "ac", "armorclass"]
|
||||
const turnSynonyms = ["turn", "doturn", "taketurn"]
|
||||
const fleeSynonyms = ["flee", "retreat", "runaway", "endcombat"]
|
||||
const versionSynonyms = ["version", "ver", "showversion"]
|
||||
const setupEnemySynonyms = ["setupenemy", "createenemy"]
|
||||
const setupAllySynonyms = ["setupally", "createally"]
|
||||
const setDamageSynonyms = ["setdamage"]
|
||||
const setProficiencySynonyms = ["setproficiency", "setweaponproficiency"]
|
||||
const healPartySynonyms = ["healparty", "healcharacters"]
|
||||
|
@ -112,6 +117,12 @@ const modifier = (text) => {
|
|||
else text = rawText
|
||||
}
|
||||
|
||||
if (state.setupAllyStep != null) {
|
||||
text = handleSetupAllyStep(text)
|
||||
if (state.setupAllyStep != null) return { text }
|
||||
else text = rawText
|
||||
}
|
||||
|
||||
if (state.stragedyShopStep != null) {
|
||||
text = handleStragedyShopStep(text)
|
||||
if (state.stragedyShopStep != null) return { text }
|
||||
|
@ -177,7 +188,7 @@ const modifier = (text) => {
|
|||
return { text }
|
||||
}
|
||||
|
||||
if (!found) found = processCommandSynonyms(command, commandName, helpSynonyms.concat(rollSynonyms, noteSynonyms, eraseNoteSynonyms, showNotesSynonyms, clearNotesSynonyms, showCharactersSynonyms, removeCharacterSynonyms, generateNameSynonyms, setDefaultDifficultySynonyms, showDefaultDifficultySynonyms, renameCharacterSynonyms, cloneCharacterSynonyms, createLocationSynonyms, showLocationsSynonyms, goToLocationSynonyms, removeLocationSynonyms, getLocationSynonyms, clearLocationsSynonyms, goNorthSynonyms, goSouthSynonyms, goEastSynonyms, goWestSynonyms, encounterSynonyms, showEnemiesSynonyms, addEnemySynonyms, removeEnemySynonyms, clearEnemiesSynonyms, initiativeSynonyms, turnSynonyms, fleeSynonyms, versionSynonyms, setupEnemySynonyms, healSynonyms, damageSynonyms, restSynonyms, addExperienceSynonyms, healPartySynonyms, blockSynonyms, repeatTurnSynonyms, lockpickSynonyms, memorySynonyms, resetSynonyms), function () {return true})
|
||||
if (!found) found = processCommandSynonyms(command, commandName, helpSynonyms.concat(rollSynonyms, noteSynonyms, eraseNoteSynonyms, showNotesSynonyms, clearNotesSynonyms, showCharactersSynonyms, removeCharacterSynonyms, generateNameSynonyms, setDefaultDifficultySynonyms, showDefaultDifficultySynonyms, renameCharacterSynonyms, cloneCharacterSynonyms, createLocationSynonyms, showLocationsSynonyms, goToLocationSynonyms, removeLocationSynonyms, getLocationSynonyms, clearLocationsSynonyms, goNorthSynonyms, goSouthSynonyms, goEastSynonyms, goWestSynonyms, encounterSynonyms, showEnemiesSynonyms, showAlliesSynonyms, addEnemySynonyms, addAllySynonyms, removeEnemySynonyms, removeAllySynonyms, clearEnemiesSynonyms, clearAlliesSynonyms, initiativeSynonyms, turnSynonyms, fleeSynonyms, versionSynonyms, setupEnemySynonyms, setupAllySynonyms, healSynonyms, damageSynonyms, restSynonyms, addExperienceSynonyms, healPartySynonyms, blockSynonyms, repeatTurnSynonyms, lockpickSynonyms, memorySynonyms, resetSynonyms), function () {return true})
|
||||
|
||||
if (found == null) {
|
||||
if (state.characterName == null) {
|
||||
|
@ -262,13 +273,18 @@ const modifier = (text) => {
|
|||
if (text == null) text = processCommandSynonyms(command, commandName, setAcSynonyms, doSetAc)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, encounterSynonyms, doEncounter)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, showEnemiesSynonyms, doShowEnemies)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, showAlliesSynonyms, doShowAllies)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, removeEnemySynonyms, doRemoveEnemy)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, removeAllySynonyms, doRemoveAlly)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, clearEnemiesSynonyms, doClearEnemies)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, clearAlliesSynonyms, doClearAllies)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, addEnemySynonyms, doAddEnemy)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, addAllySynonyms, doAddAlly)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, initiativeSynonyms, doInitiative)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, fleeSynonyms, doFlee)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, turnSynonyms, doTurn)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, setupEnemySynonyms, doSetupEnemy)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, setupAllySynonyms, doSetupAlly)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, setDamageSynonyms, doSetDamage)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, setProficiencySynonyms, doSetProficiency)
|
||||
if (text == null) text = processCommandSynonyms(command, commandName, healPartySynonyms, doHealParty)
|
||||
|
@ -1186,6 +1202,435 @@ function handleSetupEnemyStep(text) {
|
|||
return text
|
||||
}
|
||||
|
||||
function handleSetupAllyStep(text) {
|
||||
state.show = "setupAlly"
|
||||
|
||||
if (/^\s*>.*says? ".*/.test(text)) {
|
||||
text = text.replace(/^\s*>.*says? "/, "")
|
||||
text = text.replace(/"\s*$/, "")
|
||||
} else if (/^\s*>\s.*/.test(text)) {
|
||||
text = text.replace(/\s*> /, "")
|
||||
for (var i = 0; i < info.characters.length; i++) {
|
||||
var matchString = info.characters[i] == "" ? "You " : `${info.characters[i]} `
|
||||
if (text.startsWith(matchString)) {
|
||||
text = text.replace(matchString, "")
|
||||
break
|
||||
}
|
||||
}
|
||||
text = text.replace(/\.?\s*$/, "")
|
||||
} else {
|
||||
text = text.replace(/^\s+/, "")
|
||||
}
|
||||
|
||||
if (text.toLowerCase() == "q") {
|
||||
state.setupAllyStep = null
|
||||
return text
|
||||
}
|
||||
|
||||
switch (state.setupAllyStep) {
|
||||
case 0:
|
||||
text = text.toLowerCase();
|
||||
if (text.startsWith("y")) state.setupAllyStep = 100
|
||||
else if (text.startsWith("n")) state.setupAllyStep++
|
||||
break
|
||||
case 1:
|
||||
if (text.length > 0) {
|
||||
state.tempAlly.name = text
|
||||
state.setupAllyStep++
|
||||
|
||||
var allyMatches = state.allies.filter(x => x.name.toLowerCase() == state.tempAlly.name.toLowerCase() || x.name.toLowerCase() == `${state.tempAlly.name.toLowerCase()} a`)
|
||||
if (allyMatches.length > 0) {
|
||||
state.newAlly = false
|
||||
state.tempAlly.health = allyMatches[0].health
|
||||
state.tempAlly.ac = allyMatches[0].ac
|
||||
state.tempAlly.hitModifier = allyMatches[0].hitModifier
|
||||
state.tempAlly.damage = allyMatches[0].damage
|
||||
state.tempAlly.initiative = allyMatches[0].initiative
|
||||
state.tempAlly.spells = [...allyMatches[0].spells]
|
||||
} else {
|
||||
state.newAlly = true
|
||||
}
|
||||
}
|
||||
return text
|
||||
case 2:
|
||||
if (text.length > 0) {
|
||||
if (text.toLowerCase() == "default") {
|
||||
state.setupAllyStep++
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(text)) {
|
||||
state.tempAlly.health = calculateRoll(text)
|
||||
state.setupAllyStep++
|
||||
} else if (!isNaN(text)) {
|
||||
state.tempAlly.health = Math.max(0, parseInt(text))
|
||||
state.setupAllyStep++
|
||||
}
|
||||
}
|
||||
return text
|
||||
case 3:
|
||||
if (text.toLowerCase() == "default") {
|
||||
state.setupAllyStep++
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(text)) {
|
||||
state.tempAlly.ac = calculateRoll(text)
|
||||
state.setupAllyStep++
|
||||
} else if (!isNaN(text)) {
|
||||
state.tempAlly.ac = Math.max(0, parseInt(text))
|
||||
state.setupAllyStep++
|
||||
}
|
||||
return text
|
||||
case 4:
|
||||
if (text.toLowerCase() == "default") {
|
||||
state.setupAllyStep++
|
||||
} else if (!isNaN(text)) {
|
||||
state.tempAlly.hitModifier = Math.max(0, parseInt(text))
|
||||
state.setupAllyStep++
|
||||
}
|
||||
return text
|
||||
case 5:
|
||||
if (text.toLowerCase() == "default") {
|
||||
state.setupAllyStep++
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(text)) {
|
||||
state.tempAlly.damage = text
|
||||
state.setupAllyStep++
|
||||
} else if (!isNaN(text)) {
|
||||
state.tempAlly.damage = Math.max(0, parseInt(text))
|
||||
state.setupAllyStep++
|
||||
}
|
||||
return text
|
||||
case 6:
|
||||
if (text.toLowerCase() == "default") {
|
||||
state.setupAllyStep++
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(text)) {
|
||||
state.tempAlly.initiative = calculateRoll(text)
|
||||
state.setupAllyStep++
|
||||
} else if (!isNaN(text)) {
|
||||
state.tempAlly.initiative = Math.max(0, parseInt(text))
|
||||
state.setupAllyStep++
|
||||
}
|
||||
return text
|
||||
case 7:
|
||||
if (text.toLowerCase() == "s") {
|
||||
state.setupAllyStep = 500
|
||||
} else if (text.toLowerCase() == "e") {
|
||||
state.tempAlly.spells = []
|
||||
} else if (text.length > 0) {
|
||||
state.tempAlly.spells.push(text)
|
||||
state.setupAllyStep++
|
||||
}
|
||||
return text
|
||||
case 8:
|
||||
if (text.toLowerCase() == "s") {
|
||||
state.setupAllyStep = 500
|
||||
}
|
||||
else if (text.length > 0) {
|
||||
state.tempAlly.spells.push(text)
|
||||
}
|
||||
return text
|
||||
case 100:
|
||||
if (/^\d+(\s.*)?$/gi.test(text)) {
|
||||
state.setupAllyStep = 500
|
||||
state.newAlly = true
|
||||
|
||||
var value = text.match(/^\d+/gi)[0]
|
||||
var nameMatches = text.match(/(?<=\s).*/gi)
|
||||
|
||||
//name, health, ac, hitModifier, damage, initiative, ...spells
|
||||
switch (parseInt(value)) {
|
||||
case 1:
|
||||
state.tempAlly = createAlly("Fighter", calculateRoll("1d6+12"), 18, 4, "1d8+4", "d20+2", "Javelin Throw1d6+4")
|
||||
break
|
||||
case 2:
|
||||
state.tempAlly = createAlly("Cleric", calculateRoll("1d6+10"), 17, 3, "1d6+2", "d20", "Healing Word", "Sanctuary", "Guiding Bolt4d6")
|
||||
break
|
||||
case 3:
|
||||
state.tempAlly = createAlly("Rogue", calculateRoll("1d6+10"), 15, 5, "2d6+3", "d20+5", "Sneak Attack3d6+3")
|
||||
break
|
||||
case 4:
|
||||
state.tempAlly = createAlly("Ranger", calculateRoll("1d6+10"), 15, 4, "1d8+2", "d20+2", "Cure Wounds", "Hunter's Mark", "Ensaring Strike1d8+2")
|
||||
break
|
||||
case 5:
|
||||
state.tempAlly = createAlly("Barbarian", calculateRoll("1d6+15"), 17, 3, "1d12+4", "d20+1", "Rage1d12+4")
|
||||
break
|
||||
case 6:
|
||||
state.tempAlly = createAlly("Bard", calculateRoll("1d6+10"), 15, 3, "1d6", "d20", "Petrifying Bite1d4+1")
|
||||
break
|
||||
case 7:
|
||||
state.tempAlly = createAlly("Druid", calculateRoll("1d6+10"), 16, 3, "1d6+1", "d20", "Poison Bite2d4+1")
|
||||
break
|
||||
case 8:
|
||||
state.tempAlly = createAlly("Monk", calculateRoll("1d6+10"), 16, 5, "2d6+2", "d20+3", "Flurry of Blows 3d6+2")
|
||||
break
|
||||
case 9:
|
||||
state.tempAlly = createAlly("Paladin", calculateRoll("1d6+10"), 16, 3, "1d8+2", "d20+1", "Searing Smite2d6+4")
|
||||
break
|
||||
case 10:
|
||||
state.tempAlly = createAlly("Wizard", calculateRoll("1d6+8"), 14, 3, "1d6", "d20", "Ray of Frost1d8", "Mage Armor", "Ice Knife1d10+5")
|
||||
break
|
||||
case 11:
|
||||
state.tempAlly = createAlly("Sorcerer", calculateRoll("1d6+8"), 14, 3, "1d6", "d20", "Sorcerous Burst1d8", "Chromatic Orb2d8", "Burning Hands1d10")
|
||||
break
|
||||
case 12:
|
||||
state.tempAlly = createAlly("Warlock", calculateRoll("1d6+8"), 14, 3, "1d6", "d20", "Eldritch Blast1d8+5", "Chill Touch1d12", "Hex")
|
||||
break
|
||||
case 13:
|
||||
state.tempAlly = createAlly("Artificer", calculateRoll("1d6+10"), 15, 3, "2d6", "d20+1", "Archanist's Fire2d6+5", "Acid Vial1d10")
|
||||
break
|
||||
case 14:
|
||||
state.tempAlly = createAlly("Commoner", calculateRoll("1d8"), 10, 2, "1d4", "d20")
|
||||
break
|
||||
case 15:
|
||||
state.tempAlly = createAlly("Bandit", calculateRoll("2d8+2"), 12, 3, "1d6+1", "d20+1")
|
||||
break
|
||||
case 16:
|
||||
state.tempAlly = createAlly("Guard", calculateRoll("2d8+2"), 16, 3, "1d6+1", "d20+1")
|
||||
break
|
||||
case 17:
|
||||
state.tempAlly = createAlly("Cultist", calculateRoll("2d8"), 12, 3, "1d6+1", "d20+1", "Dark Devotion")
|
||||
break
|
||||
case 18:
|
||||
state.tempAlly = createAlly("Acolyte", calculateRoll("2d8"), 10, 2, "1d4", "d20", "Sacred Flame1d8", "Cure Wounds")
|
||||
break
|
||||
case 19:
|
||||
state.tempAlly = createAlly("Apprentice", calculateRoll("3d8"), 10, 4, "1d10+2", "d20", "Burning Hands3d6")
|
||||
break
|
||||
case 20:
|
||||
state.tempAlly = createAlly("Witch", calculateRoll("3d8+3"), 10, 3, "1d6+2", "d20", "Ray of Sickness2d8", "Tashas Hideous Laughter", "Invisibility", "Ray of Frost2d8")
|
||||
break
|
||||
case 21:
|
||||
state.tempAlly = createAlly("Buccaneer", calculateRoll("8d8+24"), 14, 5, "1d6+3", "d20+2", "Invade")
|
||||
break
|
||||
case 22:
|
||||
state.tempAlly = createAlly("Spy", calculateRoll("6d8"), 12, 4, "1d6+2", "d20+2", "Sneak Attack2d6+2")
|
||||
break
|
||||
case 23:
|
||||
state.tempAlly = createAlly("Captain", calculateRoll("10d8+20"), 15, 5, "3d6+9", "initiative")
|
||||
break
|
||||
case 24:
|
||||
state.tempAlly = createAlly("Charlatan", calculateRoll("8d8+8"), 15, 4, "1d6+2", "d20+2", "Charm Person", "Shatter3d8", "Thunderwave2d8", "Vicious Mockery1d4")
|
||||
break
|
||||
case 25:
|
||||
state.tempAlly = createAlly("Berserker", calculateRoll("9d8+27"), 13, 5, "1d12+3", "d20+1")
|
||||
break
|
||||
case 26:
|
||||
state.tempAlly = createAlly("Priest", calculateRoll("5d8+5"), 13, 2, "1d6", "d20", "Spirit Guardians3d8", "Spiritual Weapon1d8", "Guiding Bolt4d6", "Cure Wounds")
|
||||
break
|
||||
case 27:
|
||||
state.tempAlly = createAlly("Knight", calculateRoll("8d8+16"), 18, 5, "4d6+6", "d20", "Leadership")
|
||||
break
|
||||
case 28:
|
||||
state.tempAlly = createAlly("Archer", calculateRoll("10d8+30"), 16, 6, "2d8+8", "d20+4")
|
||||
break
|
||||
case 29:
|
||||
state.tempAlly = createAlly("Warrior", calculateRoll("6d8+12"), 16, 6, "1d8+3", "d20+1")
|
||||
break
|
||||
case 30:
|
||||
state.tempAlly = createAlly("Conjurer", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Conjure Elemental", "Cloud Kill5d8", "Cloud of Daggers5d8", "Poison Spray1d12")
|
||||
break
|
||||
case 31:
|
||||
state.tempAlly = createAlly("Mage", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Greater Invisibility", "Ice Storm4d6", "Fireball8d6", "Magic Missile3d4+3")
|
||||
break
|
||||
case 32:
|
||||
state.tempAlly = createAlly("Assassin", calculateRoll("12d8+24"), 15, 6, "2d6+6", "d20+3", "Sneak Attack6d6+6")
|
||||
break
|
||||
case 33:
|
||||
state.tempAlly = createAlly("Evoker", calculateRoll("12d8+12"), 12, 3, "1d6-1", "d20+2", "Chain Lightning10d8", "Wall of Ice", "Counter Spell", "Shatter3d8", "Magic Missile6d4+6")
|
||||
break
|
||||
case 34:
|
||||
state.tempAlly = createAlly("Necromancer", calculateRoll("12d8+12"), 12, 7, "2d4", "d20+2", "Circle of Death8d6", "Blight8d8", "Cloudkill5d8", "Animate Dead", "Chill Touch1d8")
|
||||
break
|
||||
case 35:
|
||||
state.tempAlly = createAlly("Champion", calculateRoll("22d8+44"), 18, 9, "6d6+15", "d20+2", "Second Wind")
|
||||
break
|
||||
case 36:
|
||||
state.tempAlly = createAlly("Warlord", calculateRoll("27d8+108"), 18, 9, "4d6+10", "d20+3", "Command Ally", "Frighten Foe")
|
||||
break
|
||||
case 37:
|
||||
state.tempAlly = createAlly("Archmage", calculateRoll("18d8+18"), 12, 6, "1d4+2", "d20+2", "Time Stop", "Mind Blank", "Lightning Bolt8d6", "Cone of Cold8d8", "Shocking Grasp1d8")
|
||||
break
|
||||
case 38:
|
||||
state.tempAlly = createAlly("Archdruid", calculateRoll("24d8+24"), 16, 6, "1d6+2", "d20+2", "Fire Storm7d10", "Sunbeam6d8", "Wall of Fire", "Beast Sense", "Conjure Animals")
|
||||
break
|
||||
case 39:
|
||||
state.tempAlly = createAlly("Ape", calculateRoll("3d8+6"), 12, 5, "2d4+6", "d20+2", "Throw Rock2d6+3")
|
||||
break
|
||||
case 40:
|
||||
state.tempAlly = createAlly("Badger", calculateRoll("1d4+3"), 11, 2, "1", "d20")
|
||||
break
|
||||
case 41:
|
||||
state.tempAlly = createAlly("Bat", calculateRoll("1d4-1"), 12, 4, "1", "d20+2")
|
||||
break
|
||||
case 42:
|
||||
state.tempAlly = createAlly("Black Bear", calculateRoll("3d8+6"), 11, 4, "2d6+4", "d20+1")
|
||||
break
|
||||
case 43:
|
||||
state.tempAlly = createAlly("Boar", calculateRoll("2d8+4"), 11, 3, "1d6+1", "d20", "Gore2d6+1")
|
||||
break
|
||||
case 44:
|
||||
state.tempAlly = createAlly("Brown Bear", calculateRoll("3d10+6"), 11, 5, "3d4+6", "d20+1", "Fire Storm7d10", "Sunbeam6d8", "Wall of Fire", "Beast Sense", "Conjure Animals")
|
||||
break
|
||||
case 45:
|
||||
state.tempAlly = createAlly("Camel", calculateRoll("2d10+6"), 10, 4, "1d4+2", "d20-1")
|
||||
break
|
||||
case 46:
|
||||
state.tempAlly = createAlly("Cat", calculateRoll("1d4"), 12, 4, "1", "d20+2")
|
||||
break
|
||||
case 47:
|
||||
state.tempAlly = createAlly("Constrictor Snake", calculateRoll("2d10+2"), 13, 4, "1d8+2", "d20+2", "Constrict3d4")
|
||||
break
|
||||
case 48:
|
||||
state.tempAlly = createAlly("Crab", calculateRoll("1d4+1"), 11, 2, "1", "d20")
|
||||
break
|
||||
case 49:
|
||||
state.tempAlly = createAlly("Crocodile", calculateRoll("2d10+2"), 12, 4, "1d8+2", "d20")
|
||||
break
|
||||
case 50:
|
||||
state.tempAlly = createAlly("Dire Wolf", calculateRoll("3d10+6"), 14, 5, "1d10+3", "d20+2")
|
||||
break
|
||||
case 51:
|
||||
state.tempAlly = createAlly("Draft Horse", calculateRoll("2d10+4"), 10, 6, "1d4+4", "d20")
|
||||
break
|
||||
case 52:
|
||||
state.tempAlly = createAlly("Elephant", calculateRoll("8d12+24"), 12, 8, "4d8+12", "d20-1", "Trample2d10+6")
|
||||
break
|
||||
case 53:
|
||||
state.tempAlly = createAlly("Elk", calculateRoll("2d10+5"), 10, 5, "1d6+3", "d20")
|
||||
break
|
||||
case 54:
|
||||
state.tempAlly = createAlly("Frog", calculateRoll("1d4-1"), 11, 3, "1", "d20+1")
|
||||
break
|
||||
case 55:
|
||||
state.tempAlly = createAlly("Giant Badger", calculateRoll("2d8+6"), 13, 3, "2d4+1", "d20")
|
||||
break
|
||||
case 56:
|
||||
state.tempAlly = createAlly("Giant Crab", calculateRoll("3d8"), 15, 3, "1d6+1", "d20+1")
|
||||
break
|
||||
case 57:
|
||||
state.tempAlly = createAlly("Giant Goat", calculateRoll("3d10+3"), 11, 5, "1d6+3", "d20+1")
|
||||
break
|
||||
case 58:
|
||||
state.tempAlly = createAlly("Giant Seahorse", calculateRoll("3d10"), 14, 4, "2d6+2", "d20+1", "Bubble Dash")
|
||||
break
|
||||
case 59:
|
||||
state.tempAlly = createAlly("Giant Spider", calculateRoll("4d10+4"), 14, 5, "1d8+3", "d20+3", "Web")
|
||||
break
|
||||
case 60:
|
||||
state.tempAlly = createAlly("Giant Weasel", calculateRoll("2d8"), 13, 5, "1d4+3", "d20+3")
|
||||
break
|
||||
case 61:
|
||||
state.tempAlly = createAlly("Goat", calculateRoll("1d8"), 10, 2, "1", "d20")
|
||||
break
|
||||
case 62:
|
||||
state.tempAlly = createAlly("Hawk", calculateRoll("1d4-1"), 13, 5, "1", "d20+3")
|
||||
break
|
||||
case 63:
|
||||
state.tempAlly = createAlly("Imp", calculateRoll("6d4+6"), 13, 5, "3d6+3", "d20+3", "Invisibility")
|
||||
break
|
||||
case 64:
|
||||
state.tempAlly = createAlly("Lion", calculateRoll("4d10"), 12, 5, "2d8+6", "d20+2", "Roar")
|
||||
break
|
||||
case 65:
|
||||
state.tempAlly = createAlly("Lizard", calculateRoll("1d4"), 10, 2, "1", "d20")
|
||||
break
|
||||
case 66:
|
||||
state.tempAlly = createAlly("Mastiff", calculateRoll("1d8+1"), 12, 3, "1d6+1", "d20+2")
|
||||
break
|
||||
case 67:
|
||||
state.tempAlly = createAlly("Mule", calculateRoll("2d8+2"), 10, 4, "1d4+2", "d20")
|
||||
break
|
||||
case 68:
|
||||
state.tempAlly = createAlly("Octopus", calculateRoll("1d6"), 12, 4, "1", "d20+2", "Ink Cloud")
|
||||
break
|
||||
case 69:
|
||||
state.tempAlly = createAlly("Owl", calculateRoll("1"), 11, 3, "1", "d20+1")
|
||||
break
|
||||
case 70:
|
||||
state.tempAlly = createAlly("Panther", calculateRoll("3d8"), 12, 4, "1d4+2", "d20+2")
|
||||
break
|
||||
case 71:
|
||||
state.tempAlly = createAlly("Pony", calculateRoll("2d8+2"), 10, 4, "1d4+2", "d20")
|
||||
break
|
||||
case 72:
|
||||
state.tempAlly = createAlly("Pseudodragon", calculateRoll("3d4+3"), 14, 4, "2d4+4", "d20+2", "String2d4+2")
|
||||
break
|
||||
case 73:
|
||||
state.tempAlly = createAlly("Quasit", calculateRoll("10d4"), 13, 5, "1d4+3", "d20+3", "Shape Shift", "Scare", "Invisibility")
|
||||
break
|
||||
case 74:
|
||||
state.tempAlly = createAlly("Rat", calculateRoll("1d4-1"), 10, 2, "1", "d20")
|
||||
break
|
||||
case 75:
|
||||
state.tempAlly = createAlly("Raven", calculateRoll("1d4"), 12, 4, "1", "d20+2")
|
||||
break
|
||||
case 76:
|
||||
state.tempAlly = createAlly("Reef Shark", calculateRoll("4d8+4"), 12, 4, "2d4+2")
|
||||
break
|
||||
case 77:
|
||||
state.tempAlly = createAlly("Riding Horse", calculateRoll("2d10+2"), 11, 5, "1d8+3", "d20+1")
|
||||
break
|
||||
case 78:
|
||||
state.tempAlly = createAlly("Scorpion", calculateRoll("1d4-1"), 13, 2, "1d6+1", "d20")
|
||||
break
|
||||
case 79:
|
||||
state.tempAlly = createAlly("Skeleton", calculateRoll("2d8+4"), 13, 5, "1d6+3", "d20+3", "Shortbow1d6+3", "Sword1d6+3")
|
||||
break
|
||||
case 80:
|
||||
state.tempAlly = createAlly("Slaad Tadpole", calculateRoll("3d4"), 12, 4, "1d6+2", "d20+2")
|
||||
break
|
||||
case 81:
|
||||
state.tempAlly = createAlly("Sphinx of Wonder", calculateRoll("7d4+7"), 13, 5, "1d4+3", "d20+2")
|
||||
break
|
||||
case 82:
|
||||
state.tempAlly = createAlly("Spider", calculateRoll("1d4-1"), 12, 4, "1", "d20+2")
|
||||
break
|
||||
case 83:
|
||||
state.tempAlly = createAlly("Sprite", calculateRoll("4d4"), 15, 6, "1d4+4", "d20+4", "Enchanting Bow1d4", "Invisibility")
|
||||
break
|
||||
case 84:
|
||||
state.tempAlly = createAlly("Tiger", calculateRoll("3d10+6"), 13, 5, "1d6+3", "d20+3")
|
||||
break
|
||||
case 85:
|
||||
state.tempAlly = createAlly("Venomous Snake", calculateRoll("2d4"), 12, 4, "2d4+2", "d20+2")
|
||||
break
|
||||
case 86:
|
||||
state.tempAlly = createAlly("Warhorse", calculateRoll("3d10+3"), 11, 6, "2d4+4", "d20+2")
|
||||
break
|
||||
case 87:
|
||||
state.tempAlly = createAlly("Weasel", calculateRoll("1d4-1"), 13, 5, "1", "d20+3")
|
||||
break
|
||||
case 88:
|
||||
state.tempAlly = createAlly("Wolf", calculateRoll("2d8+2"), 12, 4, "1d6+2", "d20+2")
|
||||
break
|
||||
case 89:
|
||||
state.tempAlly = createAlly("Zombie", calculateRoll("2d8+6"), 8, 3, "1d6+1", "d20-2")
|
||||
break
|
||||
}
|
||||
|
||||
if (nameMatches != null) state.tempAlly.name = nameMatches[0]
|
||||
}
|
||||
return text
|
||||
case 500:
|
||||
state.show = null
|
||||
state.setupAllyStep = null
|
||||
|
||||
var ally = createAlly(state.tempAlly.name, state.tempAlly.health, state.tempAlly.ac, state.tempAlly.hitModifier, state.tempAlly.damage, state.tempAlly.initiative)
|
||||
ally.spells = [...state.tempAlly.spells]
|
||||
|
||||
var allyMatches = state.allies.filter(x => x.name.toLowerCase() == ally.name.toLowerCase() || x.name.toLowerCase() == `${ally.name.toLowerCase()} a`)
|
||||
if (state.newAlly && allyMatches.length > 0) {
|
||||
ally.name = getUniqueName(ally.name)
|
||||
if (ally.name.endsWith("A")) {
|
||||
allyMatches[0].name = ally.name
|
||||
ally.name = ally.name.substring(0, ally.name.length - 1) + "B"
|
||||
}
|
||||
} else if (!state.newAlly) {
|
||||
let removeIndex = state.allies.indexOf(allyMatches[0])
|
||||
state.allies.splice(removeIndex, 1)
|
||||
}
|
||||
|
||||
state.allies.push(ally)
|
||||
break
|
||||
}
|
||||
return text
|
||||
}
|
||||
|
||||
function resetTempCharacterSkills() {
|
||||
state.tempCharacter.skills = [
|
||||
{name: "Acrobatics", stat: "Dexterity", modifier: 0},
|
||||
|
@ -1240,6 +1685,7 @@ function init() {
|
|||
}
|
||||
|
||||
if (state.tempEnemy == null) state.tempEnemy = createEnemy("enemy", 10, 10, "2d6", 10)
|
||||
if (state.tempAlly == null) state.tempAlly = createAlly("ally", 10, 10, "2d6", 10)
|
||||
if (state.characters == null) state.characters = []
|
||||
if (state.notes == null) state.notes = []
|
||||
if (state.locations == null) state.locations = []
|
||||
|
@ -1249,6 +1695,7 @@ function init() {
|
|||
if (state.defaultDifficulty == null) state.defaultDifficulty = 10
|
||||
if (state.day == null) state.day = 0
|
||||
if (state.enemies == null) state.enemies = []
|
||||
if (state.allies == null) state.allies = []
|
||||
if (state.initiativeOrder == null) state.initiativeOrder = []
|
||||
state.show = null
|
||||
state.prefix = null
|
||||
|
@ -1317,6 +1764,13 @@ function doSetupEnemy(command) {
|
|||
return " "
|
||||
}
|
||||
|
||||
function doSetupAlly(command) {
|
||||
state.setupAllyStep = 0
|
||||
state.tempAlly = createAlly("ally", 20, 10, 0, "2d6", 10)
|
||||
state.show = "setupAlly"
|
||||
return " "
|
||||
}
|
||||
|
||||
function doMemory(command) {
|
||||
var arg0 = getArgument(command, 0)
|
||||
if (arg0 == null) {
|
||||
|
@ -2494,6 +2948,14 @@ function doHeal(command) {
|
|||
}
|
||||
}
|
||||
|
||||
for (var ally of state.allies) {
|
||||
if (ally.name.toLowerCase() == arg1.toLowerCase()) {
|
||||
ally.health = Math.max(0, ally.health + healing)
|
||||
state.show = "none"
|
||||
return `\n[${toTitleCase(ally.name)} has been healed for ${healing} hp to a total of ${ally.health}]\n`
|
||||
}
|
||||
}
|
||||
|
||||
for (var character of state.characters) {
|
||||
if (character.name.toLowerCase() == arg1.toLowerCase()) {
|
||||
character.health += healing
|
||||
|
@ -2504,7 +2966,7 @@ function doHeal(command) {
|
|||
}
|
||||
|
||||
state.show = "none"
|
||||
return `\n[Error: Could not find an enemy or character matching the name ${arg1}. Type #enemies or #characters to see a list]`
|
||||
return `\n[Error: Could not find an enemy, ally, or character matching the name ${arg1}. Type #enemies, #allies, or #characters to see a list]`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2568,6 +3030,14 @@ function doDamage(command) {
|
|||
}
|
||||
}
|
||||
|
||||
for (var ally of state.allies) {
|
||||
if (ally.name.toLowerCase() == arg1.toLowerCase()) {
|
||||
ally.health = Math.max(0, ally.health - damage)
|
||||
state.show = "none"
|
||||
return `\n[${toTitleCase(ally.name)} has been damaged for ${damage} hp with ${ally.health} remaining] ${ally.health == 0 ? " " + toTitleCase(ally.name) + " has been defeated!" : ""}\n`
|
||||
}
|
||||
}
|
||||
|
||||
for (var character of state.characters) {
|
||||
if (character.name.toLowerCase() == arg1.toLowerCase()) {
|
||||
character.health = Math.max(0, character.health - damage)
|
||||
|
@ -2577,7 +3047,7 @@ function doDamage(command) {
|
|||
}
|
||||
|
||||
state.show = "none"
|
||||
return `\n[Error: Could not find an enemy matching the name ${arg1}. Type #enemies or #characters to see a list]`
|
||||
return `\n[Error: Could not find an enemy, ally, or character matching the name ${arg1}. Type #enemies, #allies, or #characters to see a list]`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2850,6 +3320,7 @@ function doAttack(command) {
|
|||
}
|
||||
|
||||
var enemyString = ""
|
||||
var allyString = ""
|
||||
if (state.initiativeOrder.length > 0) {
|
||||
var foundEnemy
|
||||
|
||||
|
@ -2868,6 +3339,23 @@ function doAttack(command) {
|
|||
}
|
||||
}
|
||||
|
||||
var foundAlly
|
||||
|
||||
if (foundEnemy == null) for (var ally of state.allies) {
|
||||
if (targetText.toLowerCase().includes(ally.name.toLowerCase())) {
|
||||
foundAlly = ally
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAlly == null) {
|
||||
var indexMatches = targetText.match(/(?<=ally\s*)\d+/gi)
|
||||
if (indexMatches != null) {
|
||||
foundAlly = state.allies[parseInt(indexMatches[0]) - 1]
|
||||
targetText = targetText.replace(/ally\s*d+/gi, foundAlly.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)
|
||||
|
@ -2894,6 +3382,22 @@ function doAttack(command) {
|
|||
} else enemyString += ` ${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!`
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAlly != null) {
|
||||
if (usingDefaultDifficulty) targetRoll = foundAlly.ac
|
||||
if (score == 20 || score + modifier >= targetRoll) {
|
||||
if (score == 20) allyString += `\nCritical Damage: ${damage}\n`
|
||||
else allyString += `\nDamage: ${damage}\n`
|
||||
|
||||
state.blockCharacter = foundAlly
|
||||
state.blockPreviousHealth = foundAlly.health
|
||||
foundAlly.health = Math.max(0, foundAlly.health - damage)
|
||||
if (foundAlly.health == 0) {
|
||||
allyString += ` ${toTitleCase(foundAlly.name)} has been defeated!`
|
||||
|
||||
} else allyString += ` ${toTitleCase(foundAlly.name)} has ${foundAlly.health} health remaining!`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var dieText = advantageText == "advantage" || advantageText == "disadvantage" ? `${advantageText}(${die1},${die2})` : die1
|
||||
|
@ -2901,10 +3405,10 @@ function doAttack(command) {
|
|||
state.show = "prefix"
|
||||
|
||||
if (targetRoll == 0) state.prefix = ""
|
||||
else if (score == 20) state.prefix = `\n[Enemy AC: ${targetRoll} Attack roll: ${dieText}]\n`
|
||||
else if (score == 1) state.prefix = `\n[Enemy AC: ${targetRoll} Attack roll: ${dieText}]\n`
|
||||
else if (modifier != 0) state.prefix = `\n[Enemy AC: ${targetRoll} Attack roll: ${dieText}${modifier > 0 ? "+" + modifier : modifier}=${score + modifier}. ${score + modifier >= targetRoll ? "Success!" : "Failure!"}]\n`
|
||||
else state.prefix = `\n[Enemy AC: ${targetRoll} Attack roll: ${dieText}. ${score >= targetRoll ? "Success!" : "Failure!"}]\n`
|
||||
else if (score == 20) state.prefix = `\n[Target AC: ${targetRoll} Attack roll: ${dieText}]\n`
|
||||
else if (score == 1) state.prefix = `\n[Target AC: ${targetRoll} Attack roll: ${dieText}]\n`
|
||||
else if (modifier != 0) state.prefix = `\n[Target AC: ${targetRoll} Attack roll: ${dieText}${modifier > 0 ? "+" + modifier : modifier}=${score + modifier}. ${score + modifier >= targetRoll ? "Success!" : "Failure!"}]\n`
|
||||
else state.prefix = `\n[Target AC: ${targetRoll} Attack roll: ${dieText}. ${score >= targetRoll ? "Success!" : "Failure!"}]\n`
|
||||
|
||||
var text
|
||||
if (score + modifier >= targetRoll) text = `\n${toTitleCase(character.name)} successfully hit ${targetText}!`
|
||||
|
@ -2914,6 +3418,7 @@ function doAttack(command) {
|
|||
else if (score == 1) text += " Critical failure! The attack missed in a spectacular way!"
|
||||
|
||||
if (enemyString != null) text += enemyString
|
||||
if (allyString != null) text += allyString
|
||||
|
||||
if (targetRoll > 0 && (score + modifier >= targetRoll || score == 20)) text += addXpToAll(Math.floor(state.autoXp * clamp(targetRoll, 1, 20) / 20))
|
||||
return text + "\n"
|
||||
|
@ -3252,6 +3757,11 @@ function doShowEnemies(command) {
|
|||
return " "
|
||||
}
|
||||
|
||||
function doShowAllies(command) {
|
||||
state.show = "showAllies"
|
||||
return " "
|
||||
}
|
||||
|
||||
function doRemoveEnemy(command) {
|
||||
var arg0 = getArgumentRemainder(command, 0)
|
||||
if (arg0 == null) {
|
||||
|
@ -3304,6 +3814,58 @@ function doRemoveEnemy(command) {
|
|||
return `\n[The enemy ${toTitleCase(enemy.name)} has been removed]\n`
|
||||
}
|
||||
|
||||
function doRemoveAlly(command) {
|
||||
var arg0 = getArgumentRemainder(command, 0)
|
||||
if (arg0 == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Not enough parameters. See #help]\n"
|
||||
}
|
||||
|
||||
if (/\d+\D+(\d+\D*)+/gi.test(arg0)) {
|
||||
|
||||
var list = arg0.split(/\D+/)
|
||||
list.sort(function(a, b) {
|
||||
return b - a;
|
||||
});
|
||||
|
||||
var text = "\n"
|
||||
list.forEach(x => {
|
||||
var num = parseInt(x) - 1
|
||||
if (num >= state.allies.length) {
|
||||
state.show = "none"
|
||||
return `\n[Error: Ally ${x} does not exist. See #showallies]\n`
|
||||
}
|
||||
|
||||
var ally = state.allies[num]
|
||||
state.allies.splice(num, 1)
|
||||
var index = state.initiativeOrder.indexOf(ally)
|
||||
if (index >= 0) state.initiativeOrder.splice(index, 1)
|
||||
text += `[The ally ${toTitleCase(ally.name)} has been removed]\n`
|
||||
})
|
||||
|
||||
state.show = "none"
|
||||
return text
|
||||
}
|
||||
|
||||
var ally
|
||||
if (isNaN(arg0)) arg0 = state.allies.findIndex(x => x.name.toLowerCase() == arg0.toLowerCase())
|
||||
else arg0--
|
||||
|
||||
if (arg0 == -1) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Ally not found. See #showallies]\n"
|
||||
} else if (arg0 >= state.allies.length || arg0 < 0) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Location number out of bounds. See #showallies]\n"
|
||||
} else {
|
||||
ally = state.allies[arg0]
|
||||
state.allies.splice(arg0, 1)
|
||||
}
|
||||
|
||||
state.show = "none"
|
||||
return `\n[The ally ${toTitleCase(ally.name)} has been removed]\n`
|
||||
}
|
||||
|
||||
function doClearEnemies(command) {
|
||||
var arg0 = getArgument(command, 0)
|
||||
if (arg0 != null) {
|
||||
|
@ -3317,6 +3879,19 @@ function doClearEnemies(command) {
|
|||
return "\n[The enemies have been cleared]\n"
|
||||
}
|
||||
|
||||
function doClearAllies(command) {
|
||||
var arg0 = getArgument(command, 0)
|
||||
if (arg0 != null) {
|
||||
return doRemoveAlly(command)
|
||||
}
|
||||
|
||||
state.allies = []
|
||||
state.initiativeOrder = []
|
||||
|
||||
state.show = "none"
|
||||
return "\n[The allies have been cleared]\n"
|
||||
}
|
||||
|
||||
function doAddEnemy(command) {
|
||||
var name = getArgument(command, 0)
|
||||
if (name == null) {
|
||||
|
@ -3406,6 +3981,95 @@ function doAddEnemy(command) {
|
|||
return `[Enemy ${toTitleCase(enemy.name)} has been created]`
|
||||
}
|
||||
|
||||
function doAddAlly(command) {
|
||||
var name = getArgument(command, 0)
|
||||
if (name == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Not enough parameters. See #help]\n"
|
||||
}
|
||||
|
||||
var health = getArgument(command, 1)
|
||||
if (health == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Not enough parameters. See #help]\n"
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(health)) {
|
||||
health = calculateRoll(health)
|
||||
} else if (isNaN(health)) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Expected a number. See #help]\n"
|
||||
}
|
||||
health = parseInt(health)
|
||||
|
||||
var ac = getArgument(command, 2)
|
||||
if (ac == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Not enough parameters. See #help]\n"
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(ac)) {
|
||||
ac = calculateRoll(ac)
|
||||
} else if (isNaN(ac)) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Expected a number. See #help]\n"
|
||||
}
|
||||
ac = parseInt(ac)
|
||||
|
||||
var hitModifier = getArgument(command, 3)
|
||||
if (hitModifier == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Not enough parameters. See #help]\n"
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(hitModifier)) {
|
||||
hitModifier = calculateRoll(hitModifier)
|
||||
} else if (isNaN(hitModifier)) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Expected a number. See #help]\n"
|
||||
}
|
||||
|
||||
var damage = getArgument(command, 4)
|
||||
if (damage == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Not enough parameters. See #help]\n"
|
||||
} else if (isNaN(damage) && !/^\d*d\d+((\+|-)\d+)?$/gi.test(damage)) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Expected a number. See #help]\n"
|
||||
}
|
||||
|
||||
var initiative = getArgument(command, 5)
|
||||
if (initiative == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Not enough parameters. See #help]\n"
|
||||
} else if (/^\d*d\d+((\+|-)\d+)?$/gi.test(initiative)) {
|
||||
initiative = calculateRoll(initiative)
|
||||
} else if (isNaN(initiative)) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Expected a number. See #help]\n"
|
||||
}
|
||||
initiative = parseInt(initiative)
|
||||
|
||||
var spells = []
|
||||
var spell = null
|
||||
var index = 6
|
||||
do {
|
||||
spell = getArgument(command, index++)
|
||||
if (spell != null) spells.push(spell)
|
||||
} while (spell != null)
|
||||
|
||||
var ally = createAlly(name, health, ac, hitModifier, damage, initiative)
|
||||
ally.spells = spells
|
||||
|
||||
var allyMatches = state.allies.filter(x => x.name.toLowerCase() == ally.name.toLowerCase() || x.name.toLowerCase() == `${ally.name.toLowerCase()} a`)
|
||||
if (allyMatches.length > 0) {
|
||||
ally.name = getUniqueName(ally.name)
|
||||
if (ally.name.endsWith("A")) {
|
||||
allyMatches[0].name = ally.name
|
||||
ally.name = ally.name.substring(0, ally.name.length - 1) + "B"
|
||||
}
|
||||
}
|
||||
|
||||
state.allies.push(ally)
|
||||
|
||||
state.show = "none"
|
||||
return `[Ally ${toTitleCase(ally.name)} has been created]`
|
||||
}
|
||||
|
||||
function doInitiative(command) {
|
||||
for (character of state.characters) {
|
||||
var stat = character.stats.find(element => element.name.toLowerCase() == "dexterity")
|
||||
|
@ -3418,6 +4082,11 @@ function doInitiative(command) {
|
|||
else enemy.calculatedInitiative = enemy.initiative
|
||||
}
|
||||
|
||||
for (ally of state.allies) {
|
||||
if (isNaN(ally.initiative)) ally.calculatedInitiative = calculateRoll(ally.initiative)
|
||||
else ally.calculatedInitiative = ally.initiative
|
||||
}
|
||||
|
||||
if (state.enemies.length == 0) {
|
||||
state.show = "none"
|
||||
return "\n[Error: No enemies! Type #addenemy or #encounter]\n"
|
||||
|
@ -3479,6 +4148,15 @@ function doTurn(command) {
|
|||
if (index >= 0) state.initiativeOrder.splice(index, 1)
|
||||
}
|
||||
|
||||
var defeatedAllies = 0
|
||||
for (var ally of state.allies) {
|
||||
if (ally.health > 0) continue
|
||||
|
||||
defeatedAllies++
|
||||
var index = state.initiativeOrder.findIndex(x => x.name.toLowerCase() == ally.name.toLowerCase())
|
||||
if (index >= 0) state.initiativeOrder.splice(index, 1)
|
||||
}
|
||||
|
||||
var defeatedCharacters = 0
|
||||
for (var character of state.characters) {
|
||||
if (character.health > 0) continue
|
||||
|
@ -3519,6 +4197,7 @@ function doBlock(command) {
|
|||
|
||||
var character = state.characters.find(x => x.name.toLowerCase() == state.blockCharacter.name.toLowerCase())
|
||||
if (character == null) character = state.enemies.find(x => x.name.toLowerCase() == state.blockCharacter.name.toLowerCase())
|
||||
if (character == null) character = state.allies.find(x => x.name.toLowerCase() == state.blockCharacter.name.toLowerCase())
|
||||
if (character == null) {
|
||||
state.show = "none"
|
||||
return "\n[Error: Character no longer exists. See #help]\n"
|
||||
|
@ -4265,6 +4944,7 @@ function doCastSpell(command) {
|
|||
var roll = advantage == "advantage" ? Math.max(roll1, roll2) : advantage == "disadvantage" ? Math.min(roll1, roll2) : roll1
|
||||
|
||||
var enemyString = ""
|
||||
var allyString = ""
|
||||
if (targetText != null && state.initiativeOrder.length > 0) {
|
||||
var foundEnemy
|
||||
|
||||
|
@ -4283,6 +4963,23 @@ function doCastSpell(command) {
|
|||
}
|
||||
}
|
||||
|
||||
var foundAlly
|
||||
|
||||
if (foundEnemy == null) for (var ally of state.allies) {
|
||||
if (targetText.toLowerCase().includes(ally.name.toLowerCase())) {
|
||||
foundAlly = ally
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAlly == null) {
|
||||
var indexMatches = targetText.match(/(?<=ally\s*)\d+/gi)
|
||||
if (indexMatches != null) {
|
||||
foundAlly = state.allies[parseInt(indexMatches[0]) - 1]
|
||||
targetText = targetText.replace(/ally\s*d+/gi, foundAlly.name)
|
||||
}
|
||||
}
|
||||
|
||||
var damage = roll == 20 ? calculateRoll("2d6") + calculateRoll("2d6") : calculateRoll("2d6")
|
||||
|
||||
var damageMatches = targetText.match(/\d*d\d+((\+|-)d+)?/gi)
|
||||
|
@ -4305,6 +5002,20 @@ function doCastSpell(command) {
|
|||
else enemyString += ` ${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!\n`
|
||||
}
|
||||
}
|
||||
|
||||
if (foundAlly != null) {
|
||||
if (usingDefaultDifficulty) difficulty = foundAlly.ac
|
||||
if (roll == 20 || roll + modifier >= difficulty) {
|
||||
if (roll == 20) allyString += `\nCritical Damage: ${damage}\n`
|
||||
else allyString += `\nDamage: ${damage}\n`
|
||||
|
||||
state.blockCharacter = foundAlly
|
||||
state.blockPreviousHealth = foundAlly.health
|
||||
foundAlly.health = Math.max(0, foundAlly.health - damage)
|
||||
if (foundAlly.health == 0) allyString += ` ${toTitleCase(foundAlly.name)} has been defeated!\n`
|
||||
else allyString += ` ${toTitleCase(foundAlly.name)} has ${foundAlly.health} health remaining!\n`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
state.show = "prefix"
|
||||
|
@ -4322,6 +5033,7 @@ function doCastSpell(command) {
|
|||
else text += ` The spell ${targetText != null ? "misses" : "fails"}!`
|
||||
|
||||
if (enemyString != null) text += enemyString
|
||||
if (allyString != null) text += allyString
|
||||
|
||||
if (difficulty > 0 && (roll + modifier >= difficulty || roll == 20)) text += addXpToAll(Math.floor(state.autoXp * clamp(difficulty, 1, 20) / 20))
|
||||
return `\n${text}\n`
|
||||
|
@ -4493,6 +5205,7 @@ function doReset(command) {
|
|||
state.locations = []
|
||||
state.location = null
|
||||
state.enemies = null
|
||||
state.allies = null
|
||||
state.initiativeOrder = []
|
||||
state.x = null
|
||||
state.y = null
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue