Ostatnio aktywny 1 week ago

nuno zrewidował ten Gist 1 week ago. Przejdź do rewizji

1 file changed, 100 insertions

gistfile1.txt(stworzono plik)

@@ -0,0 +1,100 @@
1 + function formatRow(
2 + row: ResultRow,
3 + context: NationContext,
4 + allNames: string[],
5 + allScores: string[],
6 + allKds: string[],
7 + allAtks: string[],
8 + allDefs: string[]
9 + ): string {
10 + const char = row.character;
11 + const goal = Config.get({ section: "wrank", key: "goal" });
12 + const wrEntry = Layout.wrankEntry(char as any, row.position, row.score?.pts, 1);
13 + const classKey = (typeof char.class === "object" ? char.class?.key : char.class) as ClassKey;
14 +
15 + const scoreEmoji = Emoji.get("score") || "📊";
16 + const scoreText = row.score ? format.scoreBold(row.score.pts) : "—";
17 + const kdText = row.score && (row.score.k || row.score.d) ? format.kd(row.score.k ?? 0, row.score.d ?? 0) : "—";
18 +
19 + // Column targets combine primary + secondary stat widths, so the main
20 + // row makes room for the stats line beneath it.
21 + const scoreColumn = [...allScores, ...allAtks];
22 + const kdColumn = [...allKds, ...allDefs];
23 +
24 + const tokens: Record<string, string> = {
25 + wrank: Layout.wrank(wrEntry, goal, context),
26 + class: Emoji.class(classKey) || classKey || "?",
27 + name: TextAlign.padToMax(char.name, allNames),
28 + indicators: Layout.indicators(char as any, { historyKey: row.historyKey }),
29 + score: `${scoreEmoji} ${TextAlign.padToMax(scoreText, scoreColumn)}`,
30 + kd: TextAlign.gap(KD_GAP) + TextAlign.padToMax(kdText, kdColumn),
31 + };
32 +
33 + const mainLine = Layout.formatRow(TEMPLATE, tokens);
34 +
35 + const hasStats = row.score && (row.score.atk || row.score.def || row.score.heal);
36 + log.debug(`formatRow stats check: char=${char.name} score=${JSON.stringify(row.score)} hasStats=${hasStats}`);
37 + if (!hasStats) return mainLine;
38 +
39 + const prefixText = `${tokens.wrank} ${tokens.class} ${tokens.name}${tokens.indicators}`;
40 + const prefixGap = TextAlign.padLeft("", TextAlign.estimateWidth(prefixText));
41 + const atkText = format.statText("anima_atk", row.score!.atk, "⚔️");
42 + const defText = format.statText("anima_def", row.score!.def, "🛡️");
43 + const healText = format.statText("circle_massheal_purple", row.score!.heal, "💚");
44 +
45 + const statsLine = `${prefixGap} ${TextAlign.gap(SCORE_GAP)}${TextAlign.padToMax(atkText, scoreColumn)} ${TextAlign.gap(DEF_GAP)}${TextAlign.padToMaxOffset(defText, kdColumn, DEF_WIDTH_OFFSET)} ${TextAlign.gap(HEAL_GAP)}${healText}`;
46 +
47 + return `${mainLine}\n${statsLine}`;
48 + }
49 +
50 + function buildEmbed(historyKey: TGKey, rows: ResultRow[]): EmbedBuilder {
51 + const { date, slot } = TGKey.parse(historyKey);
52 +
53 + const capellaRows = rows.filter((r) => r.character.nation === Nation.Capella);
54 + const procyonRows = rows.filter((r) => r.character.nation === Nation.Procyon);
55 +
56 + const capContext = Layout.nationContext(capellaRows);
57 + const proContext = Layout.nationContext(procyonRows);
58 +
59 + const sortByScore = (a: ResultRow, b: ResultRow) => (b.score?.pts ?? 0) - (a.score?.pts ?? 0);
60 +
61 + const sortedCapella = [...capellaRows].sort(sortByScore);
62 + const sortedProcyon = [...procyonRows].sort(sortByScore);
63 +
64 + const capellaNames = sortedCapella.map((r) => r.character.name);
65 + const procyonNames = sortedProcyon.map((r) => r.character.name);
66 + const capellaScores = sortedCapella.map((r) => r.score ? format.scoreBold(r.score.pts) : "—");
67 + const procyonScores = sortedProcyon.map((r) => r.score ? format.scoreBold(r.score.pts) : "—");
68 + const capellaKds = sortedCapella.map((r) => r.score && (r.score.k || r.score.d) ? format.kd(r.score.k ?? 0, r.score.d ?? 0) : "—");
69 + const procyonKds = sortedProcyon.map((r) => r.score && (r.score.k || r.score.d) ? format.kd(r.score.k ?? 0, r.score.d ?? 0) : "—");
70 +
71 + const atkEmoji = Emoji.get("atk") || "⚔️";
72 + const defEmoji = Emoji.get("def") || "🛡️";
73 + const capellaAtks = sortedCapella.map((r) => r.score?.atk ? `${atkEmoji} ${format.number.abbrev(r.score.atk)}` : "");
74 + const procyonAtks = sortedProcyon.map((r) => r.score?.atk ? `${atkEmoji} ${format.number.abbrev(r.score.atk)}` : "");
75 + const capellaDefs = sortedCapella.map((r) => r.score?.def ? `${defEmoji} ${format.number.abbrev(r.score.def)}` : "");
76 + const procyonDefs = sortedProcyon.map((r) => r.score?.def ? `${defEmoji} ${format.number.abbrev(r.score.def)}` : "");
77 +
78 + const capK = capellaRows.reduce((s, r) => s + (r.score?.k ?? 0), 0);
79 + const capD = capellaRows.reduce((s, r) => s + (r.score?.d ?? 0), 0);
80 + const proK = procyonRows.reduce((s, r) => s + (r.score?.k ?? 0), 0);
81 + const proD = procyonRows.reduce((s, r) => s + (r.score?.d ?? 0), 0);
82 +
83 + const capellaEmoji = Emoji.get("capella");
84 + const procyonEmoji = Emoji.get("procyon");
85 +
86 + const capellaFormatted = sortedCapella.map((r) => formatRow(r, capContext, capellaNames, capellaScores, capellaKds, capellaAtks, capellaDefs));
87 + const procyonFormatted = sortedProcyon.map((r) => formatRow(r, proContext, procyonNames, procyonScores, procyonKds, procyonAtks, procyonDefs));
88 +
89 + const embed = new EmbedBuilder()
90 + .setTitle(`⚔️ TG Result — ${format.date(new Date(date), "dd/MM/YYYY")} · ${slot}:00`)
91 + .setColor(0xe8a317)
92 + .setFooter({ text: `TG Result · ${TGKey.toDisplay(historyKey)}` })
93 + .setTimestamp();
94 +
95 + EmbedHelpers.addPerPlayerColumn(embed, `${capellaEmoji} Capella${(capK || capD) ? ` — ${format.kd(capK, capD)}` : ""}`, capellaFormatted);
96 + embed.addFields({ name: "\u200b", value: "\u200b", inline: false });
97 + EmbedHelpers.addPerPlayerColumn(embed, `${procyonEmoji} Procyon${(proK || proD) ? ` — ${format.kd(proK, proD)}` : ""}`, procyonFormatted);
98 +
99 + return embed;
100 + }
Nowsze Starsze