Files
Gwendolyn/gwendolyn/funcs/games/game_base.py
2021-08-24 22:28:05 +02:00

220 lines
7.7 KiB
Python

"""Base class for the games."""
import random
from shutil import copyfile
from discord import File
from discord.abc import Messageable
from PIL import ImageFont, Image, ImageDraw
class GameBase():
"""The base class for the games."""
def __init__(self, bot):
"""Initialize the class."""
self.bot = bot
self.database = self.bot.database
self.long_strings = self.bot.long_strings
self.resources = "gwendolyn/resources/games/"
self.game_name = ""
self.old_images_path = f"{self.resources}old_images/{self.game_name}"
self.draw = BaseDrawer(bot, self)
def access_document(self, channel: str, collection_name: str="games"):
collection = self.bot.database[f"{self.game_name} {collection_name}"]
return collection.find_one({"_id": channel})
def _update_document(self, channel: str, updater: dict,
collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].update_one(
{"_id": channel},
updater,
upsert=True
)
def _delete_document(self, channel: str, collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].delete_one(
{"_id": channel}
)
def _insert_document(self, data: dict, collection_name: str="games"):
self.database[f"{self.game_name} {collection_name}"].insert_one(data)
async def _delete_old_image(self, channel: Messageable):
with open(self.old_images_path + str(channel.id), "r") as file_pointer:
old_image = await channel.fetch_message(int(file_pointer.read()))
await old_image.delete()
async def _send_image(self, channel: Messageable):
self.draw.draw(str(channel.id))
file_path = f"{self.resources}images/blackjack{channel.id}.png"
old_image = await channel.send(file=File(file_path))
with open(self.old_images_path + str(channel.id), "w") as file_pointer:
file_pointer.write(str(old_image.id))
async def _start_new(self, channel: Messageable, new_game: dict):
self._insert_document(new_game)
default_image = f"{self.resources}default_images/{self.game_name}.png"
new_image = f"{self.resources}images/{self.game_name}{channel.id}.png"
copyfile(default_image, new_image)
await self._send_image(channel)
class CardGame(GameBase):
"""The class for card games."""
def __init__(self, bot):
"""Initialize the class."""
super().__init__(bot)
self.decks_used = 1
def _shuffle_cards(self, channel: str):
self.bot.log(f"Shuffling cards for {self.game_name}")
with open(f"{self.resources}deck_of_cards.txt", "r") as file_pointer:
deck = file_pointer.read()
all_decks = deck.split("\n") * self.decks_used
random.shuffle(all_decks)
self._update_document(
channel,
{"$set": {"_id": channel, "cards": all_decks}},
"cards"
)
def _draw_card(self, channel: str):
"""
Draw a card from the stack.
*Parameters*
------------
channel: str
The id of the channel the card is drawn in.
"""
self.bot.log("drawing a card")
drawn_card = self.access_document(channel, "cards")["cards"][0]
self._update_document(channel, {"$pop": {"cards": -1}}, "cards")
return drawn_card
class BaseDrawer():
"""Class for drawing games."""
def __init__(self, bot, game: GameBase):
self.bot = bot
self.game = game
self.fonts_path = "gwendolyn/resources/fonts/"
self.font_name = "futura-bold"
self.resources = game.resources
game_name = game.game_name
self.default_image = f"{self.resources}default_images/{game_name}.png"
self.images_path = f"{self.resources}images/{game_name}"
def _draw_image(self, game: dict, table: Image.Image):
pass
def draw(self, channel: str):
game = self.game.access_document(channel)
image = Image.open(self.default_image)
self._draw_image(game, image)
self._save_image(image, channel)
def _get_font(self, size: int, font_name: str = None):
if font_name is None:
font_name = self.font_name
return ImageFont.truetype(f"{self.fonts_path}{font_name}.ttf", size)
def _adjust_font(self, max_font_size: int, text: str, max_text_size: int,
font_name: str = None):
font_size = max_font_size
font = self._get_font(font_size, font_name)
text_width = font.getsize(text)[0]
while text_width > max_text_size:
font_size -= 1
font = self._get_font(font_size, font_name)
text_width = font.getsize(text)[0]
return font, font_size
def _save_image(self, image: Image.Image, channel: str):
self.bot.log("Saving image")
image.save(f"{self.images_path}{channel}.png")
def _draw_shadow_text(self, text: str, colors: list[tuple[int, int, int]],
font_size: int):
font = self._get_font(font_size)
offset = font_size//20
shadow_offset = font_size//10
text_size = list(font.getsize(text))
text_size[0] += 1 + (offset * 2)
text_size[1] += 1 + (offset * 2) + shadow_offset
image = Image.new("RGBA", tuple(text_size), (0, 0, 0, 0))
text_image = ImageDraw.Draw(image)
for color_index, color in enumerate(colors[:-1]):
color_offset = offset//(color_index+1)
if color_index == 0:
color_shadow_offset = shadow_offset
else:
color_shadow_offset = 0
for i in range(4):
x_pos = [-1,1][i % 2] * color_offset
y_pos = [-1,1][(i//2) % 2] * color_offset + color_shadow_offset
position = (offset + x_pos, offset + y_pos)
text_image.text(position, text, fill=color, font=font)
text_image.text((offset, offset), text, fill=colors[-1], font=font)
return image
class CardDrawer(BaseDrawer):
def __init__(self, bot, game: GameBase):
super().__init__(bot, game)
self.cards_path = f"{self.resources}cards/"
# pylint: disable=invalid-name
self.CARD_WIDTH = 691
self.CARD_HEIGHT = 1065
self.CARD_BORDER = 100
self.CARD_OFFSET = 125
# pylint: enable=invalid-name
def _set_card_size(self, wanted_card_width: int):
ratio = wanted_card_width / self.CARD_WIDTH
self.CARD_WIDTH = wanted_card_width
self.CARD_HEIGHT = int(self.CARD_HEIGHT * ratio)
self.CARD_BORDER = int(self.CARD_BORDER * ratio)
self.CARD_OFFSET = int(self.CARD_OFFSET * ratio)
def _draw_cards(self, hand: list[str], hidden_cards: int = 0):
image_width = (self.CARD_BORDER * 2) + self.CARD_WIDTH
image_width += (self.CARD_OFFSET * (len(hand)-1))
image_size = (image_width, (self.CARD_BORDER * 2) + self.CARD_HEIGHT)
background = Image.new("RGBA", image_size, (0, 0, 0, 0))
for i, card in enumerate(hand):
position = (
self.CARD_BORDER + (self.CARD_OFFSET*i),
self.CARD_BORDER
)
if i + hidden_cards < len(hand):
card_image = Image.open(f"{self.cards_path}{card.upper()}.png")
else:
card_image = Image.open(f"{self.cards_path}red_back.png")
card_image = card_image.resize(
(self.CARD_WIDTH, self.CARD_HEIGHT),
resample=Image.BILINEAR
)
background.paste(card_image, position, card_image)
return background