diff --git a/cogs/GamesCog.py b/cogs/GamesCog.py index 2ccf90d..45c57e2 100644 --- a/cogs/GamesCog.py +++ b/cogs/GamesCog.py @@ -24,7 +24,6 @@ class GamesCog(commands.Cog): async def give(self, ctx, *, content): commands = content.split(" ") if len(commands) == 2: - print(commands) amount = int(commands[1]) response = giveMoney("#"+str(ctx.message.author.id),commands[0],amount) await ctx.send(response) diff --git a/cogs/MiscCog.py b/cogs/MiscCog.py index 4718019..232e9ad 100644 --- a/cogs/MiscCog.py +++ b/cogs/MiscCog.py @@ -34,11 +34,11 @@ class MiscCog(commands.Cog): async def log(self, ctx, *, content): logThis(content,str("Logged by "+ctx.message.author.display_name)) - # Stops the bot - @commands.command(hidden = True) - async def stop(self, ctx): + # Restarts the bot + @commands.command(hidden = True,aliases=["stop"]) + async def restart(self, ctx): if "#"+str(ctx.message.author.id) in ["#266269899859427329", "#380732645602230272"]: - await ctx.send("Logging out...") + await ctx.send("Pulling git repo and restarting...") stopServer() diff --git a/cogs/ReactionCog.py b/cogs/ReactionCog.py index 0e88fff..df29fd3 100644 --- a/cogs/ReactionCog.py +++ b/cogs/ReactionCog.py @@ -1,6 +1,6 @@ from discord.ext import commands -from funcs import logThis, fiarReactionTest, monopolyReactionTest, emojiToCommand, fiar, runMonopoly +from funcs import logThis, fiarReactionTest, monopolyReactionTest, emojiToCommand, fiar, runMonopoly, hangmanReactionTest, runHangman class ReactionCog(commands.Cog): def __init__(self, client): @@ -23,5 +23,8 @@ class ReactionCog(commands.Cog): await fiar(channel," place "+str(piece)+" "+str(place),user.id) elif monopolyReactionTest(channel,message): await runMonopoly(channel,"roll","#"+str(user.id)) + elif hangmanReactionTest(channel,message): + guess = chr(ord(reaction.emoji)-127397) + await runHangman(channel,"#"+str(user.id),"guess "+guess) def setup(client): client.add_cog(ReactionCog(client)) diff --git a/funcs/__init__.py b/funcs/__init__.py index 39477e5..5609866 100644 --- a/funcs/__init__.py +++ b/funcs/__init__.py @@ -1,8 +1,8 @@ """A collection of all Gwendolyn functions.""" -__all__ = ["helloFunc", "cap", "imageFunc", "logThis", "findWikiPage", "makeFiles", "emojiToCommand", "fiarReactionTest", "deleteGame", "stopServer", "checkBalance", "giveMoney", "addMoney", "triviaCountPoints", "triviaStart", "triviaAnswer", "blackjackShuffle", "blackjackStart", "blackjackPlayerDrawHand", "blackjackContinue", "blackjackFinish", "blackjackHit", "blackjackStand", "blackjackDouble", "blackjackSplit", "spellFunc", "monsterFunc", "nameGen", "tavernGen", "movieFunc", "roll_dice", "parseChar", "parseRoll", "critRoll", "parseDestiny", "addToDict", "getName", "getID", "replaceMultiple", "monopolyReactionTest","parseInvest", "blackjackLoop", "fiar", "runMonopoly", "runHex", "runHangman"] +__all__ = ["helloFunc", "cap", "imageFunc", "logThis", "findWikiPage", "makeFiles", "emojiToCommand", "fiarReactionTest", "deleteGame", "stopServer", "checkBalance", "giveMoney", "addMoney", "triviaCountPoints", "triviaStart", "triviaAnswer", "blackjackShuffle", "blackjackStart", "blackjackPlayerDrawHand", "blackjackContinue", "blackjackFinish", "blackjackHit", "blackjackStand", "blackjackDouble", "blackjackSplit", "spellFunc", "monsterFunc", "nameGen", "tavernGen", "movieFunc", "roll_dice", "parseChar", "parseRoll", "critRoll", "parseDestiny", "addToDict", "getName", "getID", "replaceMultiple", "monopolyReactionTest","parseInvest", "blackjackLoop", "fiar", "runMonopoly", "runHex", "runHangman","hangmanReactionTest"] -from .miscFuncs import helloFunc, cap, imageFunc, logThis, findWikiPage, makeFiles, replaceMultiple, emojiToCommand, fiarReactionTest, deleteGame, stopServer, addToDict, getName, getID, monopolyReactionTest +from .miscFuncs import helloFunc, cap, imageFunc, logThis, findWikiPage, makeFiles, replaceMultiple, emojiToCommand, fiarReactionTest, deleteGame, stopServer, addToDict, getName, getID, monopolyReactionTest, hangmanReactionTest from .games import checkBalance, giveMoney, addMoney, triviaCountPoints, triviaStart, triviaAnswer, blackjackShuffle, blackjackStart, blackjackPlayerDrawHand, blackjackContinue, blackjackFinish, blackjackHit, blackjackStand, blackjackDouble, blackjackSplit, parseInvest, blackjackLoop, fiar, runMonopoly, runHex, runHangman diff --git a/funcs/games/gameLoops.py b/funcs/games/gameLoops.py index b7bf606..18b971c 100644 --- a/funcs/games/gameLoops.py +++ b/funcs/games/gameLoops.py @@ -13,15 +13,17 @@ from .hangman import parseHangman # Deletes a message async def deleteMessage(imageLocation,channel): try: - logThis("Finding old image") with open("resources/games/oldImages/"+imageLocation, "r") as f: - oldImage = await channel.fetch_message(int(f.read())) - logThis("Deleting old image") - await oldImage.delete() - except: - oldImage = "" + messages = f.read().splitlines() - return oldImage + for message in messages: + oldMessage = await channel.fetch_message(int(message)) + logThis("Deleting old message") + await oldMessage.delete() + except: + oldMessage = "" + + return oldMessage # Runs Hex async def runHex(channel,command,user): @@ -237,11 +239,31 @@ async def runHangman(channel,user,command = "start"): response, showImage, deleteImage, remainingLetters = parseHangman(str(channel.id),user,command) except: logThis("Error parsing command (error code 1701)") - await channel.send(response) - logThis(response,str(channel.id)) + if response != "": + await channel.send(response) + logThis(response,str(channel.id)) if showImage: if deleteImage: - oldImage = await deleteMessage("hangman"+str(channel.id),channel) + await deleteMessage("hangman"+str(channel.id),channel) oldImage = await channel.send(file = discord.File("resources/games/hangmanBoards/hangmanBoard"+str(channel.id)+".png")) + + if len(remainingLetters) > 15: + otherMessage = await channel.send("_ _") + reactionMessages = {oldImage : remainingLetters[:15],otherMessage : remainingLetters[15:]} + else: + otherMessage = "" + reactionMessages = {oldImage : remainingLetters} + + oldMessages = str(oldImage.id) + if otherMessage != "": + oldMessages += "\n"+str(otherMessage.id) with open("resources/games/oldImages/hangman"+str(channel.id), "w") as f: - f.write(str(oldImage.id)) + f.write(oldMessages) + + try: + for message, letters in reactionMessages.items(): + for letter in letters: + emoji = chr(ord(letter)+127397) + await message.add_reaction(emoji) + except: + logThis("Image deleted before adding all reactions") diff --git a/funcs/games/hangman.py b/funcs/games/hangman.py index 1abe761..b646695 100644 --- a/funcs/games/hangman.py +++ b/funcs/games/hangman.py @@ -1,6 +1,6 @@ import json, urllib, random, datetime, string -from . import hangmanDraw +from . import hangmanDraw, money from funcs import getName, logThis def hangmanStart(channel,user): @@ -8,9 +8,12 @@ def hangmanStart(channel,user): data = json.load(f) if channel not in data: - with urllib.request.urlopen("https://random-word-api.herokuapp.com/word?number=10") as p: + with urllib.request.urlopen("https://random-word-api.herokuapp.com/word?number=20") as p: words = json.load(p) - word = list(random.choice(words).upper()) + word = "disestablishmentarianism" + while len(word) > 11: + word = list(random.choice(words).upper()) + logThis("Found the word \""+"".join(word)+"\"") guessed = [False] * len(word) gameID = datetime.datetime.now().strftime('%Y%m%d%H%M%S') data[channel] = {"player" : user,"guessed letters" : [],"word" : word,"game ID" : gameID,"misses" : 0,"guessed" : guessed} @@ -26,19 +29,87 @@ def hangmanStart(channel,user): logThis("Error drawing image (error code 1710)") return f"{getName(user)} started game of hangman.", True, False, remainingLetters else: - return "There's already a game going on in the channel", False, False, [] + return "There's already a Hangman game going on in the channel", False, False, [] + +def hangmanStop(channel): + with open("resources/games/hangmanGames.json", "r") as f: + data = json.load(f) + + del data[channel] + with open("resources/games/hangmanGames.json", "w") as f: + json.dump(data,f,indent=4) + + return "Game stopped.", False, False, [] + +def hangmanGuess(channel,user,guess): + with open("resources/games/hangmanGames.json", "r") as f: + data = json.load(f) + + if channel in data: + if user == data[channel]["player"]: + if len(guess) == 1: + if guess not in data[channel]["guessed letters"]: + correctGuess = 0 + + for x, letter in enumerate(data[channel]["word"]): + if guess == letter: + correctGuess += 1 + data[channel]["guessed"][x] = True + + if correctGuess == 0: + data[channel]["misses"] += 1 + + data[channel]["guessed letters"].append(guess) + + remainingLetters = list(string.ascii_uppercase) + + for letter in data[channel]["guessed letters"]: + remainingLetters.remove(letter) + + if correctGuess == 1: + message = f"Guessed {guess}. There was 1 {guess} in the word." + else: + message = f"Guessed {guess}. There were {correctGuess} {guess}s in the word." + + with open("resources/games/hangmanGames.json", "w") as f: + json.dump(data,f) + + try: + hangmanDraw.drawImage(channel) + except: + logThis("Error drawing image (error code 1710)") + + if data[channel]["misses"] == 6: + hangmanStop(channel) + return message+" You've guessed wrong six times and have lost the game.", True, True, [] + elif all(i == True for i in data[channel]["guessed"]): + hangmanStop(channel) + money.addMoney(user,15) + return message+" You've guessed the word! Congratulations! Adding 15 GwendoBucks to your account", True, True, [] + else: + return message, True, True, remainingLetters + else: + return f"You've already guessed {guess}", False, False, [] + else: + return "", False, False, [] + else: + return "", False, False, [] + else: + return "There's no Hangman game going on in this channel", False, False, [] def parseHangman(channel,user,command): if command == "start": - return hangmanStart(channel,user) + try: + return hangmanStart(channel,user) + except: + logThis("Error starting game (error code 1730)") elif command == "stop": - with open("resources/games/hangmanGames.json", "r") as f: - data = json.load(f) - - del data[channel] - with open("resources/games/hangmanGames.json", "w") as f: - json.dump(data,f,indent=4) - - return "Game stopped.", False, False, [] + return hangmanStop(channel) + elif command.startswith("guess "): + guess = command[6:].upper() + try: + return hangmanGuess(channel,user,guess) + except: + logThis("Error in guessing (Error Code 1720)") else: return "I didn't understand that", False, False, [] diff --git a/funcs/games/hangmanDraw.py b/funcs/games/hangmanDraw.py index 4eaf85d..eb3ba31 100644 --- a/funcs/games/hangmanDraw.py +++ b/funcs/games/hangmanDraw.py @@ -4,30 +4,27 @@ from PIL import ImageDraw, Image, ImageFont from funcs import logThis circleDegrees = 360 -circleSize = 400 -lineWidth = 40 +circleSize = 120 +lineWidth = 12 -bodySize = 700 -limbSize = 300 -armPosition = 200 +bodySize =210 +limbSize = 60 +armPosition = 60 manx = (limbSize*2)+lineWidth*4 many = (circleSize+bodySize+limbSize)+lineWidth*4 -letterSize = 250 +letterSize = 75 textSize = 70 -letterLineLength = 300 -letterLineDistance = 100 +letterLineLength = 90 +letterLineDistance = 30 -gallowx, gallowy = 1200,2000 +gallowx, gallowy = 360,600 goldenRatio = 1-(1 / ((1 + 5 ** 0.5) / 2)) -smolGallowx, smolGallowy = int(gallowx * (3/10)),int(gallowy * (3/10)) -smolManx, smolMany = int(manx * (3/10)),int(many * (3/10)) - -fnt = ImageFont.truetype('resources/comic-sans.ttf', letterSize) -smolfnt = ImageFont.truetype('resources/comic-sans.ttf', textSize) +fnt = ImageFont.truetype('resources/comic-sans-bold.ttf', letterSize) +smolfnt = ImageFont.truetype('resources/comic-sans-bold.ttf', textSize) backgroundColor = (255,255,255,255) @@ -140,23 +137,23 @@ def drawMan(misses): return background -def badText(text, big): +def badText(text, big, color=(0,0,0,255)): if big: font = fnt else: font = smolfnt w, h = font.getsize(text) img = Image.new("RGBA",(w,h),color=(0,0,0,0)) d = ImageDraw.Draw(img,"RGBA") - d.text((0,0),text,font=font,fill=(0,0,0,255)) + d.text((0,0),text,font=font,fill=color) return img def drawGallows(): background = Image.new("RGBA",(gallowx,gallowy),color=(0,0,0,0)) bottomLine = badLine(int(gallowx*0.75),True) - background.paste(bottomLine,(int(gallowx*0.125),gallowy-(lineWidth*3)),bottomLine) + background.paste(bottomLine,(int(gallowx*0.125),gallowy-(lineWidth*4)),bottomLine) - lineTwo = badLine(gallowy-lineWidth*4) + lineTwo = badLine(gallowy-lineWidth*6) background.paste(lineTwo,(int(gallowx*(0.75*goldenRatio)),lineWidth*2),lineTwo) topLine = badLine(int(gallowy*0.30),True) @@ -166,49 +163,87 @@ def drawGallows(): background.paste(lastLine,((int(gallowx*(0.75*goldenRatio))+int(gallowy*0.30)-lineWidth),lineWidth*3),lastLine) return background +def drawLetterLines(word,guessed,misses): + letterLines = Image.new("RGBA",((letterLineLength+letterLineDistance)*len(word),letterLineLength+lineWidth*3),color=(0,0,0,0)) + for x, letter in enumerate(word): + line = badLine(letterLineLength,True) + letterLines.paste(line,(x*(letterLineLength+letterLineDistance),letterLineLength),line) + if guessed[x]: + letterDrawing = badText(letter,True) + letterWidth = fnt.getsize(letter)[0] + letterx = int(x*(letterLineLength+letterLineDistance)-(letterWidth/2)+(letterLineLength*0.5)+(lineWidth*2)) + letterLines.paste(letterDrawing,(letterx,0),letterDrawing) + elif misses == 6: + letterDrawing = badText(letter,True,(242,66,54)) + letterWidth = fnt.getsize(letter)[0] + letterx = int(x*(letterLineLength+letterLineDistance)-(letterWidth/2)+(letterLineLength*0.5)+(lineWidth*2)) + letterLines.paste(letterDrawing,(letterx,0),letterDrawing) + + return letterLines + +def shortestDist(positions,newPosition): + shortestDist = math.inf + x, y = newPosition + for i, j in positions: + xdist = abs(i-x) + ydist = abs(j-y) + dist = math.sqrt(xdist**2+ydist**2) + if shortestDist > dist: shortestDist = dist + return shortestDist + +def drawMisses(guesses,word): + background = Image.new("RGBA",(600,400),color=(0,0,0,0)) + pos = [] + for guess in guesses: + if guess not in word: + placed = False + while placed == False: + letter = badText(guess,True) + w, h = fnt.getsize(guess) + x = random.randint(0,600-w) + y = random.randint(0,400-h) + if shortestDist(pos,(x,y)) > 70: + pos.append((x,y)) + background.paste(letter,(x,y),letter) + placed = True + return background + def drawImage(channel): with open("resources/games/hangmanGames.json", "r") as f: data = json.load(f) - + random.seed(data[channel]["game ID"]) - background = Image.new("RGBA",(1600,1000),color=backgroundColor) + background = Image.open("resources/paper.jpg") try: gallow = drawGallows() except: logThis("Error drawing gallows (error code 1711)") - gallow = gallow.resize((smolGallowx,smolGallowy)) - try: man = drawMan(data[channel]["misses"]) except: logThis("Error drawing stick figure (error code 1712)") + random.seed(data[channel]["game ID"]) try: - man = man.resize((smolManx,smolMany)) + letterLines = drawLetterLines(data[channel]["word"],data[channel]["guessed"],data[channel]["misses"]) except: - logThis("Error resizing") + logThis("error drawing letter lines (error code 1713)") + + random.seed(data[channel]["game ID"]) + try: + misses = drawMisses(data[channel]["guessed letters"],data[channel]["word"]) + except: + logThis("Error drawing misses (error code 1714)") background.paste(gallow,(100,100),gallow) - background.paste(man,(280,210),man) + background.paste(man,(300,210),man) + background.paste(letterLines,(120,840),letterLines) + background.paste(misses,(600,150),misses) - letterLines = Image.new("RGBA",((letterLineLength+letterLineDistance)*len(data[channel]["word"]),300+lineWidth*3),color=(0,0,0,0)) - for x, letter in enumerate(data[channel]["word"]): - line = badLine(letterLineLength,True) - letterLines.paste(line,(x*(letterLineLength+letterLineDistance),300),line) - if data[channel]["guessed"][x]: - letterDrawing = badText(letter,True) - letterWidth = fnt.getsize(letter)[0] - letterx = int(x*(letterLineLength+letterLineDistance)-(letterWidth/2)+(letterLineLength*0.5)+(lineWidth*2)) - letterLines.paste(letterDrawing,(letterx,0),letterDrawing) - - llw, llh = letterLines.size - letterLines = letterLines.resize((int(llw*(3/10)),int(llh*(3/10)))) - - background.paste(letterLines,(120,860),letterLines) - - d = ImageDraw.Draw(background,"RGBA") - d.text((850,50),"Misses",font=smolfnt,fill=(0,0,0,255)) + missesText = badText("MISSES",False) + missesTextWidth = missesText.size[0] + background.paste(missesText,(850-int(missesTextWidth/2),50),missesText) background.save("resources/games/hangmanBoards/hangmanBoard"+channel+".png") diff --git a/funcs/miscFuncs.py b/funcs/miscFuncs.py index 893abb8..da1d3d2 100644 --- a/funcs/miscFuncs.py +++ b/funcs/miscFuncs.py @@ -7,6 +7,7 @@ import time # Used for logging import logging # Used for... you know... logging import wikia # Used by findWikiPage import os # Used by makeFiles +import git # Used by stopServer() logging.basicConfig(filename="gwendolyn.log", level=logging.INFO) @@ -224,8 +225,11 @@ def fiarReactionTest(channel,message,user): return False, 0 def monopolyReactionTest(channel,message): - with open("resources/games/oldImages/monopoly"+str(channel.id), "r") as f: - oldImage = int(f.read()) + try: + with open("resources/games/oldImages/monopoly"+str(channel.id), "r") as f: + oldImage = int(f.read()) + except: + return False if message.id == oldImage: logThis("They reacted to the monopoly game") @@ -233,6 +237,22 @@ def monopolyReactionTest(channel,message): else: return False +def hangmanReactionTest(channel,message): + try: + with open("resources/games/oldImages/hangman"+str(channel.id), "r") as f: + oldMessages = f.read().splitlines() + except: + return False + gameMessage = False + + for oldMessage in oldMessages: + oldMessageID = int(oldMessage) + if message.id == oldMessageID: + logThis("They reacted to the hangman game") + gameMessage = True + + return gameMessage + def stopServer(): with open("resources/games/games.json","r") as f: games = json.load(f) @@ -255,6 +275,9 @@ def stopServer(): with open("resources/games/hangmanGames.json", "w") as f: json.dump(emptyDict,f,indent=4) + g = git.cmd.Git("") + g.pull() + def deleteGame(gameType,channel): with open("resources/games/games.json", "r") as f: data = json.load(f) diff --git a/requirements.txt b/requirements.txt index 125dc1a..0f3f9fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,7 @@ IMDbPY==6.8 wikia==1.4.4 Pillow==7.1.1 numpy==1.18.2 -numexpr==2.7.1 \ No newline at end of file +numexpr==2.7.1 +d20==1.0.4 +finnhub-python==2.1.0 +GitPython==3.1.0 \ No newline at end of file diff --git a/resources/comic-sans-bold.ttf b/resources/comic-sans-bold.ttf new file mode 100644 index 0000000..a9227f1 Binary files /dev/null and b/resources/comic-sans-bold.ttf differ diff --git a/resources/comic-sans.ttf b/resources/comic-sans.ttf deleted file mode 100644 index d17e1be..0000000 Binary files a/resources/comic-sans.ttf and /dev/null differ diff --git a/resources/errorCodes.txt b/resources/errorCodes.txt index 4f03ec6..cc76bb8 100644 --- a/resources/errorCodes.txt +++ b/resources/errorCodes.txt @@ -132,3 +132,7 @@ 1710 - Error in drawImage() 1711 - Error in drawGallows() 1712 - Error in drawMan() +1713 - Error in drawLetterLines() +1714 - Error in drawMisses() +1720 - Unspecified hangmanGuess() error +1730 - Unspecified hangmanStart() error diff --git a/resources/help/help-hangman.txt b/resources/help/help-hangman.txt new file mode 100644 index 0000000..eeff703 --- /dev/null +++ b/resources/help/help-hangman.txt @@ -0,0 +1 @@ +Brug `!hangman` til at starte et spil hangman. Brug derefter reaktionerne til at gætte bogstaver. Du har 6 gæt. \ No newline at end of file diff --git a/resources/help/help.txt b/resources/help/help.txt index 561a0d4..920f9e1 100644 --- a/resources/help/help.txt +++ b/resources/help/help.txt @@ -34,4 +34,6 @@ `!hex` - Lader dig spille et spil Hex. +`!hangman` - Lader dig spille et spil hangman. + Du kan få ekstra information om kommandoerne med "!help [kommando]". diff --git a/resources/paper.jpg b/resources/paper.jpg new file mode 100644 index 0000000..cafeefb Binary files /dev/null and b/resources/paper.jpg differ