Последняя активность 3 weeks ago

nuno ревизий этого фрагмента 3 weeks ago. К ревизии

1 file changed, 150 insertions

gistfile1.txt(файл создан)

@@ -0,0 +1,150 @@
1 + import { Character, ClassKey, CharacterClass, Nation, WRankEntry } from "@src/types";
2 + import { Emoji } from "@systems/emojis";
3 +
4 + // ─── Individual formatters ────────────────────────────────────────────────────
5 +
6 + export interface CharDisplayOptions {
7 + emoji?: boolean; // show class emoji (default: true)
8 + level?: boolean; // show level (default: true)
9 + }
10 +
11 + function charButton(c: Character, options?: { shared?: boolean }): string {
12 + const sharedMark = options?.shared ? " 🔗" : "";
13 + return `${c.level} ${c.name}${sharedMark}`;
14 + }
15 +
16 + /**
17 + * Format a character for display in embeds and messages.
18 + * Output: <:class:> 79 «Flash»
19 + */
20 + function char(
21 + c: { class: ClassKey|CharacterClass; level: number; name: string },
22 + options?: CharDisplayOptions
23 + ): string {
24 + const showEmoji = options?.emoji ?? true;
25 + const showLevel = options?.level ?? true;
26 + const classStr = showEmoji ? (Emoji.class(c.class) || c.class) : c.class;
27 + const levelStr = showLevel ? `${c.level} ` : "";
28 + return `${classStr} ${levelStr}${c.name}`.trim();
29 + }
30 +
31 + /**
32 + * Format a nation name with its emoji.
33 + * Output: <:capella:> Capella
34 + */
35 + function nation(n: Nation): string {
36 + const emoji = Emoji.nation(n);
37 + return emoji ? `${emoji} ${n}` : n;
38 + }
39 +
40 + /**
41 + * Format a score line.
42 + * Output: <:score:> 3000 <:kd:> 32/18
43 + */
44 + function score(pts: number, k?: number, d?: number): string {
45 + const scoreEmoji = Emoji.get("score") || "📊";
46 + const kdEmoji = Emoji.get("kd") || "⚔️";
47 + const kdStr = k !== undefined && d !== undefined ? ` ${kdEmoji} ${k}/${d}` : "";
48 + return `${scoreEmoji} ${pts}${kdStr}`;
49 + }
50 +
51 + /**
52 + * Parse a Discord custom emoji string to an object for ButtonBuilder.setEmoji()
53 + * Input: "<:fb:1511020923510194428>"
54 + * Output: { name: "fb", id: "1511020923510194428" }
55 + */
56 + function emoji(emojiStr: string): { name: string; id: string } | string | null {
57 + if (!emojiStr) return null;
58 + const match = emojiStr.match(/^<:(\w+):(\d+)>$/);
59 + if (match) return { name: match[1], id: match[2] };
60 + return emojiStr;
61 + }
62 +
63 + // ─── W.Rank formatters ────────────────────────────────────────────────────────
64 +
65 + export interface WRankDisplayOptions {
66 + goal: number;
67 + brackets?: boolean; // wrap delta in parentheses (default: true)
68 + }
69 +
70 + /**
71 + * Format the rank indicator for a wrank entry.
72 + * Output: <:wrank_1_gold:> or <:wrank_1:> or bold/plain number fallback
73 + */
74 + function wrankRank(entry: WRankEntry, goal: number): string {
75 + const isDone = entry.tgCount >= goal;
76 + const rankKey = isDone ? `wrank_${entry.currentRank}_gold` : `wrank_${entry.currentRank}`;
77 + return Emoji.get(rankKey) || (isDone ? `**${entry.currentRank}**` : `${entry.currentRank}`);
78 + }
79 +
80 + /**
81 + * Format the delta indicator for a wrank entry.
82 + * Output: <:wrank_up:><:wrank_up_2:> or ↑2, empty string if no change
83 + */
84 + function wrankDelta(entry: WRankEntry, options?: { brackets?: boolean }): string {
85 + const brackets = options?.brackets ?? true;
86 + const prev = entry.previousRank;
87 + const delta = prev !== undefined ? entry.currentRank - prev : 0;
88 +
89 + if (delta === 0 && prev === undefined) return "";
90 +
91 + let inner: string;
92 + if (delta < 0) {
93 + const abs = Math.abs(delta);
94 + const numEmoji = Emoji.get(`wrank_up_${abs}`);
95 + inner = (Emoji.get("wrank_up") || "↑") + (numEmoji || abs);
96 + } else if (delta > 0) {
97 + const numEmoji = Emoji.get(`wrank_down_${delta}`);
98 + inner = (Emoji.get("wrank_down") || "↓") + (numEmoji || delta);
99 + } else {
100 + inner = (Emoji.get("wrank_no_dash") || "·") + (Emoji.get("wrank_neutral_0") || "0");
101 + }
102 +
103 + return brackets ? ` (${inner})` : ` ${inner}`;
104 + }
105 +
106 + /**
107 + * Format a full wrank display string: rank + delta.
108 + * Output: <:wrank_1_gold:> (<:wrank_up:><:wrank_up_2:>)
109 + */
110 + function wrankFull(entry: WRankEntry, options: WRankDisplayOptions): string {
111 + return wrankRank(entry, options.goal) + wrankDelta(entry, { brackets: options.brackets });
112 + }
113 +
114 + /**
115 + * Placeholder for characters with no W.Rank when others in their nation have one.
116 + * Output: — ( — 0 )
117 + */
118 + function wrankNoRank(): string {
119 + const norank = Emoji.get("wrank_no_dash") || "—";
120 + const dash = Emoji.get("wrank_no_rank_delta") || "—";
121 + const square = Emoji.get("wrank_no_dash") || "■";
122 + return `${norank} (${square}${dash})`;
123 + }
124 +
125 + // ─── Bringer formatters ────────────────────────────────────────────────────────
126 +
127 + function bringerDisplay(n: Nation): string {
128 + const bringerMap: Record<Nation, string> = {
129 + Capella: Emoji.get("luminous_bringer") || "🔆 Luminous Bringer",
130 + Procyon: Emoji.get("storm_bringer") || "⚡ Storm Bringer",
131 + };
132 + return bringerMap[n];
133 + }
134 +
135 + // ─── Namespace export ─────────────────────────────────────────────────────────
136 +
137 + export const format = {
138 + char,
139 + charButton,
140 + nation,
141 + score,
142 + emoji,
143 + wrank: {
144 + rank: wrankRank,
145 + delta: wrankDelta,
146 + full: wrankFull,
147 + noRank: wrankNoRank,
148 + },
149 + bringer: bringerDisplay,
150 + };
Новее Позже