Skip to content

Commit fe22a3c

Browse files
committed
fix: missing files
1 parent 5ece471 commit fe22a3c

File tree

4 files changed

+546
-5
lines changed

4 files changed

+546
-5
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import {
1313
import { createCanvas, loadImage } from "canvas";
1414
import path from "path";
1515
import { generateFighter, Fighter } from "../utils/fighterGenerator.js";
16-
import { BattleStatsManager } from "../utils/battleStats.js";
16+
import { BattleStatsManager } from "../utils/battleStatsManager.js";
1717

1818
let isBattleActive = false;
1919
let currentBattleUsers: Set<string> = new Set();

src/commands/battlestats.ts

Lines changed: 274 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,274 @@
1+
import {
2+
ChatInputCommandInteraction,
3+
SlashCommandBuilder,
4+
EmbedBuilder,
5+
MessageFlags,
6+
} from "discord.js";
7+
import { BattleStatsManager } from "../utils/battleStatsManager.js";
8+
9+
export const data = new SlashCommandBuilder()
10+
.setName("battlestats")
11+
.setDescription("View deathbattle statistics and leaderboards")
12+
.addSubcommand((subcommand) =>
13+
subcommand
14+
.setName("user")
15+
.setDescription("View a user's battle statistics")
16+
.addUserOption((option) =>
17+
option
18+
.setName("user")
19+
.setDescription(
20+
"The warrior whose stats to view (defaults to yourself)",
21+
)
22+
.setRequired(false),
23+
)
24+
.addStringOption((option) =>
25+
option
26+
.setName("mode")
27+
.setDescription("View normal or ranked stats")
28+
.addChoices(
29+
{ name: "Normal", value: "normal" },
30+
{ name: "Ranked", value: "ranked" },
31+
{ name: "Both", value: "both" },
32+
)
33+
.setRequired(false),
34+
),
35+
)
36+
.addSubcommand((subcommand) =>
37+
subcommand
38+
.setName("leaderboard")
39+
.setDescription("View the top warriors leaderboard")
40+
.addStringOption((option) =>
41+
option
42+
.setName("mode")
43+
.setDescription("View normal or ranked leaderboard")
44+
.addChoices(
45+
{ name: "Normal", value: "normal" },
46+
{ name: "Ranked", value: "ranked" },
47+
)
48+
.setRequired(false),
49+
),
50+
)
51+
.addSubcommand((subcommand) =>
52+
subcommand
53+
.setName("history")
54+
.setDescription("View recent battle history for a user")
55+
.addUserOption((option) =>
56+
option
57+
.setName("user")
58+
.setDescription(
59+
"The warrior whose history to view (defaults to yourself)",
60+
)
61+
.setRequired(false),
62+
)
63+
.addStringOption((option) =>
64+
option
65+
.setName("mode")
66+
.setDescription("View normal, ranked, or all battles")
67+
.addChoices(
68+
{ name: "Normal", value: "normal" },
69+
{ name: "Ranked", value: "ranked" },
70+
{ name: "All", value: "all" },
71+
)
72+
.setRequired(false),
73+
),
74+
);
75+
76+
export async function execute(
77+
interaction: ChatInputCommandInteraction,
78+
): Promise<void> {
79+
const subcommand = interaction.options.getSubcommand();
80+
81+
try {
82+
switch (subcommand) {
83+
case "user": {
84+
const targetUser =
85+
interaction.options.getUser("user") || interaction.user;
86+
const mode = interaction.options.getString("mode") || "both";
87+
const stats = await BattleStatsManager.getUserStats(targetUser.id);
88+
89+
if (!stats) {
90+
await interaction.reply({
91+
content: `**${targetUser.tag} has not participated in any deathbattles yet!**`,
92+
flags: MessageFlags.Ephemeral,
93+
});
94+
return;
95+
}
96+
97+
const embed = new EmbedBuilder()
98+
.setColor("#4B0082")
99+
.setTitle("⚔️ WARRIOR BATTLE RECORD")
100+
.setDescription(`Battle statistics for **${targetUser.tag}**`)
101+
.setThumbnail(targetUser.displayAvatarURL())
102+
.setTimestamp();
103+
104+
if (mode === "normal" || mode === "both") {
105+
embed.addFields(
106+
{ name: "📊 **NORMAL BATTLES**", value: "\u200B", inline: false },
107+
{ name: "🏆 Wins", value: stats.wins.toString(), inline: true },
108+
{ name: "💀 Losses", value: stats.losses.toString(), inline: true },
109+
{ name: "📊 Win Rate", value: `${stats.winRate}%`, inline: true },
110+
{
111+
name: "⚔️ Total Battles",
112+
value: stats.totalBattles.toString(),
113+
inline: true,
114+
},
115+
{
116+
name: "📅 Last Battle",
117+
value: stats.lastCasualBattleAt
118+
? `<t:${Math.floor(new Date(stats.lastCasualBattleAt).getTime() / 1000)}:R>`
119+
: "Never",
120+
inline: true,
121+
},
122+
{
123+
name: "🎯 Battle Ratio",
124+
value: `${stats.wins}W-${stats.losses}L`,
125+
inline: true,
126+
},
127+
);
128+
}
129+
130+
if (mode === "ranked" || mode === "both") {
131+
embed.addFields(
132+
{ name: "🏆 **RANKED BATTLES**", value: "\u200B", inline: false },
133+
{
134+
name: "🏆 Ranked Wins",
135+
value: stats.rankedWins.toString(),
136+
inline: true,
137+
},
138+
{
139+
name: "💀 Ranked Losses",
140+
value: stats.rankedLosses.toString(),
141+
inline: true,
142+
},
143+
{
144+
name: "📊 Ranked Win Rate",
145+
value: `${stats.rankedWinRate}%`,
146+
inline: true,
147+
},
148+
{
149+
name: "⚔️ Total Ranked",
150+
value: stats.rankedTotalBattles.toString(),
151+
inline: true,
152+
},
153+
{
154+
name: "📅 Last Ranked",
155+
value: stats.lastRankedBattleAt
156+
? `<t:${Math.floor(new Date(stats.lastRankedBattleAt).getTime() / 1000)}:R>`
157+
: "Never",
158+
inline: true,
159+
},
160+
{
161+
name: "🎯 Ranked Ratio",
162+
value: `${stats.rankedWins}W-${stats.rankedLosses}L`,
163+
inline: true,
164+
},
165+
);
166+
}
167+
168+
await interaction.reply({ embeds: [embed] });
169+
break;
170+
}
171+
172+
case "leaderboard": {
173+
const mode = interaction.options.getString("mode") || "normal";
174+
const isRanked = mode === "ranked";
175+
const leaderboard = await BattleStatsManager.getLeaderboard(
176+
10,
177+
isRanked,
178+
);
179+
180+
if (leaderboard.length === 0) {
181+
await interaction.reply({
182+
content: `**No warriors have participated in enough ${isRanked ? "ranked " : ""}battles yet! (Minimum ${isRanked ? "5" : "3"} battles required)**`,
183+
flags: MessageFlags.Ephemeral,
184+
});
185+
return;
186+
}
187+
188+
let leaderboardText = "";
189+
for (let i = 0; i < leaderboard.length; i++) {
190+
const warrior = leaderboard[i];
191+
const medal =
192+
i === 0 ? "🥇" : i === 1 ? "🥈" : i === 2 ? "🥉" : `${i + 1}.`;
193+
const wins = isRanked ? warrior.rankedWins : warrior.wins;
194+
const losses = isRanked ? warrior.rankedLosses : warrior.losses;
195+
const winRate = isRanked ? warrior.rankedWinRate : warrior.winRate;
196+
leaderboardText += `${medal} **${warrior.userTag}** - ${wins}W-${losses}L (${winRate}% WR)\n`;
197+
}
198+
199+
const embed = new EmbedBuilder()
200+
.setColor(isRanked ? "#FF6B35" : "#FFD700")
201+
.setTitle(`🏆 ${isRanked ? "RANKED " : ""}HALL OF CHAMPIONS`)
202+
.setDescription(
203+
`**Top Warriors by ${isRanked ? "Ranked " : ""}Win Rate**\n` +
204+
`*Minimum ${isRanked ? "5" : "3"} battles required*\n\n` +
205+
leaderboardText,
206+
)
207+
.setFooter({
208+
text: `Fight your way to the top${isRanked ? " in ranked battles" : ""}!`,
209+
})
210+
.setTimestamp();
211+
212+
await interaction.reply({ embeds: [embed] });
213+
break;
214+
}
215+
216+
case "history": {
217+
const targetUser =
218+
interaction.options.getUser("user") || interaction.user;
219+
const mode = interaction.options.getString("mode") || "all";
220+
221+
let rankedFilter: boolean | undefined;
222+
if (mode === "normal") rankedFilter = false;
223+
else if (mode === "ranked") rankedFilter = true;
224+
else rankedFilter = undefined; // all
225+
226+
const history = await BattleStatsManager.getUserBattleHistory(
227+
targetUser.id,
228+
10,
229+
rankedFilter,
230+
);
231+
232+
if (history.length === 0) {
233+
await interaction.reply({
234+
content: `**${targetUser.tag} has no ${mode === "all" ? "" : mode + " "}battle history!**`,
235+
flags: MessageFlags.Ephemeral,
236+
});
237+
return;
238+
}
239+
240+
let historyText = "";
241+
for (const battle of history) {
242+
const isWinner = battle.winnerId === targetUser.id;
243+
const opponent = isWinner ? battle.loserTag : battle.winnerTag;
244+
const result = isWinner ? "🏆 **WON**" : "💀 **LOST**";
245+
const date = `<t:${Math.floor(new Date(battle.battleDate).getTime() / 1000)}:d>`;
246+
const battleType = battle.isRanked ? "🏆" : "⚔️";
247+
248+
historyText += `${result} vs **${opponent}** ${battleType} (${battle.turns} turns) - ${date}\n`;
249+
}
250+
251+
const embed = new EmbedBuilder()
252+
.setColor("#800080")
253+
.setTitle(`📜 ${mode.toUpperCase()} BATTLE CHRONICLES`)
254+
.setDescription(
255+
`Recent ${mode === "all" ? "" : mode + " "}battle history for **${targetUser.tag}**\n\n` +
256+
`${mode === "all" ? "🏆 = Ranked | ⚔️ = Normal\n\n" : ""}` +
257+
historyText,
258+
)
259+
.setThumbnail(targetUser.displayAvatarURL())
260+
.setTimestamp();
261+
262+
await interaction.reply({ embeds: [embed] });
263+
break;
264+
}
265+
}
266+
} catch (error) {
267+
console.error("Error fetching battle stats:", error);
268+
await interaction.reply({
269+
content:
270+
"**THE DIVINE POWERS HAVE FAILED TO RETRIEVE THE BATTLE RECORDS!**",
271+
flags: MessageFlags.Ephemeral,
272+
});
273+
}
274+
}

0 commit comments

Comments
 (0)