diff --git a/Context.js b/Context.js
index 9faa434..2304c34 100644
--- a/Context.js
+++ b/Context.js
@@ -1,5 +1,4 @@
const modifier = (text) => {
- [text, stop] = AutoCards("context", text, stop);
return { text }
}
diff --git a/Input.js b/Input.js
index aba6921..26d2826 100644
--- a/Input.js
+++ b/Input.js
@@ -1,4 +1,4 @@
-const version = "Hashtag DnD v0.8.0"
+const version = "Hashtag DnD v0.2.0"
const rollSynonyms = ["roll"]
const createSynonyms = ["create", "generate", "start", "begin", "setup", "party", "member", "new"]
const renameCharacterSynonyms = ["renamecharacter", "renameperson"]
@@ -29,8 +29,6 @@ const noteSynonyms = ["note", "takenote", "setnote", "createnote", "remember"]
const clearNotesSynonyms = ["clearnotes"]
const eraseNoteSynonyms = ["erasenote", "removenote", "deletenote", "cancelnote"]
const takeSynonyms = ["take", "steal", "get", "grab", "receive", "loot"]
-const takeWeaponSynonyms = ["takeweapon", "stealweapon", "getweapon", "grabweapon", "receiveweapon", "lootweapon"]
-const takeArmorSynonyms = ["takearmor", "stealarmor", "getarmor", "grabarmor", "receivearmor", "lootarmor"]
const buySynonyms = ["buy", "purchase", "barter", "trade", "swap", "exchange"]
const sellSynonyms = ["sell"]
const dropSynonyms = ["remove", "discard", "drop", "leave", "dispose", "toss", "throw", "throwaway", "trash", "donate", "eat", "consume", "use", "drink", "pay", "lose"]
@@ -70,35 +68,17 @@ 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"]
-const blockSynonyms = ["block", "parry", "nullify", "invalidate"]
-const repeatTurnSynonyms = ["repeatturn", "repeat"]
-const basicDeckSynonyms = ["basicdeck", "stragedybasicdeck"]
-const cardShopSynonyms = ["cardshop", "stragedyshop", "cardstore", "stragedystore"]
-const spellShopSynonyms = ["spellshop", "spellstore"]
-const itemShopSynonyms = ["itemshop", "itemstore"]
-const stragedySynonyms = ["stragedy", "playgame", "game", "startgame", "begingame", "playcards", "playstragedy", "startstragedy", "beginstragedy"]
-const lockpickSynonyms = ["lockpick", "lockpicking", "codebreaker", "pick", "hack", "hacking", "mastermind"]
-const memorySynonyms = ["memory", "matchmaking", "matching", "matchmaker", "match2"]
-const addCardSynonyms = ["addcard"]
-const equipSynonyms = ["equip", "arm", "wear"]
-const rewardSynonyms = ["reward"]
const helpSynonyms = ["help"]
const modifier = (text) => {
@@ -112,53 +92,12 @@ const modifier = (text) => {
}
if (state.setupEnemyStep != null) {
+ state.setupEnemyStep
text = handleSetupEnemyStep(text)
if (state.setupEnemyStep != null) return { 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 }
- else text = rawText
- }
-
- if (state.stragedyTurn != null) {
- text = handleStragedyTurn(text)
- if (state.stragedyTurn != null) return { text }
- else text = rawText
- }
-
- if (state.spellShopStep != null) {
- text = handleSpellShopStep(text)
- if (state.spellShopStep != null) return { text }
- else text = rawText
- }
-
- if (state.itemShopStep != null) {
- text = handleItemShopStep(text)
- if (state.itemShopStep != null) return { text }
- else text = rawText
- }
-
- if (state.lockpickingTurn != null) {
- text = handleLockpickingTurn(text)
- if (state.lockpickingTurn != null) return { text }
- else text = rawText
- }
-
- if (state.memoryTurn != null) {
- text = handleMemoryTurn(text)
- if (state.memoryTurn != null) return { text }
- else text = rawText
- }
-
if (state.initialized == null || !text.includes("#")) {
state.initialized = true;
return { text }
@@ -184,20 +123,20 @@ const modifier = (text) => {
if (state.characterName == null && found) {
state.show = "none"
- text = `\n[Error: Character name not specified. Use the "do" or "say" modes. Alternatively, use "story" mode in the following format without quotes: "charactername #hashtag"]\n`
+ text = `\n[Error: Character name not specified. Use the "do" or "say" modes. Alternatively, use "story" mode with the following format without quotes: "charactername #hashtag"]\n`
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, damageSynonyms, resetSynonyms), function () {return true})
if (found == null) {
if (state.characterName == null) {
state.show = "none"
- text = `\n[Error: Character name not specified. Use the "do" or "say" modes. Alternatively, use "story" mode in the following format without quotes: "charactername #hashtag"]\n`
+ text = `\n[Error: Character name not specified. Use the "do" or "say" modes. Alternatively, use "story" mode with the following format without quotes: "charactername #hashtag"]\n`
return { text }
} else {
state.show = "none"
- text = `\n[Error: Character ${state.characterName} does not exist. Type #setup to create this character]\n`
+ text = `\n[Error: Character ${state.characterName} does not exist. Type #setup to create this character.]\n`
return { text }
}
}
@@ -273,35 +212,15 @@ 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)
- if (text == null) text = processCommandSynonyms(command, commandName, blockSynonyms, doBlock)
- if (text == null) text = processCommandSynonyms(command, commandName, repeatTurnSynonyms, doRepeatTurn)
- if (text == null) text = processCommandSynonyms(command, commandName, basicDeckSynonyms, doBasicDeck)
- if (text == null) text = processCommandSynonyms(command, commandName, cardShopSynonyms, doCardShop)
- if (text == null) text = processCommandSynonyms(command, commandName, spellShopSynonyms, doSpellShop)
- if (text == null) text = processCommandSynonyms(command, commandName, itemShopSynonyms, doItemShop)
- if (text == null) text = processCommandSynonyms(command, commandName, stragedySynonyms, doStragedy)
- if (text == null) text = processCommandSynonyms(command, commandName, lockpickSynonyms, doLockpick)
- if (text == null) text = processCommandSynonyms(command, commandName, memorySynonyms, doMemory)
- if (text == null) text = processCommandSynonyms(command, commandName, addCardSynonyms, doAddCard)
- if (text == null) text = processCommandSynonyms(command, commandName, equipSynonyms, doEquip)
- if (text == null) text = processCommandSynonyms(command, commandName, rewardSynonyms, doReward)
- if (text == null) text = processCommandSynonyms(command, commandName, takeWeaponSynonyms, doTakeWeapon)
- if (text == null) text = processCommandSynonyms(command, commandName, takeArmorSynonyms, doTakeArmor)
if (text == null) text = processCommandSynonyms(command, commandName, helpSynonyms, doHelp)
if (text == null) {
var character = getCharacter()
@@ -318,8 +237,6 @@ const modifier = (text) => {
if (state.flavorText != null) text += state.flavorText
- text = AutoCards("input", text);
-
return { text }
}
@@ -878,304 +795,79 @@ function handleSetupEnemyStep(text) {
state.tempEnemy = createEnemy("Wight", calculateRoll("6d8+18"), 14, 4, "1d8+2", "d20+1", "Life Drain4d6+3")
break
case 51:
- state.tempEnemy = createEnemy("Aboleth", calculateRoll("18d10"), 17, 9, "6d6+15", "d20-1", "Enslave", "Psychic Drain3d6")
+ createEnemy("Aboleth", calculateRoll("18d10"), 17, 9, "6d6+15", "d20-1", "Enslave", "Psychic Drain3d6")
break
case 52:
- state.tempEnemy = createEnemy("Assassin", calculateRoll("12d8+24"), 15, 6, "2d6+6", "d20+3")
+ createEnemy("Assassin", calculateRoll("12d8+24"), 15, 6, "2d6+6", "d20+3")
break
case 53:
- state.tempEnemy = createEnemy("Chimera", calculateRoll("12d10+48"), 14, 7, "2d6+4", "d20", "Fire Breath7d8")
+ createEnemy("Chimera", calculateRoll("12d10+48"), 14, 7, "2d6+4", "d20", "Fire Breath7d8")
break
case 54:
- state.tempEnemy = createEnemy("Cloud Giant", calculateRoll("16d12+96"), 14, 12, "6d8+16", "d20", "Throw Rock4d10+8", "Control Weather")
+ createEnemy("Cloud Giant", calculateRoll("16d12+96"), 14, 12, "6d8+16", "d20", "Throw Rock4d10+8", "Control Weather")
break
case 55:
- state.tempEnemy = createEnemy("Cyclops", calculateRoll("12d12+60"), 14, 9, "3d8+6", "d20")
+ createEnemy("Cyclops", calculateRoll("12d12+60"), 14, 9, "3d8+6", "d20")
break
case 56:
- state.tempEnemy = createEnemy("Deva", calculateRoll("16d8+64"), 17, 8, "2d6+8", "d20+4")
+ createEnemy("Deva", calculateRoll("16d8+64"), 17, 8, "2d6+8", "d20+4")
break
case 57:
- state.tempEnemy = createEnemy("Drider", calculateRoll("13d10+52"), 19, 6, "3d8", "1d10+3", "Poison Bite2d8")
+ createEnemy("Drider", calculateRoll("13d10+52"), 19, 6, "3d8", "1d10+3", "Poison Bite2d8")
break
case 58:
- state.tempEnemy = createEnemy("Frost Giant", calculateRoll("12d12+60"), 15, 9, "6d12+12", "d20-1")
+ createEnemy("Frost Giant", calculateRoll("12d12+60"), 15, 9, "6d12+12", "d20-1")
break
case 59:
- state.tempEnemy = createEnemy("Hydra", calculateRoll("15d12+75"), 15, 8, "3d10+15", "d20+1")
+ createEnemy("Hydra", calculateRoll("15d12+75"), 15, 8, "3d10+15", "d20+1")
break
case 60:
- state.tempEnemy = createEnemy("Insane Mage", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Cone of Cold8d8", "Greater Invisibility", "Fireball8d6", "Shield")
+ createEnemy("Insane Mage", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Cone of Cold8d8", "Greater Invisibility", "Fireball8d6", "Shield")
break
case 61:
- state.tempEnemy = createEnemy("Medusa", calculateRoll("17d8+51"), 15, 5, "1d6+2", "d20+2", "Petrifying Gaze", "Snake Hair5d6")
+ createEnemy("Medusa", calculateRoll("17d8+51"), 15, 5, "1d6+2", "d20+2", "Petrifying Gaze", "Snake Hair5d6")
break
case 62:
- state.tempEnemy = createEnemy("Shield Guardian", calculateRoll("15d10+60"), 17, 7, "4d6+4", "d20-1", "Shield")
+ createEnemy("Shield Guardian", calculateRoll("15d10+60"), 17, 7, "4d6+4", "d20-1", "Shield")
break
case 63:
- state.tempEnemy = createEnemy("Spirit Naga", calculateRoll("10d10+20"), 15, 7, "8d8+4", "d20+3", "Dominate Person", "Lightning Bolt9d6")
+ createEnemy("Spirit Naga", calculateRoll("10d10+20"), 15, 7, "8d8+4", "d20+3", "Dominate Person", "Lightning Bolt9d6")
break
case 64:
- state.tempEnemy = createEnemy("Stone Golem", calculateRoll("17d10+85"), 17, 10, "6d8+12", "d20-1")
+ createEnemy("Stone Golem", calculateRoll("17d10+85"), 17, 10, "6d8+12", "d20-1")
break
case 65:
- state.tempEnemy = createEnemy("Treant", calculateRoll("12d12+60"), 16, 10, "6d6+12", "d20-1")
+ createEnemy("Treant", calculateRoll("12d12+60"), 16, 10, "6d6+12", "d20-1")
break
case 66:
- state.tempEnemy = createEnemy("Young Black Dragon", calculateRoll("15d10+45"), 18, 7, "4d6+8", "d20+2", "Acid Breath11d8")
+ createEnemy("Young Black Dragon", calculateRoll("15d10+45"), 18, 7, "4d6+8", "d20+2", "Acid Breath11d8")
break
case 67:
- state.tempEnemy = createEnemy("Young Blue Dragon", calculateRoll("16d10+64"), 18, 9, "12d6+10", "d20", "Lightning Breath10d10")
+ createEnemy("Young Blue Dragon", calculateRoll("16d10+64"), 18, 9, "12d6+10", "d20", "Lightning Breath10d10")
break
case 68:
- state.tempEnemy = createEnemy("Young Brass Dragon", calculateRoll("13d10+39"), 17, 7, "2d10+4", "d20", "Fire Breath12d6", "Sleep Breath")
+ createEnemy("Young Brass Dragon", calculateRoll("13d10+39"), 17, 7, "2d10+4", "d20", "Fire Breath12d6", "Sleep Breath")
break
case 69:
- state.tempEnemy = createEnemy("Young Bronze Dragon", calculateRoll("15d10+60"), 18, 8, "4d6+10", "d20+1", "Lightning Breath10d10", "Repulsion Breath")
+ createEnemy("Young Bronze Dragon", calculateRoll("15d10+60"), 18, 8, "4d6+10", "d20+1", "Lightning Breath10d10", "Repulsion Breath")
break
case 70:
- state.tempEnemy = createEnemy("Young Copper Dragon", calculateRoll("14d10+42"), 17, 7, "4d6+8", "d20+1", "Acid Breath9d8", "Slowing Breath")
+ createEnemy("Young Copper Dragon", calculateRoll("14d10+42"), 17, 7, "4d6+8", "d20+1", "Acid Breath9d8", "Slowing Breath")
break
case 71:
- state.tempEnemy = createEnemy("Young Gold Dragon", calculateRoll("17d10+85"), 18, 10, "4d6+12", "d20+2", "Fire Breath10d10", "Weakening Breath")
+ createEnemy("Young Gold Dragon", calculateRoll("17d10+85"), 18, 10, "4d6+12", "d20+2", "Fire Breath10d10", "Weakening Breath")
break
case 72:
- state.tempEnemy = createEnemy("Young Green Dragon", calculateRoll("16d10+48"), 18, 7, "4d6+8", "d20+1", "Poison Breath12d6")
+ createEnemy("Young Green Dragon", calculateRoll("16d10+48"), 18, 7, "4d6+8", "d20+1", "Poison Breath12d6")
break
case 73:
- state.tempEnemy = createEnemy("Young Red Dragon", calculateRoll("17d10+85"), 18, 10, "4d6+12", "d20", "Fire Breath16d6")
+ createEnemy("Young Red Dragon", calculateRoll("17d10+85"), 18, 10, "4d6+12", "d20", "Fire Breath16d6")
break
case 74:
- state.tempEnemy = createEnemy("Young Silver Dragon", calculateRoll("16d10+8-"), 18, 10, "4d6+12", "d20", "Cold Breath12d8", "Paralyzing Breath")
+ createEnemy("Young Silver Dragon", calculateRoll("16d10+8-"), 18, 10, "4d6+12", "d20", "Cold Breath12d8", "Paralyzing Breath")
break
case 75:
- state.tempEnemy = createEnemy("Young White Dragon", calculateRoll("14d10+56"), 17, 7, "2d10+4", "d20", "Cold Breath10d8", "Ice Walk")
- break
- case 76:
- state.tempEnemy = createEnemy("Adult Black Dragon", calculateRoll("17d12+85"), 19, 11, "6d6+18", "d20+2", "Acid Breath12d8", "Frightful Presence", "Wing Attack2d6+6")
- break
- case 77:
- state.tempEnemy = createEnemy("Adult Bronze Dragon", calculateRoll("17d12+102"), 19, 12, "6d6+21", "d20", "Repulsion Breath", "Lightning Breath12d10", "Wing Attack2d6+6")
- break
- case 78:
- state.tempEnemy = createEnemy("Adult Copper Dragon", calculateRoll("16d12+80"), 18, 11, "6d6+18", "d20+1", "Acid Breath12d8", "Slowing Breath", "Wing Attack2d6+6")
- break
- case 79:
- state.tempEnemy = createEnemy("Adult Green Dragon", calculateRoll("18d12+90"), 19, 11, "6d6+18", "d20+1", "Poison Breath16d6")
- break
- case 80:
- state.tempEnemy = createEnemy("Animated Statue", calculateRoll("10d12+20"), 17, 7, "2d10+4", "d20-2")
- break
- case 81:
- state.tempEnemy = createEnemy("Arch Mage", calculateRoll("18d8+18"), 12, 4, "1d4+2", "d20+14", "Time Stop", "Globe of Invulnerability", "Lightning Bolt8d6", "Banishment", "Cone of Cold8d8", "Teleport")
- break
- case 82:
- state.tempEnemy = createEnemy("Behir", calculateRoll("16d12+64"), 17, 10, "5d10+12", "d20+3", "Lightning Breath12d10", "Swallow6d6", "Constrict2d10+6")
- break
- case 83:
- state.tempEnemy = createEnemy("Boneclaw", calculateRoll("17d10+34"), 16, 8, "6d10+8", "d20+3", "Shadow Jump5d12+2", "Deadly Reach")
- break
- case 84:
- state.tempEnemy = createEnemy("Deathwolf", calculateRoll("18d8+72"), 15, 10, "6d8+15", "d20+3", "Phantom Deathwolf6d6")
- break
- case 85:
- state.tempEnemy = createEnemy("Djinni", calculateRoll("14d10+84"), 17, 9, "2d6+8", "d20+2")
- break
- case 86:
- state.tempEnemy = createEnemy("Drow Inquisitor", calculateRoll("23d8+46"), 16, 10, "12d8+24", "d20+2", "Spectral Dagger1d8+5")
- break
- case 87:
- state.tempEnemy = createEnemy("Efreeti", calculateRoll("16d10+112"), 17, 10, "4d6+12", "d20+1", "Hurl Flame5d6")
- break
- case 88:
- state.tempEnemy = createEnemy("Elder Brain", calculateRoll("20d10+100"), 10, 7, "5d8+7", "d20", "Mind Blast5d10+5")
- break
- case 89:
- state.tempEnemy = createEnemy("Erinyes", calculateRoll("18d8+72"), 18, 8, "1d10+4", "d20+3")
- break
- case 90:
- state.tempEnemy = createEnemy("Ice Devil", calculateRoll("19d10+76"), 18, 10, "6d4+15", "d20+2", "Wall of Ice")
- break
- case 91:
- state.tempEnemy = createEnemy("Jabberwock", calculateRoll("10d12+50"), 18, 10, "6d10+10", "d20+1", "Regenderation")
- break
- case 92:
- state.tempEnemy = createEnemy("Megapede", calculateRoll("13d20+39"), 15, 10, "6d10+12", "d20", "LifeDrain3d10", "Psychic Bomb5d8")
- break
- case 93:
- state.tempEnemy = createEnemy("Mummy Lord", calculateRoll("13d8+39"), 17, 9, "3d6+4", "d20", "Hold Person", "Silence", "Harm14d6", "Blinding Dust", "Whirlwind of Sand")
- break
- case 94:
- state.tempEnemy = createEnemy("Purple Worm", calculateRoll("15d20+90"), 18, 14, "6d6+18", "d20-2", "Tail Stinger12d6+19")
- break
- case 95:
- state.tempEnemy = createEnemy("Remorhaz", calculateRoll("17d12+85"), 17, 11, "6d10+7", "d20+1", "Swallow6d6")
- break
- case 96:
- state.tempEnemy = createEnemy("Skull Lord", calculateRoll("15d8+45"), 18, 8, "24d6", "d20+3", "Deathly Ray5d8+5")
- break
- case 97:
- state.tempEnemy = createEnemy("Spider Dragon", calculateRoll("15d10+5"), 23, 9, "3d12+12", "d20+8", "Silk Spit", "Spider Breath7d10")
- break
- case 98:
- state.tempEnemy = createEnemy("Storm Giant", calculateRoll("20d12+100"), 16, 14, "12d6+18", "d20+2", "Control Weather", "Lightning Strike12d8")
- break
- case 99:
- state.tempEnemy = createEnemy("Vampire", calculateRoll("17d8+68"), 16, 9, "3d8+8", "d20+4", "Charm", "Shape Change")
- break
- case 100:
- state.tempEnemy = createEnemy("Zikran", calculateRoll("18d8+18"), 12, 6, "1d4+2", "d20+2", "Time Stop", "Mind Blank", "Cone of Cold 8d8", "Lightning Bolt 8d6")
- break
- case 101:
- state.tempEnemy = createEnemy("Ancient Black Dragon", calculateRoll("21d20+147"), 22, 15, "6d10+24", "d20+2", "Acid Breath15d8", "Wing Attack2d6+8")
- break
- case 102:
- state.tempEnemy = createEnemy("Adult Blue Dragon", calculateRoll("18d12+108"), 19, 12, "6d10+21", "d20", "Lightning Breath12d10", "Wing Attack2d6+7")
- break
- case 103:
- state.tempEnemy = createEnemy("Adult Gold Dragon", calculateRoll("19d12+133"), 19, 12, "6d10+21", "d20+2", "Fire Breath 12d10", "Weakening Breath")
- break
- case 104:
- state.tempEnemy = createEnemy("Adult Silver Dragon", calculateRoll("18d12+126"), 19, 13, "6d10+24", "d20", "Cold Breath13d8", "Paralyzing Breath")
- break
- case 105:
- state.tempEnemy = createEnemy("Ancient Gold Dragon", calculateRoll("28d20+252"), 22, 15, "6d10+24", "d20+2", "Fire Breath13d10", "Weakening Breath")
- break
- case 106:
- state.tempEnemy = createEnemy("Ancient Red Dragon", calculateRoll("21d20+147"), 22, 15, "6d8+30", "d20+2", "Fire Breath26d6", "Wing Attack2d6+10")
- break
- case 107:
- state.tempEnemy = createEnemy("Androsphinx", calculateRoll("19d10+95"), 17, 12, "4d10+12", "d20", "Flame Strike8d6", "Roar", "Teleport")
- break
- case 108:
- state.tempEnemy = createEnemy("Bael", calculateRoll("18d10+90"), 18, 13, "4d8+27", "d20+3", "Awaken Greed", "Teleport", "Regenerate", "Inflict Wounds4d8+27", "Invisibility")
- break
- case 109:
- state.tempEnemy = createEnemy("Balor", calculateRoll("21d12+136"), 19, 14, "6d8+16", "d20+2", "Fire Whip 5d6+8", "Teleport")
- break
- case 110:
- state.tempEnemy = createEnemy("Baphomet", calculateRoll("22d12+176"), 22, 17, "3d10+30", "d20+2", "Curse of Brutality", "Desecration Breath20d8", "Gouging Toss2d8", "Raise Labyrinth")
- break
- case 111:
- state.tempEnemy = createEnemy("Cosmic Horror", calculateRoll("16d20+112"), 15, 14, "6d6+16", "d20", "Poison Jet4d6", "Psychic Whispers6d10")
- break
- case 112:
- state.tempEnemy = createEnemy("Death Knight", calculateRoll("19d8+95"), 20, 11, "3d8+15", "d20+2", "Hellfire Orb10d6", "Parry", "Destructive Wave5d6")
- break
- case 113:
- state.tempEnemy = createEnemy("Demogorgon", calculateRoll("32d12+256"), 22, 17, "6d12+18", "d20+2", "Beguiling Gaze", "Hypnotic Gaze")
- break
- case 114:
- state.tempEnemy = createEnemy("Dragon Turtle", calculateRoll("22d20+110"), 20, 13, "6d8+21", "d20", "Steam Breath15d6")
- break
- case 115:
- state.tempEnemy = createEnemy("Drow Matron Mother", calculateRoll("35d8+105"), 17, 10, "2d6+8", "d20+4", "Levitate", "Plane Shift", "Gate", "Geas5d10", "Guardian of Faith", "Tentacle Rod3d6", "Summon Servant")
- break
- case 116:
- state.tempEnemy = createEnemy("Flesh Colossus", calculateRoll("16d20+112"), 14, 13, "6d6+14", "d20-1", "Elemental Breath9d8")
- break
- case 117:
- state.tempEnemy = createEnemy("Kraken", calculateRoll("27d20+189"), 18, 17, "9d6+30", "d20", "Lightning Storm12d10", "Ink Cloud3d10", "Fling1d6")
- break
- case 118:
- state.tempEnemy = createEnemy("Iron Golem", calculateRoll("20d10+100"), 20, 13, "6d8+14", "d20=1", "Poison Breath10d8", "Slam3d8+7")
- break
- case 119:
- state.tempEnemy = createEnemy("Leviathan", calculateRoll("16d20+160"), 17, 16, "4d10+40", "d20+7", "Tidal Wave6d10")
- break
- case 120:
- state.tempEnemy = createEnemy("Lich", calculateRoll("18d8+54"), 17, 12, "3d6", "Acid Arrow4d4", "Fireball8d6", "Dimension Door", "Animate Dead", "Ray of Frost3d8", "Disrupt Life6d6", "Frightening Gaze", "Paralyzing Touch")
- break
- case 121:
- state.tempEnemy = createEnemy("Planetar", calculateRoll("16d10+112"), 19, 12, "4d6+7", "d20+5", "Insect Plague4d10", "Blade Barrier", )
- break
- case 122:
- state.tempEnemy = createEnemy("Raeleus", calculateRoll("19d12+190"), 22, 17, "6d6+10", "d20+5", "Musket Blast6d10+10", "Auto Pistolero10d6", "Canister Grenada4d10", "Stun Grenada", "Magic Chaff Grenada")
- break
- case 123:
- state.tempEnemy = createEnemy("Solar", calculateRoll("18d10+144"), 21, 15, "8d6+16", "d20+6", "Flying Sword", "Searing Burst8d6", "Blinding Gaze")
- break
- case 124:
- state.tempEnemy = createEnemy("Tarrasque", calculateRoll("33d20+330"), 25, 19, "20d8+50", "d20")
- break
- case 125:
- state.tempEnemy = createEnemy("Zariel", calculateRoll("40d10+360"), 21, 16, "4d8+16", "d20+7", "Horrid Touch8d10", "Immolating Gaze4d10", "Teleport")
- break
- case 126:
- state.tempEnemy = createEnemy("Commoner", calculateRoll("1d8"), 10, 2, "1d4", "d20")
- break
- case 127:
- state.tempEnemy = createEnemy("Bandit", calculateRoll("2d8+2"), 12, 3, "1d6+1", "d20+1")
- break
- case 128:
- state.tempEnemy = createEnemy("Guard", calculateRoll("2d8+2"), 16, 3, "1d6+1", "d20+1")
- break
- case 129:
- state.tempEnemy = createEnemy("Cultist", calculateRoll("2d8"), 12, 3, "1d6+1", "d20+1", "Dark Devotion")
- break
- case 130:
- state.tempEnemy = createEnemy("Acolyte", calculateRoll("2d8"), 10, 2, "1d4", "d20", "Sacred Flame1d8", "Cure Wounds")
- break
- case 131:
- state.tempEnemy = createEnemy("Apprentice", calculateRoll("3d8"), 10, 4, "1d10+2", "d20", "Burning Hands3d6")
- break
- case 132:
- state.tempEnemy = createEnemy("Witch", calculateRoll("3d8+3"), 10, 3, "1d6+2", "d20", "Ray of Sickness2d8", "Tashas Hideous Laughter", "Invisibility", "Ray of Frost2d8")//
- break
- case 133:
- state.tempEnemy = createEnemy("Buccaneer", calculateRoll("8d8+24"), 14, 5, "1d6+3", "d20+2", "Invade")
- break
- case 134:
- state.tempEnemy = createEnemy("Spy", calculateRoll("6d8"), 12, 4, "1d6+2", "d20+2", "Sneak Attack2d6+2")
- break
- case 135:
- state.tempEnemy = createEnemy("Captain", calculateRoll("10d8+20"), 15, 5, "3d6+9", "initiative")
- break
- case 136:
- state.tempEnemy = createEnemy("Bard", calculateRoll("8d8+8"), 15, 4, "1d6+2", "d20+2", "Charm Person", "Shatter3d8", "Thunderwave2d8", "Vicious Mockery1d4")
- break
- case 137:
- state.tempEnemy = createEnemy("Berserker", calculateRoll("9d8+27"), 13, 5, "1d12+3", "d20+1")
- break
- case 138:
- state.tempEnemy = createEnemy("Priest", calculateRoll("5d8+5"), 13, 2, "1d6", "d20", "Spirit Guardians3d8", "Spiritual Weapon1d8", "Guiding Bolt4d6", "Cure Wounds")
- break
- case 139:
- state.tempEnemy = createEnemy("Knight", calculateRoll("8d8+16"), 18, 5, "4d6+6", "d20", "Leadership")
- break
- case 140:
- state.tempEnemy = createEnemy("Archer", calculateRoll("10d8+30"), 16, 6, "2d8+8", "d20+4")
- break
- case 141:
- state.tempEnemy = createEnemy("Warrior", calculateRoll("6d8+12"), 16, 6, "1d8+3", "d20+1")
- break
- case 142:
- state.tempEnemy = createEnemy("Conjurer", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Conjure Elemental", "Cloud Kill5d8", "Cloud of Daggers5d8", "Poison Spray1d12")
- break
- case 143:
- state.tempEnemy = createEnemy("Mage", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Greater Invisibility", "Ice Storm4d6", "Fireball8d6", "Magic Missile3d4+3")
- break
- case 144:
- state.tempEnemy = createEnemy("Assassin", calculateRoll("12d8+24"), 15, 6, "2d6+6", "d20+3", "Sneak Attack6d6+6")
- break
- case 145:
- state.tempEnemy = createEnemy("Evoker", calculateRoll("12d8+12"), 12, 3, "1d6-1", "d20+2", "Chain Lightning10d8", "Wall of Ice", "Counter Spell", "Shatter3d8", "Magic Missile6d4+6")
- break
- case 146:
- state.tempEnemy = createEnemy("Necromancer", calculateRoll("12d8+12"), 12, 7, "2d4", "d20+2", "Circle of Death8d6", "Blight8d8", "Cloudkill5d8", "Animate Dead", "Chill Touch1d8")
- break
- case 147:
- state.tempEnemy = createEnemy("Champion", calculateRoll("22d8+44"), 18, 9, "6d6+15", "d20+2", "Second Wind")
- break
- case 148:
- state.tempEnemy = createEnemy("Warlord", calculateRoll("27d8+108"), 18, 9, "4d6+10", "d20+3", "Command Ally", "Frighten Foe")
- break
- case 149:
- state.tempEnemy = createEnemy("Archmage", calculateRoll("18d8+18"), 12, 6, "1d4+2", "d20+2", "Time Stop", "Mind Blank", "Lightning Bolt8d6", "Cone of Cold8d8", "Shocking Grasp1d8")
- break
- case 150:
- state.tempEnemy = createEnemy("Archdruid", calculateRoll("24d8+24"), 16, 6, "1d6+2", "d20+2", "Fire Storm7d10", "Sunbeam6d8", "Wall of Fire", "Beast Sense", "Conjure Animals")
+ createEnemy("Young White Dragon", calculateRoll("14d10+56"), 17, 7, "2d10+4", "d20", "Cold Breath10d8", "Ice Walk")
break
}
@@ -1204,435 +896,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 +950,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 +959,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,782 +1027,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) {
- arg0 = "easy"
- }
-
- switch(arg0) {
- case "impossible":
- state.memoryWidth = 6
- state.memoryHeight = 6
- state.memoryMaxTurns = 46
- break
- case "hard":
- state.memoryWidth = 5
- state.memoryHeight = 6
- state.memoryMaxTurns = 40
- break
- case "medium":
- state.memoryWidth = 4
- state.memoryHeight = 5
- state.memoryMaxTurns = 30
- break
- case "effortless":
- state.memoryWidth = 3
- state.memoryHeight = 2
- state.memoryMaxTurns = 25
- break
- case "automatic":
- state.memoryWidth = 2
- state.memoryHeight = 2
- state.memoryMaxTurns = 25
- break
- case "easy":
- default:
- state.memoryWidth = 4
- state.memoryHeight = 3
- state.memoryMaxTurns = 25
- break
- }
-
- state.memoryTurns = 1
-
- let possibleSymbols = ["ā¤ļø", "š", "š", "šø", "āļø", "š”ļø", "š»", "š»", "š¦", "šŗ", "š", "š§", "š", "šµ", "š", "š¦", "š·ļø", "š¹", "š", "š²", "āļø", "š„", "ā”", "š³", "š¦", "š", "š„", "š"]
- shuffle(possibleSymbols)
- possibleSymbols = possibleSymbols.splice(0, state.memoryWidth * state.memoryHeight / 2)
-
- state.memoryCards = possibleSymbols.concat(possibleSymbols)
- shuffle(state.memoryCards)
- state.memorySolved = new Array(state.memoryCards.length)
- state.memoryRevealed = null
-
- state.memoryTurn = "game"
- state.show = "memory"
- return " "
-}
-
-function handleMemoryTurn(text) {
- state.show = "memory"
-
- 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+/, "")
- }
-
- text = text.toLowerCase()
- if (text == "q") {
- state.memoryTurn = "forfeit"
- return text
- }
-
- switch (state.memoryTurn) {
- case "game":
- if (isNaN(text)) return text
- let guess = parseInt(text)
- state.memoryTurns++
-
- if (guess < 1 || guess > state.memoryCards.length) return text
-
- if (state.memoryRevealed == null) {
- state.message = `Card ${guess} is revealed to be:\n${state.memoryCards[guess -1]}`
- state.memoryRevealed = guess
- } else {
- if (state.memoryRevealed == guess) {
- state.message = `Card ${guess} is the same card you already picked, silly:\n${state.memoryCards[guess - 1]}`
- state.memoryTurns--
- } else if (state.memoryCards[state.memoryRevealed - 1] == state.memoryCards[guess - 1]) {
- state.message = `Cards ${state.memoryRevealed} and ${guess} are a match!\n${state.memoryCards[state.memoryRevealed - 1]} ${state.memoryCards[guess - 1]}`
- state.memorySolved[state.memoryRevealed - 1] = true
- state.memorySolved[guess - 1] = true
- state.memoryRevealed = null
- } else {
- state.message = `Card ${guess} is revealed to be: ${state.memoryCards[guess - 1]}\nCards ${state.memoryRevealed} and ${guess} are NOT a match:${state.memoryCards[state.memoryRevealed - 1]} ${state.memoryCards[guess - 1]}`
- state.memoryRevealed = null
- }
- }
-
- let win = true
- for (let i = 0; i < state.memorySolved.length; i++) {
- if (state.memorySolved[i] != true) {
- win = false
- break
- }
- }
- if (win) state.memoryTurn = "win"
- if (state.memoryTurns > state.memoryMaxTurns) state.memoryTurn = "lose"
-
- log(state.memorySolved)
- return text
- case "win":
- case "lose":
- case "forfeit":
- state.show = null
- state.memoryTurn = null
- return text
- }
-
- return `\nUnexpected Mastermind state. Input text: ${text}`
-}
-
-function doLockpick(command) {
- var arg0 = getArgument(command, 0)
- if (arg0 == null) {
- arg0 = "easy"
- }
-
- state.lockpickingTurn = "intro"
- state.lockpickingGuesses = 0
- state.show = "lockpicking"
-
- switch(arg0) {
- case "impossible":
- state.lockpickingSlots = 7
- state.lockpickingGuessMax = 15
- break
- case "hard":
- state.lockpickingSlots = 6
- state.lockpickingGuessMax = 15
- break
- case "medium":
- state.lockpickingSlots = 5
- state.lockpickingGuessMax = 15
- break
- case "effortless":
- state.lockpickingSlots = 4
- state.lockpickingGuessMax = 15
- break
- case "automatic":
- state.lockpickingSlots = 3
- state.lockpickingGuessMax = 20
- break
- case "easy":
- default:
- state.lockpickingSlots = 4
- state.lockpickingGuessMax = 12
- break
- }
-
- state.lockpickingCombination = ""
- for (let i = 0; i < state.lockpickingSlots; i++) {
- state.lockpickingCombination += getRandomFromList("r", "y", "w", "g", "o", "b")
- }
-
- return " "
-}
-
-function handleLockpickingTurn(text) {
- state.show = "lockpicking"
-
- 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+/, "")
- }
-
- text = text.toLowerCase()
- if (text == "q") {
- state.lockpickingTurn = "forfeit"
- return text
- }
-
- switch (state.lockpickingTurn) {
- case "intro":
- state.lockpickingTurn = "game"
- case "game":
- state.lockpickingInput = text
-
- state.lockpickingCorrect = 0
- let combo = state.lockpickingCombination
- for (var i = 0; i < state.lockpickingSlots; i++) {
- let letter = text.substring(i, i + 1)
- if (letter == state.lockpickingCombination.substring(i, i + 1)) state.lockpickingCorrect++
- combo = combo.replace(letter, "")
- }
- state.lockpickingWrongPlace = state.lockpickingSlots - combo.length - state.lockpickingCorrect
-
- if (state.lockpickingInput.length == state.lockpickingSlots) state.lockpickingGuesses++
-
- if (state.lockpickingCorrect == state.lockpickingSlots) state.lockpickingTurn = "win"
- else if (state.lockpickingGuesses >= state.lockpickingGuessMax) state.lockpickingTurn = "lose"
-
- return text
- case "win":
- case "lose":
- case "forfeit":
- state.show = null
- state.lockpickingTurn = null
- return text
- }
-
- return `\nUnexpected Mastermind state. Input text: ${text}`
-}
-
-function doBasicDeck(command) {
- var character = getCharacter()
- var takeWord = character.name == "You" ? "take" : "takes"
- doTake("take Stragedy Ace Card")
- doTake("take Stragedy Queen Card")
- doTake("take Stragedy 2 Card")
- doTake("take Stragedy 2 Card")
- doTake("take Stragedy 3 Card")
- doTake("take Stragedy 3 Card")
- doTake("take Stragedy 4 Card")
- doTake("take Stragedy 4 Card")
- doTake("take Stragedy 5 Card")
- doTake("take Stragedy 5 Card")
- doTake("take Stragedy 6 Card")
- doTake("take Stragedy 6 Card")
- doTake("take Stragedy 7 Card")
- doTake("take Stragedy 7 Card")
- doTake("take Stragedy 8 Card")
- doTake("take Stragedy 8 Card")
- doTake("take Stragedy 9 Card")
- doTake("take Stragedy 9 Card")
- doTake("take Stragedy King Card")
- doTake("take Stragedy Jack Card")
- return `${toTitleCase(character.name)} ${takeWord} the Stragedy Basic Deck`
-}
-
-function doItemShop(command) {
- command = command.replace(/very rare/gi, "phenomenal")
-
- state.itemShopCategoryName = searchArgument(command, /default|weapons|armor|tools|gear|common|uncommon|rare|phenomenal|legendary|artifact/gi)
- if (state.itemShopCategoryName == null && searchArgument(command, /weapon/) != null) state.itemShopCategoryName = "weapons"
- if (state.itemShopCategoryName == null) state.itemShopCategoryName = "default"
-
- let arg1 = searchArgument(command, /free/gi)
- state.itemShopIsFree = arg1 != null
-
- let arg2 = searchArgument(command, /all/gi)
- let all = arg2 != null
- state.itemShopClearDeals = state.itemShopAll || state.itemShopAll != all
- state.itemShopAll = all
-
- state.itemShopStep = 0
- state.show = "itemShop"
- return " "
-}
-
-function handleItemShopStep(text) {
- state.show = "itemShop"
-
- 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.itemShopStep = 500
- return text
- }
-
- switch (state.itemShopStep) {
- case 0:
- case 1:
- case 2:
- case 3:
- if (isNaN(text)) return text
- var index = parseInt(text) - 1
-
- let deals = findItemShopDeals(state.itemShopCategoryName, false)
- if (index < 0 || index >= deals.length) return text
-
- let deal = deals[index]
-
- var character = getCharacter()
- var goldIndex = character.inventory.findIndex(x => x.name.toLowerCase() == "gold")
- var gold = goldIndex == -1 ? 0 : character.inventory[goldIndex].quantity
-
- if (!state.itemShopIsFree && deal.price > gold) {
- state.itemShopStep = 2
- return text
- }
-
- if ("damage" in deal) doTakeWeapon(`takeweapon ${deal.damage} ${deal.toHitBonus} ${deal.ability} ${deal.name}`)
- else if ("ac" in deal) doTakeArmor(`takearmor ${deal.ac} ${deal.name}`)
- else doTake(`take ${deal.quantity} ${deal.name}`)
- if (!state.itemShopIsFree) character.inventory[goldIndex].quantity -= deal.price
- deal.bought = true
-
- state.itemShopStep = 1
- break
- case 500:
- state.show = null
- state.itemShopStep = null
- break
- }
- return text
-}
-
-function doSpellShop(command) {
- var character = getCharacter()
-
- let arg0 = searchArgument(command, /bard|cleric|druid|paladin|ranger|sorcerer|warlock|wizard/gi)
- if (arg0 == null) {
- arg0 = character.className.toLowerCase()
- if (/bard|cleric|druid|paladin|ranger|sorcerer|warlock|wizard/gi.test(arg0) == false) arg0 = "wizard"
- }
- state.spellShopClassName = arg0
-
- let arg1 = searchArgument(command, /\d+/gi)
- if (arg1 == null) {
- let level = getLevel(character.experience)
- switch (state.spellShopClassName) {
- case "bard":
- case "cleric":
- case "druid":
- case "sorcerer":
- case "warlock":
- case "wizard":
- switch(level) {
- case 1:
- case 2:
- arg1 = 1
- break
- case 3:
- case 4:
- arg1 = 2
- break
- case 5:
- case 6:
- arg1 = 3
- break
- case 7:
- case 8:
- arg1 = 4
- break
- case 9:
- case 10:
- arg1 = 5
- break
- case 11:
- case 12:
- arg1 = 6
- break
- case 13:
- case 14:
- arg1 = 7
- break
- case 15:
- case 16:
- arg1 = 8
- break
- default:
- arg1 = 9
- break
- }
- break
- case "paladin":
- case "ranger":
- switch(level) {
- case 1:
- case 2:
- case 3:
- case 4:
- arg1 = 1
- break
- case 5:
- case 6:
- case 7:
- case 8:
- arg1 = 2
- break
- case 9:
- case 10:
- case 11:
- case 12:
- arg1 = 3
- break
- case 13:
- case 14:
- case 15:
- case 16:
- arg1 = 4
- break
- default:
- arg1 = 5
- break
- }
- break
- default:
- arg1 = 1
- break
- }
- }
- arg1 = parseInt(arg1)
- state.spellShopLevel = arg1
-
- let arg2 = searchArgument(command, /free/gi)
- state.spellShopIsFree = arg2 != null
-
- let arg3 = searchArgument(command, /all/gi)
- let all = arg3 != null
- state.spellShopClearDeals = state.spellShopAll || state.spellShopAll != all
- state.spellShopAll = all
-
- state.spellShopStep = 0
- state.show = "spellShop"
- return " "
-}
-
-function handleSpellShopStep(text) {
- state.show = "spellShop"
-
- 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.spellShopStep = 500
- return text
- }
-
- switch (state.spellShopStep) {
- case 0:
- case 1:
- case 2:
- case 3:
- if (isNaN(text)) return text
- var index = parseInt(text) - 1
-
- let deals = findSpellShopDeals(state.spellShopClassName, state.spellShopLevel, false)
- if (index < 0 || index >= deals.length) return text
-
- let deal = deals[index]
-
- var character = getCharacter()
- var goldIndex = character.inventory.findIndex(x => x.name.toLowerCase() == "gold")
- var gold = goldIndex == -1 ? 0 : character.inventory[goldIndex].quantity
- var found = character.spells.find((element) => element == deal.name) != undefined
-
- if (deal.price > gold) {
- state.spellShopStep = 2
- return text
- } else if (found) {
- state.spellShopStep = 3
- return text
- }
-
- doLearnSpell(`learnspell ${deal.name}`)
- if (!state.spellShopIsFree) character.inventory[goldIndex].quantity -= deal.price
- deal.bought = true
-
- state.spellShopStep = 1
- break
- case 500:
- state.show = null
- state.spellShopStep = null
- break
- }
- return text
-}
-
-function doCardShop(command) {
- state.stragedyShopStep = 0
- state.show = "stragedyShop"
- return " "
-}
-
-function handleStragedyShopStep(text) {
- state.show = "stragedyShop"
-
- 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.stragedyShopStep = 500
- return text
- }
-
- switch (state.stragedyShopStep) {
- case 0:
- case 1:
- case 2:
- if (isNaN(text)) return text
- var index = parseInt(text) - 1
- if (index < 0 || index >= state.cardDeals.length) return text
-
- var item = state.cardDeals[index]
- var price = state.cardPrices[index]
-
- var character = getCharacter()
- var goldIndex = character.inventory.findIndex(x => x.name.toLowerCase() == "gold")
- var gold = goldIndex == -1 ? 0 : character.inventory[goldIndex].quantity
-
- if (price > gold) {
- state.stragedyShopStep = 2
- return text
- }
-
- doTake(`take Stragedy ${item} Card`)
- character.inventory[goldIndex].quantity -= price
-
- state.cardDeals.splice(index, 1)
- state.cardPrices.splice(index, 1)
-
- state.stragedyShopStep = 1
- break
- case 500:
- state.show = null
- state.stragedyShopStep = null
- break
- }
- return text
-}
-
-function doStragedy(command) {
- var arg0 = getArgument(command, 0)
- if (arg0 == null) {
- arg0 = "easy"
- }
-
- var character = getCharacter()
- state.stragedyTurn = "intro"
- state.show = "stragedy"
-
- state.stragedyPlayerScore = 0
- state.stragedyPlayerHand = []
- state.stragedyPlayerBattlefield = []
-
- state.stragedyPlayerDeck = []
- for (item of character.inventory) {
- if (/stragedy ace card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("a")
- else if (/stragedy jack card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("j")
- else if (/stragedy queen card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("q")
- else if (/stragedy king card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("k")
- else if (/stragedy joker card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("?")
- else if (/stragedy witch card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("w")
- else if (/stragedy priest card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("p")
- else if (/stragedy brigand card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push("b")
- else if (/stragedy \d+ card/gi.test(item.name)) for (var i = 0; i < item.quantity; i++) state.stragedyPlayerDeck.push(item.name.match(/(?<=stragedy )\d+(?= card)/gi)[0])
- }
-
- shuffle(state.stragedyPlayerDeck)
- state.stragedyPlayerDeck.splice(20)
- state.stragedyPlayerDiscard = []
- state.stragedyPlayerRetired = false
-
- state.stragedyEnemyScore = 0
- state.stragedyEnemyHand = []
- state.stragedyEnemyBattlefield = []
-
- switch(arg0) {
- case "impossible":
- state.stragedyEnemyDeck = ["?", "?", "a", "q", "q", "k", "k", "w", "p", "2", "3", "4", "5", "6", "7", "7", "8", "10", "10", "10"]
- case "hard":
- state.stragedyEnemyDeck = ["j", "?", "a", "q", "q", "k", "k", "2", "3", "4", "5", "5", "6", "6", "7", "7", "8", "8", "10", "10"]
- case "medium":
- state.stragedyEnemyDeck = ["j", "j", "a", "q", "q", "k", "k", "2", "3", "4", "5", "5", "6", "6", "7", "7", "8", "8", "9", "10"]
- break
- case "effortless":
- state.stragedyEnemyDeck = ["j", "j", "a", "a", "2", "2", "3", "3", "4", "4", "5", "5", "6", "6", "6", "7", "7", "8", "8", "9"]
- case "automatic":
- state.stragedyEnemyDeck = ["2", "2", "2", "3", "3", "3", "4", "4", "4", "5", "5", "5", "6", "6", "6", "6", "7", "7", "7", "7"]
- case "easy":
- default:
- state.stragedyEnemyDeck = ["j", "q", "k", "a", "2", "2", "3", "3", "4", "4", "5", "5", "6", "6", "7", "7", "8", "8", "9", "9"]
- break
- }
-
- shuffle(state.stragedyEnemyDeck)
- state.stragedyEnemyDiscard = []
- state.stragedyEnemySkipTurn = getRandomBoolean(.5)
- state.stragedyEnemyRetired = false
- state.stragedyEnemyTurnText = null
-
- return " "
-}
-
-function handleStragedyTurn(text) {
- state.show = "stragedy"
-
- 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+/, "")
- }
-
- text = text.toLowerCase()
- if (text == "f") {
- state.stragedyTurn = "gameOver"
- state.stragedyWinner = "forfeit"
- return "You forfeit the game."
- }
-
- switch (state.stragedyTurn) {
- case "intro":
- if (text == "d") {
- if (state.stragedyPlayerDeck.length < 20) return "\nYou cannot play if you don't have at least 20 Stragedy cards.\n"
-
- state.stragedyTurn = "game"
- var drawCards = state.stragedyPlayerDeck.splice(state.stragedyPlayerDeck.length - 4)
- state.stragedyPlayerHand.push(...drawCards)
-
- drawCards = state.stragedyEnemyDeck.splice(state.stragedyEnemyDeck.length - 4)
- state.stragedyEnemyHand.push(...drawCards)
-
- stragedyCalculateScores()
- if (!state.stragedyEnemySkipTurn) stragedyEnemyTurn()
- }
- return `You deal the cards. ${state.stragedyEnemySkipTurn ? "You go first." : "The opponent goes first."}`
- case "game":
- return stragedyPlayerTurn(text)
- case "gameOver":
- state.show = null
- state.stragedyTurn = null
- return text
- }
-
- return `\nUnexpected stragedy state. Input text: ${text}`
-}
-
-function doAddCard(command) {
- var arg0 = getArgument(command, 0)
- if (arg0 == null) {
- arg0 = ""
- }
-
- arg0 = arg0.toLowerCase()
- if (arg0.startsWith("w")) arg0 = "w"
- if (arg0.startsWith("b")) arg0 = "b"
- switch(arg0) {
- case "a":
- case "ace":
- return doTake("take Stragedy Ace Card")
- case "j":
- case "jack":
- return doTake("take Stragedy Jack Card")
- case "q":
- case "queen":
- return doTake("take Stragedy Queen Card")
- case "k":
- case "king":
- return doTake("take Stragedy King Card")
- case "?":
- case "joker":
- return doTake("take Stragedy Joker Card")
- case "w":
- case "witch":
- return doTake("take Stragedy Witch Card")
- case "p":
- case "priest":
- return doTake("take Stragedy Priest Card")
- case "b":
- case "brigand":
- return doTake("take Stragedy Brigand Card")
- case "2":
- return doTake("take Stragedy 2 Card")
- case "3":
- return doTake("take Stragedy 3 Card")
- case "4":
- return doTake("take Stragedy 4 Card")
- case "5":
- return doTake("take Stragedy 5 Card")
- case "6":
- return doTake("take Stragedy 6 Card")
- case "7":
- return doTake("take Stragedy 7 Card")
- case "8":
- return doTake("take Stragedy 8 Card")
- case "9":
- return doTake("take Stragedy 9 Card")
- case "10":
- return doTake("take Stragedy 10 Card")
- case "common":
- return doTake(`take Stragedy ${getRandomFromList("2", "3", "4", "5", "6", "7", "8", "9")} Card`)
- case "rare":
- return doTake(`take Stragedy ${getRandomFromList("10", "Ace", "Jack")} Card`)
- case "epic":
- return doTake(`take Stragedy ${getRandomFromList("Queen", "King", "Joker")} Card`)
- case "legendary":
- return doTake(`take Stragedy ${getRandomFromList("Witch", "Priest", "Brigand")} Card`)
- default:
- return doTake(`take Stragedy ${getRandomFromList("2", "3", "4", "5", "6", "7", "8", "9", "10", "Ace", "Jack", "Queen", "King", "Joker", "Witch", "Priest", "Brigand")} Card`)
- }
-}
-
function doBio(command) {
state.show = "bio"
return " "
@@ -2608,7 +1093,7 @@ function doSetStat(command) {
}
state.show = "none"
- return `\n[${possessiveName} ${toTitleCase(arg0)} ability is now ${arg1}]\n`
+ return `\n[${possessiveName} ${toTitleCase(arg0)} ability is now ${arg1}.]\n`
}
function doSetSpellStat(command) {
@@ -2676,8 +1161,8 @@ function doShowAutoXp(command) {
}
function doSetDefaultDifficulty(command) {
- const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "veryeasy", "very easy", "automatic", "auto"]
- const difficultyScores = [30, 25, 20, 15, 10, 5, 5, 5, 0, 0]
+ const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "automatic"]
+ const difficultyScores = [30, 25, 20, 15, 10, 5, 0]
const difficultyPatternNames = [...new Set(difficultyNames)]
difficultyPatternNames.push("\\d+")
@@ -2745,7 +1230,7 @@ function doSetSkill(command) {
}
state.show = "none"
- return `\n[${possessiveName} ${toTitleCase(arg0)} skill is now ${arg2 >= 0 ? "+" + arg2 : "-" + arg2} and based on ${toTitleCase(arg1)}]\n`
+ return `\n[${possessiveName} ${toTitleCase(arg0)} skill is now ${arg2 >= 0 ? "+" + arg2 : "-" + arg2} and based on ${toTitleCase(arg1)}.]\n`
}
function doSetAc(command) {
@@ -2797,41 +1282,21 @@ function doAddExperience(command) {
state.show = "none"
return "\n[Error: Not enough parameters. See #help]\n"
}
-
- arg0 = searchArgument(command, /\d+/gi)
- if (arg0 == null) {
- state.show = "none"
- return "\n[Error: Expected a number. See #help]\n"
- }
arg0 = parseInt(arg0)
- var arg1 = searchArgument(command, /party/gi)
+ var possessiveName = getPossessiveName(character.name)
- if (arg1 == null && character == null) {
+ var level = getLevel(character.experience)
+ character.experience += arg0
+ var newLevel = getLevel(character.experience)
+
+ if (newLevel > level) {
state.show = "none"
- return `\n[Error: Character name not specified. Use the "do" or "say" modes. Alternatively, use "story" mode in the following format without quotes: "charactername #hashtag"]\n`
+ return `\n[${possessiveName} experience is increased to ${character.experience}. LEVEL UP! Level: ${newLevel}, Health Max: ${getHealthMax()}]\n`
}
- if (state.characters.length == 0) {
- state.show = "none"
- return `\n[Error: There are no characters. Type #setup to create a character]\n`
- }
-
- state.prefix = "\n"
- characters = arg1 == null ? [character] : state.characters
- for (var c of characters) {
- var possessiveName = getPossessiveName(c.name)
-
- var level = getLevel(c.experience)
- c.experience += arg0
- var newLevel = getLevel(c.experience)
-
- if (newLevel > level) state.prefix += `[${possessiveName} experience is increased to ${c.experience}. LEVEL UP! Level: ${newLevel}, Health Max: ${getHealthMax(c)}. Next level at ${getNextLevelXp(c.experience)}]\n`
- else state.prefix += `[${possessiveName} experience is increased to ${c.experience}. Next level at ${getNextLevelXp(c.experience)}]\n`
- }
-
- state.show = "prefixOnly"
- return " "
+ state.show = "none"
+ return `\n[${possessiveName} experience is increased to ${character.experience}.]\n`
}
function doLevelUp(command) {
@@ -2854,7 +1319,7 @@ function doSetClass(command) {
character.className = arg0
state.show = "none"
- return `\n[${possessiveName} class is set to "${character.className}"]\n`
+ return `\n[${possessiveName} class is set to "${character.className}".]\n`
}
function doSetSummary(command) {
@@ -2870,7 +1335,7 @@ function doSetSummary(command) {
character.summary = arg0
state.show = "none"
- return `\n[${possessiveName} summary is set]\n`
+ return `\n[${possessiveName} summary is set.]\n`
}
function doSetHealth(command) {
@@ -2897,79 +1362,21 @@ function doHeal(command) {
state.show = "none"
return "\n[Error: Not enough parameters. See #help]\n"
}
+ arg0 = parseInt(arg0)
- var arg1 = getArgumentRemainder(command, 1)
-
- if (arg1 == null) {
- if (character == null) {
- state.show = "none"
- return "\n[Error: Character must be specified. See #help]\n"
- }
-
- var healing
-
- var healingMatches = arg0.match(/\d*d\d+((\+|-)d+)?/gi)
- if (healingMatches != null) healing = calculateRoll(healingMatches[0])
- else {
- healingMatches = arg0.match(/\d+/g)
- if (healingMatches != null) healing = parseInt(healingMatches[healingMatches.length - 1])
- }
-
- if (healing == null) {
- state.show = "none"
- return "\n[Error: Expected a number. See #help]\n"
- }
-
- var haveWord = character.name == "You" ? "have" : "has"
-
- character.health += healing
- character.health = clamp(character.health, 0, getHealthMax())
+ var haveWord = character.name == "You" ? "have" : "has"
+ var areWord = character.name == "You" ? "are" : "is"
+ if (character.health >= getHealthMax()) {
state.show = "none"
- return `\n[${character.name} ${haveWord} been healed for ${healing} hp to a total of ${character.health}]\n`
- } else {
- var healing
-
- var healingMatches = arg0.match(/\d*d\d+((\+|-)d+)?/gi)
- if (healingMatches != null) healing = calculateRoll(healingMatches[0])
- else {
- healingMatches = arg0.match(/\d+/g)
- if (healingMatches != null) healing = parseInt(healingMatches[0])
- }
-
- if (healing == null) {
- state.show = "none"
- return "\n[Error: Expected a number. See #help]\n"
- }
-
- for (var enemy of state.enemies) {
- if (enemy.name.toLowerCase() == arg1.toLowerCase()) {
- enemy.health = Math.max(0, enemy.health + healing)
- state.show = "none"
- return `\n[${toTitleCase(enemy.name)} has been healed for ${healing} hp to a total of ${enemy.health}]\n`
- }
- }
-
- 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
- character.health = clamp(character.health, 0, getHealthMax(character))
- state.show = "none"
- return `\n[${toTitleCase(character.name)} has been healed for ${healing} hp to a total of ${character.health}]\n`
- }
- }
-
- 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[${character.name} ${areWord} at maximum health]\n`
}
+
+ character.health = character.health + arg0
+ character.health = clamp(character.health, 0, getHealthMax())
+
+ state.show = "none"
+ return `\n[${character.name} ${haveWord} been healed by ${arg0} hp to ${character.health} health]\n`
}
function doDamage(command) {
@@ -3008,15 +1415,20 @@ function doDamage(command) {
character.health = clamp(character.health, 0, getHealthMax())
state.show = "none"
- return `\n[${character.name} ${haveWord} been damaged for ${damage} hp with ${character.health} remaining] ${character.health == 0 ? " You are unconscious" : ""}\n`
+ return `\n[${character.name} ${haveWord} been damaged for ${damage} hp with ${character.health} remaining].${character.health == 0 ? " You are unconscious" : ""}\n`
} else {
+ if (arg1 == null) {
+ state.show = "none"
+ return "\n[Error: Not enough parameters. See #help]\n"
+ }
+
var damage
- var damageMatches = arg0.match(/\d*d\d+((\+|-)d+)?/gi)
+ var damageMatches = arg1.match(/\d*d\d+((\+|-)d+)?/gi)
if (damageMatches != null) damage = calculateRoll(damageMatches[0])
else {
- damageMatches = arg0.match(/\d+/g)
- if (damageMatches != null) damage = parseInt(damageMatches[0])
+ damageMatches = arg1.match(/\d+/g)
+ if (damageMatches != null) damage = parseInt(damageMatches[damageMatches.length - 1])
}
if (damage == null) {
@@ -3025,31 +1437,11 @@ function doDamage(command) {
}
for (var enemy of state.enemies) {
- if (enemy.name.toLowerCase() == arg1.toLowerCase()) {
+ if (enemy.name.toLowerCase() == arg0.toLowerCase()) {
enemy.health = Math.max(0, enemy.health - damage)
- state.show = "none"
- return `\n[${toTitleCase(enemy.name)} has been damaged for ${damage} hp with ${enemy.health} remaining] ${enemy.health == 0 ? " " + toTitleCase(enemy.name) + " has been defeated!" : ""}\n`
+ return `\n[${toTitleCase(enemy.name)} has been damaged for ${damage} hp with ${enemy.health} remaining].${enemy.health == 0 ? " " + toTitleCase(enemy.name) + " has been defeated!" : ""}\n`
}
}
-
- 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)
- state.show = "none"
- return `\n[${toTitleCase(character.name)} has been damaged for ${damage} hp with ${character.health} remaining] ${character.health == 0 ? " " + toTitleCase(character.name) + " is unconcious!" : ""}\n`
- }
- }
-
- 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]`
}
}
@@ -3057,17 +1449,13 @@ function doRest(command) {
var commandName = getCommandName(command)
state.day++
state.enemies = []
- state.cardDeals = null
- state.cardPrices = null
- state.spellShopDeals = null
- state.itemShopDeals = null
var healingFactor = 1
var text
if (commandName.toLowerCase() == "shortrest") {
state.day--
healingFactor = .5
- text = `\n[All characters have healed 50%]\n`
+ text = `\n[All characters have healed 50%.]\n`
} else {
text = `\n[All characters have rested and feel rejuvinated. It's now day ${state.day}]\n`
}
@@ -3081,37 +1469,6 @@ function doRest(command) {
return text
}
-function doHealParty(command) {
- var arg0 = getArgument(command, 0)
- if (arg0 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
-
- var healing
- var healingMatches = arg0.match(/\d*d\d+((\+|-)d+)?/gi)
- if (healingMatches != null) healing = calculateRoll(healingMatches[0])
- else {
- healingMatches = arg0.match(/\d+/g)
- if (healingMatches != null) healing = parseInt(healingMatches[healingMatches.length - 1])
- }
-
- if (healing == null) {
- state.show = "none"
- return "\n[Error: Expected a number. See #help]\n"
- }
-
- var text = `\n[All characters have been healed by ${healing}.]\n`
- state.characters.forEach(function(character) {
- var max = getHealthMax(character)
- character.health += healing
- if (character.health > max) character.health = max
- text += `[${toTitleCase(character.name)}: ${character.health} / ${max} health]\n`
- })
- state.show = "none"
- return text
-}
-
function doFlipCommandAbility(command) {
var ability = getCommandName(command)
var arg0 = getArgument(command, 0)
@@ -3126,8 +1483,8 @@ function doFlipCommandAbility(command) {
function doCheck(command) {
const advantageNames = ["normal", "advantage", "disadvantage"]
- const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "veryeasy", "very easy", "automatic", "auto"]
- const difficultyScores = [30, 25, 20, 15, 10, 5, 5, 5, 0, 0]
+ const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "automatic"]
+ const difficultyScores = [30, 25, 20, 15, 10, 5, 0]
var character = getCharacter()
var arg0 = null
@@ -3187,8 +1544,8 @@ function doTry(command) {
}
const advantageNames = ["normal", "advantage", "disadvantage"]
- const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "veryeasy", "very easy", "automatic", "auto"]
- const difficultyScores = [30, 25, 20, 15, 10, 5, 5, 5, 0, 0]
+ const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "automatic"]
+ const difficultyScores = [30, 25, 20, 15, 10, 5, 0]
var character = getCharacter()
var textIndex = 3
var failword = character.name == "You" ? "fail" : "fails"
@@ -3253,8 +1610,8 @@ function doTry(command) {
else if (modifier != 0) state.prefix = `\n[${arg0} check DC: ${target} roll: ${dieText}${modifier > 0 ? "+" + modifier : modifier}=${score + modifier}. ${score + modifier >= target ? "Success!" : "Failure!"}]\n`
else state.prefix = `\n[${arg0} check DC: ${target} roll: ${dieText}. ${score >= target ? "Success!" : "Failure!"}]\n`
var text = `\n${character.name} ${score + modifier >= target ? "successfully" : failword + " to"} ${arg3}`
- if (score == 20) text += " Critical success! The action was extremely effective."
- else if (score == 1) text += " Critical failure! There are dire consequences for this action."
+ if (score == 20) text += " Critical success! Your action was extremely effective."
+ else if (score == 1) text += " Critical failure! There are dire consequences for your action."
if (score + modifier >= target || score == 20) text += addXpToAll(Math.floor(state.autoXp * clamp(target, 1, 20) / 20)) + "\n"
return text
@@ -3262,8 +1619,8 @@ function doTry(command) {
function doAttack(command) {
const advantageNames = ["normal", "advantage", "disadvantage"]
- const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "veryeasy", "very easy", "automatic", "auto"]
- const difficultyScores = [30, 25, 20, 15, 10, 5, 5, 5, 0, 0]
+ const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "automatic"]
+ const difficultyScores = [30, 25, 20, 15, 10, 5, 0]
var character = getCharacter()
var textIndex = 3
var missWord = character.name == "You" ? "miss" : "misses"
@@ -3322,7 +1679,6 @@ function doAttack(command) {
}
var enemyString = ""
- var allyString = ""
if (state.initiativeOrder.length > 0) {
var foundEnemy
@@ -3337,27 +1693,11 @@ function doAttack(command) {
var indexMatches = targetText.match(/(?<=enemy\s*)\d+/gi)
if (indexMatches != null) {
foundEnemy = state.enemies[parseInt(indexMatches[0]) - 1]
+ log(`foundEnemy:${foundEnemy}`)
targetText = targetText.replace(/enemy\s*d+/gi, foundEnemy.name)
}
}
- var 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)
@@ -3374,30 +1714,9 @@ function doAttack(command) {
if (score == 20 || score + modifier >= targetRoll) {
if (score == 20) enemyString += `\nCritical Damage: ${damage}\n`
else enemyString += `\nDamage: ${damage}\n`
-
- state.blockCharacter = foundEnemy
- state.blockPreviousHealth = foundEnemy.health
foundEnemy.health = Math.max(0, foundEnemy.health - damage)
- if (foundEnemy.health == 0) {
- enemyString += ` ${toTitleCase(foundEnemy.name)} has been defeated!`
-
- } else enemyString += ` ${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!`
- }
- }
-
- 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!`
+ if (foundEnemy.health == 0) enemyString += ` ${toTitleCase(foundEnemy.name)} has been defeated!`
+ else enemyString += ` ${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!`
}
}
}
@@ -3406,23 +1725,21 @@ 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`
+ 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}!`
- else text = `\n${toTitleCase(character.name)} ${tryWord} to hit ${targetText}. ${toTitleCase(character.name)} ${missWord}!`
+ else text = `\n${toTitleCase(character.name)} ${tryWord} to hit ${targetText} ${toTitleCase(character.name)} ${missWord}!`
- if (score == 20) text += " Critical success! The attack is exceptionally damaging!"
- else if (score == 1) text += " Critical failure! The attack missed in a spectacular way!"
+ if (score == 20) text += " Critical success! Your attack is exceptionally damaging!"
+ else if (score == 1) text += " Critical failure! Your attack missed in a spectacular way!"
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))
+ if (score + modifier >= targetRoll || score == 20) text += addXpToAll(Math.floor(state.autoXp * clamp(targetRoll, 1, 20) / 20))
return text + "\n"
}
@@ -3655,7 +1972,7 @@ function doGoToLocation(command) {
}
function doGetLocation(command) {
- state.show = "location"
+ state.show = "none"
return `\n[You are at ${state.location == null ? "" : "the location " + toTitleCase(state.location) + " "}(${state.x},${state.y})]`
}
@@ -3691,7 +2008,7 @@ function doRemoveLocation(command) {
var num = parseInt(x) - 1
if (num >= state.locations.length) {
state.show = "none"
- return `\n[Error: Location ${x} does not exist. See #showlocations]\n`
+ return `\n[Error: Location ${x} does not exist. See #showlocations.]\n`
}
var location = state.locations[num]
@@ -3759,11 +2076,6 @@ function doShowEnemies(command) {
return " "
}
-function doShowAllies(command) {
- state.show = "showAllies"
- return " "
-}
-
function doRemoveEnemy(command) {
var arg0 = getArgumentRemainder(command, 0)
if (arg0 == null) {
@@ -3783,7 +2095,7 @@ function doRemoveEnemy(command) {
var num = parseInt(x) - 1
if (num >= state.enemies.length) {
state.show = "none"
- return `\n[Error: Enemy ${x} does not exist. See #showenemies]\n`
+ return `\n[Error: Enemy ${x} does not exist. See #showenemies.]\n`
}
var enemy = state.enemies[num]
@@ -3816,58 +2128,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 +2141,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) {
@@ -3979,99 +2226,9 @@ function doAddEnemy(command) {
state.enemies.push(enemy)
- state.show = "none"
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,14 +2241,9 @@ 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"
+ return "\n[Error: No enemies! Call #addenemy or #encounter]\n"
}
createInitiativeOrder()
@@ -4108,13 +2260,13 @@ function doInitiative(command) {
function doFlee(command) {
if (state.initiativeOrder.length == 0) {
state.show = "none"
- return "\n[Error: Not in combat. Type #initiative first]\n"
+ return "\n[Error: Not in combat. Call #initiative first]\n"
}
var difficulty = getArgument(command, 0)
if (difficulty != null) {
- const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "veryeasy", "very easy", "automatic", "auto"]
- const difficultyScores = [30, 25, 20, 15, 10, 5, 5, 5, 0, 0]
+ const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "automatic"]
+ const difficultyScores = [30, 25, 20, 15, 10, 5, 0]
const difficultyPatternNames = [...new Set(difficultyNames)]
difficultyPatternNames.push("\\d+")
@@ -4146,16 +2298,7 @@ function doTurn(command) {
if (enemy.health > 0) continue
defeatedEnemies++
- var index = state.initiativeOrder.findIndex(x => x.name.toLowerCase() == enemy.name.toLowerCase())
- 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())
+ var index = state.initiativeOrder.indexOf(enemy)
if (index >= 0) state.initiativeOrder.splice(index, 1)
}
@@ -4164,7 +2307,7 @@ function doTurn(command) {
if (character.health > 0) continue
defeatedCharacters++
- var index = state.initiativeOrder.findIndex(x => x.name.toLowerCase() == character.name.toLowerCase())
+ var index = state.initiativeOrder.indexOf(character)
if (index >= 0) state.initiativeOrder.splice(index, 1)
}
@@ -4184,56 +2327,61 @@ function doTurn(command) {
return "\nDefeat! The entire party has been incapacitated.\n"
}
- return executeTurn(state.initiativeOrder[0])
-}
+ var activeCharacter = state.initiativeOrder[0]
+ var activeCharacterName = toTitleCase(activeCharacter.name)
+ var possessiveName = getPossessiveName(activeCharacter.name)
+ if (possessiveName == "Your") possessiveName = "your"
-function doRepeatTurn(command) {
- return executeTurn(state.initiativeOrder[0])
-}
-
-function doBlock(command) {
- if (state.blockCharacter == null) {
+ if (activeCharacter.className != null) {
state.show = "none"
- return "\n[Error: No attack to block. See #help]\n"
+ return `\n[It is ${possessiveName} turn]\n`
+ } else {
+ var characters = state.characters.filter(x => x.health > 0)
+ var target = characters[getRandomInteger(0, characters.length - 1)]
+ var areWord = target.name == "You" ? "are" : "is"
+ var targetNameAdjustedCase = target.name == "You" ? "you" : toTitleCase(target.name)
+ var hit = calculateRoll(`1d20${activeCharacter.hitModifier > 0 ? "+" + activeCharacter.hitModifier : activeCharacter.hitModifier < 0 ? activeCharacter.hitModifier : ""}`) >= target.ac
+
+ var text = `\n[It is ${possessiveName} turn.]\n`
+ if (getRandomBoolean() || activeCharacter.spells.length == 0) {
+ if (hit) {
+ var damage = isNaN(activeCharacter.damage) ? calculateRoll(activeCharacter.damage) : activeCharacter.damage
+ target.health = Math.max(target.health - damage, 0)
+ text += `${activeCharacterName} attacks ${targetNameAdjustedCase} for ${damage} damage!\n`
+ if (target.health == 0) text += ` ${toTitleCase(target.name)} ${areWord} unconscious! \n`
+ else text += ` ${toTitleCase(target.name)} ${areWord} at ${target.health} health.\n`
+ } else text += `${activeCharacterName} attacks ${targetNameAdjustedCase} but misses!\n`
+ } else {
+ var spell = activeCharacter.spells[getRandomInteger(0, activeCharacter.spells.length - 1)]
+ var diceMatches = spell.match(/(?<=^.*)\d*d\d+((\+|-)\d+)?$/gi)
+ if (diceMatches == null) text += `${activeCharacterName} casts spell ${spell}!`
+ else {
+ if (hit) {
+ var damage = calculateRoll(diceMatches[0])
+ var spell = spell.substring(0, spell.length - diceMatches[0].length)
+ target.health = Math.max(target.health - damage, 0)
+
+ text += `${activeCharacterName} casts spell ${spell} at ${targetNameAdjustedCase} for ${damage} damage!`
+
+ if (target.health == 0) text += ` ${toTitleCase(target.name)} ${areWord} unconscious!\n`
+ else text += ` ${toTitleCase(target.name)} ${areWord} at ${target.health} health.\n`
+ } else text += `${activeCharacterName} casts spell ${spell} at ${targetNameAdjustedCase} but misses!\n`
+ }
+ }
+ return text
}
-
- 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"
- }
-
- character.health = state.blockPreviousHealth
-
- var properName = toTitleCase(character.name)
- state.show = "prefix"
- state.prefix = `[${properName} has ${character.health} health]`
- return `\nHowever, the damage to ${properName} was blocked!\n`
}
function doTake(command) {
- var itemIndex = 0
var arg0 = getArgument(command, 0)
if (arg0 == null) {
state.show = "none"
return "\n[Error: Not enough parameters. See #help]\n"
}
- if (arg0 == "the") {
- var tempArg = getArgument(command, 1)
- if (tempArg != null && !isNaN(tempArg)) {
- arg0 = tempArg
- itemIndex++
- }
- }
-
- if (!isNaN(arg0)) itemIndex++
-
const item = {
quantity: isNaN(arg0) ? 1 : arg0,
- name: getArgumentRemainder(command, itemIndex).replace(/^((the)|(a)|(an))\s/, "").plural(true)
+ name: getArgumentRemainder(command, isNaN(arg0) ? 0 : 1).replace(/^((the)|(a)|(an))\s/, "").plural(true)
}
var character = getCharacter()
@@ -4262,223 +2410,11 @@ function doTake(command) {
return text
}
-function doTakeWeapon(command) {
- var itemIndex = 3
- var arg0 = getArgument(command, 0)
- if (arg0 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
-
- var arg1 = getArgument(command, 1)
- if (arg1 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
- if (isNaN(arg1)) {
- state.show = "none"
- return "\n[Error: Expected a number. See #help]\n"
- }
- arg1 = parseInt(arg1)
-
- var arg2 = getArgument(command, 2)
- if (arg2 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
-
- var arg3 = getArgument(command, 3)
- if (arg3 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
-
- if (arg3 == "the") {
- var tempArg = getArgument(command, 1)
- if (tempArg != null && !isNaN(tempArg)) {
- arg3 = tempArg
- itemIndex++
- }
- }
-
- const item = {
- quantity: 1,
- name: getArgumentRemainder(command, itemIndex).replace(/^((the)|(a)|(an))\s/, "").plural(true),
- damageDice: arg0,
- toHitBonus: arg1,
- ability: arg2
- }
-
- var character = getCharacter()
- var commandName = "take"
- var commandNamePlural = commandName.plural(character.name == "You")
- var haveWord = character.name == "You" ? "have" : "has"
-
- var text = "\n"
- text += `${character.name} ${commandNamePlural} ${item.name.toLowerCase().startsWith("the ") ? "" : "the "}${item.name}.\n`
-
- var index = character.inventory.findIndex((element) => element.name.toLowerCase() == item.name.toLowerCase())
- if (index == -1) {
- character.inventory.push(item)
- } else {
- var existingItem = character.inventory[index]
- existingItem.quantity = parseInt(existingItem.quantity) + parseInt(item.quantity)
-
- let displayItemName = existingItem.name.plural(existingItem.quantity == 1)
- text += `${character.name} now ${haveWord} ${existingItem.quantity} ${displayItemName}.\n`
- }
-
- return text
-}
-
-function doTakeArmor(command) {
- var itemIndex = 1
- var arg0 = getArgument(command, 0)
- if (arg0 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
-
- var arg1 = getArgument(command, 1)
- if (arg1 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
-
- const item = {
- quantity: 1,
- name: getArgumentRemainder(command, itemIndex).replace(/^((the)|(a)|(an))\s/, "").plural(true),
- ac: arg0,
- }
-
- var character = getCharacter()
- var commandName = "take"
- var commandNamePlural = commandName.plural(character.name == "You")
- var haveWord = character.name == "You" ? "have" : "has"
-
- var text = "\n"
- text += `${character.name} ${commandNamePlural} ${item.name.toLowerCase().startsWith("the ") ? "" : "the "}${item.name}.\n`
-
- var index = character.inventory.findIndex((element) => element.name.toLowerCase() == item.name.toLowerCase())
- if (index == -1) {
- character.inventory.push(item)
- } else {
- var existingItem = character.inventory[index]
- existingItem.quantity = parseInt(existingItem.quantity) + parseInt(item.quantity)
-
- let displayItemName = existingItem.name.plural(existingItem.quantity == 1)
- text += `${character.name} now ${haveWord} ${existingItem.quantity} ${displayItemName}.\n`
- }
-
- return text
-}
-
-function doReward(command) {
- command = command.replace(/very rare/gi, "phenomenal")
-
- let quantity = getArgument(command, 0)
- if (quantity == null || isNaN(quantity)) quantity = 1
- if (!isNaN(quantity)) quantity = parseInt(quantity)
- if (quantity < 1) quantity = 1
-
- let categoryName = searchArgument(command, /default|weapons|armor|tools|gear|common|uncommon|rare|phenomenal|legendary|artifact/gi)
- if (categoryName == null && searchArgument(command, /weapon/) != null) categoryName = "weapons"
- if (categoryName == null) categoryName = "default"
-
- let loot = []
- for (let i = 0; i < quantity; i++) {
- const rand = Math.random()
- categoryName = categoryName.toLowerCase()
- let category
-
- if (categoryName == "weapons" || categoryName == "default" && rand <= .125) category = weaponsList
- else if (categoryName == "armor" || categoryName == "default" && rand <= .25) category = armorList
- else if (categoryName == "tools" || categoryName == "default" && rand <= .375) category = toolsList
- else if (categoryName == "gear" || categoryName == "default" && rand <= .50) category = gearList
- else if (categoryName == "common" || categoryName == "default" && rand <= .70) category = commonList
- else if (categoryName == "uncommon" || categoryName == "default" && rand <= .80) category = uncommonList
- else if (categoryName == "rare" || categoryName == "default" && rand <= .88) category = rareList
- else if (categoryName == "phenomenal" || categoryName == "default" && rand <= .94) category = phenomenalList
- else if (categoryName == "legendary" || categoryName == "default" && rand <= .98) category = legendaryList
- else if (categoryName == "artifact" || categoryName == "default" && rand > .98) category = artifactList
- else category = commonList
-
- let itemStoryCardName
- shuffled = [...category].sort(() => 0.5 - Math.random());
- itemStoryCardName = shuffled[0]
-
- let itemName = itemShopConvertGenericName(itemStoryCardName)
- loot.push(itemName)
-
- let itemStoryCard = findItemCard(itemName, itemStoryCardName)
-
- if (itemStoryCard != null && itemStoryCard.type == "weapon") doTakeWeapon(`takeweapon ${itemStoryCard.description.split(",")[1]} ${itemStoryCard.description.split(",")[2]} ${itemStoryCard.description.split(",")[3]} ${itemName}`)
- else if (itemStoryCard != null && itemStoryCard.type == "armor") doTakeArmor(`takearmor ${itemStoryCard.description.split(",")[1]} ${itemName}`)
- else doTake(`take ${itemName}`)
- }
-
- let text = "You have found"
- if (loot.length == 1) {
- let itemName = loot[0]
- let aWord = ['a', 'e', 'i', 'o', 'u'].indexOf(itemName.charAt(0).toLowerCase()) !== -1 ? "an" : "a"
- text += ` ${aWord} ${itemName}!`
- } else {
- text += ":"
- loot.forEach(itemName => {
- let aWord = ['a', 'e', 'i', 'o', 'u'].indexOf(itemName.charAt(0).toLowerCase()) !== -1 ? "an" : "a"
- text += `\n${aWord} ${itemName},`
- })
- }
-
- return text
-}
-
function doMap(command) {
state.show = "map"
return " "
}
-function doEquip(command) {
- let character = getCharacter()
- let arg0 = getArgument(command, 0)
- if (arg0 == null) {
- state.show = "none"
- return "\n[Error: Not enough parameters. See #help]\n"
- }
-
- var dontWord = character.name == "You" ? "don't" : "doesn't"
-
- let itemName = getArgumentRemainder(command, 0)
-
- let item = character.inventory.find((element) => element.name.toLowerCase() == itemName.toLowerCase())
-
- if (item == null) return `${character.name} tried to equip ${toTitleCase(itemName)}, but ${dontWord} possess it`
-
- let text = `\n${character.name} equipped the item ${toTitleCase(itemName)}!\n`
- if ("damageDice" in item && "toHitBonus" in item) {
- let abilityValue = character.stats.find((element) => element.name.toLowerCase() == item.ability)
- let ability = abilityValue == null ? 10 : abilityValue.value
- let abilityModifier = Math.ceil((ability - 10) / 2)
-
- let damageBase = item.damageDice.replaceAll(/\+.*/gi, "")
- let damageModifier = parseInt(item.damageDice.replaceAll(/.*\+/gi, "")) + abilityModifier
- character.damage = `${damageBase}+${damageModifier}`
- character.proficiency = abilityModifier
- character.meleeStat = item.ability
- } else if ("ac" in item) {
- let dexterityStat = character.stats.find((element) => element.name.toLowerCase() == "dexterity")
- let dexterity = dexterityStat == null ? 10 : dexterityStat.value
- let ac = parseInt(item.ac.replaceAll(/(?<=.)\+.*/gi, ""))
- if (/.*\+dmax2/i.test(item.ac)) character.ac = ac + Math.max(2, Math.ceil((dexterity - 10) / 2))
- else if (/.*\+d/i.test(item.ac)) character.ac = ac + Math.ceil((dexterity - 10) / 2)
- else if (/\+.*/i.test(item.ac)) character.ac += ac
- else character.ac = ac
- }
-
- text += "\n"
- return text
-}
-
function doDrop(command) {
var character = getCharacter()
var commandName = getCommandName(command)
@@ -4524,7 +2460,7 @@ function doDrop(command) {
if (existingItem.quantity == 1) text = `\n${character.name} ${commandName.plural(character.name == "You")} the ${displayItemName.plural(true)}.\n`
else if (parseInt(item.quantity) >= parseInt(existingItem.quantity)) text = `${character.name} ${commandName.plural(character.name == "You")} all ${existingItem.quantity} of the ${displayItemName}.`
- else text = `\n${character.name} ${commandName.plural(character.name == "You")} ${item.quantity} ${displayItemName}. \n`
+ else text = `\n${character.name} ${commandName.plural(character.name == "You")} ${item.quantity} ${displayItemName}.\n`
existingItem.quantity -= item.quantity
if (existingItem.quantity <= 0) {
@@ -4533,7 +2469,7 @@ function doDrop(command) {
}
if (existingItem.quantity > 0) {
displayItemName = existingItem.name.plural(existingItem.quantity == 1)
- text += `${character.name} now ${haveWord} ${existingItem.quantity} ${displayItemName}.\n`
+ text += ` ${character.name} now ${haveWord} ${existingItem.quantity} ${displayItemName}.\n`
}
}
@@ -4802,7 +2738,7 @@ function doLearnSpell(command) {
var tryWord = character.name == "You" ? "try" : "tries"
var found = character.spells.find((element) => element == arg0)
- if (found != null) return `\n[${character.name} ${tryWord} to learn the spell ${arg0}, but already knows it]\n`
+ if (found != null) return `\n[${character.name} ${tryWord} to learn the spell ${arg0}, but already knows it.]\n`
character.spells.push(arg0)
addStoryCard(arg0, "", "spell")
@@ -4873,8 +2809,8 @@ function doRemoveSkill(command) {
function doCastSpell(command) {
const advantageNames = ["normal", "advantage", "disadvantage"]
- const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "veryeasy", "very easy", "automatic", "auto"]
- const difficultyScores = [30, 25, 20, 15, 10, 5, 5, 5, 0, 0]
+ const difficultyNames = ["impossible", "extreme", "hard", "medium", "easy", "effortless", "automatic"]
+ const difficultyScores = [30, 25, 20, 15, 10, 5, 0]
var character = getCharacter()
const dontWord = character.name == "You" ? "don't" : "doesn't"
const tryWord = character.name == "You" ? "try" : "tries"
@@ -4930,7 +2866,7 @@ function doCastSpell(command) {
if (found == null) {
state.show = "none"
- return `\n[${toTitleCase(character.name)} ${tryWord} to cast the spell ${spell}, but ${character.name == "You" ? "you" : toTitleCase(character.name)} ${dontWord} know it]\n`
+ return `\n[${toTitleCase(character.name)} ${tryWord} to cast the spell ${spell}, but ${character.name == "You" ? "you" : toTitleCase(character.name)} ${dontWord} know it.]\n`
}
var text = `${character.name} cast the spell ${spell}${advantage != "normal" ? " with " + advantage : ""}${targetText == null ? "" : " " + targetText}.`
@@ -4946,7 +2882,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 +2900,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)
@@ -4996,26 +2914,9 @@ function doCastSpell(command) {
if (roll == 20 || roll + modifier >= difficulty) {
if (roll == 20) enemyString += `\nCritical Damage: ${damage}\n`
else enemyString += `\nDamage: ${damage}\n`
-
- state.blockCharacter = foundEnemy
- state.blockPreviousHealth = foundEnemy.health
foundEnemy.health = Math.max(0, foundEnemy.health - damage)
- if (foundEnemy.health == 0) enemyString += ` ${toTitleCase(foundEnemy.name)} has been defeated!\n`
- else enemyString += ` ${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!\n`
- }
- }
-
- 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`
+ if (foundEnemy.health == 0) enemyString += `${toTitleCase(foundEnemy.name)} has been defeated!\n`
+ else enemyString = `${toTitleCase(foundEnemy.name)} has ${foundEnemy.health} health remaining!\n`
}
}
}
@@ -5023,8 +2924,7 @@ function doCastSpell(command) {
state.show = "prefix"
var dieText = advantage == "advantage" || advantage == "disadvantage" ? `${advantage}(${roll1},${roll2})` : roll1
var difficultyWord = targetText == null ? "Difficulty" : "Armor"
- if (difficulty == 0) state.prefix = ""
- else if (roll == 20) state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}. Critcal Success!]\n`
+ if (roll == 20) state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}. Critcal Success!]\n`
else if (roll == 1) state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}. Critcal Failure!]\n`
else if (modifier != 0) state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}${modifier > 0 ? "+" + modifier : modifier}=${roll + modifier}. ${roll + modifier >= difficulty ? "Success!" : "Failure!"}]\n`
else state.prefix = `\n[${difficultyWord} Class: ${difficulty}. Roll: ${dieText}. ${roll + modifier >= difficulty ? "Success!" : "Failure!"}]\n`
@@ -5035,9 +2935,8 @@ 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))
+ if (roll + modifier >= difficulty || roll == 20) text += addXpToAll(Math.floor(state.autoXp * clamp(difficulty, 1, 20) / 20))
return `\n${text}\n`
}
@@ -5088,7 +2987,7 @@ function doEraseNote(command) {
var num = parseInt(x) - 1
if (num >= state.notes.length) {
state.show = "none"
- return `\n[Error: Note ${x} does not exist. Type #shownotes]\n`
+ return `\n[Error: Note ${x} does not exist. Call #shownotes.]\n`
}
state.notes.splice(num, 1)
@@ -5111,11 +3010,11 @@ function doRemoveCharacter(command) {
if (character.name.toLowerCase() == arg0.toLowerCase()) {
state.characters.splice(i, 1)
state.show = "none"
- return `[Character ${character.name} removed]`
+ return `[Character ${character.name} removed.]`
}
}
- return `[Character ${arg0} was not found]`
+ return `[Character ${arg0} was not found.]`
}
function doGenerateName(command) {
@@ -5207,7 +3106,6 @@ function doReset(command) {
state.locations = []
state.location = null
state.enemies = null
- state.allies = null
state.initiativeOrder = []
state.x = null
state.y = null
diff --git a/Library.js b/Library.js
index 21f6d9f..c294b91 100644
--- a/Library.js
+++ b/Library.js
@@ -1,14 +1,3 @@
-const weaponsList = ["Club", "Dagger", "Greatclub", "Handaxe", "Javelin", "Light Hammer", "Mace", "Quarterstaff", "Sickle", "Spear", "Dart", "Light Crossbow", "Shortbow", "Sling", "Battleaxe", "Flail", "Glaive", "Greataxe", "Greatsword", "Halberd", "Lance", "Longsword", "Maul", "Morningstar", "Pike", "Rapier", "Scimitar", "Shortsword", "Trident", "Warhammer", "Warhammer", "War Pick", "Whip", "Blowgun", "Hand Crossbow", "Heavy Crossbow", "Longbow", "Musket", "Pistol"]
-const armorList = ["Padded Armor", "Leather Armor", "Studded Leather Armor", "Hide Armor", "Chain Shirt", "Scale Mail", "Breastplate", "Half Plate Armor", "Ring Mail", "Chain Mail", "Splint Armor", "Plate Armor", "Shield"]
-const toolsList = ["Alchemist's Supplies", "Brewer's Supplies", "Calligrapher's Supplies", "Carpenter's Tools", "Cartographer's Tools", "Cobbler's Tools", "Cook's Utensils", "Glassblower's Tools", "Jeweler's Tools", "Leatherworker's Tools", "Mason's Tools", "Painter's Supplies", "Potter's Tools", "Smith's Tools", "Tinker's Tools", "Weaver's Tools", "Woodcarver's Tools", "Disguise Kit", "Forgery Kit", "Gaming Set", "Herbalism Kit", "Musical Instrument", "Navigator's Tools", "Poisoner's Kit", "Thieves' Tools"]
-const gearList = ["Acid", "Alchemist's Fire", "Ammunition", "Antitoxin", "Arcane Focus", "Backpack", "Ball Bearings", "Barrel", "Basket", "Bedroll", "Bell", "Blanket", "Block and Tackle", "Book", "Glass Bottle", "Bucket", "Burglar's Pack", "Caltrops", "Candle", "Crossbow Bolt Case", "Map Case", "Scroll Case", "Chain", "Chest", "Climber's Kit", "Fine Clothes", "Traveler's Clothes", "Component Pouch", "Costume", "Crowbar", "Diplomat's Pack", "Druidic Focus", "Dungeoneer's Pack", "Entertainer's Pack", "Explorer's Pack", "Flask", "Grappling Hook", "Healer's Kit", "Holy Symbol", "Holy Water", "Hunting Trap", "Ink", "Ink Pen", "Jug", "Ladder", "Lamp", "Bullseye Lantern", "Hooded Lantern", "Lock", "Magnifying Glass", "Manacles", "Map", "Mirror", "Net", "Oil", "Paper", "Parchment", "Perfume", "Basic Poison", "Pole", "Iron Pot", "Potion of Healing", "Pouch", "Priest's Pack", "Quiver", "Portable Ram", "Rations", "Robe", "Rope", "Sack", "Scholar's Pack", "Shovel", "Signal Whistle", "Spell Scroll", "Iron Spikes", "Spyglass", "String", "Tent", "Tinderbox", "Torch", "Vial", "Waterskin"]
-const commonList = ["Armor of Gleaming", "Bead of Nourishment", "Bead of Refreshment", "Boots of False Tracks", "Candle of the Deep", "Cast-Off Armor", "Charlatan's Die", "Cloak of Billowing", "Cloak of Many Fashions", "Clockwork Amulet", "Clothes of Mending", "Dark Shard Amulet", "Dread Helm", "Ear Horn of Hearing", "Enduring Spellbook", "Ersatz Eye", "Hat of Vermin", "Hat of Wizardry", "Heward's Handy Spice Pouch", "Horn of Silent Alarm", "Instrument of Illusions", "Instrument of Scribing", "Lock of Trickery", "Moon-Touched Sword", "Mystery Key", "Orb of Direction", "Orb of Time", "Perfume of Bewitching", "Pipe of Smoke Monsters", "Pole of Angling", "Pole of Collapsing", "Potion of Climbing", "Potion of Comprehension", "Pot of Awakening", "Prosthetic Limb", "Rival Coin", "Rope of Mending", "Ruby of the War Mage", "Shield of Expression", "Silvered Weapon", "Smoldering Armor", "Staff of Adornment", "Staff of Birdcalls", "Staff of Flowers", "Talking Doll", "Tankard of Sobriety", "Veteran's Cane", "Walloping Ammunition", "Wand of Conducting", "Wand of Pyrotechnics"]
-const uncommonList = ["Adamantine Armor", "Adamantine Weapon", "Alchemy Jug", "Ammunition +1", "Amulet of Proof against Detection and Location", "Baba Yaga's Dancing Broom", "Bag of Holding", "Bag of Tricks", "Boots of Elvenkind", "Boots of Striding and Springing", "Boots of the Winterlands", "Bracers of Archery", "Brooch of Shielding", "Broom of Flying", "Cap of Water Breathing", "Circlet of Blasting", "Cloak of Elvenkind", "Cloak of Protection", "Cloak of the Manta Ray", "Decanter of Endless Water", "Deck of Illusions", "Driftglobe", "Dust of Disappearance", "Dust of Dryness", "Dust of Sneezing and Choking", "Elemntal Gem", "Enspelled Armor Uncommon", "Uncommon Enspelled Staff", "Enspelled Weapon Uncommon", "Eversmoking Bottle", "Eyes of Charming", "Eyes of Minute Seeing", "Eyes of the Eagle", "Silver Raven Figurine of Wondrous Power", "Gauntlets of Ogre Power", "Gem of Brightness", "Gloves of Missile Snaring", "Gloves of Swimming and Climbing", "Gloves of Thievery", "Goggles of Night", "Hag Eye", "Hat of Disguise", "Headband of Intellect", "Helm of Comprehending Languages", "Helm of Telepathy", "Immovable Rod", "Doss Lute", "Fochlucan Bandore", "Mac-Fuirmidh Cittern", "Javelin of Lightning", "Keoghtom's Ointment", "Lantern of Revealing", "Mariner's Armor", "Medallion of Thoughts", "Nature's Mantle", "Necklace of Adaptation", "Oil of Slipperiness", "Pearl of Power", "Periapt of Health", "Periapt of Wound Closure", "Philter of Love", "Pipes of Haunting", "Pipes of the Sewers", "Potion of Animal Friendship", "Potion of Fire Breath", "Potion of Hill Giant Strength", "Potion of Growth", "Potion of Poison", "Potion of Puglism", "Potion of Resistance", "Potion of Water Breathing", "Quaal's Feather Token Uncommon", "Quiver of Ehlonna", "Ring of Jumping", "Ring of Mind Shielding", "Ring of Swimming", "Ring of Warmth", "Ring of Water Walking", "Robe of Useful Items", "Rod of the Pact Keeper +1", "Rope of Climbing", "Saddle of the Cavalier", "Sending Stones", "Sentinel Shield", "Shield +1", "Slippers of Spider Climbining", "Staff of the Adder", "Staff of the Python", "Stone of Good Luck", "Sword of Vengeance", "Trident of Fish Command", "Wand of Magic Detection", "Wand of Magic Missiles", "Wand of Secrets", "Wand of the War Mage +1", "Wand of Web", "Weapon +1", "Weapon of Warning", "Wind Fan", "Winged Boots", "Wraps of Unarmed Power +1"]
-const rareList = ["Ammunition +2", "Amulet of Health", "Armor +1", "Armor of Resistance", "Armor of Vulnerability", "Arrow-Catching Shield", "Bag of Beans", "Belt of Dwarvenkind", "Belt of Hill Giant Strength", "Berserker Axe", "Boots of Levitation", "Boots of Speed", "Bowl of Commanding Water Elementals", "Bracers of Defense", "Brazier of Commanding Fire Elementals", "Cape of the Mountebank", "Censer of Controlling Air Elementals", "Chime of Opening", "Cloak of Displacement", "Cloak of the Bat", "Cube of Force", "Cube of Summoning", "Daern's Instant Fortress", "Dagger of Venom", "Dimensional Shackles", "Dragon Slayer", "Elixir of Health", "Elven Chain", "Enspelled Armor Rare", "Rare Enspelled Staff", "Enspelled Weapon Rare", "Figurine of Wondrous Power Rare", "Flame Tongue", "Folding Boat", "Gem of Seeing", "Giant Slayer", "Glamoured Studded Leather", "Helm of Teleportation", "Heward's Handy Haversack", "Horn of Blasting", "Silver Horn of Valhalla", "Brass Horn of Valhalla", "Horseshoes of Speed", "Canaith Mandolin", "Cli Lyre", "Ioun Stone Rare", "Iron Bands of Bilarro", "Mace of Disruption", "Mace of Smiting", "Mace of Terror", "Mantle of Spell Resistance", "Necklace of Fireballs", "Necklace of Prayer Beads", "Oil of Etherealness", "Periapt of Proof against Poison", "Portable Hole", "Potion of Clairvoyance", "Potion of Diminution", "Potion of Gaseous Form", "Potion of Frost Giant Strength", "Potion of Stone Giant Strength", "Potion of Fire Giant Strength", "Potion of Heroism", "Potion of Invisibility", "Potion of Invulnerability", "Potion of Mind Reading", "Quaal's Feather Token Rare", "Ring of Animal Influence", "Ring of Evasion", "Ring of Feather Falling", "Ring of Free Action", "Ring of Protection", "Ring of Resistance", "Ring of Spell Storing", "Ring of the Ram", "Ring of X-ray Vision", "Robe of Eyes", "Rod of Rulership", "Rod of the Pact Keeper +2", "Rope of Entanglement", "Scroll of Protection", "Shield +2", "Shield of Missile Attraction", "Staff of Charming", "Staff of Swarming Insects", "Staff of the Woodlands", "Staff of Withering", "Stone of Controlling Earth Elementals", "Sun Blade", "Sword of Life Stealing", "Tentacle Rod", "Vicious Weapon", "Wand of Binding", "Wand of Enemy Detection", "Wand of Fear", "Wand of Fireballs", "Wand of Lightning Bolts", "Wand of Paralysis", "Wand of Wonder", "Weapon +2", "Wings of Flying"]
-const phenomenalList = ["Ammunition +3", "Ammunition of Slaying", "Amulet of the Planes", "Animated Shield", "Armor +2", "Bag of Devouring", "Belt of Frost Giant Strength", "Belt of Stone Giant Strength", "Belt of Fire Giant Strength", "Candle of Invocation", "Carpet of Flying", "Cauldron of Rebirth", "Cloak of Arachnida", "Crystal Ball", "Dancing Sword", "Demon Armor", "Dragon Scale Mail", "Dwarven Plate", "Dwarven Thrower", "Efreeti Bottle", "Energy Longbow", "Energy Shortbow", "Enspelled Armor Very Rare", "Enspelled Weapon Very Rare", "Executioner's Axe", "Obsidian Steed Figurine of Wondrous Power", "Frost Brand", "Hat of Many Spells", "Helm of Brilliance", "Bronze Horn of Valhalla", "Horseshoes of a Zephyr", "Ioun Stone Very Rare", "Lute of Thunderous Thumping", "Manual of Bodily Health", "Manual of Gainful Exercise", "Manual of Golems", "Manual of Quickness of Action", "Mirror of Life Trapping", "Nine Lives Stealer", "Nolzur's Marvelous Pigments", "Oathbow", "Oil of Sharpness", "Potion of Flying", "Potion of Cloud Giant Strength", "Potion of Greater Invisibility", "Potion of Longevity", "Potion of Speed", "Potion of Vitality", "Quarterstaff of the Acrobat", "Ring of Regeneration", "Ring of Shooting Stars", "Ring of Telekenisis", "Robe of Scintillating Colors", "Robe of Stars", "Rod of Absorption", "Rod of Alertness", "Rod of Security", "Rod of the Pact Keeper +3", "Scimitar of Speed", "Shield +3", "Shield of the Cavalier", "Spellguard Shield", "Spirit Board", "Staff of Fire", "Staff of Frost", "Staff of Power", "Staff of Striking", "Staff of Thunder and Lightning", "Sword of Sharpness", "Thunderous Greatclub", "Tome of Clear Thought", "Tome of Leadership and Influence", "Tome of Understanding", "Wand of Polymorph", "Weapon +3"]
-const legendaryList = ["Apparatus of Kwalish", "Armor +3", "Armor of Invulnerability", "Belt of Cloud Giant Strength", "Belt of Storm Giant Strength", "Cloak of Invisibility", "Crystal Ball of Mind Reading", "Crystal Ball of Telepathy", "Crystal Ball of True Seeing", "Cubic Gate", "Deck of Many Things", "Defender", "Efreeti Chain", "Enspelled Armor Legendary", "Legendary Enspelled Staff", "Enspelled Weapon Legendary", "Hammer of Thunderbolts", "Holy Avenger", "Ioun Stone of Greater Absorption", "Ioun Stone of Mastery", "Ioun Stone of Regeneration", "Iron Flask", "Luck Blade", "Moonblade", "Plate Armor of Etherealness", "Potion of Storm Giant Strength", "Ring of Djinni Summoning", "Ring of Elemental Command", "Ring of Invisibility", "Ring of Spell Turning", "Ring of Three Wishes", "Robe of the Archmagi", "Rod of Lordly Might", "Rod of Resurrection", "Scarab of Protection", "Scroll of Titan Summoning", "Sovereign Glue", "Sphere of Annihilation", "Staff of the Magi", "Sword of Answering", "Talisman of Pure Good", "Talisman of the Sphere", "Talisman of Ultimate Evil", "Tome of the Stilled Tongue", "Universal Solvent", "Well of Many Worlds"]
-const artifactList = ["Axe of the Dwarvish Lords", "Blackrazor", "Book of Exalted Deeds", "Book of Vile Darkness", "Demonomicon of Iggwilv", "Efreeti Chain", "Eye of Vecna", "Hand of Vecna", "Orb of Dragonkind", "Sword of Kas", "Wand of Orcus", "Wave", "Whelm"]
-
function getRandomInteger(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
@@ -27,20 +16,7 @@ function getRandom(seed) {
return x - Math.floor(x)
}
-function getRandomFromList(...choices) {
- return choices[getRandomInteger(0, choices.length - 1)]
-}
-
-function numberWithCommas(x) {
- return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
-}
-
-function isAnumber(number) {
- return !isNaN(number)
-}
-
function shuffle(array, seed) {
- if (seed == null) seed = getRandomInteger(Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER)
let currentIndex = array.length
while (currentIndex != 0) {
let randomIndex = Math.floor(getRandom(seed + currentIndex) * currentIndex)
@@ -338,105 +314,6 @@ function deleteCharacter(name) {
state.characters.splice(index, 1)
}
-function executeTurn(activeCharacter) {
- var activeCharacterName = toTitleCase(activeCharacter.name)
- var possessiveName = getPossessiveName(activeCharacter.name)
- if (possessiveName == "Your") possessiveName = "your"
-
- if (activeCharacter.className != null) {
- //player
- state.show = "none"
- return `\n[It is ${possessiveName} turn]\n`
- } else if (activeCharacter.ally == false) {
- //enemy
- var characters = state.characters.filter(x => x.health > 0)
- characters.push(...state.allies.filter(x => x.health > 0))
- var target = characters[getRandomInteger(0, characters.length - 1)]
- var areWord = target.name == "You" ? "are" : "is"
- var targetNameAdjustedCase = target.name == "You" ? "you" : toTitleCase(target.name)
- var attack = calculateRoll(`1d20${activeCharacter.hitModifier > 0 ? "+" + activeCharacter.hitModifier : activeCharacter.hitModifier < 0 ? activeCharacter.hitModifier : ""}`)
- var hit = attack >= target.ac
-
- var text = `\n[It is ${possessiveName} turn]\n`
- if (getRandomBoolean() || activeCharacter.spells.length == 0) {
- if (hit) {
- state.blockCharacter = target
- state.blockPreviousHealth = target.health
- var damage = isNaN(activeCharacter.damage) ? calculateRoll(activeCharacter.damage) : activeCharacter.damage
- target.health = Math.max(target.health - damage, 0)
-
- text += `\n[Character AC: ${target.ac} Attack roll: ${attack}]\n`
-
- text += `${activeCharacterName} attacks ${targetNameAdjustedCase} for ${damage} damage!\n`
- if (target.health == 0) text += ` ${toTitleCase(target.name)} ${areWord} unconscious! \n`
- else text += ` ${toTitleCase(target.name)} ${areWord} at ${target.health} health.\n`
- } else text += `${activeCharacterName} attacks ${targetNameAdjustedCase} but misses!\n`
- } else {
- var spell = activeCharacter.spells[getRandomInteger(0, activeCharacter.spells.length - 1)]
- var diceMatches = spell.match(/(?<=^.*)\d*d\d+((\+|-)\d+)?$/gi)
- if (diceMatches == null) text += `${activeCharacterName} casts spell ${spell}!`
- else {
- var spell = spell.substring(0, spell.length - diceMatches[0].length)
- if (hit) {
- var damage = calculateRoll(diceMatches[0])
- target.health = Math.max(target.health - damage, 0)
-
- text += `\n[Character AC: ${target.ac} Attack roll: ${attack}]\n`
-
- text += `${activeCharacterName} casts spell ${spell} at ${targetNameAdjustedCase} for ${damage} damage!`
-
- if (target.health == 0) text += ` ${toTitleCase(target.name)} ${areWord} unconscious!\n`
- else text += ` ${toTitleCase(target.name)} ${areWord} at ${target.health} health.\n`
- } else text += `${activeCharacterName} casts spell ${spell} at ${targetNameAdjustedCase} but misses!\n`
- }
- }
- return text
- } else {
- //ally
- var enemies = state.enemies.filter(x => x.health > 0)
- var target = enemies[getRandomInteger(0, enemies.length - 1)]
- var areWord = target.name == "You" ? "are" : "is"
- var targetNameAdjustedCase = target.name == "You" ? "you" : toTitleCase(target.name)
- var attack = calculateRoll(`1d20${activeCharacter.hitModifier > 0 ? "+" + activeCharacter.hitModifier : activeCharacter.hitModifier < 0 ? activeCharacter.hitModifier : ""}`)
- var hit = attack >= target.ac
-
- var text = `\n[It is ${possessiveName} turn]\n`
- if (getRandomBoolean() || activeCharacter.spells.length == 0) {
- if (hit) {
- state.blockCharacter = target
- state.blockPreviousHealth = target.health
- var damage = isNaN(activeCharacter.damage) ? calculateRoll(activeCharacter.damage) : activeCharacter.damage
- target.health = Math.max(target.health - damage, 0)
-
- text += `\n[Enemy AC: ${target.ac} Attack roll: ${attack}]\n`
-
- text += `${activeCharacterName} attacks ${targetNameAdjustedCase} for ${damage} damage!\n`
- if (target.health == 0) text += ` ${toTitleCase(target.name)} ${areWord} unconscious! \n`
- else text += ` ${toTitleCase(target.name)} ${areWord} at ${target.health} health.\n`
- } else text += `${activeCharacterName} attacks ${targetNameAdjustedCase} but misses!\n`
- } else {
- var spell = activeCharacter.spells[getRandomInteger(0, activeCharacter.spells.length - 1)]
- var diceMatches = spell.match(/(?<=^.*)\d*d\d+((\+|-)\d+)?$/gi)
- if (diceMatches == null) text += `${activeCharacterName} casts spell ${spell}!`
- else {
- var spell = spell.substring(0, spell.length - diceMatches[0].length)
- if (hit) {
- var damage = calculateRoll(diceMatches[0])
- target.health = Math.max(target.health - damage, 0)
-
- text += `\n[Character AC: ${target.ac} Attack roll: ${attack}]\n`
-
- text += `${activeCharacterName} casts spell ${spell} at ${targetNameAdjustedCase} for ${damage} damage!`
-
- if (target.health == 0) text += ` ${toTitleCase(target.name)} ${areWord} unconscious!\n`
- else text += ` ${toTitleCase(target.name)} ${areWord} at ${target.health} health.\n`
- } else text += `${activeCharacterName} casts spell ${spell} at ${targetNameAdjustedCase} but misses!\n`
- }
- }
- return text
- }
-}
-
function createEncounter(listName) {
var encounter = {
text: "",
@@ -2223,284 +2100,449 @@ function createEncounter(listName) {
case "boss":
if (encounter.cr == null) encounter.cr = 13
multiplier = 1 + (encounter.cr - 13) / 10
- switch (getRandomInteger(0, 60)) {
+ switch (getRandomInteger(0, 99)) {
case 0:
- encounter.text = "The earth is opening up around you! A giant crack in the ground bursts wide and magma spews forth."
+ encounter.text = "Text"
break
case 1:
- encounter.text = "A statue with glowing blue eyes watches over you. Its intensity is ever increasing until it final begins to shoots beams of electrical energy at you."
+ encounter.text = "Text"
break
case 2:
- encounter.text = "You all ate bad mushrooms. This is terrible news because you're starting to have a bad trip. This is when you notice that you are not alone."
+ encounter.text = "Text"
break
case 3:
- encounter.text = "A massive rock slide! Watch out!! You only have moments to react."
+ encounter.text = "Text"
break
case 4:
- encounter.text = "You can't move! Your muscles are paralyzed. You can only move your eyes which dart around feverishly looking for the cause of this malady."
+ encounter.text = "Text"
break
case 5:
- encounter.text = "A series of blocks are laid out before you in a shallow, rectangular pit. A voice is heard in your head \"Solve it or die!\""
+ encounter.text = "Text"
break
case 6:
- encounter.text = "Suddenly, you are lifted high up into the air! An area of land extending around you by about 5 feet shoots up skyward. You notice other clumps of earth being levitated by the same magical force."
+ encounter.text = "Text"
break
case 7:
- encounter.text = "Your leg is caught on a wire. Viewing its path around your leg and across the ground, you see that it is connected to a contraption of some kind. It has the semblance of a bomb. It is very large and would spell certain doom for the party, if not the entire local area, if it were to explode."
+ encounter.text = "Text"
break
case 8:
- encounter.text = "The banner of a great king marks a field pockmarked with craters. Mines!"
+ encounter.text = "Text"
break
case 9:
- encounter.text = "Large rings descend around you. As you try to escape them, you relize that an unseeable forcefield prevents you from escaping. You're trapped!"
+ encounter.text = "Text"
break
case 10:
- encounter.text = "A satyr stands before you and offers you a treat. You see an array of pots and pans and the likeness of an unkempt kitchen. Something alludes you about this situation. Something sinister."
+ encounter.text = "Text"
break
case 11:
- encounter.text = "A ruined statue dedicated to the goddess of magic, Mystra, is here. It is split in two with the torso laying haphazardly to the side. You sense a faint voice coming from within it."
+ encounter.text = "Text"
break
case 12:
- encounter.text = "A shrine dedicated to Shar, goddess of darkness, stands tall before you. A challenge is posted on a brass placard attached to the podium."
+ encounter.text = "Text"
break
case 14:
- encounter.text = "An offering plate named for Waukeen, the goddess of trade, is placed on an ornate, golden pedestal. It seems to beckon for you to make a trade."
+ encounter.text = "Text"
break
case 15:
- encounter.text = "You close your eyes only to find yourself transported to a cavern lined with skulls and mounds of the dead. In it: a leatherbound tome with a symbol of the Spider Queen, Lolth."
+ encounter.text = "Text"
break
case 16:
- encounter.text = "A crown made of bone and sinew rests on the skull of a dead king. You have the uncontrollable urge to place it upon your head."
+ encounter.text = "Text"
break
case 17:
- encounter.text = "A cosmic display of lights and magic play before you. A great power is being held here, trapped by the gravity of some celestial object."
+ encounter.text = "Text"
break
case 18:
- encounter.text = "You finally see it. A dark figure at your side. It seems like it has always been there, and yet you never noticed it. It poses a question to you: more like a riddle than anything else. You are compelled to answer or face existential consequences."
+ encounter.text = "Text"
break
case 19:
- encounter.text = "A sigil of a great house is emblazoned into a wall ahead of you. Underneath, it commands, \"Name your champion\" The walls begin to shudder violently."
+ encounter.text = "Text"
break
case 20:
- encounter.text = "A group of corpses are gathered in the center here. Their appearance shares an uncanny resemblance to your own. Never minding that, you notice that they have incredible weapons and artifacts amongst their bodies."
+ encounter.text = "Text"
break
case 21:
- encounter.text = "You see what could only be the giant tooth of a dragon. You hesitate from touching it, knowing that something as valuable as this wouldn't be so carelessly left behind in this manner."
+ encounter.text = "Text"
break
case 22:
- encounter.text = "Lord Gond smiles upon you. Choose a boon for a single item in your inventory."
+ encounter.text = "Text"
break
case 23:
- encounter.text = "You have displeased the god Bhaal, the god of murder. He demands a sacrifice as recompense."
+ encounter.text = "Text"
break
case 24:
- encounter.text = "A blank scroll, unblemished by its surroundings, is drawn open before you. An ink pen floats to its side. Will you add your name to it?"
+ encounter.text = "Text"
break
case 25:
- encounter.text = "Your feet are sinking into the floor. The false surface must have been an elaborate illusion. You must act quickly before you are completely engulfed by what lies below."
+ encounter.text = "Text"
break
case 26:
- encounter.text = "Chief among your concerns is the taste of the air which has suddenly turned sour. Poison gas!"
+ encounter.text = "Text"
break
case 27:
- encounter.text = "You can sense that this place has some greater importance. Magical leylines meet here. In this place of power, anything is possible."
+ encounter.text = "Text"
break
case 28:
- encounter.text = "A mummified monkey's paw resides here. Three fingers are extended outward. According to lore, such a powerful item can grant you three wishes. However, you should be wary of what you wish for..."
+ encounter.text = "Text"
break
case 29:
- encounter.text = "A small bell attached to a simple, wooden handle can be found here. What does it do? Or should you be afraid of what it may summon?"
+ encounter.text = "Text"
break
case 30:
- encounter.text = "A pristine wand is held in a glass case. A blue aura surrounds it. It is clear that it is protected by practical and magical means, but why?"
+ encounter.text = "Text"
break
case 31:
- encounter.text = "An adult black dragon approaches. You are not ready for this."
- encounter.enemies = [
- createEnemy("Adult Black Dragon", calculateRoll("17d12+85"), 19, 11, "6d6+18", "d20+2", "Acid Breath12d8", "Frightful Presence", "Wing Attack2d6+6")
- ]
+ encounter.text = "Text"
break
case 32:
- encounter.text = "An adult brass dragon is here. It's pissed!"
- encounter.enemies = [
- createEnemy("Adult Brass Dragon", calculateRoll("15d12+75"), 18, 11, "6d6+18", "d20", "Fire Breath13d6", "Sleep Breath", "Frightful Presence", "Wing Attack2d6+6")
- ]
+ encounter.text = "Text"
break
case 33:
- encounter.text = "An adult bronze dragon is charging up for an attack!"
- encounter.enemies = [
- createEnemy("Adult Bronze Dragon", calculateRoll("17d12+102"), 19, 12, "6d6+21", "d20", "Repulsion Breath", "Lightning Breath12d10", "Wing Attack2d6+6")
- ]
+ encounter.text = "Text"
break
case 34:
- encounter.text = "An adult copper dragon shifts its gaze at you. You are doomed."
- encounter.enemies = [
- createEnemy("Adult Copper Dragon", calculateRoll("16d12+80"), 18, 11, "6d6+18", "d20+1", "Acid Breath12d8", "Slowing Breath", "Wing Attack2d6+6")
- ]
+ encounter.text = "Text"
break
case 35:
- encounter.text = "An adult green dragon is nesting here. You really shouldn't have disturbed it."
- encounter.enemies = [
- createEnemy("Adult Green Dragon", calculateRoll("18d12+90"), 19, 11, "6d6+18", "d20+1", "Poison Breath16d6")
- ]
+ encounter.text = "Text"
break
case 36:
- encounter.text = "An adult white dragon is here. The wrong place and wrong time, unfortunately for you."
- encounter.enemies = [
- createEnemy("Adult White Dragon", calculateRoll("16d12+96"), 18, 11, "6d6+18", "d20", "Cold Breath12d8", "Wing Attack2d6+6")
- ]
+ encounter.text = "Text"
break
case 37:
- encounter.text = "The arch mage descends from the high altar. He says no words, but raises his hands as if he is going to prepare a spell. Get ready!"
- encounter.enemies = [
- createEnemy("Arch Mage", calculateRoll("18d8+18"), 12, 4, "1d4+2", "d20+14", "Time Stop", "Globe of Invulnerability", "Lightning Bolt8d6", "Banishment", "Cone of Cold8d8", "Teleport"),
- createEnemy("Disciple A", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Ice Storm4d6+8", "Fireball8d6", "Mage Armor", "Fire Bolt1d10"),
- createEnemy("Disciple B", calculateRoll("9d8"), 12, 5, "1d4+2", "d20+2", "Ice Storm4d6+8", "Fireball8d6", "Mage Armor", "Fire Bolt1d10")
- ]
+ encounter.text = "Text"
break
case 38:
- encounter.text = "The djinni mocks you as you enter its domain. It seems like it wants to pick a fight with you."
- encounter.enemies = [
- createEnemy("Djinni", calculateRoll("14d10+84"), 17, 9, "2d6+8", "d20+2")
- ]
+ encounter.text = "Text"
break
case 39:
- encounter.text = "They are as beautiful as they are wicked. The Erinyes approach with cruel intentions. Their winged visages come into clear view."
- encounter.enemies = [
- createEnemy("Erinyes A", calculateRoll("18d8+72"), 18, 8, "1d10+4", "d20+3"),
- createEnemy("Erinyes B", calculateRoll("18d8+72"), 18, 8, "1d10+4", "d20+3"),
- createEnemy("Erinyes C", calculateRoll("18d8+72"), 18, 8, "1d10+4", "d20+3")
- ]
+ encounter.text = "Text"
break
case 40:
- encounter.text = "The horned devil stalks the land before you. It grins, revealing a hideous set of teeth."
- encounter.enemies = [
- createEnemy("Horned Devil", calculateRoll("17d10+85"), 18, 10, "6d8+18", "d20+3", "Hurl Flame4d6")
- ]
+ encounter.text = "Text"
break
case 41:
- encounter.text = "The temperature of the local area has cooled significantly. You see it now: an ice devil makes its presence known."
- encounter.enemies = [
- createEnemy("Ice Devil", calculateRoll("19d10+76"), 18, 10, "6d4+15", "d20+2", "Wall of Ice")
- ]
+ encounter.text = "Text"
break
case 42:
- encounter.text = "The mummy lord resides here. It has summoned its followers and directs the assault with his decayed finger pointed at you."
- encounter.enemies = [
- createEnemy("Mummy Lord", calculateRoll("13d8+39"), 17, 9, "3d6+4", "d20", "Hold Person", "Silence", "Harm14d6", "Blinding Dust", "Whirlwind of Sand"),
- createEnemy("Mummy A", calculateRoll("9d8+18"), 11, 5, "2d6+3", "d20-1"),
- createEnemy("Mummy B", calculateRoll("9d8+18"), 11, 5, "2d6+3", "d20-1"),
- createEnemy("Mummy C", calculateRoll("9d8+18"), 11, 5, "2d6+3", "d20-1")
- ]
+ encounter.text = "Text"
break
case 43:
- encounter.text = "The signs are clear: mounds of disturbed earth, pools of slime, and the digested remains of those foolish to face the creature. Yes, it's the Purple Worm. The earth rumbles, announcing its entry into the fray."
- encounter.enemies = [
- createEnemy("Purple Worm", calculateRoll("15d20+90"), 18, 14, "6d6+18", "d20-2", "Tail Stinger12d6+19")
- ]
+ encounter.text = "Text"
break
case 44:
- encounter.text = "The only way to describe it is that it's a twisted combination of a dragon and a giant millipede. The Remorhaz makes a sickening path through the debris toward you."
- encounter.enemies = [
- createEnemy("Remorhaz", calculateRoll("17d12+85"), 17, 11, "6d10+7", "d20+1", "Swallow6d6")
- ]
+ encounter.text = "Text"
break
case 45:
- encounter.text = "The storm giant pays little heed toward you. Yet, you are in its path. Hearing the sudden crack of lightning jolts you."
- encounter.enemies = [
- createEnemy("Storm Giant", calculateRoll("20d12+100"), 16, 14, "12d6+18", "d20+2", "Control Weather", "Lightning Strike12d8")
- ]
+ encounter.text = "Text"
break
case 46:
- encounter.text = "You have entered the realm of a powerful vampire. Its coven is poised to strike!"
- encounter.enemies = [
- createEnemy("Vampire", calculateRoll("17d8+68"), 16, 9, "3d8+8", "d20+4", "Charm", "Shape Change"),
- createEnemy("Vampire Spawn", calculateRoll("11d8+33"), 15, 6, "2d4+3", "d20+3", "Bite3d6+3"),
- createEnemy("Vampire Spawn", calculateRoll("11d8+33"), 15, 6, "2d4+3", "d20+3", "Bite3d6+3")
- ]
+ encounter.text = "Text"
break
case 47:
- encounter.text = "The hulking mass of the Behir enters the scene. It rears up revealing its many clawed feet. The tail whips around haphazardly throwing rubble around like they were pebbles."
- encounter.enemies = [
- createEnemy("Behir", calculateRoll("16d12+64"), 17, 10, "5d10+12", "d20+3", "Lightning Breath12d10", "Swallow6d6", "Constrict2d10+6")
- ]
+ encounter.text = "Text"
break
case 48:
- encounter.text = "The efreeti are the genies of the elemental fire plane. One such warrior is here and is bent on exacting revenge on some forgotten slight."
- encounter.enemies = [
- createEnemy("Efreeti", calculateRoll("16d10+112"), 17, 10, "4d6+12", "d20+1", "Hurl Flame5d6")
- ]
+ encounter.text = "Text"
break
case 49:
- encounter.text = "The nalfeshnee are winged demons that are like a cross between an ape and a boar. This one angles its terrible snout you and makes a menacing grunt."
- encounter.enemies = [
- createEnemy("Nalfeshnee", calculateRoll("16d10+96"), 18, 10, "8d6+5", "d20", "Horror Nimbus")
- ]
+ encounter.text = "Text"
break
case 50:
- encounter.text = "The roc attacks! This gargantuan bird swoops in and engulfs the combat area with its awesome wings."
- encounter.enemies = [
- createEnemy("Roc", calculateRoll("16d20+80"), 15, 13, "8d8+18", "d20")
- ]
+ encounter.text = "Text"
break
case 51:
- encounter.text = "It wasn't your imagination. It moved. You swear it. Indeed, the animated statue is poised for attack!"
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Animated Statue", calculateRoll("10d12+20"), 17, 7, "2d10+4", "d20-2")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 52:
- encounter.text = "You've never seen the undead quite like this. The bone claw raises its outrageous talons. It is going to strike at any moment!"
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Boneclaw", calculateRoll("17d10+34"), 16, 8, "6d10+8", "d20+3", "Shadow Jump5d12+2", "Deadly Reach")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 53:
- encounter.text = "A werewolf is already a formidable opponent. The deathwolf is the ungodly undead version of that. It bears down on you with great ill intent."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Deathwolf", calculateRoll("18d8+72"), 15, 10, "6d8+15", "d20+3", "Phantom Deathwolf6d6")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 54:
- encounter.text = "A drow inquisitor is here. She expected you to come this way. You're going to pay for that mistake."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Drow Inquisitor", calculateRoll("23d8+46"), 16, 10, "12d8+24", "d20+2", "Spectral Dagger1d8+5")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 55:
- encounter.text = "This is proof that fate has it in for you. You somehow have stumbled upon the chamber of an Elder Brain. It rises out of its brine pool to summon its minions."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Elder Brain", calculateRoll("20d10+100"), 10, 7, "5d8+7", "d20", "Mind Blast5d10+5")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 56:
- encounter.text = "You see the Jabberwock. It's a horrific creature, born of pure hatred and evil. It has the wings of a dragon, but crawls around on four legs like a bastard insect."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Jabberwock", calculateRoll("10d12+50"), 18, 10, "6d10+10", "d20+1", "Regenderation")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 57:
- encounter.text = "Acid drips onto the floor from its nasty maw. The massive megapede marks its territory with the bodies of those foolish enough to challenge it. You may count yourself among them in short order."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Megapede", calculateRoll("13d20+39"), 15, 10, "6d10+12", "d20", "LifeDrain3d10", "Psychic Bomb5d8")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 58:
- encounter.text = "The skull lord holds dominion over this lair. It turns its three heads for you to plainly see its horrific visage."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Skull Lord", calculateRoll("15d8+45"), 18, 8, "24d6", "d20+3", "Deathly Ray5d8+5")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 59:
- encounter.text = "The zikran has the blood of genies and their power too. It harnesses the power of water. Pools of which gather at its feet. It's ready to defend itself."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Zikran", calculateRoll("18d8+18"), 12, 6, "1d4+2", "d20+2", "Time Stop", "Mind Blank", "Cone of Cold 8d8", "Lightning Bolt 8d6")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 60:
- encounter.text = "You have never seen a monstrosity of this magnitude. The eight legs of the spider dragon crash into the ground one after the other like a symphony of massive hammers. This is the fight of your life."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Spider Dragon", calculateRoll("15d10+5"), 23, 9, "3d12+12", "d20+8", "Silk Spit", "Spider Breath7d10")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 61:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 62:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 63:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 64:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 65:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 66:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 67:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 68:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 69:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 70:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 71:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 72:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 73:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 74:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 75:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 76:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 77:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 78:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 79:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 80:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 81:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 82:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 83:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 84:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 85:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 86:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 87:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 88:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 89:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 90:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 91:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 92:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 93:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 94:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 95:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 96:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 97:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 98:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 99:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
}
@@ -2508,275 +2550,449 @@ function createEncounter(listName) {
case "god":
if (encounter.cr == null) encounter.cr = 17
multiplier = 1 + (encounter.cr - 17) / 10
- switch (getRandomInteger(0, 60)) {
+ switch (getRandomInteger(0, 99)) {
case 0:
- encounter.text = "You have discovered a tunnel encased entirely with geodes and crystals of great value!"
+ encounter.text = "Text"
break
case 1:
- encounter.text = "A mighty steed is spotted here. As it breathes, it exhales flames from its nostrils. Despite this, it looks friendly and comes up to you with a slightly tilted head. It presses against your arm cautiously."
+ encounter.text = "Text"
break
case 2:
- encounter.text = "A thought enters your mind. A thought that no man should know. A thought that could change the world. And yet, it starts to escape you as fast as it came to you."
+ encounter.text = "Text"
break
case 3:
- encounter.text = "The grand master of the martial way stands before you. He will teach you one technique, but it will cost you dearly. A cost that is worth much more than mere coin."
+ encounter.text = "Text"
break
case 4:
- encounter.text = "An exceptional weapon is presented to you by one claiming to be your loyal servant. It lowers its eyes as you approach. \"Master, I do not aim to offend with such a paltry gift, but it is all I have.\""
+ encounter.text = "Text"
break
case 5:
- encounter.text = "A portal is spotted. Through it, you see a vast library with shelves and shelves of books with no end. It is a truly spectacular sight."
+ encounter.text = "Text"
break
case 6:
- encounter.text = "The way ahead is covered with fine sand. It shifts and twists, indicating that something resides underneath."
+ encounter.text = "Text"
break
case 7:
- encounter.text = "Molten hot magma leaks through the walls of this place. You hear a sudden cracking and all hell breaks loose."
+ encounter.text = "Text"
break
case 8:
- encounter.text = "Whatever is entombed here must be significantly important because there are various traps of unusual complexity laid through the path before you."
+ encounter.text = "Text"
break
case 9:
- encounter.text = "Giant axes swing like pendulums. Each blocks your way and can destroy you in a single blow."
+ encounter.text = "Text"
break
case 10:
- encounter.text = "A giant rock face is in front of you. It must be traversed in order for you to proceed. Unfortunately, the hand holds are incredibly unstable and will not hold your weight for long."
+ encounter.text = "Text"
break
case 11:
- encounter.text = "A bridge in disrepair is before you. It was sabotaged to prevent passage through this way, but you must get through. There planks placed intermitentally across its length. You imagine that it could be crossed successfully with some focus and a lot of luck."
+ encounter.text = "Text"
break
case 12:
- encounter.text = "\"You must choose wisely.\" An old sage presents two cups on a table before you. Their mouths are faced downward, hiding their contents. You can tell something is wrong from his snickering, mocking lips."
+ encounter.text = "Text"
break
case 14:
- encounter.text = "An elaborate illusion is here, hiding the way forward. Pressing ahead cautiously with your foot, you can tell that there is a significant drop where the illusion is. Safe passage is obscured from your senses."
+ encounter.text = "Text"
break
case 15:
- encounter.text = "A set of giant scales are ahead. Ball bearings of immense size are all placed on one side of the scale. They are too heavy to move by any normal person's strength. Suddenly, the walls start pushing in."
+ encounter.text = "Text"
break
case 16:
- encounter.text = "Death is playing with a set of dice. He looks bored. Then he suddenly notices you..."
+ encounter.text = "Text"
break
case 17:
- encounter.text = "A tablet is here. Written in large text, it seems to be the words to a curse or spell of some sort. At first glance, it makes no sense to you. Then you realize that each line is written in a differnt language found in the known world. That is when you started to smell the gas..."
+ encounter.text = "Text"
break
case 18:
- encounter.text = "It's starting to rain, but you realize that this is no normal rain. Each drop sizzles as it strikes an object. Acid! It is truly the end times."
+ encounter.text = "Text"
break
case 19:
- encounter.text = "The flesh wall demands a sacrifice! It quivers at you. \"Feeeeeeeed meeeeeeeeee\""
+ encounter.text = "Text"
break
case 20:
- encounter.text = "A giant hedge maze! You do not wish to participate, however you notice a mystical blue flame follow your path. It does not move swiftly, but it does block the way back. You have no choice but to play this game."
+ encounter.text = "Text"
break
case 21:
- encounter.text = "Bottles of an illuminated, golden liquid are strung up in something like a wire lattice. Upon further investigation, you can see that they are all interconnected and may fall easily if disturbed. It's too bad because this is the only way out."
+ encounter.text = "Text"
break
case 22:
- encounter.text = "You keep walking forward, but the door ahead seems to remain ever distant. This goes on for awhile, so you assume something magical is at play here."
+ encounter.text = "Text"
break
case 23:
- encounter.text = "\"Stop the rabbits! They're getting away!\" You hear somebody yell, but it's too late. Your party is surrounded by rabbits just hopping around aimlessly. Humorous at first, but it becomes dangerous as the number of rabbits increase to an unforseen number."
+ encounter.text = "Text"
break
case 24:
- encounter.text = "All your actions are being judged by a mysterious figure sitting atop a dark throne. Only the sounds of disappointment can be heard as the shady figure flips through the pages of your exploits."
+ encounter.text = "Text"
break
case 25:
- encounter.text = "You see a mirror, however its image is not a reflection of you but that of your past and possible futures."
+ encounter.text = "Text"
break
case 26:
- encounter.text = "The sphinx asks you the unsolvable riddle. Your life hangs in the balance as you try to interpret its words."
+ encounter.text = "Text"
break
case 27:
- encounter.text = "Your vision begins to blacken. All noises are muted. Your senses are ripped from you. You are completely cut off from the world. You are now trapped in the void."
+ encounter.text = "Text"
break
case 28:
- encounter.text = "A pit seemingly with no bottom can be seen here. You look back: the entrance to this pace is gone! What will you do?"
+ encounter.text = "Text"
break
case 29:
- encounter.text = "A test of faith. Prove your worth or be struck down by the power of the gods!"
+ encounter.text = "Text"
break
case 30:
- encounter.text = "A sea of bones. You try to step in, but your feet have no purchase."
+ encounter.text = "Text"
break
case 31:
- encounter.text = "The adult blue dragon claws at the ground just before it. It's bored. It has decided that you will be its new play thing."
- encounter.enemies = [
- createEnemy("Adult Blue Dragon", calculateRoll("18d12+108"), 19, 12, "6d10+21", "d20", "Lightning Breath12d10", "Wing Attack2d6+7")
- ]
+ encounter.text = "Text"
break
case 32:
- encounter.text = "The gold dragon has deemed you unworthy. Prove it wrong."
- encounter.enemies = [
- createEnemy("Adult Gold Dragon", calculateRoll("19d12+133"), 19, 12, "6d10+21", "d20+2", "Fire Breath 12d10", "Weakening Breath")
- ]
+ encounter.text = "Text"
break
case 33:
- encounter.text = "The adult red dragon has broken its chains and now stands before you. Whatever events have ocurred to bring it to this place, it places the blame on you for its centuries of torture."
- encounter.enemies = [
- createEnemy("Adult Red Dragon", calculateRoll("19d12+133"), 19, 12, "6d10+21", "d20", "Fire Breath18d6", "Wing Attack2d6+8")
- ]
+ encounter.text = "Text"
break
case 34:
- encounter.text = "You didn't do anything wrong. The adult silver dragon just doesn't like you."
- encounter.enemies = [
- createEnemy("Adult Silver Dragon", calculateRoll("18d12+126"), 19, 13, "6d10+24", "d20", "Cold Breath13d8", "Paralyzing Breath")
- ]
+ encounter.text = "Text"
break
case 35:
- encounter.text = "The ancient black dragon goads you into a fight."
- encounter.enemies = [
- createEnemy("Ancient Black Dragon", calculateRoll("21d20+147"), 22, 15, "6d10+24", "d20+2", "Acid Breath15d8", "Wing Attack2d6+8")
- ]
+ encounter.text = "Text"
break
case 36:
- encounter.text = "From the ashes, the phoenix!"
- encounter.enemies = [
- createEnemy("Phoenix", calculateRoll("10d20+70"), 18, 13, "4d6+16", "d20+8", "Fiery Talons4d8+16", "Swoop4d8+16")
- ]
+ encounter.text = "Text"
break
case 37:
- encounter.text = "The demogorgon is quite the curiosity. Your wish to study its biology is stymied by the fact that your life is in jeapodary."
- encounter.enemies = [
- createEnemy("Demogorgon", calculateRoll("32d12+256"), 22, 17, "6d12+18", "d20+2", "Beguiling Gaze", "Hypnotic Gaze")
- ]
+ encounter.text = "Text"
break
case 38:
- encounter.text = "You stand there, mouth agape trying to understand it. You can't. The cosmic horror attacks!"
- encounter.enemies = [
- createEnemy("Cosmic Horror", calculateRoll("16d20+112"), 15, 14, "6d6+16", "d20", "Poison Jet4d6", "Psychic Whispers6d10")
- ]
- break
- case 39:
- encounter.text = "The ancient red dragon has lived to see entire civilizations rise and fall. You are nothing in its presence."
- encounter.enemies = [
- createEnemy("Ancient Red Dragon", calculateRoll("21d20+147"), 22, 15, "6d8+30", "d20+2", "Fire Breath26d6", "Wing Attack2d6+10")
- ]
+ encounter.text = "Text"
+ break
+ case 39:
+ encounter.text = "Text"
break
case 40:
- encounter.text = "The ancient gold dragon holds on to a dark secret that jeopordizes everything that you know. Perhaps it will impart its knowledge onto you once you prove you're worthy. Many have tried and failed as evidenced by the bones laid asunder."
- encounter.enemies = [
- createEnemy("Ancient Gold Dragon", calculateRoll("28d20+252"), 22, 15, "6d10+24", "d20+2", "Fire Breath13d10", "Weakening Breath")
- ]
+ encounter.text = "Text"
break
case 41:
- encounter.text = "Zariel, the arch duchess of Avernus, stands at the ready. You have displeased her and now she will exact her revenge on you personally. Burning crown above her head and wings red like fire, she is ready for you."
- encounter.enemies = [
- createEnemy("Zariel", calculateRoll("40d10+360"), 21, 16, "4d8+16", "d20+7", "Horrid Touch8d10", "Immolating Gaze4d10", "Teleport")
- ]
+ encounter.text = "Text"
break
case 42:
- encounter.text = "Bael emerges from the deepest pits of the nine hells. You are stricken by his warrior-like, bovine appearance. A wicked grin betrays his truly diabolical plans for you."
- encounter.enemies = [
- createEnemy("Bael", calculateRoll("18d10+90"), 18, 13, "4d8+27", "d20+3", "Awaken Greed", "Teleport", "Regenerate", "Inflict Wounds4d8+27", "Invisibility")
- ]
+ encounter.text = "Text"
break
case 43:
- encounter.text = "The demon lord of the abyss, Baphomet, has waited eons for his chance to lead his assault onto the material world. He does not see you as a threat. Only an inconvenience."
- encounter.enemies = [
- createEnemy("Baphomet", calculateRoll("22d12+176"), 22, 17, "3d10+30", "d20+2", "Curse of Brutality", "Desecration Breath20d8", "Gouging Toss2d8", "Raise Labyrinth")
- ]
+ encounter.text = "Text"
break
case 44:
- encounter.text = "You're miles from the sea and yet its here: the leviathan. It charges at you across the water with full force!"
- encounter.enemies = [
- createEnemy("Leviathan", calculateRoll("16d20+160"), 17, 16, "4d10+40", "d20+7", "Tidal Wave6d10")
- ]
+ encounter.text = "Text"
break
case 45:
- encounter.text = "Dripping. Disgusting. You are acosted by the flesh colossus! All is lost."
- encounter.enemies = [
- createEnemy("Flesh Colossus", calculateRoll("16d20+112"), 14, 13, "6d6+14", "d20-1", "Elemental Breath9d8")
- ]
+ encounter.text = "Text"
break
case 46:
- encounter.text = "You've never seen a creature as beautiful or as regal as the androsphinx. Unfortunately, it regards you with disdain."
- encounter.enemies = [
- createEnemy("Androsphinx", calculateRoll("19d10+95"), 17, 12, "4d10+12", "d20", "Flame Strike8d6", "Roar", "Teleport")
- ]
+ encounter.text = "Text"
break
case 47:
- encounter.text = "Balor is a fiend. A huge, demonic fiend bent on destroying you and all you represent."
- encounter.enemies = [
- createEnemy("Balor", calculateRoll("21d12+136"), 19, 14, "6d8+16", "d20+2", "Fire Whip 5d6+8", "Teleport")
- ]
+ encounter.text = "Text"
break
case 48:
- encounter.text = "There is no explaining how you are face to face with a Dragon Turtle and yet... here you are. Fight!"
- encounter.enemies = [
- createEnemy("Dragon Turtle", calculateRoll("22d20+110"), 20, 13, "6d8+21", "d20", "Steam Breath15d6")
- ]
+ encounter.text = "Text"
break
case 49:
- encounter.text = "An echoing boom reverberates across the area, shaking you to your core. The Iron Golem is activated."
- encounter.enemies = [
- createEnemy("Iron Golem", calculateRoll("20d10+100"), 20, 13, "6d8+14", "d20=1", "Poison Breath10d8", "Slam3d8+7")
- ]
+ encounter.text = "Text"
break
case 50:
- encounter.text = "A lake with unkown depths is before you. The kraken's lair. It emerges, ready to strike out at you."
- encounter.enemies = [
- createEnemy("Kraken", calculateRoll("27d20+189"), 18, 17, "9d6+30", "d20", "Lightning Storm12d10", "Ink Cloud3d10", "Fling1d6")
- ]
+ encounter.text = "Text"
break
case 51:
- encounter.text = "The lich commands an incredible army of the dead. Strike now for the good of the realm!"
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Lich", calculateRoll("18d8+54"), 17, 12, "3d6", "Acid Arrow4d4", "Fireball8d6", "Dimension Door", "Animate Dead", "Ray of Frost3d8", "Disrupt Life6d6", "Frightening Gaze", "Paralyzing Touch")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 52:
- encounter.text = "So many arms. The marilith slithers into view, waving its longswords all around."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Marilith", calculateRoll("18d10+90"), 18, 9, "12d8+24", "d20+5", "Teleport", "Parry")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 53:
- encounter.text = "The pit fiend is protecting something of great value. Put that out of your mind because you should be preparing for one hell of a fight."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Pit Fiend", calculateRoll("24d10+168"), 19, 14, "8d8+32", "d20+2", "Fireball8d6", "Wall of Fire")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 54:
- encounter.text = "The planetar is a celestial in true form. This one, fallen and disgraced, shall now vanquish you in the name of some forgotten god."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Planetar", calculateRoll("16d10+112"), 19, 12, "4d6+7", "d20+5", "Insect Plague4d10", "Blade Barrier", )
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 55:
- encounter.text = "Angelic is the least you can say about the solar. Beautiful, powerful. All fear the mighty solar!"
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Solar", calculateRoll("18d10+144"), 21, 15, "8d6+16", "d20+6", "Flying Sword", "Searing Burst8d6", "Blinding Gaze")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 56:
- encounter.text = "The tarrasque laid dormant for unknowable eons. Your arrival, however, triggered a series of events leading to its awakening. Its massive form stirs, sending the earth crumbling before you."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Tarrasque", calculateRoll("33d20+330"), 25, 19, "20d8+50", "d20")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 57:
- encounter.text = "The bore worm is much like the purple worm, yet it is much more dangerous. A construct made of nigh unbreakable metals, its singular goal is clear: your destruction."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Bore Worm", calculateRoll("15d20+90"), 18, 9, "6d8+18", "d20-2")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 58:
- encounter.text = "The raeleus decides to finally make his presence known. He's been watching. Waiting. This half human, half zebra amalgamation is quite the inventor. It employs its grand arsenal on you. Run or fight, he's going to get you."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Raeleus", calculateRoll("19d12+190"), 22, 17, "6d6+10", "d20+5", "Musket Blast6d10+10", "Auto Pistolero10d6", "Canister Grenada4d10", "Stun Grenada", "Magic Chaff Grenada")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 59:
- encounter.text = "The death knight is not one to toil with. It's too late for you, but consider this a warning to the next group of fools who think they could stand toe to toe with this undead warrior."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Death Knight", calculateRoll("19d8+95"), 20, 11, "3d8+15", "d20+2", "Hellfire Orb10d6", "Parry", "Destructive Wave5d6")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
case 60:
- encounter.text = "This is getting out of hand. Demons, monsters, and now the Drow Matron Mother is on attack! She's a very powerful elven caster that commands a vast network of fiends and slaves."
+ encounter.text = "Text"
encounter.enemies = [
- createEnemy("Drow Matron Mother", calculateRoll("35d8+105"), 17, 10, "2d6+8", "d20+4", "Levitate", "Plane Shift", "Gate", "Geas5d10", "Guardian of Faith", "Tentacle Rod3d6", "Summon Servant")
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 61:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 62:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 63:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 64:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 65:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 66:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 67:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 68:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 69:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 70:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 71:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 72:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 73:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 74:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 75:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 76:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 77:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 78:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 79:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 80:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 81:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 82:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 83:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 84:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 85:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 86:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 87:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 88:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 89:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 90:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 91:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 92:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 93:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 94:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 95:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 96:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 97:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 98:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
+ ]
+ break
+ case 99:
+ encounter.text = "Text"
+ encounter.enemies = [
+ createEnemy("name", calculateRoll("health"), ac, hitModifier, "damage", "initiative")
]
break
}
@@ -2821,26 +3037,11 @@ function createEnemy(name, health, ac, hitModifier, damage, initiative, ...spell
hitModifier: hitModifier,
damage: damage,
initiative: initiative,
- spells: spells,
- ally: false
+ spells: spells
}
return enemy
}
-function createAlly(name, health, ac, hitModifier, damage, initiative, ...spells) {
- var ally = {
- name: name,
- health: health,
- ac: ac,
- hitModifier: hitModifier,
- damage: damage,
- initiative: initiative,
- spells: spells,
- ally: true
- }
- return ally
-}
-
function getUniqueName(name) {
const letters = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
var letterIndex = 0
@@ -2868,11 +3069,6 @@ function createInitiativeOrder() {
state.initiativeOrder.push(enemy)
}
- for (var ally of state.allies) {
- if (ally.health <= 0) continue
- state.initiativeOrder.push(ally)
- }
-
state.initiativeOrder.sort(function(a, b) {
return b.calculatedInitiative - a.calculatedInitiative;
});
@@ -2929,941 +3125,7 @@ function getModifier(statValue) {
}
function findSpellCardIndex(name) {
- return storyCards.findIndex((element) => element.type == "spell" && element.title == name)
-}
-
-function findSpellCard(name) {
- return storyCards[findSpellCardIndex(name)]
-}
-
-function findItemCardIndex(name, storyCardName) {
- return storyCards.findIndex((element) => (element.type == "item" || element.type == "weapon" || element.type == "armor") && (element.title == name || element.title == storyCardName))
-}
-
-function findItemCard(name, storyCardName) {
- return storyCards[findItemCardIndex(name, storyCardName)]
-}
-
-function stragedyCalculateScores() {
- state.stragedyEnemyScore = 0
- state.stragedyPlayerScore = 0
- var playerHasJoker = false
- var enemyHasJoker = false
- var playerBlessedPoints = 0
- var enemyBlessedPoints = 0
- var doubledPoints = []
-
- //check for kings
- for(card of state.stragedyPlayerBattlefield) {
- var points = parseInt(card.match(/(?<=.*)\d+/gi)[0])
-
- if (card.includes("k")) {
- doubledPoints.push(points)
- }
- }
-
- for(card of state.stragedyEnemyBattlefield) {
- var points = parseInt(card.match(/(?<=.*)\d+/gi)[0])
-
- if (card.includes("k")) {
- doubledPoints.push(points)
- }
- }
-
- //enemy
- for(card of state.stragedyEnemyBattlefield) {
- var points = parseInt(card.match(/(?<=.*)\d+/gi)[0])
-
- if (doubledPoints.includes(points)) {
- points *= 2
- }
-
- if (card.includes("q")) {
- state.stragedyPlayerScore += points
- points = 0
- }
-
- if (card.includes("?")) {
- enemyHasJoker = true
- }
-
- if (card.includes("p")) {
- enemyBlessedPoints += points
- }
-
- state.stragedyEnemyScore += points
- }
-
- if (enemyHasJoker && state.stragedyEnemyScore < 30) {
- state.stragedyEnemyScore = 30
- } else if (state.stragedyEnemyScore > 30) {
- state.stragedyEnemyScore = Math.max(30, state.stragedyEnemyScore - enemyBlessedPoints)
- }
-
- //player
- for(card of state.stragedyPlayerBattlefield) {
- var points = parseInt(card.match(/(?<=.*)\d+/gi)[0])
-
- if (doubledPoints.includes(points)) {
- points *= 2
- }
-
- if (card.includes("q")) {
- state.stragedyEnemyScore += points
- points = 0
- }
-
- if (card.includes("?")) {
- playerHasJoker = true
- }
-
- if (card.includes("p")) {
- playerBlessedPoints += points
- }
-
- state.stragedyPlayerScore += points
- }
-
- if (playerHasJoker && state.stragedyPlayerScore < 30) {
- state.stragedyPlayerScore = 30
- } else if (state.stragedyPlayerScore > 30) {
- state.stragedyPlayerScore = Math.max(30, state.stragedyPlayerScore - playerBlessedPoints)
- }
-}
-
-function stragedyEnemyTurn() {
- state.stragedyEnemySkipTurn = false
- state.stragedyEnemyTurnText = ""
- if (state.stragedyPlayerScore > 30) {
- state.stragedyEnemyTurnText = null
- stragedyCheckForWin()
- state.stragedyTurn = "gameOver"
- return
- }
-
- var score = state.stragedyEnemyScore
- var hand = state.stragedyEnemyHand
- var deck = state.stragedyEnemyDeck
- var discard = state.stragedyEnemyDiscard
- var battlefield = state.stragedyEnemyBattlefield
- var playerScore = state.stragedyPlayerScore
- var playerHand = state.stragedyPlayerHand
- var playerDeck = state.stragedyPlayerDeck
- var playerDiscard = state.stragedyPlayerDiscard
- var playerBattlefield = state.stragedyPlayerBattlefield
- var playerRetired = state.stragedyPlayerRetired
- var kingCards = new Set()
-
- var hasJokerOnBattlefield = false
- for (var card of battlefield) {
- if (card.includes("?")) {
- hasJokerOnBattlefield = true
- break
- }
- }
-
- var battlefieldNumbersOnly = []
- for (var card of battlefield) {
- var value = parseInt(card.replaceAll(/\D/g, ""))
- if (value != null) battlefieldNumbersOnly.push(value)
- }
-
- var playerBattlefieldNumbersOnly = []
- for (var card of playerBattlefield) {
- var value = parseInt(card.replaceAll(/\D/g, ""))
- if (value != null) playerBattlefieldNumbersOnly.push(value)
- }
-
- for(var card of state.stragedyPlayerBattlefield) {
- if (card.includes("k")) {
- kingCards.add(card.match(/(?<=.*)\d+/gi)[0])
- }
- }
-
- for(var card of state.stragedyEnemyBattlefield) {
- if (card.includes("k")) {
- kingCards.add(card.match(/(?<=.*)\d+/gi)[0])
- }
- }
-
- var hasNumberedCards = hand.filter(x => /^\d+$/gi.test(x)).length > 0
-
- var sortedNumberedBattlefieldCards = battlefield.filter(x => /^[kpw\?]*\d+$/gi.test(x)).sort((a, b) => parseInt(a.replaceAll(/\D/gi, "")) - parseInt(b.replaceAll(/\D/gi, "")))
- var highestNumberedBattlefieldCard = sortedNumberedBattlefieldCards.length > 0 ? sortedNumberedBattlefieldCards[sortedNumberedBattlefieldCards.length - 1] : null
- var lowestNumberedBattlefieldCard = sortedNumberedBattlefieldCards.length > 0 ? sortedNumberedBattlefieldCards[0] : null
-
- var hasAce = hand.filter(x => /^.*a.*$/gi.test(x)).length > 0
- var hasJack = hand.filter(x => /^.*j.*$/gi.test(x)).length > 0
- var hasQueen = hand.filter(x => /^.*q.*$/gi.test(x)).length > 0
- var hasKing = hand.filter(x => /^.*k.*$/gi.test(x)).length > 0
- var hasJoker = hand.filter(x => /^.*\?.*$/gi.test(x)).length > 0
- var hasWitch = hand.filter(x => /^.*w.*$/gi.test(x)).length > 0
- var hasPriest = hand.filter(x => /^.*p.*$/gi.test(x)).length > 0
- var hasBrigand = hand.filter(x => /^.*b.*$/gi.test(x)).length > 0
-
- var faceCardHandCount = hand.filter(x => /.*\D.*/gi.test(x)).length
-
- var highestNumberedHandCardToReach30 = null
- var highestNumberedHandCardToReach30Value = 0
- for (var card of hand) {
- if (isNaN(card)) continue
- var value = parseInt(card)
- if (kingCards.has(value.toString())) value *= 2
- if (value > highestNumberedHandCardToReach30Value && score + value <= 30) {
- highestNumberedHandCardToReach30 = card
- highestNumberedHandCardToReach30Value = value
- }
- }
-
- var highestNumberedHandCardToReach20 = null
- var highestNumberedHandCardToReach20Value = 0
- for (var card of hand) {
- if (isNaN(card)) continue
- var value = parseInt(card)
- if (kingCards.has(value.toString())) value *= 2
- if (value > highestNumberedHandCardToReach20Value && score + value <= 20) {
- highestNumberedHandCardToReach20 = card
- highestNumberedHandCardToReach20Value = value
- }
- }
-
- var kingNumberedCardsInHand = []
- for (var card of hand) {
- if (kingCards.has(card)) kingNumberedCardsInHand.push(card)
- }
- kingNumberedCardsInHand.sort((a, b) => parseInt(a) - parseInt(b))
-
- var bestAceCard = null
- var bestAceCardTotal = 0
- for (var card of battlefield) {
- var number = card.replaceAll(/\D/gi, "")
- var playerTotal = 0
- for (var playerCard of playerBattlefield) {
- var playerNumber = playerCard.replaceAll(/\D/gi, "")
- if (playerNumber == number) playerTotal += parseInt(playerNumber)
- }
- if (playerTotal > bestAceCardTotal) bestAceCard = card
- }
-
- var bestKingCardToBustPlayer = null
- var bestKingCardToBustPlayerValue = 0
- for (var card of battlefield) {
- var number = card.replaceAll(/\D/gi, "")
- var value = parseInt(number)
-
- if (card.includes("q")) continue
- if (kingCards.has(number)) continue
-
- var count = 0
- for (var testCard of battlefield) {
- if (testCard.replaceAll(/\D/gi, "") == number) count++
- }
-
- if (value * count > bestKingCardToBustPlayerValue && score + value * count <= 30) {
- bestKingCardToBustPlayer = card
- bestKingCardToBustPlayerValue = value
- }
- }
-
- var bestKingCardToReach30 = null
- var bestKingCardToReach30Value = 0
- for (var card of battlefield) {
- var number = card.replaceAll(/\D/gi, "")
- var value = parseInt(number)
-
- if (card.includes("q")) continue
- if (kingCards.has(number)) continue
-
- var count = 0
- for (var testCard of battlefield) {
- if (testCard.replaceAll(/\D/gi, "") == number) count++
- }
-
- if (value * count > bestKingCardToReach30Value && score + value * count <= 30) {
- bestKingCardToReach30 = card
- bestKingCardToReach30Value = value
- }
- }
-
- var bestJackCardToSave = null
- var bestJackCardToSaveValue = 0
- for (var card of battlefield) {
- if (card.includes("q")) continue
-
- var value = parseInt(card.replaceAll(/\D/gi, ""))
- if (kingCards.has(value.toString())) value *= 2
- if (value > bestJackCardToSaveValue && score - value <= 30) {
- bestJackCardToSave = card
- bestJackCardToSaveValue = value
- }
- }
-
- var bestQueenCardToBustPlayer = null
- var bestQueenCardToBustPlayerValue = 0
- for (var card of battlefield) {
- if (card.includes("q")) continue
-
- var value = parseInt(card.replaceAll(/\D/gi, ""))
- if (kingCards.has(value.toString())) value *= 2
- if (value > bestQueenCardToBustPlayerValue && playerScore + value > 30) {
- bestQueenCardToBustPlayer = card
- bestQueenCardToBustPlayerValue = value
- }
- }
-
- var bestQueenCardToSave = null
- var bestQueenCardToSaveValue = 0
- for (var card of battlefield) {
- if (card.includes("q")) continue
-
- var value = parseInt(card.replaceAll(/\D/gi, ""))
- if (kingCards.has(value.toString())) value *= 2
- if (value > bestQueenCardToSaveValue && score - value <= 30) {
- bestQueenCardToSave = card
- bestQueenCardToSaveValue = value
- }
- }
-
- var bestPriestCardToSave = null
- var bestPriestCardToSaveValue = 0
- for (var card of battlefield) {
- if (card.includes("p")) continue
-
- var value = parseInt(card.replaceAll(/\D/gi, ""))
- if (kingCards.has(value.toString())) value *= 2
- if (value > bestPriestCardToSaveValue && score - value <= 30) {
- bestPriestCardToSave = card
- bestPriestCardToSaveValue = value
- }
- }
-
- var bestPriestCard = null
- var bestPriestCardValue = 0
- for (var card of battlefield) {
- if (card.includes("p")) continue
-
- var value = parseInt(card.replaceAll(/\D/gi, ""))
- if (kingCards.has(value.toString())) value *= 2
- if (value > bestPriestCardValue) {
- bestPriestCard = card
- bestPriestCardValue = value
- }
- }
-
- if (hand.length == 0) {
- if (deck.length == 0) state.stragedyEnemyTurnText = stragedyEnemyRetire()
- else if (score > 30) state.stragedyEnemyTurnText = stragedyEnemyRetire()
- else state.stragedyEnemyTurnText = stragedyEnemyDrawCard()
- } else if (score > 30 && battlefield.length > 0) {
- if (hasQueen && bestQueenCardToSave != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + bestQueenCardToSave)
- else if (hasPriest && bestPriestCardToSave != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "p" + bestPriestCardToSave)
- else if (hasJack && bestJackCardToSave != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "j" + bestJackCardToSave)
- else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard)
- else if (kingCards.length > 0 && kingNumberedCardsInHand.length > 0) state.stragedyEnemyTurnText = stragedyPlayCard(false, kingNumberedCardsInHand[kingNumberedCardsInHand.length - 1])
- else state.stragedyEnemyTurnText = stragedyEnemyRetire()
- } else if (playerRetired && score < playerScore) {
- if (hasJoker && playerScore < 30) state.stragedyEnemyTurnText = stragedyPlayCard(false, "?" + lowestNumberedBattlefieldCard)
- else if (hasQueen && bestQueenCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + bestQueenCardToBustPlayer)
- else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard)
- else if (hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer)
- else if (hasKing && bestKingCardToReach30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToReach30)
- else if (highestNumberedHandCardToReach30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardToReach30)
- else if (hasJoker && playerScore == 30) state.stragedyEnemyTurnText = stragedyPlayCard(false, "?" + lowestNumberedBattlefieldCard)
- else state.stragedyEnemyTurnText = stragedyEnemyRetire()
- } else if (playerRetired && score > playerScore && !hasJokerOnBattlefield) {
- state.stragedyEnemyTurnText = stragedyEnemyRetire()
- } else if (playerRetired && score == playerScore) {
- if (highestNumberedHandCardToReach30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardToReach30)
- else state.stragedyEnemyTurnText = stragedyEnemyRetire()
- } else if (score - playerScore > 20 && !hasJokerOnBattlefield) {
- state.stragedyEnemyTurnText = stragedyEnemyRetire()
- } else if (deck.length > 0 && hand.length == 1) {
- state.stragedyEnemyTurnText = stragedyEnemyDiscardCard()
- } else if (hasNumberedCards && (score < playerScore || score < 15)) {
- if (score < 20 && highestNumberedHandCardToReach20 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardToReach20)
- else if (highestNumberedHandCardToReach30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardToReach30)
- else if (faceCardHandCount > 1 && hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard)
- else if (faceCardHandCount > 1 && hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer)
- else if (faceCardHandCount > 1 && hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard)
- else if (deck.length > 0) state.stragedyEnemyTurnText = stragedyEnemyDiscardCard()
- else if (hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard)
- else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard)
- else if (hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer)
- else stragedyEnemyRandom()
- } else if (score >= playerScore && hasWitch) {
- state.stragedyEnemyTurnText = stragedyPlayCard(false, "w")
- } else if (score >= playerScore && hasBrigand) {
- state.stragedyEnemyTurnText = stragedyPlayCard(false, "b")
- } else if (highestNumberedHandCardToReach20 == null && hand.length > 0) {
- if (score >= 20 && score < playerScore && faceCardHandCount > 1 && hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard)
- else if (score >= 20 && score < playerScore && faceCardHandCount > 1 && hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer)
- else if (score >= 20 && score < playerScore && faceCardHandCount > 1 && hasQueen && highestNumberedBattlefieldCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + highestNumberedBattlefieldCard)
- else if (deck.length > 0) state.stragedyEnemyTurnText = stragedyEnemyDiscardCard()
- else if (hasQueen && bestQueenCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "q" + bestQueenCardToBustPlayer)
- else if (hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer)
- else if (hasPriest && bestPriestCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "p" + bestPriestCard)
- else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard)
- else state.stragedyEnemyTurnText = stragedyEnemyRetire()
- } else {
- state.stragedyEnemyTurnText = stragedyEnemyRandom()
- }
-
- stragedyCalculateScores()
- if (state.stragedyEnemyScore > 30) {
- stragedyCheckForWin()
- state.stragedyTurn = "gameOver"
- }
-}
-
-function stragedyEnemyDrawCard() {
- var card = state.stragedyEnemyDeck.pop()
- state.stragedyEnemyHand.push(card)
- return `\nThe opponent has drawn a card.\n`
-}
-
-function stragedyEnemyDiscardCard() {
- var hand = [...state.stragedyEnemyHand]
- var score = state.stragedyEnemyScore
-
- var hasAce = hand.filter(x => /^.*a.*$/gi.test(x)).length > 0
- var hasJack = hand.filter(x => /^.*j.*$/gi.test(x)).length > 0
- var hasQueen = hand.filter(x => /^.*q.*$/gi.test(x)).length > 0
- var hasKing = hand.filter(x => /^.*k.*$/gi.test(x)).length > 0
- var hasJoker = hand.filter(x => /^.*\?.*$/gi.test(x)).length > 0
- var hasWitch = hand.filter(x => /^.*w.*$/gi.test(x)).length > 0
- var hasPriest = hand.filter(x => /^.*p.*$/gi.test(x)).length > 0
- var sortedNumberedHandCardsToAddUpTo30 = hand.filter(x => /^\d+$/gi.test(x) && parseInt(x) <= 30 - score).sort((a, b) => parseInt(a) - parseInt(b))
- var highestNumberedHandCardAddUpTo30 = sortedNumberedHandCardsToAddUpTo30.length > 0 ? sortedNumberedHandCardsToAddUpTo30[sortedNumberedHandCardsToAddUpTo30.length - 1] : null
-
- if (hand.length > 1) {
- if (hasQueen) hand.splice(hand.indexOf("q"))
- else if (hasPriest) hand.splice(hand.indexOf("p"))
- else if (hasKing) hand.splice(hand.indexOf("k"))
- else if (hasWitch) hand.splice(hand.indexOf("w"))
- else if (hasJoker) hand.splice(hand.indexOf("?"))
- else if (hasJack) hand.splice(hand.indexOf("j"))
- else if (hasAce) hand.splice(hand.indexOf("a"))
- else if (highestNumberedHandCardAddUpTo30 != null) hand.splice(hand.indexOf(highestNumberedHandCardAddUpTo30))
- }
-
- var card = state.stragedyEnemyHand.splice(state.stragedyEnemyHand.indexOf(getRandomInteger(0, hand.length - 1)), 1)
- state.stragedyEnemyDiscard.push(card)
- var newCards = state.stragedyEnemyDeck.splice(state.stragedyEnemyDeck.length - 2, 2)
- state.stragedyEnemyHand.push(...newCards)
- return `\nThe opponent has discarded a card and drawn ${newCards.length} cards.\n`
-}
-
-function stragedyEnemyRetire() {
- state.stragedyEnemyRetired = true
- return `\nThe opponent has retired at ${state.stragedyEnemyScore} points.\n`
-}
-
-function stragedyEnemyRandom(punish) {
- var hand = [...state.stragedyEnemyHand]
-
- if (hand.length == 0) {
- if (punish) return "\nThe enemy has no cards to play.\n"
- if (state.stragedyEnemyDeck.length > 0) return stragedyEnemyDrawCard()
- return stragedyEnemyRetire()
- }
-
- do {
- var index = getRandomInteger(0, hand.length - 1)
- var card = hand.splice(index, 1)[0]
-
- if (/\d+/gi.test(card)) {
- return stragedyPlayCard(false, card)
- } else if (state.stragedyEnemyBattlefield.length > 0) {
- var battlefield = [...new Set(state.stragedyEnemyBattlefield)]
- do {
- var battlefieldIndex = getRandomInteger(0, battlefield.length - 1)
- var battlefieldCard = battlefield.splice(battlefieldIndex, 1)[0]
-
- if (!battlefieldCard.includes(card)) {
- return stragedyPlayCard(false, card + battlefieldCard)
- }
- } while (battlefield.length > 0)
- }
- } while (hand.length > 0)
-
- if (punish) {
- state.stragedyEnemyDiscard.push(...state.stragedyEnemyHand)
- state.stragedyEnemyHand = []
- return "\nThe enemy could not play any cards and therfore discarded their entire hand.\n"
- }
-
- if (state.stragedyEnemyDeck.length > 0) return stragedyEnemyDrawCard()
- return stragedyEnemyRetire()
-}
-
-function stragedyPlayerRandom(punish) {
- var hand = [...state.stragedyPlayerHand]
-
- if (hand.length == 0) {
- return "\nThe player has no cards to play.\n"
- }
-
- do {
- var index = getRandomInteger(0, hand.length - 1)
- var card = hand.splice(index, 1)[0]
-
- if (/\d+/gi.test(card)) {
- return stragedyPlayCard(true, card)
- } else if (state.stragedyPlayerBattlefield.length > 0) {
- var battlefield = [...new Set(state.stragedyPlayerBattlefield)]
- do {
- var battlefieldIndex = getRandomInteger(0, battlefield.length - 1)
- var battlefieldCard = battlefield.splice(battlefieldIndex, 1)[0]
-
- if (!battlefieldCard.includes(card)) {
- return stragedyPlayCard(true, card + battlefieldCard)
- }
- } while (battlefield.length > 0)
- }
- } while (hand.length > 0)
-
- if (punish) {
- state.stragedyPlayerDiscard.push(...state.stragedyEnemyHand)
- state.stragedyPlayerHand = []
- return "\nThe player could not play any cards and therfore discarded their entire hand.\n"
- }
-
- if (state.stragedyEnemyDeck.length > 0) return stragedyEnemyDrawCard()
- return stragedyEnemyRetire()
-}
-
-function stragedyPlayerTurn(text) {
- if (text.startsWith("d") && state.stragedyPlayerHand.length > 0) {
- if (state.stragedyPlayerDeck.length == 0) return "\nYou cannot discard if you have 0 cards in your deck.\n"
-
- var targetCard = text.substring(1).toLowerCase()
- if (targetCard.length == 0) return "\nYou must specify the card you wish to discard\n"
-
- var handIndex = state.stragedyPlayerHand.findIndex(x => x.toLowerCase() == targetCard)
- if (handIndex == -1) return "\nYou cannot discard a card that is not in your hand.\n"
-
- state.stragedyPlayerHand.splice(handIndex, 1);
- state.stragedyPlayerDiscard.push(targetCard)
-
- var newCards = state.stragedyPlayerDeck.splice(state.stragedyPlayerDeck.length - 2)
- state.stragedyPlayerHand.push(...newCards)
-
- text = `You discard the "${targetCard}" card. You draw `
- if (newCards.length == 1) text += `a "${newCards[0]}" card.`
- else text += `the "${newCards[0]}" and "${newCards[1]}" cards.`
-
- stragedyCalculateScores()
- if (state.stragedyEnemyRetired) {
- stragedyCheckForWin()
- state.stragedyTurn = "gameOver"
- } else stragedyEnemyTurn()
- return text
- } else if (text.startsWith("d") && state.stragedyPlayerHand.length == 0) {
- if (state.stragedyPlayerDeck.length == 0) return "\nYou cannot draw if you have 0 cards in your deck.\n"
-
- var drawCard = state.stragedyPlayerDeck.pop()
- state.stragedyPlayerHand.push(drawCard)
-
- stragedyCalculateScores()
- if (state.stragedyEnemyRetired) {
- stragedyCheckForWin()
- state.stragedyTurn = "gameOver"
- } else stragedyEnemyTurn()
- return `You draw a ${drawCard}`
- } else if (text == "r") {
- var hasJokerOnBattlefield = false
- for (var card of state.stragedyPlayerBattlefield) {
- if (card.includes("?")) {
- hasJokerOnBattlefield = true
- break
- }
- }
-
- if (hasJokerOnBattlefield) {
- return "\nYou cannot retire while you have a joker on the battlefield.\n"
- }
-
- state.stragedyPlayerRetired = true
- stragedyCalculateScores()
- var text = `You retire at ${state.stragedyPlayerScore}.`
- stragedyEnemyTurn()
- stragedyCalculateScores()
- stragedyCheckForWin()
- state.stragedyTurn = "gameOver"
- return text
- } else {
- var text = stragedyPlayCard(true, text)
- if (state.stragedyEnemyRetired) {
- stragedyCheckForWin()
- state.stragedyTurn = "gameOver"
- } else stragedyEnemyTurn()
-
- return text
- }
-}
-
-function stragedyPlayCard(player, text) {
- var character = getCharacter()
- if (player) {
- var battlefield = state.stragedyPlayerBattlefield
- var hand = state.stragedyPlayerHand
- var deck = state.stragedyPlayerDeck
- var discard = state.stragedyPlayerDiscard
- var characterName = toTitleCase(character.name)
- var playedWord = character.name == "You" ? "played" : "play"
- var enemyName = "The opponent"
- var enemyDeck = state.stragedyEnemyDeck
- var enemyHand = state.stragedyEnemyHand
- var enemyDiscard = state.stragedyEnemyDiscard
- var enemyBattlefield = state.stragedyEnemyBattlefield
- } else {
- var battlefield = state.stragedyEnemyBattlefield
- var hand = state.stragedyEnemyHand
- var deck = state.stragedyEnemyDeck
- var discard = state.stragedyEnemyDiscard
- var characterName = "The opponent"
- var playedWord = "played"
- var enemyName = toTitleCase(character.name)
- var enemyDeck = state.stragedyPlayerDeck
- var enemyHand = state.stragedyPlayerHand
- var enemyDiscard = state.stragedyPlayerDiscard
- var enemyBattlefield = state.stragedyPlayerBattlefield
- }
-
- var isNumberedCard = /^\d+$/.test(text)
- var handCard = isNumberedCard ? text : text.substring(0, 1).toLowerCase()
- var targetCard = isNumberedCard ? null : text.substring(1).toLowerCase()
-
- var handIndex = hand.findIndex(x => x.toLowerCase() == handCard)
- if (handIndex == -1) {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou can only play cards that are in your hand\n"
- }
-
- var targetIndex = targetCard == "" ? -1 : battlefield.findIndex(x => x.toLowerCase() == targetCard)
- if (!isNumberedCard && targetCard != "" && targetIndex == -1) {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou must specify a target that is placed on your side of the battlefield.\n"
- }
-
- switch (handCard) {
- case "a":
- if (targetCard == "") {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou must specify a target to use the Ace (ie. a2)\n"
- }
-
- hand.splice(handIndex, 1)
-
- for (var i = battlefield.length - 1; i >= 0; i--) {
- if (battlefield[i].endsWith(targetCard)) {
- discard.push(...battlefield[i])
- battlefield.splice(i, 1)
- }
- }
-
- for (var i = enemyBattlefield.length - 1; i >= 0; i--) {
- if (enemyBattlefield[i].endsWith(targetCard)) {
- enemyDiscard.push(...enemyBattlefield[i])
- enemyBattlefield.splice(i, 1)
- }
- }
-
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} an ace on ${targetCard}. All ${targetCard}s are removed.\n`
- case "j":
- if (targetCard == "") {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou must specify a target to use the Jack (ie. j2)\n"
- }
-
- battlefield.splice(targetIndex, 1)
- var discardCards = [...targetCard]
-
- hand.splice(handIndex, 1)
- discardCards.push(handCard)
-
- discard.push(...discardCards)
-
- shuffle(discard)
- var addCard = discard.pop()
- hand.push(addCard)
-
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a jack on the ${targetCard}. The ${targetCard} is removed. ${player ? `${characterName} drew a ${addCard} from the discard pile.` : ""}\n`
- case "q":
- if (targetCard == "") {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou must specify a target to use the Queen (ie. q2)\n"
- }
-
- hand.splice(handIndex, 1)
- battlefield.splice(targetIndex, 1)
-
- battlefield.push(handCard + targetCard)
-
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a queen on the ${targetCard}. The value is added to the opponent.\n`
- case "k":
- if (targetCard == "") {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou must specify a target to use the King (ie. k2)\n"
- }
-
- hand.splice(handIndex, 1)
- battlefield.splice(targetIndex, 1)
-
- battlefield.push(handCard + targetCard)
-
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a king on the ${targetCard}. All ${targetCard.match(/\d+/g)} values are doubled.\n`
- case "?":
- if (targetCard == "") {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou must specify a target to use the Joker (ie. ?2)\n"
- }
-
- hand.splice(handIndex, 1)
- battlefield.splice(targetIndex, 1)
-
- battlefield.push(handCard + targetCard)
-
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a joker on the ${targetCard}. The card's value is increased to make the total score 30.\n`
- case "w":
- hand.splice(handIndex, 1)
- discard.push(handCard)
-
- var enemyMove = !player ? stragedyPlayerRandom(true) : stragedyEnemyRandom(true)
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a witch on ${enemyName}. ${enemyMove}\n`
- case "p":
- if (targetCard == "") {
- if (player) state.stragedyEnemySkipTurn = true
- return "\nYou must specify a target to use the Priest (ie. p2)\n"
- }
-
- hand.splice(handIndex, 1)
- battlefield.splice(targetIndex, 1)
-
- battlefield.push(handCard + targetCard)
-
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a priest on the ${targetCard}. This card is prevented from causing ${characterName} to bust.\n`
- case "b":
- hand.splice(handIndex, 1)
- discard.push(handCard)
-
- var i
- for (i = 0; i < 5 && enemyDeck.length > 0; i++) {
- var card = enemyDeck.pop()
- enemyDiscard.push(...card)
- }
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a brigand on ${enemyName}. They are forced to discard ${i} cards from their deck\n`
- case "2":
- case "3":
- case "4":
- case "5":
- case "6":
- case "7":
- case "8":
- case "9":
- case "10":
- battlefield.push(handCard)
- hand.splice(handIndex, 1)
- stragedyCalculateScores()
- return `\n${characterName} ${playedWord} a ${handCard}.\n`
- default:
- if (player) state.stragedyEnemySkipTurn = true
- return "\nUnrecognized card specified. Stop playing with counterfit cards!\n"
- }
-}
-
-function stragedyCheckForWin() {
- if (state.stragedyEnemyScore > 30 && state.stragedyPlayerScore > 30) state.stragedyWinner = "tie"
- else if (state.stragedyEnemyScore > 30) state.stragedyWinner = "player"
- else if (state.stragedyPlayerScore > 30) state.stragedyWinner = "enemy"
- else if (state.stragedyPlayerScore > state.stragedyEnemyScore) state.stragedyWinner = "player"
- else if (state.stragedyEnemyScore > state.stragedyPlayerScore) state.stragedyWinner = "enemy"
- else state.stragedyWinner = "tie"
-}
-
-const simpleMeleeWeapons = ["Club", "Dagger", "Greatclub", "Handaxe", "Javelin", "Light Hammer", "Mace", "Quarterstaff", "Sickle", "Spear", "Dart"]
-const simpleRangedWeapons = ["Light Crossbow", "Shortbow", "Sling"]
-const martialMeleeWeapons = ["Battleaxe", "Flail", "Glaive", "Greataxe", "Greatsword", "Halberd", "Lance", "Longsword", "Maul", "Morningstar", "Pike", "Rapier", "Scimitar", "Shortsword", "Trident", "Warhammer", "War Pick", "Whip"]
-const martialRangedWeapons = ["Blowgun", "Hand Crossbow", "Heavy Crossbow", "Longbow", "Musket", "Pistol"]
-const lightArmor = ["Padded Armor", "Leather Armor", "Studded Leather Armor"]
-const mediumArmor = ["Hide Armor", "Chain Shirt", "Scale Mail", "Breastplate", "Half Plate Armor"]
-const heavyArmor = ["Ring Mail", "Chain Mail", "Splint Armor", "Plate Armor"]
-const ammunition = ["Arrow", "Bolt", "Bullet", "Needle"]
-
-function itemShopConvertGenericName(name) {
- switch (name) {
- case "Armor of Gleaming":
- name = itemShopNameAddPrefix("of Gleaming", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Cast-Off Armor":
- name = itemShopNameAddSuffix("Cast-Off", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Moon-Touched Sword":
- name = itemShopNameAddSuffix("Moon-Touched", "Glaive", "Greatsword", "Longsword", "Rapier", "Scimitar", "Shortsword")
- break
- case "Silvered Weapon":
- name = itemShopNameAddSuffix("Silvered", ...simpleMeleeWeapons.concat(simpleRangedWeapons, martialMeleeWeapons, martialRangedWeapons))
- break
- case "Smoldering Armor":
- name = itemShopNameAddSuffix("Smoldering", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Sylvan Talon":
- name = itemShopNameAddSuffix("Sylvan Talon", "Dagger", "Rapier", "Scimitar", "Shortsword", "Sickle", "Spear")
- break
- case "Walloping Ammunition":
- name = itemShopNameAddSuffix("Walloping", ...ammunition)
- quantity = 10
- break
- case "Adamantine Armor":
- name = itemShopNameAddSuffix("Adamantine", ...mediumArmor.concat(heavyArmor))
- break
- case "Adamantine Weapon":
- name = itemShopNameAddSuffix("Adamantine", ...martialMeleeWeapons.concat(ammunition, simpleMeleeWeapons))
- break
- case "Ammunition +1":
- name = itemShopNameAddPrefix("+1", ...ammunition)
- break
- case "Enspelled Armor Uncommon":
- name = itemShopNameAddSuffix("Uncommon Enspelled", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Enspelled Weapon Uncommon":
- name = itemShopNameAddSuffix("Uncommon Enspelled", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Mariner's Armor":
- name = itemShopNameAddSuffix("Mariner's", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Quaal's Feather Token Uncommon":
- name = itemShopNameAddSuffix("Quaal's Feather Token of", "Anchor", "Fan", "Tree")
- break
- case "Sword of Vengeance":
- name = itemShopNameAddPrefix("of Vengeance", "Glaive", "Greatsword", "Longsword", "Rapier", "Scimitar", "Shortsword")
- break
- case "Weapon +1":
- name = itemShopNameAddPrefix("+1", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Weapon of Warning":
- name = itemShopNameAddPrefix("of Warning", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Ammunition +2":
- name = itemShopNameAddPrefix("+2", ...ammunition)
- break
- case "Armor +1":
- name = itemShopNameAddPrefix("+1", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Armor of Resistance":
- name = itemShopNameAddPrefix("of Resistance", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Armor of Vulnerability":
- name = itemShopNameAddPrefix("of Vulnerability", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Enspelled Armor Rare":
- name = itemShopNameAddSuffix("Rare Enspelled", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Enspelled Weapon Rare":
- name = itemShopNameAddSuffix("Rare Enspelled", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Figurine of Wondrous Power Rare":
- name = itemShopNameAddPrefix("Figurine of Wondrous Power", "Bronze Griffon", "Ebony Fly", "Golden Lions", "Ivory Goats", "Marble Elephant", "Onyx Dog", "Serpentine Owl")
- break
- case "Flame Tongue":
- name = itemShopNameAddSuffix("Flame Tongue", ...simpleMeleeWeapons.concat(martialMeleeWeapons))
- break
- case "Giant Slayer":
- name = itemShopNameAddSuffix("Giant Slayer", ...simpleMeleeWeapons.concat(martialMeleeWeapons))
- break
- case "Ioun Stone Rare":
- name = itemShopNameAddSuffix("Ioun Stone of", "Awareness", "Protection", "Reserve", "Sustenance")
- break
- case "Quaal's Feather Token Rare":
- name = itemShopNameAddSuffix("Quaal's Feather Token of", "Bird", "Swan Boat", "Whip")
- break
- case "Sword of Life Stealing":
- name = itemShopNameAddPrefix("of Life Stealing", "Glaive", "Greatsword", "Longsword", "Rapier", "Scimitar", "Shortsword")
- break
- case "Sword of Wounding":
- name = itemShopNameAddPrefix("of Wounding", "Glaive", "Greatsword", "Longsword", "Rapier", "Scimitar", "Shortsword")
- break
- case "Vicious Weapon":
- name = itemShopNameAddSuffix("Vicious", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Weapon +2":
- name = itemShopNameAddPrefix("+2", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Ammunition +3":
- name = itemShopNameAddPrefix("+3", ...ammunition)
- break
- case "Ammunition of Slaying":
- let type = getRandomFromList("Aberration", "Beast", "Celestial", "Construct", "Dragon", "Elemental", "Humonoid", "Fey", "Fiend", "Giant", "Monstrosity", "Ooze", "Plant", "Undead")
- name = itemShopNameAddPrefix(`of ${type} Slaying`, ...ammunition)
- break
- case "Armor +2":
- name = itemShopNameAddPrefix("+2", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Dancing Sword":
- name = itemShopNameAddSuffix("Dancing", "Greatsword", "Longsword", "Rapier", "Scimitar", "Shortsword")
- break
- case "Demon Armor":
- name = itemShopNameAddPrefix("Demon", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Enspelled Armor Very Rare":
- name = itemShopNameAddSuffix("Very Rare Enspelled", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Enspelled Weapon Very Rare":
- name = itemShopNameAddSuffix("Very Rare Enspelled", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Frost Brand":
- name = itemShopNameAddSuffix("Frost Brand", "Glaive", "Greatsword", "Longsword", "Rapier", "Scimitar", "Shortsword")
- break
- case "Ioun Stone Very Rare":
- name = itemShopNameAddSuffix("Ioun Stone of", "Absorption", "Fortitude", "Insight", "Intellect", "Leadership", "Strength")
- break
- case "Sword of Sharpness":
- name = itemShopNameAddPrefix("of Sharpness", "Glaive", "Greatsword", "Longsword", "Rapier", "Scimitar")
- break
- case "Weapon +3":
- name = itemShopNameAddPrefix("+3", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Armor +3":
- name = itemShopNameAddPrefix("+3", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Enspelled Armor Legendary":
- name = itemShopNameAddSuffix("Very Rare Enspelled", ...lightArmor.concat(mediumArmor, heavyArmor))
- break
- case "Enspelled Weapon Legendary":
- name = itemShopNameAddSuffix("Legendary Enspelled", ...simpleMeleeWeapons.concat(martialMeleeWeapons, simpleRangedWeapons, martialRangedWeapons))
- break
- case "Luck Blade":
- name = itemShopNameAddPrefix("Luck Blade", "Glaive", "Greatsword", "Longsword", "Rapier", "Scimitar", "Sickle", "Shortsword")
- break
- case "Moonblade":
- name = itemShopNameAddPrefix("Moonblade", "Greatsword", "Longsword", "Rapier", "Scimitar", "Shortsword")
- break
- }
- return name
-}
-
-function itemShopNameAddPrefix(name, ...prefixes) {
- return getRandomFromList(...prefixes) + " " + name
-}
-
-function itemShopNameAddSuffix(name, ...suffixes) {
- return name + " " + getRandomFromList(...suffixes)
-}
-
-function findItemShopDeals(className, bought) {
- return state.itemShopDeals.filter(element => element.className == className && (bought == null || element.bought == bought))
-}
-
-function findSpellShopDeals(className, level, bought) {
- return state.spellShopDeals.filter(element => element.className == className && element.level == level && (bought == null || element.bought == bought))
+ return storyCards.findIndex((element) => element.type == "spell" && element.keys == name)
}
String.prototype.replaceAt = function(index, replacement) {
@@ -4098,5974 +3360,4 @@ function generateName(genre, male) {
return nordicFemaleNames[state.nordicFemaleIndex++]
}
}
-}
-
-
-/*
-Auto-Cards
-Made by LewdLeah on May 21, 2025
-This AI Dungeon script automatically creates and updates plot-relevant story cards while you play
-General-purpose usefulness and compatibility with other scenarios/scripts were my design priorities
-Auto-Cards is fully open-source, please copy for use within your own projects! ā¤ļø
-*/
-function AutoCards(inHook, inText, inStop) {
- "use strict";
- /*
- Default Auto-Cards settings
- Feel free to change these settings to customize your scenario's default gameplay experience
- The default values for your scenario are specified below:
- */
-
- // Is Auto-Cards already enabled when the adventure begins?
- const DEFAULT_DO_AC = true
- // (true or false)
-
- // Pin the "Configure Auto-Cards" story card at the top of the player's story cards list?
- const DEFAULT_PIN_CONFIGURE_CARD = true
- // (true or false)
-
- // Minimum number of turns in between automatic card generation events?
- const DEFAULT_CARD_CREATION_COOLDOWN = 22
- // (0 to 9999)
-
- // Use a bulleted list format for newly generated card entries?
- const DEFAULT_USE_BULLETED_LIST_MODE = true
- // (true or false)
-
- // Maximum allowed length for newly generated story card entries?
- const DEFAULT_GENERATED_ENTRY_LIMIT = 750
- // (200 to 2000)
-
- // Do newly generated cards have memory updates enabled by default?
- const DEFAULT_NEW_CARDS_DO_MEMORY_UPDATES = true
- // (true or false)
-
- // Default character limit before the card's memory bank is summarized?
- const DEFAULT_NEW_CARDS_MEMORY_LIMIT = 2750
- // (1750 to 9900)
-
- // Approximately how much shorter should recently compressed memories be? (ratio = 10 * old / new)
- const DEFAULT_MEMORY_COMPRESSION_RATIO = 25
- // (20 to 1250)
-
- // Ignore all-caps during title candidate detection?
- const DEFAULT_IGNORE_ALL_CAPS_TITLES = true
- // (true or false)
-
- // Should player input actions (Do/Say/Story) be considered during title detection?
- const DEFAULT_DETECT_TITLES_FROM_INPUTS = false
- // (true or false)
-
- // How many (minimum) actions in the past does Auto-Cards look for named entities?
- const DEFAULT_MINIMUM_LOOK_BACK_DISTANCE = 5
- // (2 to 88)
-
- // Is Live Script Interface v2 enabled?
- const DEFAULT_DO_LSI_V2 = false
- // (true or false)
-
- // Should the "Debug Data" story card be visible?
- const DEFAULT_SHOW_DEBUG_DATA = false
- // (true or false)
-
- // AI prompt used to generate new story card entries?
- const DEFAULT_CARD_GENERATION_PROMPT = prose(
- "-----",
- "",
- "",
- "# Stop the story and ignore previous instructions. Write a brief and coherent informational entry for %{title} following these instructions:",
- "- Write only third-person pure prose information about %{title} using complete sentences with correct punctuation",
- "- Avoid short-term temporary details or appearances, instead focus on plot-significant information",
- "- Prioritize story-relevant details about %{title} first to ensure seamless integration with the previous plot",
- "- Create new information based on the context and story direction",
- "- Mention %{title} in every sentence",
- "- Use semicolons if needed",
- "- Add additional details about %{title} beneath incomplete entries",
- "- Be concise and grounded",
- "- Imitate the story's writing style and infer the reader's preferences",
- "",
- "Continue the entry for %{title} below while avoiding repetition:",
- "%{entry}"
- ); // (mimic this multi-line "text" format)
-
- // AI prompt used to summarize a given story card's memory bank?
- const DEFAULT_CARD_MEMORY_COMPRESSION_PROMPT = prose(
- "-----",
- "",
- "",
- "# Stop the story and ignore previous instructions. Summarize and condense the given paragraph into a narrow and focused memory passage while following these guidelines:",
- "- Ensure the passage retains the core meaning and most essential details",
- "- Use the third-person perspective",
- "- Prioritize information-density, accuracy, and completeness",
- "- Remain brief and concise",
- "- Write firmly in the past tense",
- "- The paragraph below pertains to old events from far earlier in the story",
- "- Integrate %{title} naturally within the memory; however, only write about the events as they occurred",
- "- Only reference information present inside the paragraph itself, be specific",
- "",
- "Write a summarized old memory passage for %{title} based only on the following paragraph:",
- "\"\"\"",
- "%{memory}",
- "\"\"\"",
- "Summarize below:"
- ); // (mimic this multi-line "text" format)
-
- // Titles banned from future card generation attempts?
- const DEFAULT_BANNED_TITLES_LIST = (
- "North, East, South, West, Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, January, February, March, April, May, June, July, August, September, October, November, December"
- ); // (mimic this comma-list "text" format)
-
- // Default story card "type" used by Auto-Cards? (does not matter)
- const DEFAULT_CARD_TYPE = "class"
- // ("text")
-
- // Should titles mentioned in the "opening" plot component be banned from future card generation by default?
- const DEFAULT_BAN_TITLES_FROM_OPENING = true
- // (true or false)
-
- //āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
-
- /*
- Useful API functions for coders (otherwise ignore)
- Here's what each one does in plain terms:
-
- AutoCards().API.postponeEvents();
- Pauses Auto-Cards activity for n many turns
-
- AutoCards().API.emergencyHalt();
- Emergency stop or resume
-
- AutoCards().API.suppressMessages();
- Hides Auto-Cards toasts by preventing assignment to state.message
-
- AutoCards().API.debugLog();
- Writes to the debug log card
-
- AutoCards().API.toggle();
- Turns Auto-Cards on/off
-
- AutoCards().API.generateCard();
- Initiates AI generation of the requested card
-
- AutoCards().API.redoCard();
- Regenerates an existing card
-
- AutoCards().API.setCardAsAuto();
- Flags or unflags a card as automatic
-
- AutoCards().API.addCardMemory();
- Adds a memory to a specific card
-
- AutoCards().API.eraseAllAutoCards();
- Deletes all auto-cards
-
- AutoCards().API.getUsedTitles();
- Lists all current card titles
-
- AutoCards().API.getBannedTitles();
- Shows your current banned titles list
-
- AutoCards().API.setBannedTitles();
- Replaces the banned titles list with a new list
-
- AutoCards().API.buildCard();
- Makes a new card from scratch, using exact parameters
-
- AutoCards().API.getCard();
- Finds cards that match a filter
-
- AutoCards().API.eraseCard();
- Deletes cards matching a filter
- */
-
- /*** Postpones internal Auto-Cards events for a specified number of turns
- *
- * @function
- * @param {number} turns A non-negative integer representing the number of turns to postpone events
- * @returns {Object} An object containing cooldown values affected by the postponement
- * @throws {Error} If turns is not a non-negative integer
- */
- // AutoCards().API.postponeEvents();
-
- /*** Sets or clears the emergency halt flag to pause Auto-Cards operations
- *
- * @function
- * @param {boolean} shouldHalt A boolean value indicating whether to engage (true) or disengage (false) emergency halt
- * @returns {boolean} The value that was set
- * @throws {Error} If called from within isolateLSIv2 scope or with a non-boolean argument
- */
- // AutoCards().API.emergencyHalt();
-
- /*** Enables or disables state.message assignments from Auto-Cards
- *
- * @function
- * @param {boolean} shouldSuppress If true, suppresses all Auto-Cards messages; false enables them
- * @returns {Array} The current pending messages after setting suppression
- * @throws {Error} If shouldSuppress is not a boolean
- */
- // AutoCards().API.suppressMessages();
-
- /*** Logs debug information to the "Debug Log card console
- *
- * @function
- * @param {...any} args Arguments to log for debugging purposes
- * @returns {any} The story card object reference
- */
- // AutoCards().API.debugLog();
-
- /*** Toggles Auto-Cards behavior or sets it directly
- *
- * @function
- * @param {boolean|null|undefined} toggleType If undefined, toggles the current state. If boolean or null, sets the state accordingly
- * @returns {boolean|null|undefined} The state that was set or inferred
- * @throws {Error} If toggleType is not a boolean, null, or undefined
- */
- // AutoCards().API.toggle();
-
- /*** Generates a new card using optional prompt details or a card request object
- *
- * This function supports two usage modes:
- *
- * 1. Object Mode:
- * Pass a single object containing card request parameters. The only mandatory property is "title"
- * All other properties are optional and customize the card generation
- *
- * Example:
- * AutoCards().API.generateCard({
- * type: "character", // The category or type of the card; defaults to "class" if omitted
- * title: "Leah the Lewd", // The card's title (required)
- * keysStart: "Lewd,Leah", // Optional trigger keywords associated with the card
- * entryStart: "You are a woman named Leah.", // Existing content to prepend to the AI-generated entry
- * entryPrompt: "", // Global prompt guiding AI content generation
- * entryPromptDetails: "Focus on Leah's works of artifice and ingenuity", // Additional prompt info
- * entryLimit: 750, // Target character length for the AI-generated entry
- * description: "Player character!", // Freeform notes
- * memoryStart: "Leah purchased a new sweater.", // Existing memory content
- * memoryUpdates: true, // Whether the card's memory bank will update on its own
- * memoryLimit: 2750 // Preferred memory bank size before summarization/compression
- * });
- *
- * 2. String Mode:
- * Pass a string as the title and optionally two additional strings to specify prompt details
- * This mode is shorthand for quick card generation without an explicit card request object
- *
- * Examples:
- * AutoCards().API.generateCard("Leah the Lewd");
- * AutoCards().API.generateCard("Leah the Lewd", "Focus on Leah's works of artifice and ingenuity");
- * AutoCards().API.generateCard(
- * "Leah the Lewd",
- * "Focus on Leah's works of artifice and ingenuity",
- * "You are a woman named Leah."
- * );
- *
- * @function
- * @param {Object|string} request Either a fully specified card request object or a string title
- * @param {string} [extra1] Optional detailed prompt text when using string mode
- * @param {string} [extra2] Optional entry start text when using string mode
- * @returns {boolean} Returns true if the generation attempt succeeded, false otherwise
- * @throws {Error} Throws if called with invalid arguments or missing a required title property
- */
- // AutoCards().API.generateCard();
-
- /*** Regenerates a card by title or object reference, optionally preserving or modifying its input info
- *
- * @function
- * @param {Object|string} request Either a fully specified card request object or a string title for the card to be regenerated
- * @param {boolean} [useOldInfo=true] If true, preserves old info in the new generation; false omits it
- * @param {string} [newInfo=""] Additional info to append to the generation prompt
- * @returns {boolean} True if regeneration succeeded; false otherwise
- * @throws {Error} If the request format is invalid, or if the second or third parameters are the wrong types
- */
- // AutoCards().API.redoCard();
-
- /*** Flags or unflags a card as an auto-card, controlling its automatic generation behavior
- *
- * @function
- * @param {Object|string} targetCard The card object or title to mark/unmark as an auto-card
- * @param {boolean} [setOrUnset=true] If true, marks the card as an auto-card; false removes the flag
- * @returns {boolean} True if the operation succeeded; false if the card was invalid or already matched the target state
- * @throws {Error} If the arguments are invalid types
- */
- // AutoCards().API.setCardAsAuto();
-
- /*** Appends a memory to a story card's memory bank
- *
- * @function
- * @param {Object|string} targetCard A card object reference or title string
- * @param {string} newMemory The memory text to add
- * @returns {boolean} True if the memory was added; false if it was empty, already present, or the card was not found
- * @throws {Error} If the inputs are not a string or valid card object reference
- */
- // AutoCards().API.addCardMemory();
-
- /*** Removes all previously generated auto-cards and resets various states
- *
- * @function
- * @returns {number} The number of cards that were removed
- */
- // AutoCards().API.eraseAllAutoCards();
-
- /*** Retrieves an array of titles currently used by the adventure's story cards
- *
- * @function
- * @returns {Array} An array of strings representing used titles
- */
- // AutoCards().API.getUsedTitles();
-
- /*** Retrieves an array of banned titles
- *
- * @function
- * @returns {Array} An array of banned title strings
- */
- // AutoCards().API.getBannedTitles();
-
- /*** Sets the banned titles array, replacing any previously banned titles
- *
- * @function
- * @param {string|Array} titles A comma-separated string or array of strings representing titles to ban
- * @returns {Object} An object containing oldBans and newBans arrays
- * @throws {Error} If the input is neither a string nor an array of strings
- */
- // AutoCards().API.setBannedTitles();
-
- /*** Creates a new story card with the specified parameters
- *
- * @function
- * @param {string|Object} title Card title string or full card template object containing all fields
- * @param {string} [entry] The entry text for the card
- * @param {string} [type] The card type (e.g., "character", "location")
- * @param {string} [keys] The keys (triggers) for the card
- * @param {string} [description] The notes or memory bank of the card
- * @param {number} [insertionIndex] Optional index to insert the card at a specific position within storyCards
- * @returns {Object|null} The created card object reference, or null if creation failed
- */
- // AutoCards().API.buildCard();
-
- /*** Finds and returns story cards satisfying a user-defined condition
- * Example:
- * const leahCard = AutoCards().API.getCard(card => (card.title === "Leah"));
- *
- * @function
- * @param {Function} predicate A function which takes a card and returns true if it matches
- * @param {boolean} [getAll=false] If true, returns all matching cards; otherwise returns the first match
- * @returns {Object|Array