- TextAlign: column alignment for embeds using real gg sans font metrics - EmbedHelpers: per-player grid/column layouts immune to 1024-char field limit - Layout: domain-aware formatting wrapper (wrank, bringer, cockroach, tgCount) - PersistentMessage: multi-slot support for independently-updatable embeds - Leaderboard: weekly rankings + highlights embed (most kills/deaths, next Bringer) - Result: per-TG breakdown with wRankAtSubmission snapshot for historical accuracy - /tg call, /tg poll confirm-no, /tg-admin score-inject, result/leaderboard post commands - Fix: CharacterRegistry wasn't hydrating ownerKey, breaking K/D bot-wide - Fix: Leaderboard.buildEntries used current week instead of passed-in week param - /tg-admin test-align: permanent calibration tool for embed text alignment Includes data/emojis/anima-mastery.json for new combat stat icons.
70 lines
No EOL
3.1 KiB
TypeScript
70 lines
No EOL
3.1 KiB
TypeScript
/**
|
|
* Leaderboard highlights — secondary embed with weekly standout stats.
|
|
* Most kills, most deaths, next bringer per nation, etc.
|
|
*/
|
|
|
|
import { EmbedBuilder } from "discord.js";
|
|
import { Nation } from "@types";
|
|
import { Emoji } from "@systems/emojis";
|
|
import { Config } from "@systems/config";
|
|
import { LeaderboardRow } from "./index";
|
|
|
|
function topByKills(rows: LeaderboardRow[]): LeaderboardRow | null {
|
|
return [...rows].sort((a, b) => b.totalKills - a.totalKills)[0] ?? null;
|
|
}
|
|
|
|
function topByDeaths(rows: LeaderboardRow[]): LeaderboardRow | null {
|
|
return [...rows].sort((a, b) => b.totalDeaths - a.totalDeaths)[0] ?? null;
|
|
}
|
|
|
|
function nextBringerCandidate(rows: LeaderboardRow[], goal: number): LeaderboardRow | null {
|
|
// Rank 1 with goal TGs met is eligible — pick the rank-1 player if they qualify
|
|
const rank1 = rows.find((r) => r.position?.currentRank === 1);
|
|
if (!rank1) return null;
|
|
return rank1.tgCount >= goal ? rank1 : null;
|
|
}
|
|
|
|
export function buildHighlightsEmbed(allRows: LeaderboardRow[], weekKey: string): EmbedBuilder {
|
|
const goal = Config.get({ section: "wrank", key: "goal" });
|
|
|
|
const capellaRows = allRows.filter((r) => r.character.nation === Nation.Capella);
|
|
const procyonRows = allRows.filter((r) => r.character.nation === Nation.Procyon);
|
|
|
|
const topKillsCapella = topByKills(capellaRows);
|
|
const topKillsProcyon = topByKills(procyonRows);
|
|
const topDeathsAll = topByDeaths(allRows);
|
|
|
|
// Storm Bringer -> Procyon, Luminous Bringer -> Capella
|
|
const nextLuminousBringer = nextBringerCandidate(capellaRows, goal); // Capella
|
|
const nextStormBringer = nextBringerCandidate(procyonRows, goal); // Procyon
|
|
|
|
const capellaEmoji = Emoji.get("capella");
|
|
const procyonEmoji = Emoji.get("procyon");
|
|
const killEmoji = Emoji.get("wrank_down_1") || "⚔️";
|
|
const deathEmoji = Emoji.get("wrank_up_1") || "💀";
|
|
const stormEmoji = Emoji.get("storm_bringer") || "⚡";
|
|
const luminousEmoji = Emoji.get("luminous_bringer") || "🌟";
|
|
|
|
const lines: string[] = [];
|
|
|
|
if (topKillsCapella && topKillsCapella.totalKills > 0) {
|
|
lines.push(`${killEmoji} Most Kills (${capellaEmoji}): **${topKillsCapella.character.name}** (${topKillsCapella.totalKills})`);
|
|
}
|
|
if (topKillsProcyon && topKillsProcyon.totalKills > 0) {
|
|
lines.push(`${killEmoji} Most Kills (${procyonEmoji}): **${topKillsProcyon.character.name}** (${topKillsProcyon.totalKills})`);
|
|
}
|
|
if (topDeathsAll && topDeathsAll.totalDeaths > 0) {
|
|
lines.push(`${deathEmoji} Most Deaths: **${topDeathsAll.character.name}** (${topDeathsAll.totalDeaths})`);
|
|
}
|
|
|
|
lines.push(""); // spacer
|
|
|
|
lines.push(`${luminousEmoji} Next Luminous Bringer (${capellaEmoji}): ${nextLuminousBringer ? `**${nextLuminousBringer.character.name}**` : "—"}`);
|
|
lines.push(`${stormEmoji} Next Storm Bringer (${procyonEmoji}): ${nextStormBringer ? `**${nextStormBringer.character.name}**` : "—"}`);
|
|
|
|
return new EmbedBuilder()
|
|
.setTitle("📊 Weekly Highlights")
|
|
.setColor(0x5865f2)
|
|
.setDescription(lines.join("\n"))
|
|
.setFooter({ text: `Highlights · ${weekKey}` });
|
|
} |