Skip to content

Commit

Permalink
Format and lint
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyrannicodin committed Nov 5, 2024
1 parent 91ab163 commit 6a0d6c2
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 39 deletions.
73 changes: 56 additions & 17 deletions exts/card.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,15 @@
from matplotlib import pyplot as plt
from PIL import Image

from util import TYPE_COLORS, Card, EffectCard, HermitCard, Server, ServerManager, probability
from util import (
TYPE_COLORS,
Card,
EffectCard,
HermitCard,
Server,
ServerManager,
probability,
)


def take(items: int, iterable: Iterable) -> list:
Expand Down Expand Up @@ -70,9 +78,7 @@ def best_factors(number: int) -> tuple[int, int]:
class CardExt(Extension):
"""Get information about cards and decks."""

def __init__(
self: "CardExt", _: Client, manager: ServerManager
) -> None:
def __init__(self: "CardExt", _: Client, manager: ServerManager) -> None:
"""Get information about cards and decks.
Args:
Expand Down Expand Up @@ -133,7 +139,9 @@ def get_stats(
async def card_autocomplete(self: "CardExt", ctx: AutocompleteContext) -> None:
"""Autocomplete a card name."""
if not ctx.input_text:
await ctx.send([card.rarityName for card in take(25, self.universe.values())])
await ctx.send(
[card.rarityName for card in take(25, self.universe.values())]
)
return
await ctx.send(
[
Expand All @@ -149,13 +157,17 @@ async def card(self: "CardExt", _: SlashContext) -> None:

@card.subcommand()
@slash_option("code", "The deck's export code", OptionType.STRING, required=True)
@slash_option("hide_hash", "If the deck's hash should be hidden", OptionType.BOOLEAN)
@slash_option(
"hide_hash", "If the deck's hash should be hidden", OptionType.BOOLEAN
)
async def deck(
self: "CardExt", ctx: SlashContext, code: str, *, hide_hash: bool = False
) -> None:
"""Get information about a deck."""
if str(ctx.guild_id) not in self.manager.discord_links.keys():
await ctx.send("Couldn't find an online server for this discord!", ephemeral=True)
await ctx.send(
"Couldn't find an online server for this discord!", ephemeral=True
)
return
server: Server = self.manager.discord_links[str(ctx.guild_id)]

Expand All @@ -164,7 +176,9 @@ async def deck(
await ctx.send("Invalid deck: Perhaps you're looking for /card info")
return
if len(deck["cards"]) > 100:
await ctx.send(f"A deck of {len(deck["cards"])} cards is too large!", ephemeral=True)
await ctx.send(
f"A deck of {len(deck["cards"])} cards is too large!", ephemeral=True
)
return
im, card_type_counts, hermit_type_counts, cost = self.get_stats(
[self.universe[card["props"]["id"]] for card in deck["cards"]]
Expand All @@ -187,7 +201,13 @@ async def deck(
)
.add_field(
"Types",
len([typeList for typeList in hermit_type_counts.values() if typeList != 0]),
len(
[
typeList
for typeList in hermit_type_counts.values()
if typeList != 0
]
),
inline=True,
)
.set_footer("Bot by Tyrannicodin16")
Expand All @@ -202,7 +222,9 @@ async def deck(
custom_id=f"delete_deck:{ctx.author_id}",
)
if hide_hash:
await ctx.send("This message handily obscures your deck hash!", ephemeral=True)
await ctx.send(
"This message handily obscures your deck hash!", ephemeral=True
)
await ctx.send(
embeds=e,
files=File(im_binary, "deck.png"),
Expand All @@ -220,12 +242,18 @@ async def handle_delete(self: "CardExt", ctx: ComponentContext) -> None:

@card.subcommand()
@slash_option(
"card_name", "The card to get", OptionType.STRING, required=True, autocomplete=True
"card_name",
"The card to get",
OptionType.STRING,
required=True,
autocomplete=True,
)
async def info(self: "CardExt", ctx: SlashContext, card_name: str) -> None:
"""Get information about a card."""
cards = [
card for card in self.universe.values() if card_name.lower() in card.rarityName.lower()
card
for card in self.universe.values()
if card_name.lower() in card.rarityName.lower()
]
cards.sort(key=lambda val: val.rarityName)
if len(cards) > 0:
Expand All @@ -249,7 +277,9 @@ async def info(self: "CardExt", ctx: SlashContext, card_name: str) -> None:
inline=False,
)
.add_field("Attack damage", card.attacks[0]["damage"], inline=True)
.add_field("Items required", count(card.attacks[0]["cost"]), inline=True)
.add_field(
"Items required", count(card.attacks[0]["cost"]), inline=True
)
.add_field(
"Secondary attack",
card.attacks[1]["name"]
Expand All @@ -260,7 +290,9 @@ async def info(self: "CardExt", ctx: SlashContext, card_name: str) -> None:
inline=False,
)
.add_field("Attack damage", card.attacks[1]["damage"], inline=True)
.add_field("Items required", count(card.attacks[1]["cost"]), inline=True)
.add_field(
"Items required", count(card.attacks[1]["cost"]), inline=True
)
)
else:
e = Embed(
Expand All @@ -284,14 +316,19 @@ async def info(self: "CardExt", ctx: SlashContext, card_name: str) -> None:

@card.subcommand()
@slash_option(
"hermits", "The number of hermits in your deck", OptionType.INTEGER, required=True
"hermits",
"The number of hermits in your deck",
OptionType.INTEGER,
required=True,
)
@slash_option(
"desired_chance",
"The chance of getting a number of hermits (default 2) on a turn",
OptionType.INTEGER,
)
@slash_option("desired_hermits", "The number of hermits you want", OptionType.INTEGER)
@slash_option(
"desired_hermits", "The number of hermits you want", OptionType.INTEGER
)
async def two_hermits(
self: "CardExt",
ctx: SlashContext,
Expand All @@ -306,7 +343,9 @@ async def two_hermits(
plt.figure()
xs = list(range(35))
ys = [probability(hermits, i, desired_hermits) * 100 for i in xs]
surpass = next((idx[0] for idx in enumerate(ys) if idx[1] >= desired_chance), None)
surpass = next(
(idx[0] for idx in enumerate(ys) if idx[1] >= desired_chance), None
)
plt.plot(xs, list(ys))
plt.xlabel("Draws")
plt.ylabel("Probability")
Expand Down
91 changes: 69 additions & 22 deletions util/datagen.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ def change_color(


def draw_no_fade(
image: Image.Image, method: str, color: tuple[int, int, int], *args: tuple, **kwargs: dict
image: Image.Image,
method: str,
color: tuple[int, int, int],
*args: tuple,
**kwargs: dict,
) -> None:
"""Perform an image modification ensuring no fade is made between two colors.
Expand All @@ -65,7 +69,9 @@ def draw_no_fade(
image.paste(Image.fromarray(rgba), (0, 0), Image.fromarray(rgba))


def drop_shadow(image: Image.Image, radius: int, color: tuple[int, int, int, 0]) -> Image.Image:
def drop_shadow(
image: Image.Image, radius: int, color: tuple[int, int, int, 0]
) -> Image.Image:
"""Generate a drop shadow for an image.
Args:
Expand All @@ -78,7 +84,9 @@ def drop_shadow(image: Image.Image, radius: int, color: tuple[int, int, int, 0])
-------
Image containg the drop shadow
"""
base = Image.new("RGBA", (image.width + radius * 2, image.height + radius * 2), color)
base = Image.new(
"RGBA", (image.width + radius * 2, image.height + radius * 2), color
)
alpha = Image.new("L", (image.width + radius * 2, image.height + radius * 2))
alpha.paste(image.getchannel("A"), (radius, radius))
base.putalpha(alpha.filter(GaussianBlur(radius)))
Expand Down Expand Up @@ -133,12 +141,16 @@ def __init__(self: "Card", data: dict, generator: "DataGenerator") -> None:
self.cost: int = data["tokens"]
self.image: str = data["image"]
self.rarity: str = (
"Ultra rare" if data["rarity"] == "ultra_rare" else data["rarity"].capitalize()
"Ultra rare"
if data["rarity"] == "ultra_rare"
else data["rarity"].capitalize()
)
self.name: str = data["name"]
self.rarityName: str = f"{data['name']} ({self.rarity})"

self.palette: Palette = palettes[data["palette"] if "palette" in data.keys() else "base"]
self.palette: Palette = palettes[
data["palette"] if "palette" in data.keys() else "base"
]
self.full_image = self.render()

def render(self: "Card") -> Image.Image:
Expand Down Expand Up @@ -195,14 +207,18 @@ def render(self: "HermitCard") -> Image.Image:
im_draw.text(
(200, y_coord),
attack["name"].upper(),
self.palette.SPECIAL_ATTACK if attack["power"] else self.palette.BASIC_ATTACK,
self.palette.SPECIAL_ATTACK
if attack["power"]
else self.palette.BASIC_ATTACK,
font,
"mt",
)
im_draw.text(
(380, y_coord),
f"{attack['damage']:02d}",
self.palette.SPECIAL_DAMAGE if attack["power"] else self.palette.BASIC_DAMAGE,
self.palette.SPECIAL_DAMAGE
if attack["power"]
else self.palette.BASIC_DAMAGE,
damage_font,
"rt",
) # Ensures always at least 2 digits and is blue if attack is special
Expand All @@ -215,11 +231,15 @@ def render(self: "HermitCard") -> Image.Image:
im.paste(type_image, (327, 12), type_image) # The type in top right
if self.cost > 0: # No star if it is 0 rarity
im.paste(
self.generator.rank_stars[self.cost], (60, 70), self.generator.rank_stars[self.cost]
self.generator.rank_stars[self.cost],
(60, 70),
self.generator.rank_stars[self.cost],
)

im_draw.text((45, 20), self.name.upper(), self.palette.NAME, damage_font, "lt")
im_draw.text((305, 20), str(self.health), self.palette.HEALTH, damage_font, "rt")
im_draw.text(
(305, 20), str(self.health), self.palette.HEALTH, damage_font, "rt"
)

im = im.resize((200, 200), Image.Resampling.NEAREST)
return im
Expand All @@ -233,7 +253,9 @@ def background(self: "HermitCard") -> Image.Image:
) # Creates beige centre with white outline

im_draw.ellipse((305, -5, 405, 95), self.palette.TYPE_BACKGROUND) # Type circle
im_draw.rectangle((20, 315, 380, 325), Colors.WHITE) # White bar between attacks
im_draw.rectangle(
(20, 315, 380, 325), Colors.WHITE
) # White bar between attacks
im_draw.rectangle((45, 60, 355, 256), Colors.WHITE) # White border for image

return im
Expand All @@ -244,7 +266,9 @@ def hermit_feature_image(self: "HermitCard") -> Image.Image:
if bg.size == (0, 0): # Set background
error = f"Image not found for hermit {self.name}"
raise Exception(error)
bg = bg.resize((290, int(bg.height * (290 / bg.width))), Image.Resampling.NEAREST)
bg = bg.resize(
(290, int(bg.height * (290 / bg.width))), Image.Resampling.NEAREST
)
skin = self.generator.get_image(self.image).convert("RGBA")
try:
skin = skin.resize(
Expand Down Expand Up @@ -303,7 +327,10 @@ def background(self: "EffectCard") -> Image.Image:
to_paste = (
self.generator.get_star(self.palette.BACKGROUND)
.resize(
(390, int(self.generator.star.height * (390 / self.generator.star.width))),
(
390,
int(self.generator.star.height * (390 / self.generator.star.width)),
),
Image.Resampling.NEAREST,
)
.convert("RGBA")
Expand Down Expand Up @@ -363,7 +390,10 @@ def background(self: "ItemCard") -> Image.Image:
.resize(
(
390,
int(self.generator.get_star().height * (390 / self.generator.get_star().width)),
int(
self.generator.get_star().height
* (390 / self.generator.get_star().width)
),
),
Image.Resampling.NEAREST,
)
Expand All @@ -375,12 +405,16 @@ def background(self: "ItemCard") -> Image.Image:
im, "rounded_rectangle", Colors.WHITE, (20, 20, 380, 95), 15
) # The item header
font = self.generator.font.font_variant(size=72)
draw_no_fade(im, "text", self.palette.NAME, (200, 33), "ITEM", font=font, anchor="mt")
draw_no_fade(
im, "text", self.palette.NAME, (200, 33), "ITEM", font=font, anchor="mt"
)
return im

def overlay_x2(self: "ItemCard") -> Image.Image:
"""Create an image that contains the rarity star and 2x text for a 2x item."""
im = Image.new("RGBA", (400, 100)) # Only 100 tall as it's just the two bottom circles
im = Image.new(
"RGBA", (400, 100)
) # Only 100 tall as it's just the two bottom circles
im_draw = ImageDraw.Draw(im, "RGBA")

im_draw.ellipse((0, 0, 100, 100), Colors.WHITE) # Rarity star circle
Expand Down Expand Up @@ -408,7 +442,9 @@ def get_card(data: dict, data_generator: "DataGenerator") -> Card:
class DataGenerator:
"""Generate card images for hc-tcg."""

def __init__(self: "DataGenerator", url: str, font: ImageFont.FreeTypeFont = None) -> None:
def __init__(
self: "DataGenerator", url: str, font: ImageFont.FreeTypeFont = None
) -> None:
"""Init generator.
Args:
Expand Down Expand Up @@ -481,9 +517,9 @@ def load_types(self: "DataGenerator") -> dict[str, Image.Image]:
if has_progression:
iterator = tqdm(iterator, "Loading types")
for hermit_type in iterator:
type_icons[hermit_type["type"]] = self.get_image(hermit_type["icon"]).resize(
(70, 70), Image.Resampling.NEAREST
)
type_icons[hermit_type["type"]] = self.get_image(
hermit_type["icon"]
).resize((70, 70), Image.Resampling.NEAREST)
return type_icons

def load_data(self: "DataGenerator") -> list[Card]:
Expand All @@ -504,7 +540,13 @@ def get_health_cards(self: "DataGenerator") -> list[Image.Image]:
draw_no_fade(base, "rounded_rectangle", Colors.REPLACE, (20, 20, 380, 95), 15)
font = self.font.font_variant(size=72)
draw_no_fade(
base, "text", palettes["base"].NAME, (200, 33), "HEALTH", font=font, anchor="mt"
base,
"text",
palettes["base"].NAME,
(200, 33),
"HEALTH",
font=font,
anchor="mt",
)
base.resize((200, 200), Image.Resampling.NEAREST)

Expand All @@ -513,12 +555,17 @@ def get_health_cards(self: "DataGenerator") -> list[Image.Image]:
health_cards.append(change_color(base, Colors.REPLACE, color))
return health_cards

def get_star(self: "DataGenerator", color: tuple[int, int, int] = Colors.WHITE) -> Image.Image:
def get_star(
self: "DataGenerator", color: tuple[int, int, int] = Colors.WHITE
) -> Image.Image:
"""Get a star image in any color."""
im = Image.new("RGBA", (1057, 995))
im_draw = ImageDraw.Draw(im)
points = (
self.get(f"images/star_white.svg").text.split('points="')[1].split('"')[0].split(" ")
self.get(f"images/star_white.svg")
.text.split('points="')[1]
.split('"')[0]
.split(" ")
)
im_draw.polygon(
[
Expand Down

0 comments on commit 6a0d6c2

Please sign in to comment.