function getRandomInteger(min, max) { return Math.floor(Math.random() * (max - min + 1) ) + min; } function sanitizeText(text) { if (/^\s*>.*says? ".*/.test(text)) { text = text.replace(/^\s*>\s/, "") text = text.replace(/says? "/, "") text = text.replace(/"\n$/, "") } else if (/^\s*>\s.*/.test(text)) { text = text.replace(/^\s*>\s/, "") text = text.replace(/\.?\n$/, "") } return text } function getCharacterName(rawText) { var matches = rawText.match(/(?<=\s+> ).*(?=(\s+#)|( says? "))/) if (matches != null && matches[0].trim() != "") { return matches[0].trim() } matches = rawText.match(/.*(?= #)/) if (matches != null && matches[0].trim() != "") { return matches[0].trim() } return null } function getPossessiveName(name) { var possesiveName = "Your" if (name != "You") { possesiveName = name if (name.endsWith("s")) possesiveName += "'" else possesiveName += "'s" } return possesiveName } function getCommandName(command) { var args = getArguments(command) if (args.length == 0) return null return args[0] } const argumentPattern = /("[^"\\]*(?:\\[\S\s][^"\\]*)*"|'[^'\\]*(?:\\[\S\s][^'\\]*)*'|\/[^\/\\]*(?:\\[\S\s][^\/\\]*)*\/[gimy]*(?=\s|$)|(?:\\\s|\S)+)/g function getArguments(command) { var matches = command.match(new RegExp(argumentPattern)) var returnValue = [] matches.forEach(match => { match = match.replaceAll(/(^")|("$)/g, "").replaceAll(/\\"/g, '"') returnValue.push(match) }) return returnValue } function getArgument(command, index) { var args = getArguments(command) index++ if (index >= args.length) return null return args[index] } function getArgumentRemainder(command, index) { var counter = 0 const pattern = new RegExp(argumentPattern) while ((match = pattern.exec(command)) != null) { if (counter++ == index + 1) { return command.substring(match.index).replace(/^"/, "").replace(/"$/, "").replaceAll(/\\"/g, '"') } } } function searchArgument(command, pattern) { var index = searchArgumentIndex(command, pattern) if (index == -1) return null return getArgument(command, index) } function searchArgumentIndex(command, pattern) { var args = getArguments(command) if (args.length <= 1) return -1 args.splice(0, 1) const search = (element) => pattern.test(element) var index = args.findIndex(search) if (index != -1) return index return -1 } function arrayToOrPattern(array) { var pattern = "^" array.forEach(element => { pattern += `(${element})|` }) pattern += pattern.substring(0, pattern.length - 1) pattern += "$" return new RegExp(pattern, "gi") } function statsToOrPattern(stats) { var array = [] stats.forEach(element => { array.push(element.name) }) return arrayToOrPattern(array) } function getDice(rolltext) { var matches = rolltext.match(/\d+(?=d)/) if (matches != null) { return matches[0] } return 1 } function getSides(rolltext) { var matches = rolltext.match(/(?<=d)\d+/) if (matches != null) { return matches[0] } return 20 } function formatRoll(text) { var matches = text.match(/(?<=.*)\d*d\d+(?=.*)/) if (matches != null) { return matches[0] } matches = text.match(/\d+/) if (matches != null) { return "d" + matches[0] } return "d20" } function calculateRoll(rolltext) { rolltext = rolltext.toLowerCase() var dice = getDice(rolltext) var sides = getSides(rolltext) var score = 0; for (i = 0; i < dice; i++) { score += getRandomInteger(1, sides) } return score } function getCharacter(characterName) { if (characterName == null) characterName = state.characterName return state.characters.find((element) => element.name.toLowerCase() == characterName.toLowerCase()) } function hasCharacter(characterName) { return getCharacter(characterName) != null } function createCharacter(name) { var existingCharacter = getCharacter(name) if (existingCharacter != null) { existingCharacter.name = name existingCharacter.className = "adventurer" existingCharacter.summary = "An auto generated character. Use #create to create this character" existingCharacter.inventory = [] existingCharacter.spells = [] existingCharacter.stats = [] existingCharacter.spellStat = null existingCharacter.meleeStat = null existingCharacter.rangedStat = null existingCharacter.skills = [] existingCharacter.experience = 0 existingCharacter.health = 10 return existingCharacter } var character = { name: name, className: "adventurer", summary: "An auto generated character. Use #create to create this character", inventory: [], spells: [], stats: [], spellStat: null, meleeStat: null, rangedStat: null, skills: [], experience: 0, health: 10 } state.characters.push(character) return character } function deleteCharacter(name) { var index = state.characters.findIndex((element) => element.name == name) state.characters.splice(index, 1) } const levelSplits = [0, 300, 900, 2700, 6500, 14000, 23000, 34000, 48000, 64000, 85000, 100000, 120000, 140000, 165000, 195000, 225000, 265000, 305000, 355000] function getLevel(experience) { if (experience < 0) experience = 0 var level for (level = 0; level < levelSplits.length; level++) { if (experience < levelSplits[level]) break } return level } function getNextLevelXp(experience) { if (experience < 0) experience = 0 var level for (level = 0; level < levelSplits.length; level++) { if (experience < levelSplits[level]) return levelSplits[level] } return -1 } function addXpToAll(experience) { if (experience == 0) return "" var leveledUp = `\n[The party has gained ${experience} experience!]` state.characters.forEach(x => { const oldLevel = getLevel(x.experience) x.experience += experience const newLevel = getLevel(x.experience) if (newLevel > oldLevel) leveledUp += `\n[${x.name} has leveled up to ${newLevel}!]` }) return leveledUp } function getHealthMax(character) { if (character == null) character = getCharacter() var modifier = 0 var stat = character.stats.find((element) => element.name.toLowerCase() == "constitution") if (stat != null) modifier = getModifier(stat.value) var level = getLevel(character.experience) return 10 + level * (6 + modifier) } function getModifier(statValue) { return Math.floor((statValue - 10) / 2) } function findSpellCardIndex(name) { return storyCards.findIndex((element) => element.type == "spell" && element.keys == name) } String.prototype.plural = function(revert) { var plural = { '(quiz)$' : "$1zes", '^(ox)$' : "$1en", '([m|l])ouse$' : "$1ice", '(matr|vert|ind)ix|ex$' : "$1ices", '(x|ch|ss|sh)$' : "$1es", '([^aeiouy]|qu)y$' : "$1ies", '(hive)$' : "$1s", '(?:([^f])fe|([lr])f)$' : "$1$2ves", '(shea|lea|loa|thie)f$' : "$1ves", 'sis$' : "ses", '([ti])um$' : "$1a", '(tomat|potat|ech|her|vet)o$': "$1oes", '(bu)s$' : "$1ses", '(alias)$' : "$1es", '(octop)us$' : "$1i", '(ax|test)is$' : "$1es", '(us)$' : "$1es", '([^s]+)$' : "$1s" }; var singular = { '(quiz)zes$' : "$1", '(matr)ices$' : "$1ix", '(vert|ind)ices$' : "$1ex", '^(ox)en$' : "$1", '(alias)es$' : "$1", '(octop|vir)i$' : "$1us", '(cris|ax|test)es$' : "$1is", '(shoe)s$' : "$1", '(o)es$' : "$1", '(bus)es$' : "$1", '([m|l])ice$' : "$1ouse", '(x|ch|ss|sh)es$' : "$1", '(m)ovies$' : "$1ovie", '(s)eries$' : "$1eries", '([^aeiouy]|qu)ies$' : "$1y", '([lr])ves$' : "$1f", '(tive)s$' : "$1", '(hive)s$' : "$1", '(li|wi|kni)ves$' : "$1fe", '(shea|loa|lea|thie)ves$': "$1f", '(^analy)ses$' : "$1sis", '((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$': "$1$2sis", '([ti])a$' : "$1um", '(n)ews$' : "$1ews", '(h|bl)ouses$' : "$1ouse", '(corpse)s$' : "$1", '(us)es$' : "$1", 's$' : "" }; var irregular = { 'move' : 'moves', 'foot' : 'feet', 'goose' : 'geese', 'sex' : 'sexes', 'child' : 'children', 'man' : 'men', 'tooth' : 'teeth', 'person' : 'people', 'woman' : 'women', }; var uncountable = [ 'sheep', 'fish', 'deer', 'moose', 'series', 'species', 'money', 'rice', 'information', 'equipment', 'gold', 'bass', 'milk', 'food', 'water', 'bread', 'sugar', 'tea', 'cheese', 'coffee', 'currency', 'seafood', 'oil', 'software' ]; // save some time in the case that singular and plural are the same if(uncountable.indexOf(this.toLowerCase()) >= 0) return this; // check for irregular forms for(word in irregular){ if(revert){ var pattern = new RegExp(irregular[word]+'$', 'i'); var replace = word; } else{ var pattern = new RegExp(word+'$', 'i'); var replace = irregular[word]; } if(pattern.test(this)) return this.replace(pattern, replace); } if(revert) var array = singular; else var array = plural; // check for matches using regular expressions for(reg in array){ var pattern = new RegExp(reg, 'i'); if(pattern.test(this)) return this.replace(pattern, array[reg]); } return this; } function clamp(num, min, max) { return num <= min ? min : num >= max ? max : num } function toTitleCase(str) { return str.replace( /\w\S*/g, text => text.charAt(0).toUpperCase() + text.substring(1).toLowerCase() ); }