diff --git a/Input.js b/Input.js index 1682770..59396d7 100644 --- a/Input.js +++ b/Input.js @@ -118,6 +118,12 @@ const modifier = (text) => { else text = rawText } + if (state.spellShopStep != null) { + text = handleSpellShopStep(text) + if (state.spellShopStep != null) return { text } + else text = rawText + } + if (state.lockpickingTurn != null) { text = handleLockpickingTurn(text) if (state.lockpickingTurn != null) return { text } @@ -252,6 +258,7 @@ const modifier = (text) => { 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, stragedySynonyms, doStragedy) if (text == null) text = processCommandSynonyms(command, commandName, lockpickSynonyms, doLockpick) if (text == null) text = processCommandSynonyms(command, commandName, addCardSynonyms, doAddCard) @@ -1416,6 +1423,186 @@ function doBasicDeck(command) { return `${toTitleCase(character.name)} ${takeWord} the Stragedy Basic Deck` } +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 + log(`spell shop all:${state.spellShopAll}`) + + 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 + log(`index:${index}`) + + let deals = findSpellShopDeals(state.spellShopClassName, state.spellShopLevel, false) + if (index < 0 || index >= deals.length) return text + + let deal = deals[index] + log(`Deal name:${deal.name}`) + + 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 + log(`Found:${found}`) + + 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" @@ -1450,6 +1637,7 @@ function handleStragedyShopStep(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 @@ -2147,6 +2335,7 @@ function doRest(command) { state.enemies = [] state.cardDeals = null state.cardPrices = null + state.spellShopDeals = null var healingFactor = 1 var text diff --git a/Library.js b/Library.js index 747f00b..185c1af 100644 --- a/Library.js +++ b/Library.js @@ -20,6 +20,10 @@ 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) } @@ -2848,7 +2852,11 @@ function getModifier(statValue) { } function findSpellCardIndex(name) { - return storyCards.findIndex((element) => element.type == "spell" && element.keys == name) + return storyCards.findIndex((element) => element.type == "spell" && element.title == name) +} + +function findSpellCard(name) { + return storyCards[findSpellCardIndex(name)] } function stragedyCalculateScores() { @@ -2939,7 +2947,6 @@ function stragedyCalculateScores() { } function stragedyEnemyTurn() { - log(`enemy turn: ${state.stragedyEnemyHand}`) state.stragedyEnemySkipTurn = false state.stragedyEnemyTurnText = "" if (state.stragedyPlayerScore > 30) { @@ -3159,12 +3166,10 @@ function stragedyEnemyTurn() { } if (hand.length == 0) { - log("Enemy has no cards in hand") 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) { - log("Enemy is going to bust") 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) @@ -3172,7 +3177,6 @@ function stragedyEnemyTurn() { 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) { - log("Enemy is reacting to the player retiring while behind") 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) @@ -3182,20 +3186,15 @@ function stragedyEnemyTurn() { else if (hasJoker && playerScore == 30) state.stragedyEnemyTurnText = stragedyPlayCard(false, "?" + lowestNumberedBattlefieldCard) else state.stragedyEnemyTurnText = stragedyEnemyRetire() } else if (playerRetired && score > playerScore && !hasJokerOnBattlefield) { - log("Enemy is reacting to the player retiring while ahead") state.stragedyEnemyTurnText = stragedyEnemyRetire() } else if (playerRetired && score == playerScore) { - log("Enemy is reacting to the player retiring while tied") if (highestNumberedHandCardToReach30 != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, highestNumberedHandCardToReach30) else state.stragedyEnemyTurnText = stragedyEnemyRetire() } else if (score - playerScore > 20 && !hasJokerOnBattlefield) { - log("Enemy has a significant lead") state.stragedyEnemyTurnText = stragedyEnemyRetire() } else if (deck.length > 0 && hand.length == 1) { - log("Enemy only has one card in hand") state.stragedyEnemyTurnText = stragedyEnemyDiscardCard() } else if (hasNumberedCards && (score < playerScore || score < 15)) { - log("Enemy is behind or needs to reach at least 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) @@ -3207,13 +3206,10 @@ function stragedyEnemyTurn() { else if (hasKing && bestKingCardToBustPlayer != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "k" + bestKingCardToBustPlayer) else stragedyEnemyRandom() } else if (score >= playerScore && hasWitch) { - log("Enemy has lead and has a witch") state.stragedyEnemyTurnText = stragedyPlayCard(false, "w") } else if (score >= playerScore && hasBrigand) { - log("Enemy has lead and has a brigand") state.stragedyEnemyTurnText = stragedyPlayCard(false, "b") } else if (highestNumberedHandCardToReach20 == null && hand.length > 0) { - log("Enemy can't reach 20 and has cards in hand") 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) @@ -3224,7 +3220,6 @@ function stragedyEnemyTurn() { else if (hasAce && bestAceCard != null) state.stragedyEnemyTurnText = stragedyPlayCard(false, "a" + bestAceCard) else state.stragedyEnemyTurnText = stragedyEnemyRetire() } else { - log("Enemy has ran out of options and is doing random stuff") state.stragedyEnemyTurnText = stragedyEnemyRandom() } @@ -3236,14 +3231,12 @@ function stragedyEnemyTurn() { } function stragedyEnemyDrawCard() { - log(`Enemy draw a card`) var card = state.stragedyEnemyDeck.pop() state.stragedyEnemyHand.push(card) return `\nThe opponent has drawn a card.\n` } function stragedyEnemyDiscardCard() { - log(`Enemy discard a card`) var hand = [...state.stragedyEnemyHand] var score = state.stragedyEnemyScore @@ -3276,13 +3269,11 @@ function stragedyEnemyDiscardCard() { } function stragedyEnemyRetire() { - log(`Enemy retire`) state.stragedyEnemyRetired = true return `\nThe opponent has retired at ${state.stragedyEnemyScore} points.\n` } function stragedyEnemyRandom(punish) { - log(`Enemy random`) var hand = [...state.stragedyEnemyHand] if (hand.length == 0) { @@ -3321,7 +3312,6 @@ function stragedyEnemyRandom(punish) { } function stragedyPlayerRandom(punish) { - log(`Player random`) var hand = [...state.stragedyPlayerHand] if (hand.length == 0) { @@ -3358,7 +3348,6 @@ function stragedyPlayerRandom(punish) { } function stragedyPlayerTurn(text) { - log(`player turn`) 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" @@ -3429,7 +3418,6 @@ function stragedyPlayerTurn(text) { } function stragedyPlayCard(player, text) { - log(`${player ? "Player" : "Enemy"} play card ${text}`) var character = getCharacter() if (player) { var battlefield = state.stragedyPlayerBattlefield @@ -3616,6 +3604,10 @@ function stragedyCheckForWin() { else state.stragedyWinner = "tie" } +function findSpellShopDeals(className, level, bought) { + return state.spellShopDeals.filter(element => element.className == className && element.level == level && (bought == null || element.bought == bought)) +} + String.prototype.replaceAt = function(index, replacement) { return this.substring(0, index) + replacement + this.substring(index + replacement.length); } diff --git a/Output.js b/Output.js index f90ef41..4164f41 100644 --- a/Output.js +++ b/Output.js @@ -93,6 +93,8 @@ const modifier = (text) => { case "stragedyShop": text += handleStragedyShop() break + case "spellShop": + text += handleSpellShop() case "lockpicking": text += handleLockpicking() break @@ -467,6 +469,8 @@ const modifier = (text) => { text += "\n Removes all spells from the character's spellbook." text += "\n#spellbook" text += "\n Shows the list of spells that the character has learned." + text += "\n#spellshop (bard|cleric|druid|paladin|ranger|sorcerer|warlock|wizard) (level) (free) (all)" + text += "\n This opens the spell shop where characters can spend gold to purchase new spells. The selection is randomized based on the day and on the character's class and spell level. Full casters, such as bards, clerics, druids, sorcerers, warlocks, and wizards, have spell levels from 0-9. Half casters, such as paladins and rangers, have spell levels from 1-5. Include the argument \"free\" to not require gold to purchase the spell. Include the argument \"all\" to list all available spells for that level. Otherwise, the list is randomized and a selection of lower level spells are included." text += "\n\n--Combat--" text += "\n#setupenemy" @@ -733,6 +737,343 @@ Type d to draw a card. ${!hasJokerOnBattlefield ? "Type r to retire. " : ""}Type return text } +function spellShopPushDeal(items, name, price) { + state.spellShopDeals.push({ + className: state.spellShopClassName, + level: state.spellShopLevel, + name: name, + price: price, + bought: false + }) + if (!state.spellShopAll) items.splice(index, 1) +} + +var spellShopSeed + +function spellShopSelectSpells(spells, price, numberOfSpells) { + if (numberOfSpells == null) numberOfSpells = 1 + + spellShopSeed += 100 + index = Math.floor(getRandom(spellShopSeed) * spells.length) + if (state.spellShopAll) { + log(`Spells:${spells}`) + for (const spell of spells) { + log(`Spell:${spell}`) + spellShopPushDeal(spells, spell, price) + } + log(state.spellShopDeals) + return + } + + for (let i = 0; i < numberOfSpells; i++) { + spellShopPushDeal(spells, spells[i], price) + } +} + +function handleSpellShop() { + var character = getCharacter() + var goldIndex = character.inventory.findIndex(x => x.name.toLowerCase() == "gold") + var gold = goldIndex == -1 ? 0 : character.inventory[goldIndex].quantity + var text = " " + spellShopSeed = state.day + + if (state.spellShopDeals == null || state.spellShopClearDeals) state.spellShopDeals = [] + + if (findSpellShopDeals(state.spellShopClassName, state.spellShopLevel).length == 0) switch(state.spellShopClassName) { + case "bard": + switch(state.spellShopLevel) { + case 9: + spellShopSelectSpells(["Foresight", "Power Word Heal", "Power Word Kill", "Prismatic Wall", "True Polymorph"], 50000) + if (state.spellShopAll) break + case 8: + spellShopSelectSpells(["Antipathy/Sympathy", "Befuddlement", "Dominate Monster", "Glibness", "Mind Blank", "Power Word Stun"], 25000) + if (state.spellShopAll) break + case 7: + spellShopSelectSpells(["Etherealness", "Forcecage", "Mirage Arcane", "Mordenkainen's Magnificent Mansion", "Mordenkainen's Sword", "Power Word Fortify", "Prismatic Spray", "Project Image", "Regenerate", "Resurrection", "Symbol", "Teleport"], 20000, state.spellShopLevel == 7 ? 2 : 1) + if (state.spellShopAll) break + case 6: + spellShopSelectSpells(["Eyebite", "Find the Path", "Guards and Wards", "Heroes' Feast", "Mass Suggestion", "Otto's Irresistible Dance", "Programmed Illusion", "True Seeing"], 10000, state.spellShopLevel == 6 ? 2 : 1) + if (state.spellShopAll) break + case 5: + spellShopSelectSpells(["Animate Objects", "Awaken", "Dominate Person", "Dream", "Dream", "Geas", "Greater Restoration", "Hold Monster", "Legend Lore", "Mass Cure Wounds", "Mislead", "Modify Memory", "Planar Binding", "Raise Dead", "Rary's Telepathic Bond", "Scrying", "Seeming", "Synaptic Static", "Teleportation Circle", "Yolande's Regal Presence"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Charm Monster", "Compulsion", "Confusion", "Dimension Door", "Fount of Moonlight", "Freedom of Movement", "Greater Invisibility", "Hallucinatory Terrain", "Locate Creature", "Phantasmal Killer", "Polymorph"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Bestow Curse", "Clairvoyance", "Dispel Magic", "Fear", "Feign Death", "Glyph of Warding", "Hypnotic Pattern", "Leomund's Tiny Hut", "Major Image", "Mass Healing Word", "Nondetection", "Plant Growth", "Sending", "Slow", "Speak with Dead", "Speak with Plants", "Stinking Cloud", "Tongues"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Aid", "Animal Messenger", "Blindness/Deafness", "Calm Emotions", "Cloud of Daggers", "Crown of Madness", "Detect Thoughts", "Enhance Ability", "Enlarge/Reduce", "Enthrall", "Heat Metal", "Hold Person", "Invisibility", "Knock", "Lesser Restoration", "Locate Animals or Plants", "Locate Object", "Magic Mouth", "Mirror Image", "Phantasmal Force", "See Invisibility", "Shater", "Silence", "Suggestion", "Zone of Truth"], 500, state.spellShopLevel == 2 ? 5 : 1) + case 1: + spellShopSelectSpells(["Animal Friendship", "Bane", "Charm Person", "Color Spray", "Command", "Comprehend Languages", "Cure Wounds", "Detect Magic", "Disguise Self", "Dissonant Whispers", "Faerie Fire", "Feather Fall", "Healing Word", "Heroism", "Identify", "Illusory Script", "Longstrider", "Silent Image", "Sleep", "Speak with Animals", "Tasha's Hideous Laughter", "Thunderwave", "Unseen Servant"], 250, state.spellShopLevel == 1 ? 5 : 1) + case 0: + spellShopSelectSpells(["Blade Ward", "Dancing Lights", "Friends", "Light", "Mage Hand", "Mending", "Message", "Minor Illusion", "Prestidigitation", "Starry Wisp", "Thunderclap", "True Strike", "Vicious Mockery"], 50, state.spellShopLevel == 0 ? 3 : 1) + } + break + case "cleric": + switch(state.spellShopLevel) { + case 9: + spellShopSelectSpells(["Astral Projection", "Gate", "Mass Heal", "Power Word Heal", "True Resurrection"], 50000) + if (state.spellShopAll) break + case 8: + spellShopSelectSpells(["Antimagic Field", "Control Weather", "Earthquake", "Holy Aura", "Sunburst"], 25000) + if (state.spellShopAll) break + case 7: + spellShopSelectSpells(["Conjure Celestial", "Divine Word", "Etherealness", "Fire Storm", "Plane Shift", "Power Word Fortify", "Regenerate", "Resurrection", "Symbol"], 20000, state.spellShopLevel == 7 ? 2 : 1) + if (state.spellShopAll) break + case 6: + spellShopSelectSpells(["Blade Barrier", "Create Undead", "Find the Path", "Forbiddance", "Harm", "Heroes' Feast", "Planar Ally", "Sunbeam", "True Seeing", "Word of Recall"], 10000, state.spellShopLevel == 6 ? 2 : 1) + if (state.spellShopAll) break + case 5: + spellShopSelectSpells(["Circle of Power", "Commune", "Contagion", "Dispel Evil and Good", "Flame Strike", "Geas", "Greater Restoration", "Hallow", "Insect Plague", "Legend Lore", "Mass Cure Wounds", "Planar Binding", "Raise Dead", "Scrying", "Summon Celestial"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Aura of Life", "Aura of Purity", "Banishment", "Control Weather", "Death Ward", "Divination", "Freedom of Movement", "Guardian of Faith", "Locate Creature", "Stone Shape"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Animate Dead", "Aura of Vitality", "Beacon of Hope", "Bestow Curse", "Clairvoyance", "Create Food and Water", "Daylight", "Dispel Magic", "Feign Death", "Glyph of Warding", "Magic Circle", "Mass Healing Ward", "Meld into Stone", "Protection from Energy", "Remove Curse", "Revivify", "Sending", "Speak with Dead", "Spirit Guardians", "Tongues", "Water Walk"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Aid", "Augury", "Blindness/Deafness", "Calm Emotions", "Continual Flame", "Enhance Ability", "Find Traps", "Gentle Repose", "Hold Person", "Lesser Restoration", "Locate Object", "Prayer of Healing", "Protection from Poison", "Silence", "Spiritual Weapon", "Warding Bond", "Zone of Truth"], 500, state.spellShopLevel == 2 ? 5 : 1) + if (state.spellShopAll) break + case 1: + spellShopSelectSpells(["Bane", "Bless", "Command", "Create or Destroy Water", "Cure Wounds", "Detect Evil and Good", "Detect Magic", "Detect Poison and Disease", "Guiding Bolt", "Healing Word", "Inflict Wounds", "Protection from Evil and Good", "Purify Food and Drink", "Sanctuary", "Shield of Faith"], 250, state.spellShopLevel == 1 ? 5 : 1) + if (state.spellShopAll) break + case 0: + spellShopSelectSpells(["Guidance", "Light", "Mending", "Resistance", "Sacred Flame", "Spare the Dying", "Thaumaturgy", "Toll the Dead", "Word of Radiance"], 50, state.spellShopLevel == 0 ? 3 : 1) + break + } + break + case "druid": + switch(state.spellShopLevel) { + case 9: + spellShopSelectSpells(["Foresight", "Shapechange", "Storm of Vengeance", "True Resurrection"], 50000) + if (state.spellShopAll) break + case 8: + spellShopSelectSpells(["Animal Shapes", "Antipathy/Sympathy", "Befuddlement", "Control Weather", "Earthquake", "Incendiary Cloud", "Sunburst", "Tsunami"], 25000) + if (state.spellShopAll) break + case 7: + spellShopSelectSpells(["Fire Storm", "Mirage Arcane", "Plane Shift", "Regenerate", "Reverse Gravity", "Symbol"], 20000, state.spellShopLevel == 7 ? 2 : 1) + if (state.spellShopAll) break + case 6: + spellShopSelectSpells(["Conjure Fey", "Find the Path", "Flesh to Stone", "Heal", "Heroes' Feast", "Move Earth", "Sunbeam", "Transport via Plants", "Wall of Thorns", "Wind Walk"], 10000, state.spellShopLevel == 6 ? 2 : 1) + if (state.spellShopAll) break + case 5: + spellShopSelectSpells(["Antilife Shell", "Awaken", "Commune with Nature", "Cone of Cold", "Conjure Elemental", "Contagion", "Geas", "Greater Restoration", "Insect Plague", "Mass Cure Wounds", "Planar Binding", "Reincarnate", "Scrying", "Tree Stride", "Wall of Stone"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Blight", "Charm Monster", "Confusion", "Conjure Woodland Beings", "Control Water", "Divination", "Dominate Beast", "Fire Shield", "Fount of Moonlight", "Freedom of Movement", "Giant Insect", "Grasping Vine", "Hallucinatory Terrain", "Ice Storm", "Locate Creature", "Polymorph", "Stone Shape", "Stoneskin", "Summon Elemental", "Wall of Fire"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Aura of Vitality", "Call Lightning", "Conjure Animals", "Daylight", "Dispel Magic", "Elemental Weapon", "Feign Death", "Meld into Stone", "Plant Growth", "Protection from Energy", "Revivify", "Sleet Storm", "Speak with Plants", "Summon Fey", "Water Breathing", "Water Walk", "Wind Wall"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Aid", "Animal Messenger", "Augury", "Barkskin", "Beast Sense", "Continual Flame", "Darkvision", "Enhance Ability", "Enlarge/Reduce", "Find Traps", "Flame Blade", "Flaming Sphere", "Gust of Wind", "Heat Metal", "Hold Person", "Lesser Restoration", "Locate Animals or Plants", "Locate Object", "Moonbeam", "Pass without Trace", "Protection from Poison", "Spike Growth", "Summon Beast"], 500, state.spellShopLevel == 2 ? 5 : 1) + if (state.spellShopAll) break + case 1: + spellShopSelectSpells(["Animal Friendship", "Charm Person", "Create or Destroy Water", "Cure Wounds", "Detect Magic", "Detect Poison and Disease", "Entangle", "Faerie Fire", "Fog Cloud", "Goodberry", "Healing Word", "Ice Knife", "Jump", "Longstrider", "Protection from Evil and Good", "Purify Food and Drink", "Speak with Animals", "Thunderwave"], 250, state.spellShopLevel == 1 ? 5 : 1) + if (state.spellShopAll) break + case 0: + spellShopSelectSpells(["Druidcraft", "Elementalism", "Guidance", "Mending", "Message", "Poison Spray", "Produce Flame", "Resistance", "Shillelagh", "Spare the Dying", "Starry Wisp", "Thorn Whip", "Thunderclap"], 50, state.spellShopLevel == 0 ? 3 : 1) + if (state.spellShopAll) break + break + } + break + case "paladin": + switch(state.spellShopLevel) { + case 5: + spellShopSelectSpells(["Banishing Smite", "Circle of Power", "Destructive Wave", "Dispel Evil and Good", "Geas", "Greater Restoration", "Raise Dead", "Summon Celestial"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Aura of Life", "Aura of Purity", "Banishment", "Death Ward", "Locate Creature", "Staggering Smite"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Aura of Vitality", "Blinding Smite", "Create Food and Water", "Crusader's Mantle", "Daylight", "Dispel Magic", "Elemental Weapon", "Magic Circle", "Remove Curse", "Revivify"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Aid", "Find Steed", "Gentle Repose", "Lesser Restoration", "Locate Object", "Magic Weapon", "Prayer of Healing", "Protection from Poison", "Shining Smite", "Warding Bond", "Zone of Truth"], 500, state.spellShopLevel == 2 ? 5 : 1) + if (state.spellShopAll) break + case 1: + spellShopSelectSpells(["Bless", "Command", "Compelled Duel", "Cure Wounds", "Detect Evil and Good", "Detect Magic", "Detect Poison and Disease", "Divine Favor", "Divine Smite", "Heroism", "Protection from Evil and Good", "Purify Food and Drink", "Searing Smite", "Shield of Faith", "Thunderous Smite", "Wrathful Smite"], 250, state.spellShopLevel == 1 ? 5 : 1) + if (state.spellShopAll) break + break + } + break + case "ranger": + switch(state.spellShopLevel) { + case 5: + spellShopSelectSpells(["Commune with Nature", "Conjure Volley", "Greater Restoration", "Steel Wind Strike", "Swift Quiver", "Tree Stride"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Conjure Woodland Beings", "Dominate Beast", "Freedom of Movement", "Grasping Vine", "Locate Creature", "Stoneskin", "Summon Elemental"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Conjure Animals", "Conjure Barrage", "Daylight", "Dispel Magic", "Elemental Weapon", "Lightning Arrow", "Meld into Stone", "Nondetection", "Plant Growth", "Protection from Energy", "Revivify", "Speak with Plants", "Summon Fey", "Water Breathing", "Water Walk", "Wind Wall"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Aid", "Animal Messenger", "Barkskin", "Beast Sense", "Cordon of Arrows", "Darkvision", "Enhance Ability", "Find Traps", "Gust of Wind", "Lesser Restoration", "Locate Animals or Plants", "Locate Object", "Magic Weapon", "Pass without Trace", "Protection from Poison", "Silence", "Spike Growth", "Summon Beast"], 500, state.spellShopLevel == 2 ? 5 : 1) + if (state.spellShopAll) break + case 1: + spellShopSelectSpells(["Alarm", "Animal Friendship", "Cure Wounds", "Detect Magic", "Detect Poison and Disease", "Ensnaring Strike", "Entangle", "Fog Cloud", "Goodberry", "Hail of Thorns", "Hunter's Mark", "Jump", "Longstrider", "Speak with Animals"], 250, state.spellShopLevel == 1 ? 5 : 1) + if (state.spellShopAll) break + break + } + break + case "sorcerer": + switch(state.spellShopLevel) { + case 9: + spellShopSelectSpells(["Gate", "Meteor Swarm", "Power Word Kill", "Time Stop", "Wish"], 50000) + if (state.spellShopAll) break + case 8: + spellShopSelectSpells(["Demiplane", "Dominate Monster", "Earthquake", "Incendiary Cloud", "Power Word Stun", "Sunburst"], 25000) + if (state.spellShopAll) break + case 7: + spellShopSelectSpells(["Delayed Blast Fireball", "Etherealness", "Finger of Death", "Fire Storm", "Plane Shift", "Prismatic Spray", "Reverse Gravity", "Teleport"], 20000, state.spellShopLevel == 7 ? 2 : 1) + if (state.spellShopAll) break + case 6: + spellShopSelectSpells(["Arcane Gate", "Chain Lightning", "Circle of Death", "Disintegrate", "Eyebite", "Flesh to Stone", "Globe of Invulnerability", "Mass Suggestion", "Move Earth", "Otiluke's Freezing Sphere", "Sunbeam", "True Seeing"], 10000, state.spellShopLevel == 6 ? 2 : 1) + if (state.spellShopAll) break + case 5: + spellShopSelectSpells(["Animate Objects", "Bigby's Hand", "Cloudkill", "Cone of Cold", "Creation", "Dominate Person", "Hold Monster", "Insect Plague", "Seeming", "Synaptic Static", "Telekinesis", "Teleportation Circle", "Wall of Stone"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Banishment", "Blight", "Charm Monster", "Confusion", "Dimension Door", "Dominate Beast", "Fire Shield", "Greater Invisibility", "Ice Storm", "Polymorph", "Stoneskin", "Vitriolic Sphere", "Wall of Fire"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Blink", "Clairvoyance", "Counterspell", "Daylight", "Dispel Magic", "Fear", "Fireball", "Fly", "Gaseous Form", "Haste", "Hypnotic Pattern", "Lightning Bolt", "Major Image", "Protection from Energy", "Sleet Storm", "Slow", "Stinking Cloud", "Tongues", "Vampiric Touch", "Water Breathing", "Water Walk"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Alter Self", "Arcane Vigor", "Blindness/Deafness", "Blur", "Cloud of Daggers", "Crown of Madness", "Darkness", "Darkvision", "Detect Thoughts", "Dragon's Breath", "Enhance Ability", "Enlarge/Reduce", "Flame Blade", "Flaming Sphere", "Gust of Wind", "Hold Person", "Invisibility", "Knock", "Levitate", "Magic Weapon", "Mind Spike", "Mirror Image", "Misty Step", "Phantasmal Force", "Scorching Ray", "See Invisibility", "Shatter", "Spider Climb", "Suggestion", "Web"], 500, state.spellShopLevel == 2 ? 5 : 1) + if (state.spellShopAll) break + case 1: + spellShopSelectSpells(["Burning Hands", "Charm Person", "Chromatic Orb", "Color Spray", "Comprehend Languages", "Detect Magic", "Disguise Self", "Expeditious Retreat", "False Life", "Feather Fall", "Fog Cloud", "Grease", "Ice Knife", "Jump", "Mage Armor", "Magic Missile", "Ray of Sickness", "Shield", "Silent Image", "Sleep", "Thunderwave", "Witch Bolt"], 250, state.spellShopLevel == 1 ? 5 : 1) + if (state.spellShopAll) break + case 0: + spellShopSelectSpells(["Acid Splash", "Blade Ward", "Chill Touch", "Dancing Lights", "Elementalism", "Fire Bolt", "Friends", "Light", "Mage Hand", "Mending", "Message", "Minor Illusion", "Poison Spray", "Prestidigitation", "Ray of Frost", "Shocking Grasp", "Sorcerous Burst", "Thunderclap", "True Strike"], 50, state.spellShopLevel == 0 ? 3 : 1) + if (state.spellShopAll) break + break + } + break + case "warlock": + log("warlock") + switch(state.spellShopLevel) { + case 9: + spellShopSelectSpells(["Astral Projection", "Dominate Person", "Foresight", "Gate", "Geas", "Greater Restoration", "Imprisonment", "Insect Plague", "Modify Memory", "Power Word Kill", "Seeming", "Summon Celestial", "Telekinesis", "True Polymorph", "Weird"], 50000) + case 8: + spellShopSelectSpells(["Befuddlement", "Demiplane", "Dominate Monster", "Glibness", "Power Word Stun"], 25000) + if (state.spellShopAll) break + case 7: + spellShopSelectSpells(["Dominate Beast", "Etherealness", "Finger of Death", "Fire Shield", "Forcecage", "Greater Invisibility", "Guardian of Faith", "Plane Shift", "Wall of Fire"], 20000, state.spellShopLevel == 7 ? 2 : 1) + if (state.spellShopAll) break + case 6: + spellShopSelectSpells(["Arcane Gate", "Circle of Death", "Create Undead", "Eyebite", "Summon Fiend", "Tasha's Bubbling Cauldron", "True Seeing"], 10000, state.spellShopLevel == 6 ? 2 : 1) + if (state.spellShopAll) break + case 5: + spellShopSelectSpells(["Blink", "Clairvoyance", "Confusion", "Contact Other Plane", "Daylight", "Dream", "Fireball", "Hold Monster", "Hunger of Hadar", "Jallarzi's Storm of Radiance", "Mislead", "Planar Binding", "Plant Growth", "Revivify", "Scrying", "Stinking Cloud", "Summon Aberration", "Synaptic Static", "Teleportation Circle"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Banishment", "Blight", "Charm Monster", "Dimension Door", "Hallucinatory Terrain", "Summon Aberration"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Counterspell", "Dispel Magic", "Fear", "Fly", "Gaseous Form", "Hunger of Hadar", "Hypnotic Pattern", "Magic Circle", "Major Image", "Remove Curse", "Summon Fey", "Summon Undead", "Tongues", "Vampiric Touch"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Cloud of Daggers", "Crown of Madness", "Darkness", "Enthrall", "Hold Person", "Invisibility", "Mind Spike", "Mirror Image", "Misty Step", "Ray of Enfeeblement", "Spider Climb", "Suggestion"], 500, state.spellShopLevel == 2 ? 5 : 1) + if (state.spellShopAll) break + case 1: + spellShopSelectSpells(["Armor of Agathys", "Arms of Hadar", "Bane", "Charm Person", "Comprehend Languages", "Detect Magic", "Expeditious Retreat", "Hellish Rebuke", "Hex", "Illusory Script", "Protection from Evil and Good", "Speak with Animals", "Tasha's Hideous Laughter", "Unseen Servant", "Witch Bolt"], 250, state.spellShopLevel == 1 ? 5 : 1) + if (state.spellShopAll) break + case 0: + spellShopSelectSpells(["Blade Ward", "Chill Touch", "Eldritch Blast", "Friends", "Mage Hand", "Mind Sliver", "Minor Illusion", "Poison Spray"], 50, state.spellShopLevel == 0 ? 3 : 1) + if (state.spellShopAll) break + break + } + break + case "wizard": + switch(state.spellShopLevel) { + case 9: + spellShopSelectSpells(["Astral Projection", "Foresight", "Gate", "Imprisonment", "Meteor Swarm", "Power Word Kill", "Prismatic Wall", "Shapechange", "Time Stop", "True Polymorph", "Weird", "Wish"], 50000) + if (state.spellShopAll) break + case 8: + spellShopSelectSpells(["Antimagic Field", "Antipathy/Sympathy", "Befuddlement", "Clone", "Control Weather", "Demiplane", "Dominate Monster", "Incendiary Cloud", "Maze", "Mind Blank", "Power Word Stun", "Sunburst", "Telepathy"], 25000) + if (state.spellShopAll) break + case 7: + spellShopSelectSpells(["Delayed Blast Fireball", "Etherealness", "Finger of Death", "Forcecage", "Mirage Arcane", "Mordenkainen's Magnificent Mansion", "Mordenkainen's Sword", "Plane Shift", "Prismatic Spray", "Project Image", "Reverse Gravity", "Sequester", "Simulacrum", "Symbol", "Teleport"], 20000, state.spellShopLevel == 7 ? 2 : 1) + if (state.spellShopAll) break + case 6: + spellShopSelectSpells(["Arcane Gate", "Chain Lightning", "Circle of Death", "Contingency", "Create Undead", "Disintegrate", "Drawmij's Instant Summons", "Eyebite", "Flesh to Stone", "Globe of Invulnerability", "Guards and Wards", "Magic Jar", "Mass Suggestion", "Move Earth", "Otiluke's Freezing Sphere", "Otto's Irresistible Dance", "Programmed Illusion", "Summon Fiend", "Sunbeam", "Tasha's Bubbling Cauldron", "True Seeing", "Wall of Ice"], 10000, state.spellShopLevel == 6 ? 2 : 1) + if (state.spellShopAll) break + case 5: + spellShopSelectSpells(["Animate Objects", "Bigby's Hand", "Circle of Power", "Cloudkill", "Cone of Cold", "Conjure Elemental", "Contact Other Plane", "Creation", "Dominate Person", "Dream", "Geas", "Hold Monster", "Jallarzi's Storm of Radiance", "Legend Lore", "Mislead", "Modify Memory", "Passwall", "Planar Binding", "Rary's Telepathic Bond", "Scrying", "Seeming", "Steel Wind Strike", "Summon Dragon", "Synaptic Static", "Telekinesis", "Teleportation Circle", "Wall of Force", "Wall of Stone", "Yolande's Regal Presence"], 5000, state.spellShopLevel == 5 ? 3 : 1) + if (state.spellShopAll) break + case 4: + spellShopSelectSpells(["Arcane Eye", "Banishment", "Blight", "Charm Monster", "Confusion", "Conjure Minor Elementals", "Control Water", "Dimension Door", "Divination", "Evard's Black Tentacles", "Fabricate", "Fire Shield", "Greater Invisibility", "Hallucinatory Terrain", "Ice Storm", "Leomund's Secret Chest", "Locate Creature", "Mordenkainen's Faithful Hound", "Mordenkainen's Private Sanctum", "Otiluke's Resilient Sphere", "Phantasmal Killer", "Polymorph", "Stoneskin", "Summon Aberration", "Summon Construct", "Summon Elemental", "Vitriolic Sphere", "Wall of Fire"], 2500, state.spellShopLevel == 4 ? 3 : 1) + if (state.spellShopAll) break + case 3: + spellShopSelectSpells(["Animate Dead", "Bestow Curse", "Blink", "Clairvoyance", "Counterspell", "Dispel Magic", "Fear", "Feign Death", "Fireball", "Fly", "Gaseous Form", "Glyph of Warding", "Haste", "Hypnotic Pattern", "Leomund's Tiny Hut", "Lightning Bolt", "Magic Circle", "Major Image", "Nondetection", "Phantom Steed", "Protection from Energy", "Remove Curse", "Sending", "Sleet Storm", "Slow", "Speak with Dead", "Stinking Cloud", "Summon Fey", "Summon Undead", "Tongues", "Vampiric Touch", "Water Breathing"], 1000, state.spellShopLevel == 3 ? 5 : 1) + if (state.spellShopAll) break + case 2: + spellShopSelectSpells(["Alter Self", "Arcane Lock", "Arcane Vigor", "Augury", "Blindness/Deafness", "Blur", "Cloud of Daggers", "Continual Flame", "Crown of Madness", "Darkness", "Darkvision", "Detect Thoughts", "Dragon's Breath", "Enhance Ability", "Enlarge/Reduce", "Flaming Sphere", "Gentle Repose", "Gust of Wind", "Hold Person", "Invisibility", "Knock", "Levitate", "Locate Object", "Magic Mouth", "Magic Weapon", "Melf's Acid Arrow", "Mind Spike", "Mirror Image", "Misty Step", "Nystul's Magic Aura", "Phantasmal Force", "Ray of Enfeeblement", "Rope Trick", "Scorching Ray", "See Invisibility", "Shatter", "Spider Climb", "Suggestion", "Web"], 500, state.spellShopLevel == 2 ? 5 : 1) + if (state.spellShopAll) break + case 1: + spellShopSelectSpells(["Alarm", "Burning Hands", "Charm Person", "Chromatic Orb", "Color Spray", "Comprehend Languages", "Detect Magic", "Disguise Self", "Expeditious Retreat", "False Life", "Feather Fall", "Find Familiar", "Fog Cloud", "Grease", "Ice Knife","Identify", "Illusory Script", "Jump", "Longstrider", "Mage Armor", "Magic Missile", "Protection from Evil and Good", "Ray of Sickness", "Shield", "Silent Image", "Sleep", "Tasha's Hideous Laughter", "Tenser's Floating Disk", "Thunderwave", "Unseen Servant", "Witch Bolt"], 250, state.spellShopLevel == 1 ? 5 : 1) + if (state.spellShopAll) break + case 0: + spellShopSelectSpells(["Acid Splash", "Blade Ward", "Chill Touch", "Dancing Lights", "Elementalism", "Fire Bolt", "Friends", "Light", "Mage Hand", "Mending", "Message", "Mind Sliver", "Minor Illusion", "Poison Spray", "Prestidigitation", "Ray of Frost", "Shocking Grasp", "Thunderclap", "Toll the Dead", "True Strike"], 50, state.spellShopLevel == 0 ? 3 : 1) + if (state.spellShopAll) break + break + } + break + } + + switch (state.spellShopStep) { + case 0: + text = `**Welcome to the Spell Shop** +Deals change every day!` + break + case 1: + text = "Spell purchased!" + break + case 2: + text = "You do not have enough gold!" + case 3: + text = "You already know that spell!" + } + + switch (state.spellShopStep) { + case 0: + case 1: + case 2: + case 3: + text += ` +Select a number from the list below to purchase a spell: + +` + let deals = findSpellShopDeals(state.spellShopClassName, state.spellShopLevel, false) + if (deals.length == 0) text += "There are no spells left for sale!\n" + for (var i = 0; i < deals.length; i++) { + let spellStoryCard = findSpellCard(deals[i].name) + let description = spellStoryCard == null ? "\nERROR: Story card is missing. You may import the latest story cards from the Hashtag DnD Github: https://github.com/raeleus/Hashtag-DnD/blob/master/story-cards.json\n\n" : `:\n${spellStoryCard.entry}\n\n` + let found = character.spells.find((element) => element == deals[i].name) != undefined + text += `${i + 1}. ${deals[i].name}${state.spellShopIsFree ? "" : ` for ${numberWithCommas(deals[i].price)} gold`}` + if (found) text += " (Already Known)" + text += description + } + + text += ` +${state.spellShopIsFree ? "These spells come at no cost!" : `You have ${numberWithCommas(gold)} gold`} +Enter the number or q to quit: +` + break + case 500: + text = "Thank you for shopping at the Spell Shop!" + break + } + + return text +} + function handleStragedyShop() { var character = getCharacter() var goldIndex = character.inventory.findIndex(x => x.name.toLowerCase() == "gold") @@ -781,11 +1122,11 @@ Select a number from the list below to purchase a card: ` if (state.cardDeals.length == 0) text += "There are no cards left for sale!\n" for (var i = 0; i < state.cardDeals.length; i++) { - text += `${i + 1}. Stragedy ${state.cardDeals[i]} Card for ${state.cardPrices[i]} gold\n` + text += `${i + 1}. Stragedy ${state.cardDeals[i]} Card for ${numberWithCommas(state.cardPrices[i])} gold\n` } text += ` -You have ${gold} gold +You have ${numberWithCommas(gold)} gold Enter the number or q to quit: ` break diff --git a/README.md b/README.md index 37c353d..b0c144b 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ See the [user guide here](https://github.com/raeleus/Hashtag-DnD/wiki). Watch the [tutorial video](https://youtu.be/E5TYU7rDaBQ). v. 0.5.0 +* Added Mastermind/Lockpicking Game +* Added Spell Shop - Make sure to import the latest story cards: https://github.com/raeleus/Hashtag-DnD/blob/master/story-cards.json * Added descriptions for all DnD Player's Handbook spells to story cards. v. 0.4.0 diff --git a/story-cards.json b/story-cards.json index ccc476d..54624a4 100644 --- a/story-cards.json +++ b/story-cards.json @@ -95,6 +95,14 @@ "description": "", "useForCharacterCreation": false }, + { + "keys": "Antipathy/Sympathy", + "value": "Spell that causes a creature to be frightened must move as far away from the caster as possible or to be charmed and must move as close as possible to the caster.", + "type": "spell", + "title": "Antipathy/Sympathy", + "description": "", + "useForCharacterCreation": false + }, { "keys": "Arcane Eye", "value": "Spell creates an invisible eye which allows the caster to see from that eye in every direction. The eye can be moved.", @@ -311,6 +319,14 @@ "description": "", "useForCharacterCreation": false }, + { + "keys": "Blindness/Deafness", + "value": "Spell that blinds or deafens the target.", + "type": "spell", + "title": "Blindness/Deafness", + "description": "", + "useForCharacterCreation": false + }, { "keys": "Blink spell, cast Blink", "value": "Spell with a chance to cause the caster to enter the ethereal plane at the end of their turn, preventing them from being targeted by attacks.", @@ -328,10 +344,10 @@ "useForCharacterCreation": false }, { - "keys": "Burning Hand", + "keys": "Burning Hands", "value": "Spell that causes flames to shoot from your hands causing damage in a cone in front of the caster.", "type": "spell", - "title": "Burning Hand", + "title": "Burning Hands", "description": "", "useForCharacterCreation": false }, @@ -552,10 +568,10 @@ "useForCharacterCreation": false }, { - "keys": "Conjure Minor Elemntals", + "keys": "Conjure Minor Elementals", "value": "Spell that summons elemental spirits that surround the caster in a range of fifteen feet. They cause acid, cold, fire, or lightning damage to enemies in this area.", "type": "spell", - "title": "Conjure Minor Elemntals", + "title": "Conjure Minor Elementals", "description": "", "useForCharacterCreation": false }, @@ -633,12 +649,28 @@ }, { "keys": "Counterspell", - "value": "A spell that attempts to interrupt a spell casted by an opponent.", + "value": "Spell that attempts to interrupt a spell casted by an opponent.", "type": "spell", "title": "Counterspell", "description": "", "useForCharacterCreation": false }, + { + "keys": "Create Food and Water", + "value": "Spell that creates either 45 pounds of food or ten gallons of clean water.", + "type": "spell", + "title": "Enlarge", + "description": "", + "useForCharacterCreation": false + }, + { + "keys": "Create Food", + "value": "Spell that creates 45 pounds of food. It is bland and spoils after 24 hours if uneaten.", + "type": "spell", + "title": "Create Food", + "description": "", + "useForCharacterCreation": false + }, { "keys": "Create Water", "value": "Spell that creates up to ten gallons of clean water in a container or by raining down in an area.", @@ -768,10 +800,10 @@ "useForCharacterCreation": false }, { - "keys": "Detect Good and Evil", + "keys": "Detect Evil and Good", "value": "Spell that allows the caster to sense celestials, elementals, fey, fiends, or undead and their location.", "type": "spell", - "title": "Detect Good and Evil", + "title": "Detect Evil and Good", "description": "", "useForCharacterCreation": false }, @@ -947,7 +979,7 @@ "keys": "Eldritch Blast", "value": "Spell that casts a beam of pure energy at a creature, causing magical force damage.", "type": "spell", - "title": "Eldritch Eldritch Blast", + "title": "Eldritch Blast", "description": "", "useForCharacterCreation": false }, @@ -983,6 +1015,14 @@ "description": "", "useForCharacterCreation": false }, + { + "keys": "Enlarge/Reduce", + "value": "Spell that increases or decreases the size of the target which affects how easy they are to move and the damage they output.", + "type": "spell", + "title": "Enlarge/Reduce", + "description": "", + "useForCharacterCreation": false + }, { "keys": "Ensnaring Strike", "value": "Spell that creates grasping vines around the target when it is hit with an attack that restrains the target.", @@ -1475,7 +1515,7 @@ "keys": "Heroes' Feast", "value": "Spell that magically conjures a feast that allows anyone who eats from it to have resistance to poison damage and cannot be frightened or poisoned.", "type": "spell", - "title": "Heroe's Feast", + "title": "Heroes' Feast", "description": "", "useForCharacterCreation": false }, @@ -1872,10 +1912,10 @@ "useForCharacterCreation": false }, { - "keys": "Meteor Storm", + "keys": "Meteor Swarm", "value": "Spell that sends explosive orbs down that cause fire and bludgeoning damage.", "type": "spell", - "title": "Meteor Storm", + "title": "Meteor Swarm", "description": "", "useForCharacterCreation": false }, @@ -1968,10 +2008,10 @@ "useForCharacterCreation": false }, { - "keys": "Modenkainen's Magnificent Mansion", + "keys": "Mordenkainen's Magnificent Mansion", "value": "Spell that creates a magical door that allows the caster and whoever they specify to enter an extradimensional mansion of any design. There are near transparent servants that do your bidding.", "type": "spell", - "title": "Modenkainen's Magnificent Mansion", + "title": "Mordenkainen's Magnificent Mansion", "description": "", "useForCharacterCreation": false }, @@ -2048,10 +2088,10 @@ "useForCharacterCreation": false }, { - "keys": "Pass Without Trace", + "keys": "Pass without Trace", "value": "Spell that creates an emanation around the caster that causes their allies to be more undetectable.", "type": "spell", - "title": "Pass Without Trace", + "title": "Pass without Trace", "description": "", "useForCharacterCreation": false }, @@ -2185,7 +2225,7 @@ }, { "keys": "Prismatic Wall", - "value": "Spelldescription", + "value": "Spell that creates a shimmering, impassible plane of light forms. It is comprised of seven layers, red, orange, yellow, green, blue, indigo, and violet. Each layer has different magical properties must take a different type of damage to be destroyed.", "type": "spell", "title": "Prismatic Wall", "description": "", @@ -2216,26 +2256,26 @@ "useForCharacterCreation": false }, { - "keys": "Protection From Energy", + "keys": "Protection from Energy", "value": "Spell that causes the target to have resistance against a specified type of damage.", "type": "spell", - "title": "Protection From Energy", + "title": "Protection from Energy", "description": "", "useForCharacterCreation": false }, { - "keys": "Protection From Evil and Good", + "keys": "Protection from Evil and Good", "value": "Spell that protects the target from aberrations, celestials, elementals, fey, fiends, and the undead. The target cannot be possessed, charmed, or frightened.", "type": "spell", - "title": "Protection From Evil and Good", + "title": "Protection from Evil and Good", "description": "", "useForCharacterCreation": false }, { - "keys": "Protection From Poison", + "keys": "Protection from Poison", "value": "Spell that prevents the target from being poisoned.", "type": "spell", - "title": "Protection From Poison", + "title": "Protection from Poison", "description": "", "useForCharacterCreation": false }, @@ -2400,10 +2440,10 @@ "useForCharacterCreation": false }, { - "keys": "See Invisiblity", + "keys": "See Invisibility", "value": "Spell that allows the caster to see invisible creatures and see into the ethereal plane.", "type": "spell", - "title": "See Invisiblity", + "title": "See Invisibility", "description": "", "useForCharacterCreation": false }, @@ -2744,10 +2784,10 @@ "useForCharacterCreation": false }, { - "keys": "Summon Abberation", + "keys": "Summon Aberration", "value": "Spell that allows the caster to summon an abberation that must follow the caster's commands.", "type": "spell", - "title": "Summon Abberation", + "title": "Summon Aberration", "description": "", "useForCharacterCreation": false },