fixing blackjack and improving lookup

This commit is contained in:
Nikolaj
2022-01-28 11:33:47 +01:00
parent cbf2ca765e
commit 05cd5831e1
10 changed files with 219 additions and 160 deletions

View File

@ -18,13 +18,13 @@ class LookupCog(commands.Cog):
@cog_ext.cog_slash(**params["spell"])
async def spell(self, ctx, query):
"""Look up a spell."""
await self.bot.lookup_funcs.spellFunc(ctx, query)
await self.bot.lookup_funcs.spell_func(ctx, query)
# Looks up a monster
@cog_ext.cog_slash(**params["monster"])
async def monster(self, ctx, query):
"""Look up a monster."""
await self.bot.lookup_funcs.monsterFunc(ctx, query)
await self.bot.lookup_funcs.monster_func(ctx, query)
def setup(bot):

View File

@ -1,3 +1,5 @@
"""Exceptions for Gwendolyn"""
class GameNotInDatabase(Exception):
def __init__(self, game: str, channel: str):
self.message = f"There is no {game} game in channel {channel}"

View File

@ -65,7 +65,7 @@ class Blackjack(CardGame):
def __init__(self, bot):
"""Initialize the class."""
super().__init__(bot, "blackjack", DrawBlackjack(bot, self), 4)
super().__init__(bot, "blackjack", DrawBlackjack, 4)
default_buttons = ["Hit", "Stand", "Double", "Split"]
self.default_buttons = [(i, [i, "0"], 1) for i in default_buttons]
@ -295,7 +295,7 @@ class Blackjack(CardGame):
return user_hands, all_standing, pre_all_standing
def _blackjack_finish(self, channel: Messageable):
async def _blackjack_finish(self, channel: Messageable):
"""
Generate the winnings message after the blackjack game ends.
@ -442,7 +442,7 @@ class Blackjack(CardGame):
self.bot.log(log_message, str(channel.id))
await self._blackjack_loop(channel, game_round+1, game_id)
else:
await channel.send(self._blackjack_finish(channel))
await channel.send(await self._blackjack_finish(channel))
async def _get_hand_number(self, ctx: IntCont, command: str,
hands_amount: int):
@ -776,7 +776,7 @@ class Blackjack(CardGame):
if len(game["user hands"]) == 0:
await ctx.channel.send("No one entered the game. Ending the game.")
await ctx.channel.send(self._blackjack_finish(ctx.channel))
await ctx.channel.send(await self._blackjack_finish(ctx.channel))
return
game_id = game["game_id"]

View File

@ -75,7 +75,7 @@ class ConnectFour(BoardGame):
def __init__(self, bot):
"""Initialize the class."""
super().__init__(bot, "connectfour", DrawConnectFour(bot))
super().__init__(bot, "connectfour", DrawConnectFour)
self.get_name = self.bot.database_funcs.get_name
# pylint: disable=invalid-name
self.AISCORES = {
@ -108,7 +108,7 @@ class ConnectFour(BoardGame):
await self.bot.defer(ctx)
channel = str(ctx.channel_id)
opponent_info = self._test_opponent(ctx, opponent)
opponent_info = await self._test_opponent(ctx, opponent)
if not opponent_info:
return
@ -710,7 +710,7 @@ class DrawConnectFour():
White, but with the alpha set to win_bar_alpha.
"""
def __init__(self, bot):
def __init__(self, bot, game):
"""Initialize the class."""
self.bot = bot
self.get_name = self.bot.database_funcs.get_name

View File

@ -22,7 +22,7 @@ class GameBase():
self.long_strings = self.bot.long_strings
self.resources = "gwendolyn/resources/games/"
self.game_name = game_name
self.draw = drawer
self.draw = drawer(bot, self)
def _get_action_rows(self, buttons: list[tuple[str, list]]):
self.bot.log("Generation action rows")
@ -100,7 +100,7 @@ class DatabaseGame(GameBase):
async def _send_image(self, channel: Messageable,
buttons: list[tuple[str, list]] = None, delete=True):
old_image = super()._send_image(channel, buttons, delete)
old_image = await super()._send_image(channel, buttons, delete)
with open(self.old_images_path + str(channel.id), "w") as file_pointer:
file_pointer.write(str(old_image.id))
@ -153,7 +153,7 @@ class CardGame(DatabaseGame):
return drawn_card
class BoardGame(GameBase):
def _test_opponent(self, ctx: IntCont, opponent: Union[int, User]):
async def _test_opponent(self, ctx: IntCont, opponent: Union[int, User]):
if isinstance(opponent, int):
# Opponent is Gwendolyn
if opponent in range(1, 6):

View File

@ -2,4 +2,4 @@
__all__ = ["LookupFuncs"]
from .lookup_funcs import LookupFuncs
from .lookup_funcs import LookupFuncs

View File

@ -4,22 +4,162 @@ import discord
from gwendolyn.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"]
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"
]
# Calculates D&D stat modifier
def modifier(self, statistic):
mods = math.floor((statistic-10)/2)
if mods >= 0:
mods = "+"+str(mods)
return(str(mods))
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 monsterFunc(self, ctx, query):
async def monster_func(self, ctx, query):
query = cap(query)
self.bot.log("Looking up "+query)
@ -27,124 +167,41 @@ class LookupFuncs():
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:
# Opens "monsters.json"
data = json.load(open('gwendolyn/resources/lookup/monsters.json', encoding = "utf8"))
for monster in data:
if "name" in monster and str(query) == monster["name"]:
self.bot.log("Found it!")
# Looks at the information about the monster and returns that information
# in separate variables, allowing Gwendolyn to know where to separate
# the messages
if monster["subtype"] != "":
types = (monster["type"]+" ("+monster["subtype"]+")")
else:
types = monster["type"]
con_mod = math.floor((monster["constitution"]-10)/2)
hit_dice = monster["hit_dice"]
stats = ("**Str:** "+str(monster["strength"])+" ("+self.modifier(monster["strength"])+")\t**Dex:** "+str(monster["dexterity"])+" ("+self.modifier(monster["dexterity"])+")\t**Con:** "+str(monster["constitution"])+" ("+self.modifier(monster["constitution"])+")\n**Int: **"+str(monster["intelligence"])+" ("+self.modifier(monster["intelligence"])+")\t**Wis: **"+str(monster["wisdom"])+" ("+self.modifier(monster["wisdom"])+")\t**Cha: **"+str(monster["charisma"])+" ("+self.modifier(monster["charisma"])+")")
saving_throws = ""
for save in self.saves:
if save in monster:
if monster[save] >= 0:
saving_throws += " "+cap(save[:3])+" +"+str(monster[save])+","
else:
saving_throws += " "+cap(save[:3])+" "+str(monster[save])+","
if saving_throws != "":
saving_throws = "\n**Saving Throws**"+saving_throws[:-1]
skills = ""
for skill in self.abilities:
if skill in monster:
if monster[skill] >= 0:
skills += " "+cap(skill.replace("_"," "))+" +"+str(monster[skill])+","
else:
skills += " "+cap(skill.replace("_"," "))+" "+str(monster[skill])+","
if skills != "":
skills = "\n**Skills**"+skills[:-1]
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
specialAbilities = ""
if "special_abilities" in monster:
for ability in monster["special_abilities"]:
specialAbilities += "\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"]
if con_mod < 0:
hit_dice += (" - "+str(con_mod * int(monster["hit_dice"].replace("d"," ").split()[0])*(-1)))
if con_mod > 0:
hit_dice += (" + "+str(con_mod * int(monster["hit_dice"].replace("d"," ").split()[0])))
new_part = "\n--------------------"
monster_type = 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"])
monsterInfo = [(info, query),
(specialAbilities, "Special Abilities"),
(act, "Actions"),
(react, "Reactions"),
(legendaryActions, "Legendary Actions")]
self.bot.log("Returning monster information")
# 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 monsterInfo:
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...")
self.bot.log("Monster not in database")
await ctx.send("I don't know that monster...")
# Looks up a spell
async def spellFunc(self, ctx, query):
async def spell_func(self, ctx, query):
query = cap(query)
self.bot.log("Looking up "+query)

View File

@ -63,6 +63,7 @@ class EventHandler():
self.bot.log(log_message, str(ctx.channel_id), level=25)
async def on_component(self, ctx: ComponentContext):
"""Handle component interaction."""
info = decode_id(ctx.custom_id)
self.bot.log(f"Component action with info {info}")
channel = ctx.channel
@ -77,23 +78,22 @@ class EventHandler():
)
return
if info[1].lower() == "show":
elif info[1].lower() == "show":
await self.bot.other.plex.add_show(
ctx.origin_message,
info[2],
not isinstance(channel, discord.DMChannel)
)
return
else:
raise InvalidInteraction(ctx.custom_id, info)
elif info[0].lower() == "hangman" and author == info[2]:
if info[1].lower() == "guess":
await self.bot.games.hangman.guess(ctx, *info[3:])
return
if info[1].lower() == "end":
elif info[1].lower() == "end":
await self.bot.games.hangman.stop(ctx, *info[3:])
return
else:
raise InvalidInteraction(ctx.custom_id, info)
elif info[0].lower() == "connectfour":
connect_four = self.bot.games.connect_four
if info[1].lower() == "place" and author == info[2]:
@ -106,19 +106,16 @@ class EventHandler():
ctx.author_id,
int(info[8])
)
return
if info[1].lower() == "end" and author in [info[2], info[3]]:
elif info[1].lower() == "end" and author in [info[2], info[3]]:
await connect_four.surrender(
ctx, [int(info[2]), int(info[3])], info[4], info[5]
)
return
else:
raise InvalidInteraction(ctx.custom_id, info)
elif info[0].lower() == "blackjack":
await self.bot.games.blackjack.decode_interaction(ctx, info[1:])
return
raise InvalidInteraction(ctx.custom_id, info)
else:
raise InvalidInteraction(ctx.custom_id, info)

View File

@ -139,7 +139,7 @@ class DatabaseFuncs():
old_images_path = "gwendolyn/resources/games/old_images/"
file_path = old_images_path + f"connect_four{channel.id}"
if os.path.isfile(file_path):
with open(file_path, "r") as file_pointer:
with open(file_path, "r", encoding="utf-8") as file_pointer:
old_image = int(file_pointer.read())
else:
old_image = 0

View File

@ -109,7 +109,7 @@ def get_options():
options: dict
The options of the bot.
"""
with open("options.txt", "r") as file_pointer:
with open("options.txt", "r", encoding="utf-8") as file_pointer:
data = sanitize(file_pointer.read(), True)
options = {}
@ -128,7 +128,7 @@ def get_credentials():
credentials: dict
The credentials used by the bot.
"""
with open("credentials.txt", "r") as file_pointer:
with open("credentials.txt", "r", encoding="utf-8") as file_pointer:
data = sanitize(file_pointer.read())
credentials = {}
@ -155,7 +155,8 @@ def long_strings():
data: dict
The long strings and their keys.
"""
with open("gwendolyn/resources/long_strings.json", "r") as file_pointer:
long_strings_path = "gwendolyn/resources/long_strings.json"
with open(long_strings_path, "r", encoding="utf-8") as file_pointer:
data = json.load(file_pointer)
return data
@ -170,7 +171,8 @@ def get_params():
params: dict
The parameters for every slash command.
"""
with open("gwendolyn/resources/slash_parameters.json", "r") as file_pointer:
path = "gwendolyn/resources/slash_parameters.json"
with open(path, "r", encoding="utf-8") as file_pointer:
slash_parameters = json.load(file_pointer)
options = get_options()
@ -256,14 +258,14 @@ def make_files():
"""Create json file if it doesn't exist."""
if not os.path.isfile(path):
log_this(path.split("/")[-1]+" didn't exist. Making it now.")
with open(path, "w") as file_pointer:
with open(path, "w", encoding="utf-8") as file_pointer:
json.dump(content, file_pointer, indent=4)
def make_txt_file(path, content):
"""Create txt file if it doesn't exist."""
if not os.path.isfile(path):
log_this(path.split("/")[-1]+" didn't exist. Making it now.")
with open(path, "w") as file_pointer:
with open(path, "w", encoding="utf-8") as file_pointer:
file_pointer.write(content)
def directory(path):
@ -272,7 +274,8 @@ def make_files():
os.makedirs(path)
log_this("The "+path.split("/")[-1]+" directory didn't exist")
with open("gwendolyn/resources/starting_files.json") as file_pointer:
file_path = "gwendolyn/resources/starting_files.json"
with open(file_path, "r", encoding="utf-8") as file_pointer:
data = json.load(file_pointer)
for path, content in data["json"].items():