mirror of
https://github.com/raeleus/Hashtag-DnD.git
synced 2025-09-22 12:26:47 -04:00
Compare commits
No commits in common. "master" and "0.6.0" have entirely different histories.
6 changed files with 20 additions and 6871 deletions
|
@ -1,5 +1,4 @@
|
|||
const modifier = (text) => {
|
||||
[text, stop] = AutoCards("context", text, stop);
|
||||
return { text }
|
||||
}
|
||||
|
||||
|
|
731
Input.js
731
Input.js
|
@ -1,4 +1,4 @@
|
|||
const version = "Hashtag DnD v0.8.0"
|
||||
const version = "Hashtag DnD v0.6.0"
|
||||
const rollSynonyms = ["roll"]
|
||||
const createSynonyms = ["create", "generate", "start", "begin", "setup", "party", "member", "new"]
|
||||
const renameCharacterSynonyms = ["renamecharacter", "renameperson"]
|
||||
|
@ -70,20 +70,15 @@ 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"]
|
||||
|
@ -117,12 +112,6 @@ 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 }
|
||||
|
@ -188,7 +177,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, 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) 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 == null) {
|
||||
if (state.characterName == null) {
|
||||
|
@ -273,18 +262,13 @@ 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)
|
||||
|
@ -318,8 +302,6 @@ const modifier = (text) => {
|
|||
|
||||
if (state.flavorText != null) text += state.flavorText
|
||||
|
||||
text = AutoCards("input", text);
|
||||
|
||||
return { text }
|
||||
}
|
||||
|
||||
|
@ -1204,435 +1186,6 @@ 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},
|
||||
|
@ -1687,7 +1240,6 @@ 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 = []
|
||||
|
@ -1697,7 +1249,6 @@ 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
|
||||
|
@ -1766,13 +1317,6 @@ 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) {
|
||||
|
@ -2950,14 +2494,6 @@ 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
|
||||
|
@ -2968,7 +2504,7 @@ function doHeal(command) {
|
|||
}
|
||||
|
||||
state.show = "none"
|
||||
return `\n[Error: Could not find an enemy, ally, or character matching the name ${arg1}. Type #enemies, #allies, or #characters to see a list]`
|
||||
return `\n[Error: Could not find an enemy or character matching the name ${arg1}. Type #enemies or #characters to see a list]`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3032,14 +2568,6 @@ 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)
|
||||
|
@ -3049,7 +2577,7 @@ function doDamage(command) {
|
|||
}
|
||||
|
||||
state.show = "none"
|
||||
return `\n[Error: Could not find an enemy, ally, or character matching the name ${arg1}. Type #enemies, #allies, or #characters to see a list]`
|
||||
return `\n[Error: Could not find an enemy matching the name ${arg1}. Type #enemies or #characters to see a list]`
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3322,7 +2850,6 @@ function doAttack(command) {
|
|||
}
|
||||
|
||||
var enemyString = ""
|
||||
var allyString = ""
|
||||
if (state.initiativeOrder.length > 0) {
|
||||
var foundEnemy
|
||||
|
||||
|
@ -3341,23 +2868,6 @@ 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)
|
||||
|
@ -3384,22 +2894,6 @@ 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
|
||||
|
@ -3407,10 +2901,10 @@ function doAttack(command) {
|
|||
state.show = "prefix"
|
||||
|
||||
if (targetRoll == 0) state.prefix = ""
|
||||
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`
|
||||
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`
|
||||
|
||||
var text
|
||||
if (score + modifier >= targetRoll) text = `\n${toTitleCase(character.name)} successfully hit ${targetText}!`
|
||||
|
@ -3420,7 +2914,6 @@ 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"
|
||||
|
@ -3759,11 +3252,6 @@ function doShowEnemies(command) {
|
|||
return " "
|
||||
}
|
||||
|
||||
function doShowAllies(command) {
|
||||
state.show = "showAllies"
|
||||
return " "
|
||||
}
|
||||
|
||||
function doRemoveEnemy(command) {
|
||||
var arg0 = getArgumentRemainder(command, 0)
|
||||
if (arg0 == null) {
|
||||
|
@ -3816,58 +3304,6 @@ 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) {
|
||||
|
@ -3881,19 +3317,6 @@ 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) {
|
||||
|
@ -3983,95 +3406,6 @@ 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")
|
||||
|
@ -4084,11 +3418,6 @@ 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"
|
||||
|
@ -4150,15 +3479,6 @@ 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
|
||||
|
@ -4199,7 +3519,6 @@ 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"
|
||||
|
@ -4946,7 +4265,6 @@ 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
|
||||
|
||||
|
@ -4965,23 +4283,6 @@ 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)
|
||||
|
@ -5004,20 +4305,6 @@ 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"
|
||||
|
@ -5035,7 +4322,6 @@ 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`
|
||||
|
@ -5207,7 +4493,6 @@ function doReset(command) {
|
|||
state.locations = []
|
||||
state.location = null
|
||||
state.enemies = null
|
||||
state.allies = null
|
||||
state.initiativeOrder = []
|
||||
state.x = null
|
||||
state.y = null
|
||||
|
|
6044
Library.js
6044
Library.js
File diff suppressed because it is too large
Load diff
85
Output.js
85
Output.js
|
@ -1,8 +1,5 @@
|
|||
const modifier = (text) => {
|
||||
if (state.show == null) {
|
||||
text = AutoCards("output", text);
|
||||
return { text }
|
||||
}
|
||||
if (state.show == null) return { text }
|
||||
|
||||
var character = getCharacter()
|
||||
var possessiveName = character == null ? null : getPossessiveName(character.name)
|
||||
|
@ -90,54 +87,6 @@ const modifier = (text) => {
|
|||
break
|
||||
}
|
||||
break
|
||||
case "setupAlly":
|
||||
switch (state.setupAllyStep) {
|
||||
case 0:
|
||||
text += `***ALLY CREATION***\nWould you like to use a preset ally? (y/n/q to quit)\n`
|
||||
break
|
||||
case 1:
|
||||
text += `What is the ally's name? This must be a unique name that has no duplicates in the current encounter. Typing the name of an existing ally will modify that ally's properties. Type q to quit.\n`
|
||||
break
|
||||
case 2:
|
||||
text += `${!state.newAlly ? "Ally name already exists. You are now modifying the existing ally " + state.tempAlly.name + ". " : ""}What is the ally's health? This can be any positive integer or a dice roll (ie. 3d6+5). Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.health : ""}\n`
|
||||
break
|
||||
case 3:
|
||||
text += `What is the ally's armor class (AC)? This can be any positive integer with 10 being easy and 20 being incredibly difficult. It can also be a dice roll (ie. 2d4+5). Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.ac : ""}\n`
|
||||
break
|
||||
case 4:
|
||||
text += `What is the ally's hit modifier? This affects how accurate their attacks are. This can be any integer. 0 is normal accuracy. Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.hitModifier : ""}\n`
|
||||
break
|
||||
case 5:
|
||||
text += `What is the ally's damage? This can be any positive integer or a dice roll (ie. 2d6+5). The dice roll is calculated at the time of each attack. Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.damage : ""}\n`
|
||||
break
|
||||
case 6:
|
||||
text += `What is the ally's initiative? Initiative controls turn order. This can be any positive integer with higher numbers going first in battle. This can also be a dice roll (ie. 1d20+3). Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.initiative : ""}\n`
|
||||
break
|
||||
case 7:
|
||||
text += `Enter the name of a spell that the ally knows. If it can target this spell at an enemy character, add a dice roll for the damage calculation after it (ie. Ray of Frost3d6+2). Type s to stop entering spells or type q to quit.${!state.newAlly ? " Type e to erase all current spells." : ""}\n`
|
||||
break
|
||||
case 8:
|
||||
text += `Enter the name of another spell that the ally knows. If it can target this spell at an enemy character, add a dice roll for the damage calculation after it (ie. Ray of Frost3d6+2). Type s to stop entering spells or type q to quit.\n`
|
||||
break
|
||||
case 100:
|
||||
text += `What ally preset will you choose?\nHeroes\n1. Fighter\n2. Cleric\n3. Rogue\n4. Ranger\n5. Barbarian\n6. Bard\n7. Druid\n8. Monk\n9. Paladin\n10. Wizard\n11. Sorcerer\n12. Warlock\n13. Artificer`
|
||||
text += `\n\nHumanoid\n14. Commoner\n15. Bandit\n16. Guard\n17. Cultist\n18. Acolyte\n19. Apprentice\n20. Witch\n21. Buccaneer\n22. Spy\n123. Captain\n24. Bard\n25. Berserker\n26 Priest\n27. Knight\n28. Archer\n29. Warrior\n30. Conjurer\n31. Mage\n32. Assassin\n33. Evoker\n34. Necromancer\n35. Champion\n36. Warlord\n37. Archmage\n38. Archdruid`
|
||||
text += `\n\nFamiliars\n39. Ape\n40. Badger\n41. Bat\n42. Black Bear\n43. Boar\n44. Brown Bear\n45. Camel\n46. Cat\n47. Constrictor Snake\n48. Crab\n49. Crocodile\n50. Dire Wolf\n51. Draft Horse\n52. Elephant\n53. Elk\n54. Frog\n55. Giant Badger\n56. Giant Crab\n57. Giant Goat\n58. Giant Seahorse\n59. Giant Spider\n60. Giant Weasel\n61. Goat\n62. Hawk\n63. Imp\n64. Lion\n65. Lizard\n66. Mastiff\n67. Mule\n68. Octopus\n69. Owl\n70. Panther\n71. Pony\n72. Pseudodragon\n73. Quasit\n74. Rat\n75. Raven\n76. Reef Shark\n77. Riding Horse\n78. Scorpion\n79. Skeleton\n80. Slaad Tadpole\n81. Sphinx of Wonder\n82. Spider\n83. Sprite\n84. Tiger\n85. Venomous Snake\n86. Warhorse\n87. Weasel\n88. Wolf\n89. Zombie`
|
||||
text += `\n\nEnter the number or q to quit. If you want to rename the ally, add a space and type the name\n(ie. 25 Thuggish Zombie B)\n`
|
||||
break
|
||||
case 500:
|
||||
var hashtag = `#addally "${state.tempAlly.name}" ${state.tempAlly.health} ${state.tempAlly.ac} ${state.tempAlly.hitModifier} ${state.tempAlly.damage} ${state.tempAlly.initiative}`
|
||||
for (var spell of state.tempAlly.spells) {
|
||||
hashtag += ` "${spell}"`
|
||||
}
|
||||
|
||||
text += `${state.tempAlly.name} has been created.\nType #showallies to show the list of all allies.\nCopy and paste the following hashtag to create another identical ally like this:\n${hashtag}\n***********\n`
|
||||
break;
|
||||
case null:
|
||||
text += `[Ally creation has been aborted!]\n`
|
||||
break
|
||||
}
|
||||
break
|
||||
case "stragedy":
|
||||
text += handleStragedy()
|
||||
break
|
||||
|
@ -372,20 +321,6 @@ const modifier = (text) => {
|
|||
}
|
||||
}
|
||||
|
||||
text += "******************\n\n"
|
||||
break
|
||||
case "showAllies":
|
||||
text += "*** ALLIES ***\n"
|
||||
|
||||
if (state.allies.length == 0) {
|
||||
text += "There are no allies present here. Type #encounter to generate a scripted set or #addally to add your own\n"
|
||||
} else {
|
||||
var index = 0
|
||||
for (var ally of state.allies) {
|
||||
text += `${++index}. ${toTitleCase(ally.name)} (Health: ${ally.health} AC: ${ally.ac} Initiative: ${ally.initiative})\n`
|
||||
}
|
||||
}
|
||||
|
||||
text += "******************\n\n"
|
||||
break
|
||||
case "initiative":
|
||||
|
@ -564,9 +499,7 @@ const modifier = (text) => {
|
|||
text += "\n#addenemy name health ac hitModifier damage initiative spells"
|
||||
text += "\n Adds the specified enemy to the list of enemies. health, ac, hitModifier, damage, and initiative can be numbers or dice rolls such as 3d6+5. Type the name in quotes if the name contains a space. The rest of the parameters can be a list of spells. Each spell must be typed in quotes if it has a space. If the spell does damage, write the name and damage roll in the following format: \"Ray of Frost5d10\""
|
||||
text += "\n#removeenemy name or index"
|
||||
text += "\n Removes the enemy as specified by the name or index. To delete multiple enemies, type the numbers with spaces or commas between them. This is safer than calling #removeenemy multiple times because the numbers shift as enemies are deleted. Quotes are not necessary."
|
||||
text += "\n#clearenemies"
|
||||
text += "\n Removes all enemies."
|
||||
text += "\n Removes the enemy as specified by the name or index. To delete multiple enemies, type the numbers with spaces or commas between them. This is safer than calling #removenote multiple times because the numbers shift as enemies are deleted. Quotes are not necessary."
|
||||
text += "\n#initiative"
|
||||
text += "\n Assigns initiative to all characters and enemies. This begins combat."
|
||||
text += "\n#turn"
|
||||
|
@ -578,18 +511,6 @@ const modifier = (text) => {
|
|||
text += "\n#flee (difficulty_class or automatic|effortless|easy|medium|hard|impossible)"
|
||||
text += "\n Attempt to flee from combat. If the difficulty is not specified, the default difficulty will be used instead."
|
||||
|
||||
text += "\n\n--Allies--"
|
||||
text += "\n#setupally"
|
||||
text += "\nFollow prompts to create an ally from a template or completely from scratch. It will be added to the existing encounter if there is one already specified."
|
||||
text += "\n#showallies"
|
||||
text += "\n Shows the list of current allies."
|
||||
text += "\n#addally name health ac hitModifier damage initiative spells"
|
||||
text += "\n Adds the specified ally to the list of allies. health, ac, hitModifier, damage, and initiative can be numbers or dice rolls such as 3d6+5. Type the name in quotes if the name contains a space. The rest of the parameters can be a list of spells. Each spell must be typed in quotes if it has a space. If the spell does damage, write the name and damage roll in the following format: \"Ray of Frost5d10\""
|
||||
text += "\n#removeally name or index"
|
||||
text += "\n Removes the ally as specified by the name or index. To delete multiple allies, type the numbers with spaces or commas between them. This is safer than calling #removeally multiple times because the numbers shift as allies are deleted. Quotes are not necessary."
|
||||
text += "\n#clearallies"
|
||||
text += "\n Removes all allies."
|
||||
|
||||
text += "\n\n--Locations--"
|
||||
text += "\n#createlocation [(x) (y) or (here|far) or (distance)] location_name"
|
||||
text += "\n Creates a location at the given coordinates. The coordinates must be integers. If the coordinates are not provided, they are randomized within a range of 10 units from the party's current location. You can also use \"here\" to indicate that the location is at party's coordinates. \"far\" indicates that the coordinates will be randomly generated 50-100 units away. You may also just specify a distance. Multiple locations may exist at the same coordinates. A story card is created for the location. Quotes are not necessary."
|
||||
|
@ -648,8 +569,6 @@ const modifier = (text) => {
|
|||
}
|
||||
|
||||
state.show = null
|
||||
|
||||
text = AutoCards("output", text);
|
||||
return { text }
|
||||
}
|
||||
|
||||
|
|
18
README.md
18
README.md
|
@ -4,29 +4,19 @@ A Scenario script for AI Dungeon
|
|||
Hashtag DnD is a scripted AI Dungeon scenario that gives you a variety of tools to enhance your adventure!
|
||||
|
||||
Features:
|
||||
Fully working inventory system, loot drops, and item/spell shops<br>
|
||||
Hit points, turn-based battles, and a focus on strategic combat<br>
|
||||
Fully working inventory system<br>
|
||||
Health points and a focus on strategic combat.<br>
|
||||
Skill/Ability based gameplay guarantees a challenge at every turn<br>
|
||||
Advanced dice rolling syntax allows you to set the precise odds<br>
|
||||
Unlimited party size with each character having their own inventory, stats, and biographies<br>
|
||||
Multiplayer compatible<br>
|
||||
Personalized note system that does not take up context space<br>
|
||||
Create locations to travel to and view them in a map<br>
|
||||
Minigames including Mastermind, Memory, and Stragedy, a fully developed trading card game
|
||||
Create locations to travel to and view them in a map
|
||||
Minigames including Mastermind and Stragedy, a fully developed trading card game
|
||||
|
||||
See the [user guide here](https://github.com/raeleus/Hashtag-DnD/wiki).
|
||||
Watch the [tutorial video](https://youtu.be/E5TYU7rDaBQ).
|
||||
|
||||
Hashtag-DnD has another new scenario! Check out the [VTOL-Knights Repository](https://github.com/raeleus/Hashtag-DnD/tree/VTOL-Knights)
|
||||
This script implements Auto-Cards by LewdLeah. See more details [on github](https://github.com/LewdLeah/Auto-Cards)
|
||||
|
||||
v. 0.8.0
|
||||
* Added Auto-Cards by LewdLeah. This feature is disabled by default. Activate it by changing the "Disabled" setting to "false" in the "Configure Auto-Cards" story card.
|
||||
|
||||
v. 0.7.0
|
||||
* Added allies which are NPC characters that fight alongside the characters in encounters
|
||||
* Fixed enemy spells showing the damage dice in the output text.
|
||||
|
||||
v. 0.6.0
|
||||
* Added Memory/Matchmaking Game
|
||||
* Added Item Shop - Make sure to import the latest story cards: https://github.com/raeleus/Hashtag-DnD/blob/master/story-cards.json
|
||||
|
|
|
@ -5187,7 +5187,7 @@
|
|||
"keys": "Create Food and Water",
|
||||
"value": "Spell that creates either 45 pounds of food or ten gallons of clean water.",
|
||||
"type": "spell",
|
||||
"title": "Create Food and Water",
|
||||
"title": "Enlarge",
|
||||
"description": "",
|
||||
"useForCharacterCreation": false
|
||||
},
|
||||
|
@ -5769,7 +5769,7 @@
|
|||
},
|
||||
{
|
||||
"keys": "Fog Cloud",
|
||||
"value": "Spell that creates a twenty foot sphere of fog which obstructs view through it.",
|
||||
"value": "Spell that creates a twentyh foot sphere of fog which obstructs view through it.",
|
||||
"type": "spell",
|
||||
"title": "Fog Cloud",
|
||||
"description": "",
|
||||
|
@ -7790,13 +7790,5 @@
|
|||
"title": "Stragedy Witch Card",
|
||||
"description": "",
|
||||
"useForCharacterCreation": false
|
||||
},
|
||||
{
|
||||
"keys": "Edit the entry above to adjust your story card automation settings",
|
||||
"value": "> Auto-Cards automatically creates and updates plot-relevant story cards while you play. You may configure the following settings by replacing \"false\" with \"true\" (and vice versa) or by adjusting numbers for the appropriate settings.\n> Disable Auto-Cards: true\n> Show detailed guide: false\n> Delete all automatic story cards: false\n> Reset all config settings and prompts: false\n> Pin this config card near the top: true\n> Minimum turns cooldown for new cards: 22\n> New cards use a bulleted list format: true\n> Maximum entry length for new cards: 750\n> New cards perform memory updates: true\n> Card memory bank preferred length: 2750\n> Memory summary compression ratio: 25\n> Exclude all-caps from title detection: true\n> Also detect titles from player inputs: false\n> Minimum turns age for title detection: 5\n> Use Live Script Interface v2: false\n> Log debug data in a separate card: false",
|
||||
"type": "class",
|
||||
"title": "Configure \nAuto-Cards",
|
||||
"description": "> Please visit my AI Dungeon profile @LewdLeah and read my bio for simple instructions on adding Auto-Cards to your scenarios! ❤️\n\nhttps://play.aidungeon.com/profile/LewdLeah\n\n> Auto-Cards v1.0.0 is an open-source script for AI Dungeon made by LewdLeah. You have my full permission to use Auto-Cards within your personal or published scenarios!\n> You're also welcome to edit the AI prompts and title exclusions provided below. I sincerely hope you enjoy your adventures! 🥰\n——————————————————————————\n> AI prompt to generate new cards:\n-----\n\n<SYSTEM>\n# Stop the story and ignore previous instructions. Write a brief and coherent informational entry for %{title} following these instructions:\n- Write only third-person pure prose information about %{title} using complete sentences with correct punctuation\n- Avoid short-term temporary details or appearances, instead focus on plot-significant information\n- Prioritize story-relevant details about %{title} first to ensure seamless integration with the previous plot\n- Create new information based on the context and story direction\n- Mention %{title} in every sentence\n- Use semicolons if needed\n- Add additional details about %{title} beneath incomplete entries\n- Be concise and grounded\n- Imitate the story's writing style and infer the reader's preferences\n</SYSTEM>\nContinue the entry for %{title} below while avoiding repetition:\n%{entry}\n——————————————————————————\n> AI prompt to summarize card memories:\n-----\n\n<SYSTEM>\n# Stop the story and ignore previous instructions. Summarize and condense the given paragraph into a narrow and focused memory passage while following these guidelines:\n- Ensure the passage retains the core meaning and most essential details\n- Use the third-person perspective\n- Prioritize information-density, accuracy, and completeness\n- Remain brief and concise\n- Write firmly in the past tense\n- The paragraph below pertains to old events from far earlier in the story\n- Integrate %{title} naturally within the memory; however, only write about the events as they occurred\n- Only reference information present inside the paragraph itself, be specific\n</SYSTEM>\nWrite a summarized old memory passage for %{title} based only on the following paragraph:\n\"\"\"\n%{memory}\n\"\"\"\nSummarize below:\n——————————————————————————\n> Titles banned from new card creation:\nNorth, East, South, West, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, January, February, March, April, May, June, July, August, September, October, November, December",
|
||||
"useForCharacterCreation": false
|
||||
}
|
||||
]
|
Loading…
Add table
Add a link
Reference in a new issue