Skip to content

Commit

Permalink
feat: add "damage" button with image
Browse files Browse the repository at this point in the history
  • Loading branch information
ledouxm committed Mar 2, 2024
1 parent ad55b86 commit 1072974
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 9 deletions.
Binary file added damage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 23 additions & 0 deletions src/commands/buttons.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { db } from "@/db/db";
import { match } from "@/db/schema";
import { createMatchDamageFile } from "@/features/details/matchDamage";
import { createMatchDetailsFile } from "@/features/details/matchDetails";
import { galeforce } from "@/features/summoner";
import { ButtonInteraction } from "discord.js";
Expand Down Expand Up @@ -27,4 +28,26 @@ export const executeButtonInteraction = async (interaction: ButtonInteraction) =
files: [file],
});
}

if (interaction.customId.startsWith("damages")) {
const matchId = interaction.customId.split("-")[1];

if (!matchId) {
return void console.log("No matchId found in customId", interaction.customId);
}

const game = await db.select().from(match).where(eq(match.matchId, matchId)).limit(1);
if (!game[0]) {
return void console.log("No game found for matchId", matchId);
}

const { details, participantIndex } = game[0];
const participant = details.info.participants[participantIndex];

const file = await createMatchDamageFile(details, participant);

await interaction.reply({
files: [file],
});
}
};
116 changes: 116 additions & 0 deletions src/features/details/matchDamage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import Galeforce from "galeforce";
import { Participant } from "../summoner";
import { createCanvas } from "canvas";
import { blueSide, redSide, sortPlayersByTeamAndRole } from "./matchDetails";
import { DDImageLoader, getChampionIconUrl } from "../lol/icons";
import fs from "fs/promises";

const options = {
championIconSize: 48,
championIconSpace: 5,
padding: 10,
damageMargin: 4,
};

const imageLoader = new DDImageLoader();

export const createMatchDamageFile = async (match: Galeforce.dto.MatchDTO, participant: Participant) => {
const canva = createCanvas(630, 565);
const ctx = canva.getContext("2d");

ctx.fillStyle = "black";
ctx.fillRect(0, 0, 630, 565);

const sortedPlayers = sortPlayersByTeamAndRole(match.info.participants);
const blueSidePlayers = sortedPlayers[blueSide];
const redSidePlayers = sortedPlayers[redSide];

const maxDamage = Math.max(...match.info.participants.map((p) => p.totalDamageDealtToChampions));
const maxWidth = 630 - options.padding * 3 - options.championIconSize;

const { x, y, width, height } = getRectArguments(participant, blueSidePlayers, redSidePlayers);
ctx.strokeStyle = "#FDB05F";
ctx.lineWidth = 4;
ctx.strokeRect(x - ctx.lineWidth / 2, y - ctx.lineWidth / 2, width + ctx.lineWidth, height + ctx.lineWidth);

const baseX = options.padding;
const baseY = options.padding;

for (const [index, player] of blueSidePlayers.entries()) {
const topY = baseY + (options.championIconSize + options.championIconSpace) * index;

const icon = await imageLoader.loadImage(await getChampionIconUrl(player.championName));
ctx.drawImage(icon, baseX, topY, options.championIconSize, options.championIconSize);

const damage = player.totalDamageDealtToChampions;
const width = (damage / maxDamage) * maxWidth;

ctx.fillStyle = "#2AA3CC";
ctx.fillRect(
baseX + options.championIconSize + options.padding,
topY + options.damageMargin,
width,
options.championIconSize - options.damageMargin * 2
);

ctx.font = "18px Arial";
ctx.fillStyle = "white";

const text = `${player.totalDamageDealtToChampions.toLocaleString()}`;
ctx.fillText(
text,
baseX + options.championIconSize + options.padding * 2,
topY + options.championIconSize / 2 + 6
);
}

const redSideY = options.padding + 5 * (options.championIconSize + options.championIconSpace) + options.padding * 2;
for (const [index, player] of redSidePlayers.entries()) {
const topY = redSideY + (options.championIconSize + options.championIconSpace) * index;

const icon = await imageLoader.loadImage(await getChampionIconUrl(player.championName));
ctx.drawImage(icon, baseX, topY, options.championIconSize, options.championIconSize);

const damage = player.totalDamageDealtToChampions;
const width = (damage / maxDamage) * maxWidth;

ctx.fillStyle = "#ff5859";
ctx.fillRect(
baseX + options.championIconSize + options.padding,
topY + options.damageMargin,
width,
options.championIconSize - options.damageMargin * 2
);

ctx.font = "18px Arial";
ctx.fillStyle = "white";

const text = `${player.totalDamageDealtToChampions.toLocaleString()}`;
ctx.fillText(
text,
baseX + options.championIconSize + options.padding * 2,
topY + options.championIconSize / 2 + 6
);
}

return canva.toBuffer();
};

const getRectArguments = (participant: Participant, blueSidePlayers: Participant[], redSidePlayers: Participant[]) => {
const blueSideIndex = blueSidePlayers.findIndex((p) => p.puuid === participant.puuid);
const redSideIndex = redSidePlayers.findIndex((p) => p.puuid === participant.puuid);

const isBlueSide = blueSideIndex !== -1;
const index = isBlueSide ? blueSideIndex : redSideIndex;

const width = options.championIconSize;
const height = options.championIconSize;

const x = options.padding;
const y =
options.padding +
(options.championIconSize + options.championIconSpace) * index +
(isBlueSide ? 0 : options.padding * 2);

return { x, y, width, height };
};
7 changes: 3 additions & 4 deletions src/features/details/matchDetails.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Participant, getDamageDealtPercent, getKillParticipation } from "../sum
import { groupBy, sortArrayOfObjectByPropFromArray, sortBy } from "pastable";
import { createCanvas, loadImage, type CanvasRenderingContext2D } from "canvas";
import { DDImageLoader, getChampionIconUrl, getItemIconImageData, getSummonerSpellIconImageData } from "../lol/icons";
import fs from "fs/promises";
import { DataDragon } from "data-dragon";

const imageLoader = new DDImageLoader();
Expand Down Expand Up @@ -257,7 +256,7 @@ const drawCreepScore = async ({
ctx.fillText((participant.totalMinionsKilled + participant.neutralMinionsKilled).toString() + "cs", x, y);
};

const sortPlayersByTeamAndRole = (players: Participant[]) => {
export const sortPlayersByTeamAndRole = (players: Participant[]) => {
const sortedByRole = sortArrayOfObjectByPropFromArray(players, "teamPosition", roleOrder);
const sortedByTeam = groupBy(sortedByRole, "teamId");

Expand Down Expand Up @@ -288,7 +287,7 @@ const getTopY = (index: number) => {
};

const roleOrder = ["TOP", "JUNGLE", "MIDDLE", "BOTTOM", "UTILITY"];
const blueSide = 100 as const;
const redSide = 200 as const;
export const blueSide = 100 as const;
export const redSide = 200 as const;

type AnySide = typeof blueSide | typeof redSide;
7 changes: 6 additions & 1 deletion src/features/lol/elo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,12 @@ export const getCheckEloEmbedAndButton = async ({
.setCustomId(`details-${lastGame.metadata.matchId}`)
.setStyle(ButtonStyle.Secondary);

const row = new ActionRowBuilder().addComponents(detailsButton);
const damageButton = new ButtonBuilder()
.setLabel("Damages")
.setCustomId(`damages-${lastGame.metadata.matchId}`)
.setStyle(ButtonStyle.Danger);

const row = new ActionRowBuilder().addComponents(detailsButton, damageButton);

if (!lastRank) {
const embed = await getFirstRankEmbed(summ, newRank, elo, lastGame);
Expand Down
2 changes: 0 additions & 2 deletions src/features/lol/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ export class DDImageLoader {
? url
: `https://ddragon.leagueoflegends.com/cdn/${await getDDVersion()}/img/${url}`;

console.log(fullUrl);

if (this.cache[fullUrl]) return this.cache[fullUrl];

const image = await loadImage(fullUrl);
Expand Down
3 changes: 1 addition & 2 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"module": "CommonJS",
"lib": ["es6", "DOM"],
"target": "ESNext",
"declaration": true,
"removeComments": true,
"noImplicitAny": false,
"experimentalDecorators": true,
Expand All @@ -20,5 +19,5 @@
"paths": {
"@/*": ["./src/*"]
}
},
}
}

0 comments on commit 1072974

Please sign in to comment.