import math import json import discord from gwendolyn_old.utils import cap STATS = [ "strength", "dexterity", "constitution", "intelligence", "wisdom", "charisma" ] def mod(statistic): """Calculates D&D modifier.""" modifier = math.floor((statistic-10)/2) if modifier >= 0: modifier = "+"+str(modifier) return modifier class LookupFuncs(): def __init__(self, bot): self.bot = bot self.saves = [ "strength_save", "dexterity_save", "constitution_save", "intelligence_save", "wisdom_save", "charisma_save" ] self.abilities = [ "acrobatics", "animal_handling", "arcana", "athletics", "deception", "history", "insight", "intimidation", "investigation", "medicine", "nature", "perception", "performance", "persuasion", "religion", "sleight_of_hand", "stealth", "survival" ] def _format_monster(self, monster): # Looks at the information about the monster and # returns that information in separate variables, # allowing Gwendolyn to know where to separate # the messages types = monster["type"] if monster["subtype"] != "": types += " ("+monster["subtype"]+")" stats = [] for stat in STATS: value = monster[stat] stats.append(f"**{cap(stat[:3])}:** {value} ({mod(value)})") stats = "\t".join(stats[:3]) + "\n" + "\t".join(stats[3:]) saving_throws = [] for save in self.saves: if save in monster: value = monster[save] if monster[save] >= 0: saving_throws.append(f"{cap(save[:3])} +{value}") else: saving_throws.append(f"{cap(save[:3])} {value}") if saving_throws: saving_throws = f"\n**Saving Throws:** {', '.join(saving_throws)}" else: saving_throws = "" skills = [] for skill in self.abilities: if skill in monster: skill_name = cap(skill.replace("_"," ")) if monster[skill] >= 0: skills.append(f"{skill_name} +{monster[skill]}") else: skills.append(f"{skill_name} {monster[skill]}") if skills: skills = f"\n**Skills:** {', '.join(skills)}" else: skills = "" vulnerabilities = monster["damage_vulnerabilities"] if vulnerabilities != "": vulnerabilities = "\n**Damage Vulnerabilities** "+vulnerabilities resistances = monster["damage_resistances"] if resistances != "": resistances = "\n**Damage Resistances** "+resistances immunities = monster["damage_immunities"] if immunities != "": immunities = "\n**Damage Immunities** "+immunities c_immunities = monster["condition_immunities"] if c_immunities != "": c_immunities = "\n**Condition Immunities** "+c_immunities special_abilities = "" if "special_abilities" in monster: for ability in monster["special_abilities"]: special_abilities += "\n\n***"+ability["name"]+".*** "+ability["desc"] act = "" if "actions" in monster: for action in monster["actions"]: act += "\n\n***"+action["name"]+".*** "+action["desc"] react = "" if "reactions" in monster: for reaction in monster["reactions"]: react += "\n\n***"+reaction["name"]+".*** "+reaction["desc"] legendaryActions = "" if "legendary_actions" in monster: for action in monster["legendary_actions"]: legendaryActions += "\n\n***"+action["name"]+".*** "+action["desc"] hit_dice = monster["hit_dice"] dice_amount = int(monster["hit_dice"].replace("d"," ").split()[0]) con_mod = math.floor((monster['constitution']-10)/2) if con_mod < 0: hit_dice += f" - {abs(con_mod) * dice_amount}" elif con_mod > 0: hit_dice += (f" + {con_mod * dice_amount}") new_part = "\n--------------------" monster_type = f"*{monster['size']} {types}, {monster['alignment']}*" basic_info = "\n**Armor Class** "+str(monster["armor_class"])+"\n**Hit Points** "+str(monster["hit_points"])+" ("+hit_dice+")\n**Speed **"+monster["speed"]+new_part+"\n" info = (monster_type+new_part+basic_info+stats+new_part+saving_throws+skills+vulnerabilities+resistances+immunities+c_immunities+"\n**Senses** "+monster["senses"]+"\n**Languages** "+monster["languages"]+"\n**Challenge** "+monster["challenge_rating"]) monster_info = [(info, monster['name']), (special_abilities, "Special Abilities"), (act, "Actions"), (react, "Reactions"), (legendaryActions, "Legendary Actions")] self.bot.log("Returning monster information") return monster_info # Looks up a monster async def monster_func(self, ctx, query): query = cap(query) self.bot.log("Looking up "+query) # 1-letter monsters don't exist if len(query) < 2: self.bot.log("Monster name too short") await ctx.send("I don't know that monster...") return # Opens "monsters.json" monster_file_path = "gwendolyn/resources/lookup/monsters.json" with open(monster_file_path,"r", encoding="utf-8") as file_pointer: data = json.load(file_pointer) for monster in data: if "name" in monster and str(query) == monster["name"]: self.bot.log("Found it!") monster_info = self._format_monster(monster) # Sends the received information. Separates into separate messages if # there is too much text await ctx.send(f"Result for \"{query}\"") for text, title in monster_info: if text != "": if len(text) < 2000: em = discord.Embed(title = title, description = text, colour=0xDEADBF) await ctx.channel.send(embed = em) else: index = text[:2000].rfind(".")+1 em1 = discord.Embed(title = title, description = text[:index], colour=0xDEADBF) await ctx.channel.send(embed = em1) em2 = discord.Embed(title = "", description = text[index+1:], colour=0xDEADBF) await ctx.channel.send(embed = em2) break else: self.bot.log("Monster not in database") await ctx.send("I don't know that monster...") # Looks up a spell async def spell_func(self, ctx, query): query = cap(query) self.bot.log("Looking up "+query) # Opens "spells.json" data = json.load(open('gwendolyn/resources/lookup/spells.json', encoding = "utf8")) if query in data: self.bot.log("Returning spell information") send_message = (f"***{query}***\n*{data[query]['level']} level {data[query]['school']}\nCasting Time: {data[query]['casting_time']}\nRange:{data[query]['range']}\nComponents:{data[query]['components']}\nDuration:{data[query]['duration']}*\n \n{data[query]['description']}") else: self.bot.log("I don't know that spell") send_message = "I don't think that's a spell" if len(send_message) > 2000: await ctx.send(send_message[:2000]) await ctx.send(send_message[2000:]) else: await ctx.send(send_message)