Compare commits

..

36 commits

Author SHA1 Message Date
raeleus
2a05c60a50 Fixed autocards not updating on output in certain circumstances. 2025-08-05 22:52:10 -07:00
raeleus
a0b34e059b Update version to 8.0 2025-08-04 22:31:29 -07:00
raeleus
8fe451aab8 Fixed configuration of Auto-Cards. 2025-08-04 22:24:32 -07:00
raeleus
28327a9ceb Added Auto-Cards by LewdLeah. 2025-08-03 23:36:24 -07:00
raeleus
226c82442b Fixed enemy spells showing the damage dice in the output text. 2025-05-17 10:55:15 -07:00
raeleus
8db353f486 Added allies. 2025-05-17 09:17:21 -07:00
raeleus
1a6a605251
Update README.md 2025-05-12 10:42:41 -07:00
raeleus
12ee7d35e3
Update README.md 2025-05-12 10:39:30 -07:00
raeleus
79c77c7c18
Update README.md 2025-05-12 10:35:20 -07:00
raeleus
89f9ee32a8
Update README.md 2025-05-12 10:30:03 -07:00
raeleus
2a40d2691a Update story-cards.json 2025-04-07 18:57:31 -07:00
raeleus
1931b51474 Update story-cards.json 2025-04-07 15:41:58 -07:00
raeleus
bd2c9b4792 Update README.md 2025-04-07 08:22:42 -07:00
raeleus
58cb8cd15d Remove line indicating that there is a guide for the memory minigame. 2025-04-06 07:11:29 -07:00
raeleus
98408feaf3 Added #memory 2025-04-06 00:01:59 -07:00
raeleus
29ee398f9d Added #reward 2025-04-05 07:10:38 -07:00
raeleus
38590f6e6c Fixed #addcard not giving out numbered cards and instead giving out random ones 2025-04-03 22:46:17 -07:00
raeleus
737b2aa335 Fixed #spellshop not giving properly randomized spells 2025-04-03 22:38:47 -07:00
raeleus
83790f66e0 Added default option to #itemshop 2025-04-03 22:31:20 -07:00
raeleus
2896dc0362 Added #takeweapon, #takearmor, #equip. 2025-04-03 21:32:50 -07:00
raeleus
5ebdcf7bea Update Output.js 2025-04-02 08:04:08 -07:00
raeleus
496490306e Only show items that haven't been bought 2025-04-02 07:56:45 -07:00
raeleus
b67e8db996 Change version to 6.0 2025-04-01 23:47:58 -07:00
raeleus
88331579b0 Added Item Shop 2025-04-01 23:43:43 -07:00
raeleus
cdacdf7cf2 Update version to 0.5.1 2025-01-20 17:12:42 -08:00
raeleus
2466c71804 Added Spell Shop 2025-01-18 09:18:49 -08:00
raeleus
336b15fe01 Added the combination to the failure message for lockpicking. 2025-01-05 23:59:44 -08:00
raeleus
88f0d3b5fe Update Input.js 2025-01-05 10:31:51 -08:00
raeleus
ecf2e1d036 Lockpicking no longer requires a character associated with it. 2025-01-05 10:30:51 -08:00
raeleus
d795d500ca Added Mastermind minigame. 2025-01-04 23:55:32 -08:00
raeleus
0102b9ea56 Update version to 0.5.0 2025-01-04 17:47:30 -08:00
raeleus
fd4be27608 Update README.md 2025-01-04 17:46:36 -08:00
raeleus
d9885330b8 Added the remaining DnD spell descriptions. 2025-01-04 15:49:58 -08:00
raeleus
6bd7a8b82a Update story-cards.json 2024-12-29 22:30:40 -08:00
raeleus
d4647f8832 Spells partial update. 2024-12-19 23:10:20 -08:00
raeleus
5b69740764 Alphabetized story cards. 2024-12-03 08:34:46 -08:00
6 changed files with 16182 additions and 161 deletions

View file

@ -1,4 +1,5 @@
const modifier = (text) => {
[text, stop] = AutoCards("context", text, stop);
return { text }
}

1500
Input.js

File diff suppressed because it is too large Load diff

6270
Library.js

File diff suppressed because it is too large Load diff

725
Output.js
View file

@ -1,5 +1,8 @@
const modifier = (text) => {
if (state.show == null) return { text }
if (state.show == null) {
text = AutoCards("output", text);
return { text }
}
var character = getCharacter()
var possessiveName = character == null ? null : getPossessiveName(character.name)
@ -87,12 +90,72 @@ const modifier = (text) => {
break
}
break
case "setupAlly":
switch (state.setupAllyStep) {
case 0:
text += `***ALLY CREATION***\nWould you like to use a preset ally? (y/n/q to quit)\n`
break
case 1:
text += `What is the ally's name? This must be a unique name that has no duplicates in the current encounter. Typing the name of an existing ally will modify that ally's properties. Type q to quit.\n`
break
case 2:
text += `${!state.newAlly ? "Ally name already exists. You are now modifying the existing ally " + state.tempAlly.name + ". " : ""}What is the ally's health? This can be any positive integer or a dice roll (ie. 3d6+5). Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.health : ""}\n`
break
case 3:
text += `What is the ally's armor class (AC)? This can be any positive integer with 10 being easy and 20 being incredibly difficult. It can also be a dice roll (ie. 2d4+5). Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.ac : ""}\n`
break
case 4:
text += `What is the ally's hit modifier? This affects how accurate their attacks are. This can be any integer. 0 is normal accuracy. Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.hitModifier : ""}\n`
break
case 5:
text += `What is the ally's damage? This can be any positive integer or a dice roll (ie. 2d6+5). The dice roll is calculated at the time of each attack. Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.damage : ""}\n`
break
case 6:
text += `What is the ally's initiative? Initiative controls turn order. This can be any positive integer with higher numbers going first in battle. This can also be a dice roll (ie. 1d20+3). Type q to quit.${!state.newAlly ? " Type default to leave as current value " + state.tempAlly.initiative : ""}\n`
break
case 7:
text += `Enter the name of a spell that the ally knows. If it can target this spell at an enemy character, add a dice roll for the damage calculation after it (ie. Ray of Frost3d6+2). Type s to stop entering spells or type q to quit.${!state.newAlly ? " Type e to erase all current spells." : ""}\n`
break
case 8:
text += `Enter the name of another spell that the ally knows. If it can target this spell at an enemy character, add a dice roll for the damage calculation after it (ie. Ray of Frost3d6+2). Type s to stop entering spells or type q to quit.\n`
break
case 100:
text += `What ally preset will you choose?\nHeroes\n1. Fighter\n2. Cleric\n3. Rogue\n4. Ranger\n5. Barbarian\n6. Bard\n7. Druid\n8. Monk\n9. Paladin\n10. Wizard\n11. Sorcerer\n12. Warlock\n13. Artificer`
text += `\n\nHumanoid\n14. Commoner\n15. Bandit\n16. Guard\n17. Cultist\n18. Acolyte\n19. Apprentice\n20. Witch\n21. Buccaneer\n22. Spy\n123. Captain\n24. Bard\n25. Berserker\n26 Priest\n27. Knight\n28. Archer\n29. Warrior\n30. Conjurer\n31. Mage\n32. Assassin\n33. Evoker\n34. Necromancer\n35. Champion\n36. Warlord\n37. Archmage\n38. Archdruid`
text += `\n\nFamiliars\n39. Ape\n40. Badger\n41. Bat\n42. Black Bear\n43. Boar\n44. Brown Bear\n45. Camel\n46. Cat\n47. Constrictor Snake\n48. Crab\n49. Crocodile\n50. Dire Wolf\n51. Draft Horse\n52. Elephant\n53. Elk\n54. Frog\n55. Giant Badger\n56. Giant Crab\n57. Giant Goat\n58. Giant Seahorse\n59. Giant Spider\n60. Giant Weasel\n61. Goat\n62. Hawk\n63. Imp\n64. Lion\n65. Lizard\n66. Mastiff\n67. Mule\n68. Octopus\n69. Owl\n70. Panther\n71. Pony\n72. Pseudodragon\n73. Quasit\n74. Rat\n75. Raven\n76. Reef Shark\n77. Riding Horse\n78. Scorpion\n79. Skeleton\n80. Slaad Tadpole\n81. Sphinx of Wonder\n82. Spider\n83. Sprite\n84. Tiger\n85. Venomous Snake\n86. Warhorse\n87. Weasel\n88. Wolf\n89. Zombie`
text += `\n\nEnter the number or q to quit. If you want to rename the ally, add a space and type the name\n(ie. 25 Thuggish Zombie B)\n`
break
case 500:
var hashtag = `#addally "${state.tempAlly.name}" ${state.tempAlly.health} ${state.tempAlly.ac} ${state.tempAlly.hitModifier} ${state.tempAlly.damage} ${state.tempAlly.initiative}`
for (var spell of state.tempAlly.spells) {
hashtag += ` "${spell}"`
}
text += `${state.tempAlly.name} has been created.\nType #showallies to show the list of all allies.\nCopy and paste the following hashtag to create another identical ally like this:\n${hashtag}\n***********\n`
break;
case null:
text += `[Ally creation has been aborted!]\n`
break
}
break
case "stragedy":
text += handleStragedy()
break
case "stragedyShop":
text += handleStragedyShop()
break
case "spellShop":
text += handleSpellShop()
break
case "itemShop":
text += handleItemShop()
break
case "lockpicking":
text += handleLockpicking()
break
case "memory":
text += handleMemory()
break
case "bio":
text += `*** ${possessiveName.toUpperCase()} BIO ***\n`
text += `Class: ${character.className}\n`
@ -309,6 +372,20 @@ const modifier = (text) => {
}
}
text += "******************\n\n"
break
case "showAllies":
text += "*** ALLIES ***\n"
if (state.allies.length == 0) {
text += "There are no allies present here. Type #encounter to generate a scripted set or #addally to add your own\n"
} else {
var index = 0
for (var ally of state.allies) {
text += `${++index}. ${toTitleCase(ally.name)} (Health: ${ally.health} AC: ${ally.ac} Initiative: ${ally.initiative})\n`
}
}
text += "******************\n\n"
break
case "initiative":
@ -440,6 +517,12 @@ const modifier = (text) => {
text += "\n\n--Inventory--"
text += "\n#take (quantity) item"
text += "\n Adds the specified quantity of item to the character's inventory. If a quantity is omitted, it's assumed to be 1. The words the, a, and an are ignored. Quotes are not necessary."
text += "\n#takeweapon damage_dice hit_bonus ability weapon_name"
text += "\n Allows a character to manually add a weapon to their inventory that is compatible with the #equip command. It is highly recommended to use #itemstore instead. damage_dice is the dice roll (e.g. 1d12+2) used to calculate the damage of the weapon. hit_bonus is a positive or negative number that modifies how accurate the weapon is. Ability is the base ability that is used in conjunction with the weapon. Typically, melee weapons use strength and ranged weapons use dexterity."
text += "\n#takearmor ac weapon_name"
text += "\n Allows a character to manually add armor to their inventory that is compatible with the #equip command. It is highly recommended to use #itemstore instead. ac is the armor class or how hard the character is to hit. If you have an item that adds to the current armor class, precede the number with a plus sign (e.g. +2)."
text += "\n#equip weapon_or_armor_name"
text += "\n Equips a weapon or armor and automatically changes the character's damage/weapon proficiency or armor class respectively. weapon_or_armor_name must be a weapon or type of armor purchased through #itemshop or added to the character inventory through #takeweapon or #takearmor. Shields should be equipped after equipping armor because shield AC is added to the total."
text += "\n#buy (buy_quantity) buy_item (sell_quantity) sell_item"
text += "\n Adds the specified buy_quantity of the buy_item to the character's inventory and also removes the sell_quantity of sell_item. If quantities are omitted, they are assumed to be 1. Quotes are necessary for items with spaces in the name. The words for, with, the, a, and an are ignored."
text += "\n#sell (sell_quantity) sell_item (buy_quantity) buy_item"
@ -448,6 +531,10 @@ const modifier = (text) => {
text += "\n Removes the specified quantity of item from the character's inventory. If a quantity is omitted, it's assumed to be 1. The words the, a, and an are ignored. Quotes are not necessary."
text += "\n#give other_character (quantity or all|every) item"
text += "\n Removes the quantity of item from the character's inventory and adds it to the other_character's inventory. If a quantity is omitted, it's assumed to be 1. The words the, a, and an are ignored. Quotes are not necessary for the item."
text += "\n#itemshop (default|weapons|armor|tools|gear|common|uncommon|rare|very rare|legendary|artifact) (free) (all)"
text += "\n This opens the items shop where characters can spend gold to purchase new equipment. default is a general store with a variety of items and a small chance for magically enhanced loot. The selection is randomized based on the day. Include the argument \"free\" to not require gold to purchase the item. Include the argument \"all\" to list all available items. Otherwise, the list is randomized and a small selection of the item list is presented."
text += "\n#reward (count) (default|weapons|armor|tools|gear|common|uncommon|rare|very rare|legendary|artifact)"
text += "\n Gives the character a random item selected from the given list. count determines how many rewards are drawn (default is 1). The default list has a weighted chance of drawing from any of the lists with increasing rarity."
text += "\n#renameitem original_name new_name"
text += "\n Renames the item indicated by original_name to the new_name. The quantity remains the same. Quotes are necessary for items with spaces in the name."
text += "\n#inventory"
@ -464,6 +551,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"
@ -475,7 +564,9 @@ const modifier = (text) => {
text += "\n#addenemy name health ac hitModifier damage initiative spells"
text += "\n Adds the specified enemy to the list of enemies. health, ac, hitModifier, damage, and initiative can be numbers or dice rolls such as 3d6+5. Type the name in quotes if the name contains a space. The rest of the parameters can be a list of spells. Each spell must be typed in quotes if it has a space. If the spell does damage, write the name and damage roll in the following format: \"Ray of Frost5d10\""
text += "\n#removeenemy name or index"
text += "\n Removes the enemy as specified by the name or index. To delete multiple enemies, type the numbers with spaces or commas between them. This is safer than calling #removenote multiple times because the numbers shift as enemies are deleted. Quotes are not necessary."
text += "\n Removes the enemy as specified by the name or index. To delete multiple enemies, type the numbers with spaces or commas between them. This is safer than calling #removeenemy multiple times because the numbers shift as enemies are deleted. Quotes are not necessary."
text += "\n#clearenemies"
text += "\n Removes all enemies."
text += "\n#initiative"
text += "\n Assigns initiative to all characters and enemies. This begins combat."
text += "\n#turn"
@ -487,6 +578,18 @@ const modifier = (text) => {
text += "\n#flee (difficulty_class or automatic|effortless|easy|medium|hard|impossible)"
text += "\n Attempt to flee from combat. If the difficulty is not specified, the default difficulty will be used instead."
text += "\n\n--Allies--"
text += "\n#setupally"
text += "\nFollow prompts to create an ally from a template or completely from scratch. It will be added to the existing encounter if there is one already specified."
text += "\n#showallies"
text += "\n Shows the list of current allies."
text += "\n#addally name health ac hitModifier damage initiative spells"
text += "\n Adds the specified ally to the list of allies. health, ac, hitModifier, damage, and initiative can be numbers or dice rolls such as 3d6+5. Type the name in quotes if the name contains a space. The rest of the parameters can be a list of spells. Each spell must be typed in quotes if it has a space. If the spell does damage, write the name and damage roll in the following format: \"Ray of Frost5d10\""
text += "\n#removeally name or index"
text += "\n Removes the ally as specified by the name or index. To delete multiple allies, type the numbers with spaces or commas between them. This is safer than calling #removeally multiple times because the numbers shift as allies are deleted. Quotes are not necessary."
text += "\n#clearallies"
text += "\n Removes all allies."
text += "\n\n--Locations--"
text += "\n#createlocation [(x) (y) or (here|far) or (distance)] location_name"
text += "\n Creates a location at the given coordinates. The coordinates must be integers. If the coordinates are not provided, they are randomized within a range of 10 units from the party's current location. You can also use \"here\" to indicate that the location is at party's coordinates. \"far\" indicates that the coordinates will be randomly generated 50-100 units away. You may also just specify a distance. Multiple locations may exist at the same coordinates. A story card is created for the location. Quotes are not necessary."
@ -523,6 +626,14 @@ const modifier = (text) => {
text += "\n#stragedy (automatic|effortless|easy|medium|hard|impossible)"
text += "\n Initiates a game of Stragedy, a card game played against an AI opponent. Specifying a difficulty (default is easy) grants the opponent a corresponding deck. Please see the game manual on github for rules, tactics, and a complete tutorial: github.com/raeleus/Hashtag-DnD/"
text += "\n\n--Lockpicking Minigame--"
text += "\n#lockpick (automatic|effortless|easy|medium|hard|impossible)"
text += "\n Initiates a lockpicking minigame similar to Mastermind where you have to guess the correct combination with a limited number of tries in order to defeat a lock. Specifying a difficulty (default is easy) sets the number of combinations and tries accordingly. Please see the game manual on github for rules, tactics, and a complete tutorial: github.com/raeleus/Hashtag-DnD/"
text += "\n\n--Memory Minigame--"
text += "\n#memory (automatic|effortless|easy|medium|hard|impossible)"
text += "\n Initiates a memory minigame where you have to flip cards one at a time until you make a matching pair. You only have a set number of turns to finish the game. Specifying a difficulty (default is easy) sets the number of cards and maximum turns accordingly."
text += "\n\n--Danger Zone--"
text += "\n#reset"
text += "\n Removes all characters, locations, and notes. Changes all settings to their defaults. Use with caution!"
@ -537,6 +648,8 @@ const modifier = (text) => {
}
state.show = null
text = AutoCards("output", text);
return { text }
}
@ -563,6 +676,115 @@ function mapReplace(map, x, y, character) {
return map
}
function handleMemory() {
let text = " "
switch (state.memoryTurn) {
case "game":
text = `**Memory**
Select a card from below by typing its number or type q to quit:
`
let counter = 0
for (let y = 0; y < state.memoryHeight; y++) {
for (let x = 0; x < state.memoryWidth; x++) {
counter++
const solved = state.memorySolved[counter - 1] != null
let cardText = ""
if (solved) {
cardText = state.memoryCards[counter - 1]
} else {
cardText = counter
}
text += `${cardText.toString().length == 1 ? " " : ""}${cardText} `
}
text += "\n"
}
text += `
It is turn ${state.memoryTurns} of ${state.memoryMaxTurns}`
break
case "win":
text = `You won the game in ${state.memoryTurns} out of ${state.memoryMaxTurns} turns!\n`
let counter1 = 0
for (let y = 0; y < state.memoryHeight; y++) {
for (let x = 0; x < state.memoryWidth; x++) {
counter1++
text += `${state.memoryCards[counter1 - 1]} `
}
text += "\n"
}
break
case "lose":
text = `After ${state.memoryMaxTurns} turns, you were unable to complete the game.\n`
let counter2 = 0
for (let y = 0; y < state.memoryHeight; y++) {
for (let x = 0; x < state.memoryWidth; x++) {
counter2++
text += `${state.memoryCards[counter2 - 1]} `
}
text += "\n"
}
break
case "forfeit":
text = "You decided to give up on finishing the game.\n"
let counter3 = 0
for (let y = 0; y < state.memoryHeight; y++) {
for (let x = 0; x < state.memoryWidth; x++) {
counter3++
text += `${state.memoryCards[counter3 - 1]} `
}
text += "\n"
}
break
}
return text
}
function handleLockpicking() {
var text = " "
switch (state.lockpickingTurn) {
case "intro":
text = `**Mastermind**
Welcome to Mastermind! A minigame to stand in for lockpicking, hacking, and other tasks of skill.
Please see the game manual on github for rules, tactics, and a complete tutorial:
github.com/raeleus/Hashtag-DnD/
You must solve the ${state.lockpickingSlots} color combination within ${state.lockpickingGuessMax} guesses!
Colors: r (red), y (yellow), w (white), g (green), o (orange), b (blue)
Enter your first guess below by typing the letter for each color. Type "q" to quit:
`
break
case "game":
if (state.lockpickingInput.length != state.lockpickingSlots) text = `\nAn incorrect number of colors was input. Only type ${state.lockpickingSlots} letters!\n`
else text = `
Correct: ${state.lockpickingCorrect}. Wrong position: ${state.lockpickingWrongPlace}. ${state.lockpickingGuessMax - state.lockpickingGuesses} ${state.lockpickingGuessMax - state.lockpickingGuesses == 1 ? "try" : "tries"} left.
Colors: r (red), y (yellow), w (white), g (green), o (orange), b (blue)
Enter your guess below by typing the letter for each color. Type "q" to quit:
`
break
case "win":
text = `You solved the combination with ${state.lockpickingGuesses} ${state.lockpickingGuesses == 1 ? "guess" : "guesses"}!`
break
case "lose":
text = `After ${state.lockpickingGuesses} ${state.lockpickingGuesses == 1 ? "guess" : "guesses"}, you were unable to solve the combination...
The combination was ${state.lockpickingCombination}`
break
case "forfeit":
text = "You decided to give up on solving the combination."
break
}
return text
}
function handleStragedy() {
var character = getCharacter()
var haveWord = character.name == "You" ? "have" : "has"
@ -690,6 +912,501 @@ Type d to draw a card. ${!hasJokerOnBattlefield ? "Type r to retire. " : ""}Type
return text
}
function itemShopPushDeal(items, name) {
let quantity = 1
let storyCardName = name
name = itemShopConvertGenericName(name)
state.itemShopDeals.push({
className: state.itemShopCategoryName,
name: name,
storyCardName: storyCardName,
price: 0,
quantity: quantity,
bought: false
})
}
var itemShopSeed
function itemShopSelectItems(items, numberOfItems) {
if (numberOfItems == null) numberOfItems = 1
itemShopSeed += 100
if (state.itemShopAll) {
for (let i = 0; i < items.length; i++) {
itemShopPushDeal(items, items[i])
}
return
}
shuffle(items, itemShopSeed)
for (let i = 0; i < numberOfItems; i++) {
itemShopPushDeal(items, items[i])
}
}
function handleItemShop() {
var character = getCharacter()
var goldIndex = character.inventory.findIndex(x => x.name.toLowerCase() == "gold")
var gold = goldIndex == -1 ? 0 : character.inventory[goldIndex].quantity
var text = " "
itemShopSeed = state.day
if (state.itemShopDeals == null || state.itemShopClearDeals) state.itemShopDeals = []
if (findItemShopDeals(state.itemShopCategoryName).length == 0) switch(state.itemShopCategoryName) {
case "weapons":
itemShopSelectItems(weaponsList, 5)
break
case "armor":
itemShopSelectItems(armorList, 5)
break
case "tools":
itemShopSelectItems(toolsList, 5)
break
case "gear":
itemShopSelectItems(gearList, 10)
break
case "common":
itemShopSelectItems(commonList, 5)
break
case "uncommon":
itemShopSelectItems(uncommonList, 5)
break
case "rare":
itemShopSelectItems(rareList, 5)
break
case "phenomenal":
itemShopSelectItems(phenomenalList, 5)
break
case "legendary":
itemShopSelectItems(legendaryList, 200000)
break
case "artifact":
itemShopSelectItems(artifactList, 5)
break
case "default":
let shuffled = [...weaponsList].sort(() => 0.5 - Math.random());
let list = shuffled.slice(0, 3)
shuffled = [...armorList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 3))
shuffled = [...toolsList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 3))
shuffled = [...gearList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 5))
let rand = Math.random()
if (rand <= .50) {
shuffled = [...commonList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 1))
} else if (rand <= .70) {
shuffled = [...uncommonList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 1))
} else if (rand <= .86) {
shuffled = [...rareList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 1))
} else if (rand <= .94) {
shuffled = [...phenomenalList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 1))
} else if (rand <= .98) {
shuffled = [...legendaryList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 1))
} else {
shuffled = [...artifactList].sort(() => 0.5 - Math.random());
list = list.concat(shuffled.slice(0, 1))
}
itemShopSelectItems(list, 15)
break
}
switch (state.itemShopStep) {
case 0:
text = `**Welcome to the Item Shop**
-${toTitleCase(state.itemShopCategoryName)}-
Deals change every day!`
break
case 1:
text = "Item purchased!"
break
case 2:
text = "You do not have enough gold!"
}
switch (state.itemShopStep) {
case 0:
case 1:
case 2:
text += `
Select a number from the list below to purchase an item:
`
let deals = findItemShopDeals(state.itemShopCategoryName, false)
deals = deals.filter(item => !item.bought)
if (deals.length == 0) text += "There are no items left for sale!\n"
for (var i = 0; i < deals.length; i++) {
let itemStoryCard = findItemCard(deals[i].name, deals[i].storyCardName)
let description = itemStoryCard == 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${itemStoryCard.entry}\n\n`
deals[i].price = itemStoryCard == null ? 0 : parseInt(itemStoryCard.description.split(",")[0])
if (itemStoryCard != null && itemStoryCard.type == "weapon") {
deals[i].damage = itemStoryCard.description.split(",")[1]
deals[i].toHitBonus = itemStoryCard.description.split(",")[2]
deals[i].ability = itemStoryCard.description.split(",")[3]
} else if (itemStoryCard != null && itemStoryCard.type == "armor") {
deals[i].ac = itemStoryCard.description.split(",")[1]
}
text += `${i + 1}. ${deals[i].name}${state.itemShopIsFree ? "" : ` for ${numberWithCommas(deals[i].price)} gold`}`
text += description
}
text += `
${state.itemShopIsFree ? "These items 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 Item Shop!"
break
}
return text
}
function spellShopPushDeal(items, name, price) {
state.spellShopDeals.push({
className: state.spellShopClassName,
level: state.spellShopLevel,
name: name,
price: price,
bought: false
})
}
var spellShopSeed
function spellShopSelectSpells(spells, price, numberOfSpells) {
if (numberOfSpells == null) numberOfSpells = 1
spellShopSeed += 100
index = Math.floor(getRandom(spellShopSeed) * spells.length)
if (state.spellShopAll) {
for (const spell of spells) {
spellShopPushDeal(spells, spell, price)
}
return
}
shuffle(spells, spellShopSeed)
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":
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")
@ -738,11 +1455,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

View file

@ -4,18 +4,43 @@ A Scenario script for AI Dungeon
Hashtag DnD is a scripted AI Dungeon scenario that gives you a variety of tools to enhance your adventure!
Features:
Fully working inventory system<br>
Health points and a focus on strategic combat.<br>
Fully working inventory system, loot drops, and item/spell shops<br>
Hit points, turn-based battles, and a focus on strategic combat<br>
Skill/Ability based gameplay guarantees a challenge at every turn<br>
Advanced dice rolling syntax allows you to set the precise odds<br>
Unlimited party size with each character having their own inventory, stats, and biographies<br>
Multiplayer compatible<br>
Personalized note system that does not take up context space<br>
Create locations to travel to and view them in a map
Create locations to travel to and view them in a map<br>
Minigames including Mastermind, Memory, and Stragedy, a fully developed trading card game
See the [user guide here](https://github.com/raeleus/Hashtag-DnD/wiki).
Watch the [tutorial video](https://youtu.be/E5TYU7rDaBQ).
Hashtag-DnD has another new scenario! Check out the [VTOL-Knights Repository](https://github.com/raeleus/Hashtag-DnD/tree/VTOL-Knights)
This script implements Auto-Cards by LewdLeah. See more details [on github](https://github.com/LewdLeah/Auto-Cards)
v. 0.8.0
* Added Auto-Cards by LewdLeah. This feature is disabled by default. Activate it by changing the "Disabled" setting to "false" in the "Configure Auto-Cards" story card.
v. 0.7.0
* Added allies which are NPC characters that fight alongside the characters in encounters
* Fixed enemy spells showing the damage dice in the output text.
v. 0.6.0
* Added Memory/Matchmaking Game
* Added Item Shop - Make sure to import the latest story cards: https://github.com/raeleus/Hashtag-DnD/blob/master/story-cards.json
* Added commands #takeweapon, #takearmor, and #equip to allow automatic stat changes when using gear
* Added command #reward for random loot dropping
* Added descriptions for all DnD Player's Handbook and Dungeon Master's Guide items to story cards
* Fixed #spellshop not giving properly randomized spells
* Fixed #addcard not giving out numbered cards and instead giving out random ones
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
* Added Stragedy Trading Card Game

File diff suppressed because it is too large Load diff