@mauricode/token-derby 2.5.4 → 2.5.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin.js +434 -458
- package/dist/bin.js.map +1 -1
- package/package.json +1 -1
package/dist/bin.js
CHANGED
|
@@ -1,14 +1,180 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/commands/stable-create.ts
|
|
4
|
-
import
|
|
4
|
+
import React3 from "react";
|
|
5
5
|
import { render } from "ink";
|
|
6
6
|
|
|
7
7
|
// src/ui/HorseCreator.tsx
|
|
8
|
-
import { useState } from "react";
|
|
9
|
-
import { Box as
|
|
8
|
+
import { useState as useState2 } from "react";
|
|
9
|
+
import { Box as Box3, Text as Text3, useInput } from "ink";
|
|
10
10
|
import TextInput from "ink-text-input";
|
|
11
11
|
|
|
12
|
+
// ../shared/dist/constants.js
|
|
13
|
+
var CLI_VERSION_HEADER = "x-cli-version";
|
|
14
|
+
var USER_ID_HEADER = "x-user-id";
|
|
15
|
+
var USER_TOKEN_HEADER = "x-user-token";
|
|
16
|
+
var USER_NAME_MAX_LENGTH = 40;
|
|
17
|
+
var ORG_NAME_MAX_LENGTH = 12;
|
|
18
|
+
var ORG_NAME_PATTERN = /^[A-Za-z0-9]{1,12}$/;
|
|
19
|
+
|
|
20
|
+
// ../shared/dist/version-match.js
|
|
21
|
+
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/;
|
|
22
|
+
function parseSemver(v) {
|
|
23
|
+
if (typeof v !== "string")
|
|
24
|
+
return null;
|
|
25
|
+
const m = SEMVER_RE.exec(v.trim());
|
|
26
|
+
if (!m)
|
|
27
|
+
return null;
|
|
28
|
+
return {
|
|
29
|
+
major: Number(m[1]),
|
|
30
|
+
minor: Number(m[2]),
|
|
31
|
+
patch: Number(m[3])
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function gteSemver(a, b) {
|
|
35
|
+
const pa = parseSemver(a);
|
|
36
|
+
const pb = parseSemver(b);
|
|
37
|
+
if (!pa || !pb)
|
|
38
|
+
return false;
|
|
39
|
+
if (pa.major !== pb.major)
|
|
40
|
+
return pa.major > pb.major;
|
|
41
|
+
if (pa.minor !== pb.minor)
|
|
42
|
+
return pa.minor > pb.minor;
|
|
43
|
+
return pa.patch >= pb.patch;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ../shared/dist/levels.js
|
|
47
|
+
var MAX_LEVEL = 30;
|
|
48
|
+
function xpForLevel(n) {
|
|
49
|
+
return 1.8 * n ** 3 + 18 * n ** 2 + 50 * n - 19.8;
|
|
50
|
+
}
|
|
51
|
+
function thresholdForLevel(level) {
|
|
52
|
+
if (level <= 1)
|
|
53
|
+
return 0;
|
|
54
|
+
return Math.round(xpForLevel(level - 1));
|
|
55
|
+
}
|
|
56
|
+
var XP_THRESHOLDS = Array.from({ length: MAX_LEVEL }, (_, i) => thresholdForLevel(i + 1));
|
|
57
|
+
function levelFromXp(xp) {
|
|
58
|
+
const v = Math.max(0, Math.floor(xp));
|
|
59
|
+
let level = 1;
|
|
60
|
+
while (level < MAX_LEVEL && v >= thresholdForLevel(level + 1)) {
|
|
61
|
+
level++;
|
|
62
|
+
}
|
|
63
|
+
return level;
|
|
64
|
+
}
|
|
65
|
+
function levelInfo(xp) {
|
|
66
|
+
const v = Math.max(0, Math.floor(xp));
|
|
67
|
+
const level = levelFromXp(v);
|
|
68
|
+
const level_start_xp = thresholdForLevel(level);
|
|
69
|
+
const isMax = level >= MAX_LEVEL;
|
|
70
|
+
const next_level_xp = isMax ? null : thresholdForLevel(level + 1);
|
|
71
|
+
const xp_into_level = v - level_start_xp;
|
|
72
|
+
const xp_for_level = isMax ? null : next_level_xp - level_start_xp;
|
|
73
|
+
const progress = isMax ? 1 : Math.min(1, xp_into_level / Math.max(1, xp_for_level));
|
|
74
|
+
return { level, xp: v, level_start_xp, next_level_xp, xp_into_level, xp_for_level, progress };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// ../shared/dist/midrace.js
|
|
78
|
+
var ACHIEVEMENT_DESCRIPTIONS = {
|
|
79
|
+
"Racer!": "Raced continuously for an hour",
|
|
80
|
+
"Overtake!": "Overtook another horse",
|
|
81
|
+
"Pacesetter!": "Led the race for an hour straight",
|
|
82
|
+
"Stampede!": "Gained 7,000+ tokens in a single minute",
|
|
83
|
+
"Took the lead!": "Charged into first place",
|
|
84
|
+
"Comeback!": "Climbed from last place to the top half",
|
|
85
|
+
"Pulled Away!": "Grew the lead by 5,000+ tokens in a minute"
|
|
86
|
+
};
|
|
87
|
+
function overtakeDescription(positionsClimbed) {
|
|
88
|
+
if (positionsClimbed <= 1)
|
|
89
|
+
return "Overtook another horse";
|
|
90
|
+
return `Overtook ${positionsClimbed} horses`;
|
|
91
|
+
}
|
|
92
|
+
var TOKEN_INPUT_MULTIPLIER = 10;
|
|
93
|
+
function tokenMultiplier(race) {
|
|
94
|
+
return race.counts_input ? TOKEN_INPUT_MULTIPLIER : 1;
|
|
95
|
+
}
|
|
96
|
+
function describeAchievement(event, race) {
|
|
97
|
+
if (event.name === "Overtake!") {
|
|
98
|
+
return overtakeDescription(Math.floor(event.xp / 3));
|
|
99
|
+
}
|
|
100
|
+
const m = tokenMultiplier(race);
|
|
101
|
+
if (event.name === "Stampede!") {
|
|
102
|
+
return `Gained ${(MIDRACE_THRESHOLDS.stampede_tokens * m).toLocaleString("en-US")}+ tokens in a single minute`;
|
|
103
|
+
}
|
|
104
|
+
if (event.name === "Pulled Away!") {
|
|
105
|
+
return `Grew the lead by ${(MIDRACE_THRESHOLDS.pulled_away_gap * m).toLocaleString("en-US")}+ tokens in a minute`;
|
|
106
|
+
}
|
|
107
|
+
return ACHIEVEMENT_DESCRIPTIONS[event.name];
|
|
108
|
+
}
|
|
109
|
+
var MIDRACE_THRESHOLDS = {
|
|
110
|
+
warm_up_fraction: 0.08,
|
|
111
|
+
// first 8% of race time
|
|
112
|
+
streak_hour_ms: 36e5,
|
|
113
|
+
// 1 hour for Racer!/Pacesetter!
|
|
114
|
+
racer_dt_cap_ms: 9e4,
|
|
115
|
+
// single-tick credit cap for Racer!
|
|
116
|
+
stampede_tokens: 7e3,
|
|
117
|
+
// tokens-in-a-minute threshold
|
|
118
|
+
stampede_cooldown_ms: 72e5,
|
|
119
|
+
// 2 hours
|
|
120
|
+
pulled_away_gap: 5e3,
|
|
121
|
+
// gap-growth threshold per minute
|
|
122
|
+
pulled_away_cooldown_ms: 72e5,
|
|
123
|
+
// 2 hours
|
|
124
|
+
recent_events_retention_ms: 9e4
|
|
125
|
+
// sliding window for recent_events
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// ../shared/dist/hats.js
|
|
129
|
+
function hatById(id) {
|
|
130
|
+
return HATS.find((h) => h.id === id);
|
|
131
|
+
}
|
|
132
|
+
var HATS = [
|
|
133
|
+
// ── COMMON (18) ────────────────────────────────────────────────────────
|
|
134
|
+
{ id: "flat_cap", name: "Flat Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...........", "....AAAA...", "...AAAAAA..", "..AAAAAAA.."], variants: [{ A: "#8B6914" }, { A: "#12301b" }, { A: "#aab5cb" }, { A: "#bdcbaa" }] },
|
|
135
|
+
{ id: "bucket_hat", name: "Bucket Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...QQQQQ...", "...AAAAA...", "...AAAAA...", ".AAAAAAAAA."], variants: [{ A: "#CC2200", Q: "#FFFFFF" }, { A: "#0007cc", Q: "#FFFFFF" }, { A: "#becc00", Q: "#4d3dc7" }, { A: "#cc00c5", Q: "#d6d6dc" }] },
|
|
136
|
+
{ id: "beanie", name: "Beanie", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....AAA....", "...AAAAA...", "...AAAAA...", "..AAAAAAA.."], variants: [{ A: "#4A7C59" }, { A: "#b21f35" }, { A: "#ec9418" }, { A: "#3a17e6" }] },
|
|
137
|
+
{ id: "stetson", name: "Stetson", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...AA.AA...", "...AAAAA...", "...AAAAA...", "A..AAAAA..A", "AAAAAAAAAAA", "AAAAAAAAAAA"], variants: [{ A: "#C49A00" }, { A: "#272004" }, { A: "#040404" }, { A: "#373606" }] },
|
|
138
|
+
{ id: "party_hat", name: "Party Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "....AQA....", "...AQAQA...", "...AAAAA..."], variants: [{ A: "#FF69B4", Q: "#FFD700" }, { A: "#c46bff", Q: "#ff000d" }, { A: "#fdff6b", Q: "#0400ff" }, { A: "#6bff97", Q: "#05050a" }] },
|
|
139
|
+
{ id: "fez", name: "Fez", rarity: "common", width: 11, anchor_x: 22, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....Q......", "....AAA....", "....AAA....", "....AAA...."], variants: [{ A: "#CC0000", Q: "#8B0000" }, { A: "#CC0000", Q: "#f5e5e5" }, { A: "#CC0000", Q: "#f1e209" }, { A: "#CC0000", Q: "#0956f1" }] },
|
|
140
|
+
{ id: "sailor_hat", name: "Sailor Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...QQQQQ...", "..AAAAAAA..", "..QQQQQQQ..", "..AAAAAAA.."], variants: [{ A: "#FFFFFF", Q: "#000080" }, { A: "#FFFFFF", Q: "#c0c0ed" }, { A: "#FFFFFF", Q: "#00000a" }, { A: "#FFFFFF", Q: "#0505ef" }] },
|
|
141
|
+
{ id: "newsboy_cap", name: "Newsboy Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAAAA...", "..AAAQAAA..", "..AAAAAAA..", "..AAAAAA..."], variants: [{ A: "#5C4033", Q: "#3D2B1F" }, { A: "#e96020", Q: "#131211" }, { A: "#342c28", Q: "#131211" }, { A: "#0a0300", Q: "#ddab78" }] },
|
|
142
|
+
{ id: "tam_o_shanter", name: "Tam O'Shanter", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....Q.....", "....AAA....", "..AAAAAAA..", "..AAAAAAA..", "...AAAAA..."], variants: [{ A: "#006400", Q: "#FF0000" }, { A: "#ff0000", Q: "#016400" }] },
|
|
143
|
+
{ id: "trucker_cap", name: "Trucker Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAQQ....", "...AAQQ....", "..AAQQQAA..", "..AAQQQAA.."], variants: [{ A: "#2196F3", Q: "#FFFFFF" }, { A: "#f41fe3", Q: "#FFFFFF" }, { A: "#1ff45f", Q: "#FFFFFF" }] },
|
|
144
|
+
{ id: "hard_hat", name: "Hard Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....AAA....", "...AAAAA...", "..AAAAAAA..", "..AAAAAAAAA"], variants: [{ A: "#FFD600" }, { A: "#d74c1d" }] },
|
|
145
|
+
{ id: "chef_toque", name: "Chef's Toque", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...AAAAA...", "..AAQAQAA..", "...AQAQA...", "...AQAQA...", "...AQAQA...", "...AAAAA..."], variants: [{ A: "#FFFFFF", Q: "#f9f9f9" }] },
|
|
146
|
+
{ id: "bobble_hat", name: "Bobble Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", ".....Q.....", "...AAAAA...", "...AAAAA...", "..AAAAAAA.."], variants: [{ A: "#C62828", Q: "#FFFFFF" }, { A: "#293bc7", Q: "#FFFFFF" }, { A: "#29c775", Q: "#231f1f" }, { A: "#a529c7", Q: "#d50707" }] },
|
|
147
|
+
{ id: "cowboy_hat", name: "Cowboy Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAAAA...", "...AAAAA...", "A.AAAAAAA.A", ".AAAAAAAAA."], variants: [{ A: "#8B4513" }, { A: "#0a0400" }, { A: "#956c50" }] },
|
|
148
|
+
{ id: "baseball_cap", name: "Baseball Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAAAA...", "..AAAAAA...", "..AAAAAAA..", "..AAAAAAAAA"], variants: [{ A: "#1565C0" }, { A: "#8515c1" }, { A: "#15c157" }, { A: "#ef2d0b" }] },
|
|
149
|
+
{ id: "tinfoil_hat", name: "Tinfoil Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "...AAAAA...", "..AAAAAAA.."], variants: [{ A: "#B0BEC5" }, { A: "#6a6c6c" }] },
|
|
150
|
+
{ id: "dunce_cap", name: "Dunce Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", ".....Q.....", "....QAQ....", "...QAAAQ...", "...QAAAQ...", "..QAAAAAQ..", "..AAAAAAA.."], variants: [{ A: "#F8F8F8", Q: "#F44336" }] },
|
|
151
|
+
{ id: "mini_top_hat", name: "Mini Top Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...AAAAA...", "...AAAAA...", "...AAAAA...", "..AAAAAAA..", "..AAAAAAA.."], variants: [{ A: "#212121" }, { A: "#626060" }] },
|
|
152
|
+
// ── RARE (10) ──────────────────────────────────────────────────────────
|
|
153
|
+
{ id: "bicorne", name: "Bicorne", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..AAAQQQQ..", "..AAAAAAA..", "..AAAAAAA..", "...QQQQQ..."], variants: [{ A: "#1A237E", Q: "#FFD700" }, { A: "#01052d", Q: "#544803" }] },
|
|
154
|
+
{ id: "viking_helmet", name: "Viking Helmet", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..A.AAA.A..", "..AAAAAAA..", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#9E9E9E", Q: "#8D6E63" }, { A: "#dcdbdb", Q: "#bf3908" }] },
|
|
155
|
+
{ id: "jesters_cap", name: "Jester's Cap", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..AQAQAQA..", "..AQAQAQA..", "...AAAAA...", "...AAAAA..."], variants: [{ A: "#E53935", Q: "#FFD600" }, { A: "#e5a434", Q: "#0040ff" }, { A: "#34e537", Q: "#eeff00" }, { A: "#e234e5", Q: "#00ff40" }] },
|
|
156
|
+
{ id: "plague_doctor", name: "Plague Doctor Beak", rarity: "rare", width: 11, anchor_x: 24, rows: ["...........", "...........", "...........", "...........", "....QQQ....", "...QAAAQQ..", "...QQQQQQQ.", "..QQQQQ.QQQ", "..QQQQ....Q", "..QQQQ....."], variants: [{ A: "#F5F5F5", Q: "#795548" }, { A: "#e1dbdb", Q: "#080707" }] },
|
|
157
|
+
{ id: "morion", name: "Conquistador Morion", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....Q.....", "...QQQQQ...", "...QAAAQ...", ".A..AAA..A.", "..AAQQQAA.."], variants: [{ A: "#B0BEC5", Q: "#FFD600" }, { A: "#0a4768", Q: "#e6c10a" }] },
|
|
158
|
+
{ id: "shako", name: "Shako", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "........Q..", "........Q..", "........Q..", "........Q..", "..AAAAAAA..", "..AAAAAAA..", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#45464f", Q: "#00ff2a" }, { A: "#45464f", Q: "#ff4d00" }, { A: "#45464f", Q: "#ff0019" }, { A: "#45464f", Q: "#fff700" }] },
|
|
159
|
+
{ id: "centurion_helm", name: "Centurion Helm", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "....QQQ....", "...QQQQQQ..", "..QQQQQQQQ.", ".QQ..A...Q.", ".Q..AAA....", "...AAAAA...", "..AAAAAAA..", "..AAAAAAA.."], variants: [{ A: "#B0BEC5", Q: "#C62828" }, { A: "#2f3131", Q: "#C62828" }] },
|
|
160
|
+
{ id: "papal_mitre", name: "Papal Mitre", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....QQQ....", "...QQQQQ...", "...QAAAQ...", "..AAAAAAA.."], variants: [{ A: "#FFFFFF", Q: "#FFD700" }] },
|
|
161
|
+
{ id: "headdress", name: "Headdress", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..AQAQAQA..", "..AQAQAQA..", "..AAAAAAA..", "...QQQQQ..."], variants: [{ A: "#FF8F00", Q: "#1565C0" }] },
|
|
162
|
+
{ id: "sombrero", name: "Sombrero", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "...AAAAA...", "...AQAQA...", "AAAAAAAAAAA"], variants: [{ A: "#F57F17", Q: "#BF360C" }, { A: "#f8b377", Q: "#3e1204" }, { A: "#d6f877", Q: "#04133e" }, { A: "#d6f877", Q: "#04133e" }] },
|
|
163
|
+
// ── EPIC (6) ───────────────────────────────────────────────────────────
|
|
164
|
+
{ id: "papal_tiara", name: "Papal Tiara", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "....QQ.....", "...QQQQQ...", "...QAAQQ...", "...QQQQQ...", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#FFFFFF", Q: "#FFD700" }, { A: "#b18e10", Q: "#f3f3f1" }] },
|
|
165
|
+
{ id: "samurai_kabuto", name: "Samurai Kabuto", rarity: "epic", width: 11, anchor_x: 23, rows: ["....Q...Q..", "....QQ.QQ..", ".....QQQ...", ".......Q...", "...AAAAQ...", "..AAAAAQQ..", "..AAAAAQQ..", ".AAAAAAAQ..", "AAAAAAAAQ..", "AAAAAAAAQ.."], variants: [{ A: "#B0BEC5", Q: "#C62828" }, { A: "#050505", Q: "#C62828" }, { A: "#675f5f", Q: "#c4c729" }] },
|
|
166
|
+
{ id: "gladiator_galea", name: "Gladiator Galea", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "....QQQQQQ.", "...QQQQQ...", "...QQQQQ...", "...QAAA....", "..AAAAAAA..", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#B0BEC5", Q: "#C62828" }, { A: "#ffe907", Q: "#C62828" }] },
|
|
167
|
+
{ id: "pharaoh_nemes", name: "Pharaoh Nemes", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "..AAAAAAA..", "..AAQAQAA..", "..QAQAQAQ..", "..QAQAQAQ..", "..QQAAAQQ..", "..QQAAAAA..", "...QAQA...."], variants: [{ A: "#FFD700", Q: "#1565C0" }, { A: "#8d342a", Q: "#63686e" }] },
|
|
168
|
+
{ id: "spartan_helmet", name: "Spartan Helmet", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "........Q..", ".......QQ..", ".....QQQQ..", "....QQQQQ..", "..QQQQQQ...", ".QQQAA.....", "QQAAAAAAA..", "Q.AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#B0BEC5", Q: "#B71C1C" }, { A: "#B0BEC5", Q: "#b2b51c" }, { A: "#B0BEC5", Q: "#b5611c" }] },
|
|
169
|
+
{ id: "conquistador_full", name: "Conquistador Helm", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "...AAAAA...", "..AAAAAAA..", "...AQQQA...", "...AQQQA..."], variants: [{ A: "#B0BEC5", Q: "#FFD700" }, { A: "#B0BEC5", Q: "#0040ff" }, { A: "#B0BEC5", Q: "#36123b" }] },
|
|
170
|
+
// ── LEGENDARY (5) ──────────────────────────────────────────────────────
|
|
171
|
+
{ id: "rainbow_crown", name: "Rainbow Crown", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "...........", ".....A.....", "....AAA....", "....AQA....", "....AAA....", "...AAQAA...", "...AAAAA...", "..AAAQAAA..", "..AAAAAAA.."], colors: { A: "#FFD700", Q: "#553f3f" }, animation: { type: "cycle", frames: ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#8B00FF"], fps: 8 } },
|
|
172
|
+
{ id: "inferno_cap", name: "Inferno Cap", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "....AAA....", "...AAAAA...", "....AAA....", ".....Q.....", "A....Q....A", "A...QQQ...A", ".A..QQQ..A.", "..AAAAAAA..", "..AAAAAAA.."], colors: { A: "#FF4500", Q: "#FFD700" }, animation: { type: "cycle", frames: ["#FF0000", "#FF2200", "#FF4500", "#FF6600", "#FF8C00", "#FFA500"], fps: 12 } },
|
|
173
|
+
{ id: "void_hood", name: "Void Hood", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...AAA.....", "..AAAAAA...", ".AAAAAAA...", ".AAAAAAAA..", "AAAAAAAAA..", "AAAAQQQAA..", "AAAAQQQAA.."], colors: { A: "#1A0033", Q: "#d9cfe3" }, animation: { type: "cycle", frames: ["#0D0019", "#1A0033", "#2D004D", "#3D0066", "#2D004D", "#1A0033"], fps: 3 } },
|
|
174
|
+
{ id: "prismatic_jester", name: "Prismatic Jester", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "..Q.Q.Q.Q..", "Q..A.A.A..Q", ".Q.A.A.A.Q.", "..AQAQAQA..", "..AQAQAQA..", "..AAAAAAA..", "..AAAAAAA..", "..AAAAAAA..", "..AAAAAAA.."], colors: { A: "#FF0000", Q: "#0000FF" }, animation: { type: "cycle", frames: ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#8B00FF", "#FF00FF", "#00FFFF"], fps: 15 } },
|
|
175
|
+
{ id: "aurora_helm", name: "Aurora Helm", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...QQQQQ...", "...QQQQQ...", "..AAAAAAA..", "..AAAAAAA..", "..AQAAAQA..", "..AQAAAQA.."], colors: { A: "#00CED1", Q: "#00FF7F" }, animation: { type: "cycle", frames: ["#0000FF", "#0066FF", "#00BFFF", "#00CED1", "#00FF7F", "#7CFC00", "#00FF7F", "#00CED1"], fps: 4 } }
|
|
176
|
+
];
|
|
177
|
+
|
|
12
178
|
// src/ui/HorseSprite.tsx
|
|
13
179
|
import { Box, Text } from "ink";
|
|
14
180
|
|
|
@@ -235,6 +401,29 @@ function rowToAnsi(row) {
|
|
|
235
401
|
return out;
|
|
236
402
|
}
|
|
237
403
|
|
|
404
|
+
// src/ui/AnimatedHorseSprite.tsx
|
|
405
|
+
import { useEffect, useState } from "react";
|
|
406
|
+
import { Box as Box2, Text as Text2 } from "ink";
|
|
407
|
+
import { jsx as jsx2 } from "react/jsx-runtime";
|
|
408
|
+
function AnimatedHorseSprite({ sprite, colors, hat }) {
|
|
409
|
+
const isLegendary = hat.rarity === "legendary";
|
|
410
|
+
const frames = isLegendary ? hat.animation.frames : [];
|
|
411
|
+
const fps = isLegendary ? hat.animation.fps : 1;
|
|
412
|
+
const [idx, setIdx] = useState(0);
|
|
413
|
+
useEffect(() => {
|
|
414
|
+
if (!isLegendary || frames.length <= 1) return;
|
|
415
|
+
const interval = setInterval(
|
|
416
|
+
() => setIdx((i) => (i + 1) % frames.length),
|
|
417
|
+
Math.max(1, Math.round(1e3 / fps))
|
|
418
|
+
);
|
|
419
|
+
return () => clearInterval(interval);
|
|
420
|
+
}, [isLegendary, frames.length, fps]);
|
|
421
|
+
const renderedHat = isLegendary && frames[idx] ? { ...hat, colors: { ...hat.colors, A: frames[idx] } } : hat;
|
|
422
|
+
const { grid } = composeHatGrid(sprite, renderedHat, 0, colors);
|
|
423
|
+
const lines = hexGridToHalfBlocks(grid);
|
|
424
|
+
return /* @__PURE__ */ jsx2(Box2, { flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx2(Text2, { children: line }, i)) });
|
|
425
|
+
}
|
|
426
|
+
|
|
238
427
|
// src/ui/palette.ts
|
|
239
428
|
var SLOTS = ["body", "mane", "tail", "saddle"];
|
|
240
429
|
var PALETTES = {
|
|
@@ -332,14 +521,37 @@ function defaultColors() {
|
|
|
332
521
|
}
|
|
333
522
|
|
|
334
523
|
// src/ui/HorseCreator.tsx
|
|
335
|
-
import { jsx as
|
|
336
|
-
function HorseCreator({
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
524
|
+
import { Fragment, jsx as jsx3, jsxs } from "react/jsx-runtime";
|
|
525
|
+
function HorseCreator({
|
|
526
|
+
onSubmit,
|
|
527
|
+
onCancel,
|
|
528
|
+
initialColors,
|
|
529
|
+
initialName,
|
|
530
|
+
lockName,
|
|
531
|
+
initialLevel,
|
|
532
|
+
hats,
|
|
533
|
+
initialEquipped
|
|
534
|
+
}) {
|
|
535
|
+
const [colors, setColors] = useState2(initialColors ?? defaultColors());
|
|
536
|
+
const [hatChoice, setHatChoice] = useState2(initialEquipped ?? null);
|
|
537
|
+
const [rowIdx, setRowIdx] = useState2(0);
|
|
538
|
+
const [namingMode, setNamingMode] = useState2(false);
|
|
539
|
+
const [name, setName] = useState2(initialName ?? "");
|
|
540
|
+
const [error, setError] = useState2(null);
|
|
541
|
+
const hatRowEnabled = !!hats && hats.length > 0;
|
|
542
|
+
const rows = [
|
|
543
|
+
...SLOTS.map((s) => ({ kind: "color", slot: s })),
|
|
544
|
+
...hatRowEnabled ? [{ kind: "hat" }] : []
|
|
545
|
+
];
|
|
546
|
+
const row = rows[rowIdx];
|
|
547
|
+
const cycleHat = (dir) => {
|
|
548
|
+
if (!hats || hats.length === 0) return;
|
|
549
|
+
const entries = [null, ...hats.map((_, i) => i)];
|
|
550
|
+
const cur = entries.findIndex((e) => e === hatChoice);
|
|
551
|
+
const start = cur < 0 ? 0 : cur;
|
|
552
|
+
const next = (start + dir + entries.length) % entries.length;
|
|
553
|
+
setHatChoice(entries[next]);
|
|
554
|
+
};
|
|
343
555
|
useInput((input, key) => {
|
|
344
556
|
if (namingMode) return;
|
|
345
557
|
if (key.escape) {
|
|
@@ -347,67 +559,115 @@ function HorseCreator({ onSubmit, onCancel, initialColors, initialName, lockName
|
|
|
347
559
|
return;
|
|
348
560
|
}
|
|
349
561
|
if (key.upArrow) {
|
|
350
|
-
|
|
562
|
+
setRowIdx((rowIdx - 1 + rows.length) % rows.length);
|
|
351
563
|
return;
|
|
352
564
|
}
|
|
353
565
|
if (key.downArrow) {
|
|
354
|
-
|
|
566
|
+
setRowIdx((rowIdx + 1) % rows.length);
|
|
355
567
|
return;
|
|
356
568
|
}
|
|
357
569
|
if (key.leftArrow) {
|
|
358
|
-
setColors({ ...colors, [slot]: prevColor(slot, colors[slot]) });
|
|
570
|
+
if (row.kind === "color") setColors({ ...colors, [row.slot]: prevColor(row.slot, colors[row.slot]) });
|
|
571
|
+
else cycleHat(-1);
|
|
359
572
|
return;
|
|
360
573
|
}
|
|
361
574
|
if (key.rightArrow) {
|
|
362
|
-
setColors({ ...colors, [slot]: nextColor(slot, colors[slot]) });
|
|
575
|
+
if (row.kind === "color") setColors({ ...colors, [row.slot]: nextColor(row.slot, colors[row.slot]) });
|
|
576
|
+
else cycleHat(1);
|
|
363
577
|
return;
|
|
364
578
|
}
|
|
365
579
|
if (key.return) {
|
|
366
580
|
if (lockName) {
|
|
367
|
-
|
|
581
|
+
submit(initialName ?? "");
|
|
368
582
|
return;
|
|
369
583
|
}
|
|
370
584
|
setNamingMode(true);
|
|
371
585
|
return;
|
|
372
586
|
}
|
|
373
587
|
});
|
|
588
|
+
const submit = (finalName) => {
|
|
589
|
+
if (hatRowEnabled) onSubmit(finalName, colors, hatChoice);
|
|
590
|
+
else onSubmit(finalName, colors);
|
|
591
|
+
};
|
|
374
592
|
const handleNameSubmit = (value) => {
|
|
375
593
|
if (!value.trim()) {
|
|
376
594
|
setError("Name required");
|
|
377
595
|
return;
|
|
378
596
|
}
|
|
379
|
-
|
|
597
|
+
submit(value.trim());
|
|
380
598
|
};
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
599
|
+
const previewHat = hatRowEnabled && hatChoice !== null && hats ? hats[hatChoice] : void 0;
|
|
600
|
+
const previewHatDef = previewHat ? hatById(previewHat.id) : void 0;
|
|
601
|
+
return /* @__PURE__ */ jsxs(Box3, { flexDirection: "column", children: [
|
|
602
|
+
lockName && initialName && /* @__PURE__ */ jsxs(Box3, { marginBottom: 1, children: [
|
|
603
|
+
/* @__PURE__ */ jsx3(Text3, { bold: true, children: initialName }),
|
|
604
|
+
typeof initialLevel === "number" && /* @__PURE__ */ jsxs(Text3, { color: "cyan", children: [
|
|
385
605
|
" [Lvl. ",
|
|
386
606
|
initialLevel,
|
|
387
607
|
"]"
|
|
388
608
|
] })
|
|
389
609
|
] }),
|
|
390
|
-
/* @__PURE__ */
|
|
391
|
-
/* @__PURE__ */
|
|
392
|
-
i ===
|
|
393
|
-
"
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
610
|
+
/* @__PURE__ */ jsx3(Box3, { marginBottom: 1, children: previewHatDef ? previewHatDef.rarity === "legendary" ? /* @__PURE__ */ jsx3(AnimatedHorseSprite, { sprite: MAIN_SPRITE, colors, hat: previewHatDef }) : /* @__PURE__ */ jsx3(HorseSprite, { sprite: MAIN_SPRITE, colors, hat: { hat: previewHatDef, variant: previewHat?.variant ?? 0 } }) : /* @__PURE__ */ jsx3(HorseSprite, { sprite: MAIN_SPRITE, colors }) }),
|
|
611
|
+
/* @__PURE__ */ jsx3(Box3, { flexDirection: "column", children: rows.map((r, i) => {
|
|
612
|
+
const cursor = i === rowIdx ? "\u25BA" : " ";
|
|
613
|
+
if (r.kind === "color") {
|
|
614
|
+
return /* @__PURE__ */ jsxs(Text3, { children: [
|
|
615
|
+
cursor,
|
|
616
|
+
" ",
|
|
617
|
+
r.slot.padEnd(7),
|
|
618
|
+
" ",
|
|
619
|
+
/* @__PURE__ */ jsx3(Text3, { color: colors[r.slot], children: "\u2588\u2588" }),
|
|
620
|
+
" ",
|
|
621
|
+
colors[r.slot]
|
|
622
|
+
] }, r.slot);
|
|
623
|
+
}
|
|
624
|
+
return /* @__PURE__ */ jsxs(Text3, { children: [
|
|
625
|
+
cursor,
|
|
626
|
+
" ",
|
|
627
|
+
"hat".padEnd(7),
|
|
628
|
+
" ",
|
|
629
|
+
renderHatLabel(hats, hatChoice, initialEquipped ?? null)
|
|
630
|
+
] }, "hat");
|
|
631
|
+
}) }),
|
|
632
|
+
!namingMode && /* @__PURE__ */ jsx3(Box3, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "\u2191/\u2193 select row \xB7 \u2190/\u2192 cycle \xB7 Enter accept \xB7 Esc cancel" }) }),
|
|
633
|
+
namingMode && /* @__PURE__ */ jsxs(Box3, { marginTop: 1, flexDirection: "column", children: [
|
|
634
|
+
/* @__PURE__ */ jsx3(Text3, { children: "Name your horse: " }),
|
|
635
|
+
/* @__PURE__ */ jsx3(TextInput, { value: name, onChange: (v) => {
|
|
404
636
|
setName(v);
|
|
405
637
|
setError(null);
|
|
406
638
|
}, onSubmit: handleNameSubmit }),
|
|
407
|
-
error && /* @__PURE__ */
|
|
639
|
+
error && /* @__PURE__ */ jsx3(Text3, { color: "red", children: error })
|
|
408
640
|
] })
|
|
409
641
|
] });
|
|
410
642
|
}
|
|
643
|
+
function renderHatLabel(hats, hatChoice, initialEquipped) {
|
|
644
|
+
if (!hats || hats.length === 0) return /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "none" });
|
|
645
|
+
if (hatChoice === null) {
|
|
646
|
+
const equippedMark2 = initialEquipped === null ? " \u2713" : "";
|
|
647
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
648
|
+
/* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "Unequip" }),
|
|
649
|
+
equippedMark2
|
|
650
|
+
] });
|
|
651
|
+
}
|
|
652
|
+
const collected = hats[hatChoice];
|
|
653
|
+
if (!collected) return /* @__PURE__ */ jsx3(Text3, { dimColor: true, children: "?" });
|
|
654
|
+
const hat = hatById(collected.id);
|
|
655
|
+
const name = hat?.name ?? collected.id;
|
|
656
|
+
const variantSuffix = hat && hat.rarity !== "legendary" && collected.variant !== void 0 ? ` #${collected.variant + 1}` : "";
|
|
657
|
+
const rarityColor = hat ? hat.rarity === "legendary" ? "yellow" : hat.rarity === "epic" ? "magenta" : hat.rarity === "rare" ? "blue" : "gray" : "gray";
|
|
658
|
+
const equippedMark = initialEquipped === hatChoice ? " \u2713" : "";
|
|
659
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
660
|
+
name,
|
|
661
|
+
variantSuffix,
|
|
662
|
+
" ",
|
|
663
|
+
/* @__PURE__ */ jsxs(Text3, { color: rarityColor, children: [
|
|
664
|
+
"[",
|
|
665
|
+
hat?.rarity ?? "?",
|
|
666
|
+
"]"
|
|
667
|
+
] }),
|
|
668
|
+
equippedMark
|
|
669
|
+
] });
|
|
670
|
+
}
|
|
411
671
|
|
|
412
672
|
// src/config.ts
|
|
413
673
|
var DEFAULT_API_BASE = "https://token-derby.mauricode.co.uk/api";
|
|
@@ -420,8 +680,8 @@ var HEARTBEAT_RETRY_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
|
|
|
420
680
|
// src/version.ts
|
|
421
681
|
import { createRequire } from "module";
|
|
422
682
|
function readVersion() {
|
|
423
|
-
if ("2.5.
|
|
424
|
-
return "2.5.
|
|
683
|
+
if ("2.5.5".length > 0) {
|
|
684
|
+
return "2.5.5";
|
|
425
685
|
}
|
|
426
686
|
try {
|
|
427
687
|
const req = createRequire(import.meta.url);
|
|
@@ -433,172 +693,6 @@ function readVersion() {
|
|
|
433
693
|
}
|
|
434
694
|
var CLI_VERSION = readVersion();
|
|
435
695
|
|
|
436
|
-
// ../shared/dist/constants.js
|
|
437
|
-
var CLI_VERSION_HEADER = "x-cli-version";
|
|
438
|
-
var USER_ID_HEADER = "x-user-id";
|
|
439
|
-
var USER_TOKEN_HEADER = "x-user-token";
|
|
440
|
-
var USER_NAME_MAX_LENGTH = 40;
|
|
441
|
-
var ORG_NAME_MAX_LENGTH = 12;
|
|
442
|
-
var ORG_NAME_PATTERN = /^[A-Za-z0-9]{1,12}$/;
|
|
443
|
-
|
|
444
|
-
// ../shared/dist/version-match.js
|
|
445
|
-
var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/;
|
|
446
|
-
function parseSemver(v) {
|
|
447
|
-
if (typeof v !== "string")
|
|
448
|
-
return null;
|
|
449
|
-
const m = SEMVER_RE.exec(v.trim());
|
|
450
|
-
if (!m)
|
|
451
|
-
return null;
|
|
452
|
-
return {
|
|
453
|
-
major: Number(m[1]),
|
|
454
|
-
minor: Number(m[2]),
|
|
455
|
-
patch: Number(m[3])
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
function gteSemver(a, b) {
|
|
459
|
-
const pa = parseSemver(a);
|
|
460
|
-
const pb = parseSemver(b);
|
|
461
|
-
if (!pa || !pb)
|
|
462
|
-
return false;
|
|
463
|
-
if (pa.major !== pb.major)
|
|
464
|
-
return pa.major > pb.major;
|
|
465
|
-
if (pa.minor !== pb.minor)
|
|
466
|
-
return pa.minor > pb.minor;
|
|
467
|
-
return pa.patch >= pb.patch;
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
// ../shared/dist/levels.js
|
|
471
|
-
var MAX_LEVEL = 30;
|
|
472
|
-
function xpForLevel(n) {
|
|
473
|
-
return 1.8 * n ** 3 + 18 * n ** 2 + 50 * n - 19.8;
|
|
474
|
-
}
|
|
475
|
-
function thresholdForLevel(level) {
|
|
476
|
-
if (level <= 1)
|
|
477
|
-
return 0;
|
|
478
|
-
return Math.round(xpForLevel(level - 1));
|
|
479
|
-
}
|
|
480
|
-
var XP_THRESHOLDS = Array.from({ length: MAX_LEVEL }, (_, i) => thresholdForLevel(i + 1));
|
|
481
|
-
function levelFromXp(xp) {
|
|
482
|
-
const v = Math.max(0, Math.floor(xp));
|
|
483
|
-
let level = 1;
|
|
484
|
-
while (level < MAX_LEVEL && v >= thresholdForLevel(level + 1)) {
|
|
485
|
-
level++;
|
|
486
|
-
}
|
|
487
|
-
return level;
|
|
488
|
-
}
|
|
489
|
-
function levelInfo(xp) {
|
|
490
|
-
const v = Math.max(0, Math.floor(xp));
|
|
491
|
-
const level = levelFromXp(v);
|
|
492
|
-
const level_start_xp = thresholdForLevel(level);
|
|
493
|
-
const isMax = level >= MAX_LEVEL;
|
|
494
|
-
const next_level_xp = isMax ? null : thresholdForLevel(level + 1);
|
|
495
|
-
const xp_into_level = v - level_start_xp;
|
|
496
|
-
const xp_for_level = isMax ? null : next_level_xp - level_start_xp;
|
|
497
|
-
const progress = isMax ? 1 : Math.min(1, xp_into_level / Math.max(1, xp_for_level));
|
|
498
|
-
return { level, xp: v, level_start_xp, next_level_xp, xp_into_level, xp_for_level, progress };
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
// ../shared/dist/midrace.js
|
|
502
|
-
var ACHIEVEMENT_DESCRIPTIONS = {
|
|
503
|
-
"Racer!": "Raced continuously for an hour",
|
|
504
|
-
"Overtake!": "Overtook another horse",
|
|
505
|
-
"Pacesetter!": "Led the race for an hour straight",
|
|
506
|
-
"Stampede!": "Gained 7,000+ tokens in a single minute",
|
|
507
|
-
"Took the lead!": "Charged into first place",
|
|
508
|
-
"Comeback!": "Climbed from last place to the top half",
|
|
509
|
-
"Pulled Away!": "Grew the lead by 5,000+ tokens in a minute"
|
|
510
|
-
};
|
|
511
|
-
function overtakeDescription(positionsClimbed) {
|
|
512
|
-
if (positionsClimbed <= 1)
|
|
513
|
-
return "Overtook another horse";
|
|
514
|
-
return `Overtook ${positionsClimbed} horses`;
|
|
515
|
-
}
|
|
516
|
-
var TOKEN_INPUT_MULTIPLIER = 10;
|
|
517
|
-
function tokenMultiplier(race) {
|
|
518
|
-
return race.counts_input ? TOKEN_INPUT_MULTIPLIER : 1;
|
|
519
|
-
}
|
|
520
|
-
function describeAchievement(event, race) {
|
|
521
|
-
if (event.name === "Overtake!") {
|
|
522
|
-
return overtakeDescription(Math.floor(event.xp / 3));
|
|
523
|
-
}
|
|
524
|
-
const m = tokenMultiplier(race);
|
|
525
|
-
if (event.name === "Stampede!") {
|
|
526
|
-
return `Gained ${(MIDRACE_THRESHOLDS.stampede_tokens * m).toLocaleString("en-US")}+ tokens in a single minute`;
|
|
527
|
-
}
|
|
528
|
-
if (event.name === "Pulled Away!") {
|
|
529
|
-
return `Grew the lead by ${(MIDRACE_THRESHOLDS.pulled_away_gap * m).toLocaleString("en-US")}+ tokens in a minute`;
|
|
530
|
-
}
|
|
531
|
-
return ACHIEVEMENT_DESCRIPTIONS[event.name];
|
|
532
|
-
}
|
|
533
|
-
var MIDRACE_THRESHOLDS = {
|
|
534
|
-
warm_up_fraction: 0.08,
|
|
535
|
-
// first 8% of race time
|
|
536
|
-
streak_hour_ms: 36e5,
|
|
537
|
-
// 1 hour for Racer!/Pacesetter!
|
|
538
|
-
racer_dt_cap_ms: 9e4,
|
|
539
|
-
// single-tick credit cap for Racer!
|
|
540
|
-
stampede_tokens: 7e3,
|
|
541
|
-
// tokens-in-a-minute threshold
|
|
542
|
-
stampede_cooldown_ms: 72e5,
|
|
543
|
-
// 2 hours
|
|
544
|
-
pulled_away_gap: 5e3,
|
|
545
|
-
// gap-growth threshold per minute
|
|
546
|
-
pulled_away_cooldown_ms: 72e5,
|
|
547
|
-
// 2 hours
|
|
548
|
-
recent_events_retention_ms: 9e4
|
|
549
|
-
// sliding window for recent_events
|
|
550
|
-
};
|
|
551
|
-
|
|
552
|
-
// ../shared/dist/hats.js
|
|
553
|
-
function hatById(id) {
|
|
554
|
-
return HATS.find((h) => h.id === id);
|
|
555
|
-
}
|
|
556
|
-
var HATS = [
|
|
557
|
-
// ── COMMON (18) ────────────────────────────────────────────────────────
|
|
558
|
-
{ id: "flat_cap", name: "Flat Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...........", "....AAAA...", "...AAAAAA..", "..AAAAAAA.."], variants: [{ A: "#8B6914" }, { A: "#12301b" }, { A: "#aab5cb" }, { A: "#bdcbaa" }] },
|
|
559
|
-
{ id: "bucket_hat", name: "Bucket Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...QQQQQ...", "...AAAAA...", "...AAAAA...", ".AAAAAAAAA."], variants: [{ A: "#CC2200", Q: "#FFFFFF" }, { A: "#0007cc", Q: "#FFFFFF" }, { A: "#becc00", Q: "#4d3dc7" }, { A: "#cc00c5", Q: "#d6d6dc" }] },
|
|
560
|
-
{ id: "beanie", name: "Beanie", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....AAA....", "...AAAAA...", "...AAAAA...", "..AAAAAAA.."], variants: [{ A: "#4A7C59" }, { A: "#b21f35" }, { A: "#ec9418" }, { A: "#3a17e6" }] },
|
|
561
|
-
{ id: "stetson", name: "Stetson", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...AA.AA...", "...AAAAA...", "...AAAAA...", "A..AAAAA..A", "AAAAAAAAAAA", "AAAAAAAAAAA"], variants: [{ A: "#C49A00" }, { A: "#272004" }, { A: "#040404" }, { A: "#373606" }] },
|
|
562
|
-
{ id: "party_hat", name: "Party Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "....AQA....", "...AQAQA...", "...AAAAA..."], variants: [{ A: "#FF69B4", Q: "#FFD700" }, { A: "#c46bff", Q: "#ff000d" }, { A: "#fdff6b", Q: "#0400ff" }, { A: "#6bff97", Q: "#05050a" }] },
|
|
563
|
-
{ id: "fez", name: "Fez", rarity: "common", width: 11, anchor_x: 22, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....Q......", "....AAA....", "....AAA....", "....AAA...."], variants: [{ A: "#CC0000", Q: "#8B0000" }, { A: "#CC0000", Q: "#f5e5e5" }, { A: "#CC0000", Q: "#f1e209" }, { A: "#CC0000", Q: "#0956f1" }] },
|
|
564
|
-
{ id: "sailor_hat", name: "Sailor Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...QQQQQ...", "..AAAAAAA..", "..QQQQQQQ..", "..AAAAAAA.."], variants: [{ A: "#FFFFFF", Q: "#000080" }, { A: "#FFFFFF", Q: "#c0c0ed" }, { A: "#FFFFFF", Q: "#00000a" }, { A: "#FFFFFF", Q: "#0505ef" }] },
|
|
565
|
-
{ id: "newsboy_cap", name: "Newsboy Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAAAA...", "..AAAQAAA..", "..AAAAAAA..", "..AAAAAA..."], variants: [{ A: "#5C4033", Q: "#3D2B1F" }, { A: "#e96020", Q: "#131211" }, { A: "#342c28", Q: "#131211" }, { A: "#0a0300", Q: "#ddab78" }] },
|
|
566
|
-
{ id: "tam_o_shanter", name: "Tam O'Shanter", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....Q.....", "....AAA....", "..AAAAAAA..", "..AAAAAAA..", "...AAAAA..."], variants: [{ A: "#006400", Q: "#FF0000" }, { A: "#ff0000", Q: "#016400" }] },
|
|
567
|
-
{ id: "trucker_cap", name: "Trucker Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAQQ....", "...AAQQ....", "..AAQQQAA..", "..AAQQQAA.."], variants: [{ A: "#2196F3", Q: "#FFFFFF" }, { A: "#f41fe3", Q: "#FFFFFF" }, { A: "#1ff45f", Q: "#FFFFFF" }] },
|
|
568
|
-
{ id: "hard_hat", name: "Hard Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....AAA....", "...AAAAA...", "..AAAAAAA..", "..AAAAAAAAA"], variants: [{ A: "#FFD600" }, { A: "#d74c1d" }] },
|
|
569
|
-
{ id: "chef_toque", name: "Chef's Toque", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...AAAAA...", "..AAQAQAA..", "...AQAQA...", "...AQAQA...", "...AQAQA...", "...AAAAA..."], variants: [{ A: "#FFFFFF", Q: "#f9f9f9" }] },
|
|
570
|
-
{ id: "bobble_hat", name: "Bobble Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", ".....Q.....", "...AAAAA...", "...AAAAA...", "..AAAAAAA.."], variants: [{ A: "#C62828", Q: "#FFFFFF" }, { A: "#293bc7", Q: "#FFFFFF" }, { A: "#29c775", Q: "#231f1f" }, { A: "#a529c7", Q: "#d50707" }] },
|
|
571
|
-
{ id: "cowboy_hat", name: "Cowboy Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAAAA...", "...AAAAA...", "A.AAAAAAA.A", ".AAAAAAAAA."], variants: [{ A: "#8B4513" }, { A: "#0a0400" }, { A: "#956c50" }] },
|
|
572
|
-
{ id: "baseball_cap", name: "Baseball Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "...AAAAA...", "..AAAAAA...", "..AAAAAAA..", "..AAAAAAAAA"], variants: [{ A: "#1565C0" }, { A: "#8515c1" }, { A: "#15c157" }, { A: "#ef2d0b" }] },
|
|
573
|
-
{ id: "tinfoil_hat", name: "Tinfoil Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "...AAAAA...", "..AAAAAAA.."], variants: [{ A: "#B0BEC5" }, { A: "#6a6c6c" }] },
|
|
574
|
-
{ id: "dunce_cap", name: "Dunce Cap", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", ".....Q.....", "....QAQ....", "...QAAAQ...", "...QAAAQ...", "..QAAAAAQ..", "..AAAAAAA.."], variants: [{ A: "#F8F8F8", Q: "#F44336" }] },
|
|
575
|
-
{ id: "mini_top_hat", name: "Mini Top Hat", rarity: "common", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...AAAAA...", "...AAAAA...", "...AAAAA...", "..AAAAAAA..", "..AAAAAAA.."], variants: [{ A: "#212121" }, { A: "#626060" }] },
|
|
576
|
-
// ── RARE (10) ──────────────────────────────────────────────────────────
|
|
577
|
-
{ id: "bicorne", name: "Bicorne", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..AAAQQQQ..", "..AAAAAAA..", "..AAAAAAA..", "...QQQQQ..."], variants: [{ A: "#1A237E", Q: "#FFD700" }, { A: "#01052d", Q: "#544803" }] },
|
|
578
|
-
{ id: "viking_helmet", name: "Viking Helmet", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..A.AAA.A..", "..AAAAAAA..", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#9E9E9E", Q: "#8D6E63" }, { A: "#dcdbdb", Q: "#bf3908" }] },
|
|
579
|
-
{ id: "jesters_cap", name: "Jester's Cap", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..AQAQAQA..", "..AQAQAQA..", "...AAAAA...", "...AAAAA..."], variants: [{ A: "#E53935", Q: "#FFD600" }, { A: "#e5a434", Q: "#0040ff" }, { A: "#34e537", Q: "#eeff00" }, { A: "#e234e5", Q: "#00ff40" }] },
|
|
580
|
-
{ id: "plague_doctor", name: "Plague Doctor Beak", rarity: "rare", width: 11, anchor_x: 24, rows: ["...........", "...........", "...........", "...........", "....QQQ....", "...QAAAQQ..", "...QQQQQQQ.", "..QQQQQ.QQQ", "..QQQQ....Q", "..QQQQ....."], variants: [{ A: "#F5F5F5", Q: "#795548" }, { A: "#e1dbdb", Q: "#080707" }] },
|
|
581
|
-
{ id: "morion", name: "Conquistador Morion", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....Q.....", "...QQQQQ...", "...QAAAQ...", ".A..AAA..A.", "..AAQQQAA.."], variants: [{ A: "#B0BEC5", Q: "#FFD600" }, { A: "#0a4768", Q: "#e6c10a" }] },
|
|
582
|
-
{ id: "shako", name: "Shako", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "........Q..", "........Q..", "........Q..", "........Q..", "..AAAAAAA..", "..AAAAAAA..", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#45464f", Q: "#00ff2a" }, { A: "#45464f", Q: "#ff4d00" }, { A: "#45464f", Q: "#ff0019" }, { A: "#45464f", Q: "#fff700" }] },
|
|
583
|
-
{ id: "centurion_helm", name: "Centurion Helm", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "....QQQ....", "...QQQQQQ..", "..QQQQQQQQ.", ".QQ..A...Q.", ".Q..AAA....", "...AAAAA...", "..AAAAAAA..", "..AAAAAAA.."], variants: [{ A: "#B0BEC5", Q: "#C62828" }, { A: "#2f3131", Q: "#C62828" }] },
|
|
584
|
-
{ id: "papal_mitre", name: "Papal Mitre", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "....QQQ....", "...QQQQQ...", "...QAAAQ...", "..AAAAAAA.."], variants: [{ A: "#FFFFFF", Q: "#FFD700" }] },
|
|
585
|
-
{ id: "headdress", name: "Headdress", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", "...........", "..AQAQAQA..", "..AQAQAQA..", "..AAAAAAA..", "...QQQQQ..."], variants: [{ A: "#FF8F00", Q: "#1565C0" }] },
|
|
586
|
-
{ id: "sombrero", name: "Sombrero", rarity: "rare", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "...AAAAA...", "...AQAQA...", "AAAAAAAAAAA"], variants: [{ A: "#F57F17", Q: "#BF360C" }, { A: "#f8b377", Q: "#3e1204" }, { A: "#d6f877", Q: "#04133e" }, { A: "#d6f877", Q: "#04133e" }] },
|
|
587
|
-
// ── EPIC (6) ───────────────────────────────────────────────────────────
|
|
588
|
-
{ id: "papal_tiara", name: "Papal Tiara", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "....QQ.....", "...QQQQQ...", "...QAAQQ...", "...QQQQQ...", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#FFFFFF", Q: "#FFD700" }, { A: "#b18e10", Q: "#f3f3f1" }] },
|
|
589
|
-
{ id: "samurai_kabuto", name: "Samurai Kabuto", rarity: "epic", width: 11, anchor_x: 23, rows: ["....Q...Q..", "....QQ.QQ..", ".....QQQ...", ".......Q...", "...AAAAQ...", "..AAAAAQQ..", "..AAAAAQQ..", ".AAAAAAAQ..", "AAAAAAAAQ..", "AAAAAAAAQ.."], variants: [{ A: "#B0BEC5", Q: "#C62828" }, { A: "#050505", Q: "#C62828" }, { A: "#675f5f", Q: "#c4c729" }] },
|
|
590
|
-
{ id: "gladiator_galea", name: "Gladiator Galea", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "....QQQQQQ.", "...QQQQQ...", "...QQQQQ...", "...QAAA....", "..AAAAAAA..", "..AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#B0BEC5", Q: "#C62828" }, { A: "#ffe907", Q: "#C62828" }] },
|
|
591
|
-
{ id: "pharaoh_nemes", name: "Pharaoh Nemes", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "..AAAAAAA..", "..AAQAQAA..", "..QAQAQAQ..", "..QAQAQAQ..", "..QQAAAQQ..", "..QQAAAAA..", "...QAQA...."], variants: [{ A: "#FFD700", Q: "#1565C0" }, { A: "#8d342a", Q: "#63686e" }] },
|
|
592
|
-
{ id: "spartan_helmet", name: "Spartan Helmet", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "........Q..", ".......QQ..", ".....QQQQ..", "....QQQQQ..", "..QQQQQQ...", ".QQQAA.....", "QQAAAAAAA..", "Q.AAAAAAA..", "..AQQQQQA.."], variants: [{ A: "#B0BEC5", Q: "#B71C1C" }, { A: "#B0BEC5", Q: "#b2b51c" }, { A: "#B0BEC5", Q: "#b5611c" }] },
|
|
593
|
-
{ id: "conquistador_full", name: "Conquistador Helm", rarity: "epic", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", ".....A.....", "....AAA....", "...AAAAA...", "..AAAAAAA..", "...AQQQA...", "...AQQQA..."], variants: [{ A: "#B0BEC5", Q: "#FFD700" }, { A: "#B0BEC5", Q: "#0040ff" }, { A: "#B0BEC5", Q: "#36123b" }] },
|
|
594
|
-
// ── LEGENDARY (5) ──────────────────────────────────────────────────────
|
|
595
|
-
{ id: "rainbow_crown", name: "Rainbow Crown", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "...........", ".....A.....", "....AAA....", "....AQA....", "....AAA....", "...AAQAA...", "...AAAAA...", "..AAAQAAA..", "..AAAAAAA.."], colors: { A: "#FFD700", Q: "#553f3f" }, animation: { type: "cycle", frames: ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#8B00FF"], fps: 8 } },
|
|
596
|
-
{ id: "inferno_cap", name: "Inferno Cap", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "....AAA....", "...AAAAA...", "....AAA....", ".....Q.....", "A....Q....A", "A...QQQ...A", ".A..QQQ..A.", "..AAAAAAA..", "..AAAAAAA.."], colors: { A: "#FF4500", Q: "#FFD700" }, animation: { type: "cycle", frames: ["#FF0000", "#FF2200", "#FF4500", "#FF6600", "#FF8C00", "#FFA500"], fps: 12 } },
|
|
597
|
-
{ id: "void_hood", name: "Void Hood", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...AAA.....", "..AAAAAA...", ".AAAAAAA...", ".AAAAAAAA..", "AAAAAAAAA..", "AAAAQQQAA..", "AAAAQQQAA.."], colors: { A: "#1A0033", Q: "#d9cfe3" }, animation: { type: "cycle", frames: ["#0D0019", "#1A0033", "#2D004D", "#3D0066", "#2D004D", "#1A0033"], fps: 3 } },
|
|
598
|
-
{ id: "prismatic_jester", name: "Prismatic Jester", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "..Q.Q.Q.Q..", "Q..A.A.A..Q", ".Q.A.A.A.Q.", "..AQAQAQA..", "..AQAQAQA..", "..AAAAAAA..", "..AAAAAAA..", "..AAAAAAA..", "..AAAAAAA.."], colors: { A: "#FF0000", Q: "#0000FF" }, animation: { type: "cycle", frames: ["#FF0000", "#FF7F00", "#FFFF00", "#00FF00", "#0000FF", "#8B00FF", "#FF00FF", "#00FFFF"], fps: 15 } },
|
|
599
|
-
{ id: "aurora_helm", name: "Aurora Helm", rarity: "legendary", width: 11, anchor_x: 23, rows: ["...........", "...........", "...........", "...........", "...QQQQQ...", "...QQQQQ...", "..AAAAAAA..", "..AAAAAAA..", "..AQAAAQA..", "..AQAAAQA.."], colors: { A: "#00CED1", Q: "#00FF7F" }, animation: { type: "cycle", frames: ["#0000FF", "#0066FF", "#00BFFF", "#00CED1", "#00FF7F", "#7CFC00", "#00FF7F", "#00CED1"], fps: 4 } }
|
|
600
|
-
];
|
|
601
|
-
|
|
602
696
|
// src/identity/identity.ts
|
|
603
697
|
import { promises as fs } from "fs";
|
|
604
698
|
import * as path2 from "path";
|
|
@@ -812,7 +906,7 @@ function equipHat(stableHorseId, body) {
|
|
|
812
906
|
async function stableCreateCommand() {
|
|
813
907
|
let exitCode = 0;
|
|
814
908
|
const app = render(
|
|
815
|
-
|
|
909
|
+
React3.createElement(HorseCreator, {
|
|
816
910
|
onSubmit: async (name, colors) => {
|
|
817
911
|
try {
|
|
818
912
|
await createStableHorse({ name, colors });
|
|
@@ -844,9 +938,9 @@ async function stableCreateCommand() {
|
|
|
844
938
|
}
|
|
845
939
|
|
|
846
940
|
// src/commands/stable-list.tsx
|
|
847
|
-
import
|
|
848
|
-
import { render as render2, Box as
|
|
849
|
-
import { jsx as
|
|
941
|
+
import React4 from "react";
|
|
942
|
+
import { render as render2, Box as Box4, Text as Text4 } from "ink";
|
|
943
|
+
import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
850
944
|
async function stableListCommand() {
|
|
851
945
|
let horses;
|
|
852
946
|
try {
|
|
@@ -864,28 +958,28 @@ async function stableListCommand() {
|
|
|
864
958
|
return 0;
|
|
865
959
|
}
|
|
866
960
|
const app = render2(
|
|
867
|
-
|
|
961
|
+
React4.createElement(StableList, { horses })
|
|
868
962
|
);
|
|
869
963
|
await app.waitUntilExit();
|
|
870
964
|
return 0;
|
|
871
965
|
}
|
|
872
966
|
function StableList({ horses }) {
|
|
873
|
-
|
|
967
|
+
React4.useEffect(() => {
|
|
874
968
|
setImmediate(() => process.exit(0));
|
|
875
969
|
}, []);
|
|
876
|
-
return /* @__PURE__ */ jsxs2(
|
|
877
|
-
/* @__PURE__ */ jsxs2(
|
|
970
|
+
return /* @__PURE__ */ jsxs2(Box4, { flexDirection: "column", children: [
|
|
971
|
+
/* @__PURE__ */ jsxs2(Text4, { bold: true, children: [
|
|
878
972
|
"Your stable (",
|
|
879
973
|
horses.length,
|
|
880
974
|
"):"
|
|
881
975
|
] }),
|
|
882
|
-
horses.map((h) => /* @__PURE__ */ jsxs2(
|
|
883
|
-
/* @__PURE__ */
|
|
884
|
-
/* @__PURE__ */ jsxs2(
|
|
976
|
+
horses.map((h) => /* @__PURE__ */ jsxs2(Box4, { flexDirection: "row", marginTop: 1, children: [
|
|
977
|
+
/* @__PURE__ */ jsx4(HorseSprite, { sprite: MINI_SPRITE, colors: h.colors }),
|
|
978
|
+
/* @__PURE__ */ jsxs2(Text4, { children: [
|
|
885
979
|
" ",
|
|
886
980
|
h.name,
|
|
887
981
|
" ",
|
|
888
|
-
/* @__PURE__ */ jsxs2(
|
|
982
|
+
/* @__PURE__ */ jsxs2(Text4, { color: "cyan", children: [
|
|
889
983
|
"[Lvl. ",
|
|
890
984
|
levelFromXp(h.xp),
|
|
891
985
|
"]"
|
|
@@ -939,120 +1033,16 @@ async function stableDeleteCommand(name) {
|
|
|
939
1033
|
}
|
|
940
1034
|
|
|
941
1035
|
// src/commands/stable-edit.ts
|
|
942
|
-
import
|
|
1036
|
+
import React6 from "react";
|
|
943
1037
|
import { render as render3 } from "ink";
|
|
944
1038
|
|
|
945
|
-
// src/ui/
|
|
1039
|
+
// src/ui/HorsePicker.tsx
|
|
946
1040
|
import { useState as useState3 } from "react";
|
|
947
1041
|
import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
|
|
948
|
-
|
|
949
|
-
// src/ui/AnimatedHorseSprite.tsx
|
|
950
|
-
import { useEffect, useState as useState2 } from "react";
|
|
951
|
-
import { Box as Box4, Text as Text4 } from "ink";
|
|
952
|
-
import { jsx as jsx4 } from "react/jsx-runtime";
|
|
953
|
-
function AnimatedHorseSprite({ sprite, colors, hat }) {
|
|
954
|
-
const isLegendary = hat.rarity === "legendary";
|
|
955
|
-
const frames = isLegendary ? hat.animation.frames : [];
|
|
956
|
-
const fps = isLegendary ? hat.animation.fps : 1;
|
|
957
|
-
const [idx, setIdx] = useState2(0);
|
|
958
|
-
useEffect(() => {
|
|
959
|
-
if (!isLegendary || frames.length <= 1) return;
|
|
960
|
-
const interval = setInterval(
|
|
961
|
-
() => setIdx((i) => (i + 1) % frames.length),
|
|
962
|
-
Math.max(1, Math.round(1e3 / fps))
|
|
963
|
-
);
|
|
964
|
-
return () => clearInterval(interval);
|
|
965
|
-
}, [isLegendary, frames.length, fps]);
|
|
966
|
-
const renderedHat = isLegendary && frames[idx] ? { ...hat, colors: { ...hat.colors, A: frames[idx] } } : hat;
|
|
967
|
-
const { grid } = composeHatGrid(sprite, renderedHat, 0, colors);
|
|
968
|
-
const lines = hexGridToHalfBlocks(grid);
|
|
969
|
-
return /* @__PURE__ */ jsx4(Box4, { flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx4(Text4, { children: line }, i)) });
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// src/ui/HatPicker.tsx
|
|
973
1042
|
import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
974
|
-
function HatPicker({ hats, equipped, colors, onPick, onCancel }) {
|
|
975
|
-
const entries = [
|
|
976
|
-
{ kind: "unequip" },
|
|
977
|
-
...hats.map((c, idx) => ({ kind: "hat", idx, collected: c }))
|
|
978
|
-
];
|
|
979
|
-
const [cursor, setCursor] = useState3(0);
|
|
980
|
-
useInput2((input, key) => {
|
|
981
|
-
if (key.escape) {
|
|
982
|
-
onCancel();
|
|
983
|
-
return;
|
|
984
|
-
}
|
|
985
|
-
if (key.upArrow) {
|
|
986
|
-
setCursor((cursor - 1 + entries.length) % entries.length);
|
|
987
|
-
return;
|
|
988
|
-
}
|
|
989
|
-
if (key.downArrow) {
|
|
990
|
-
setCursor((cursor + 1) % entries.length);
|
|
991
|
-
return;
|
|
992
|
-
}
|
|
993
|
-
if (key.return) {
|
|
994
|
-
const e = entries[cursor];
|
|
995
|
-
onPick(e.kind === "unequip" ? null : e.idx);
|
|
996
|
-
return;
|
|
997
|
-
}
|
|
998
|
-
});
|
|
999
|
-
const focused = entries[cursor];
|
|
1000
|
-
return /* @__PURE__ */ jsxs3(Box5, { flexDirection: "column", children: [
|
|
1001
|
-
/* @__PURE__ */ jsx5(Text5, { children: "Pick a hat to equip:" }),
|
|
1002
|
-
entries.map((e, i) => {
|
|
1003
|
-
const isCursor = i === cursor;
|
|
1004
|
-
if (e.kind === "unequip") {
|
|
1005
|
-
const isEquipped2 = equipped == null;
|
|
1006
|
-
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "row", children: /* @__PURE__ */ jsxs3(Text5, { children: [
|
|
1007
|
-
isCursor ? "\u25BA" : " ",
|
|
1008
|
-
" ",
|
|
1009
|
-
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Unequip" }),
|
|
1010
|
-
isEquipped2 ? " \u2713" : ""
|
|
1011
|
-
] }) }, "unequip");
|
|
1012
|
-
}
|
|
1013
|
-
const hat = hatById(e.collected.id);
|
|
1014
|
-
const name = hat?.name ?? e.collected.id;
|
|
1015
|
-
const variantSuffix = hat && hat.rarity !== "legendary" && e.collected.variant !== void 0 ? ` #${e.collected.variant + 1}` : "";
|
|
1016
|
-
const isEquipped = equipped === e.idx;
|
|
1017
|
-
const rarityColor = hat ? hat.rarity === "legendary" ? "yellow" : hat.rarity === "epic" ? "magenta" : hat.rarity === "rare" ? "blue" : "gray" : "gray";
|
|
1018
|
-
return /* @__PURE__ */ jsx5(Box5, { flexDirection: "row", children: /* @__PURE__ */ jsxs3(Text5, { children: [
|
|
1019
|
-
isCursor ? "\u25BA" : " ",
|
|
1020
|
-
" ",
|
|
1021
|
-
name,
|
|
1022
|
-
variantSuffix,
|
|
1023
|
-
" ",
|
|
1024
|
-
/* @__PURE__ */ jsxs3(Text5, { color: rarityColor, children: [
|
|
1025
|
-
"[",
|
|
1026
|
-
hat?.rarity ?? "?",
|
|
1027
|
-
"]"
|
|
1028
|
-
] }),
|
|
1029
|
-
isEquipped ? " \u2713" : ""
|
|
1030
|
-
] }) }, `hat-${e.idx}`);
|
|
1031
|
-
}),
|
|
1032
|
-
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Preview:" }) }),
|
|
1033
|
-
/* @__PURE__ */ jsx5(PreviewArea, { focused, colors }),
|
|
1034
|
-
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2191/\u2193 choose \xB7 Enter pick \xB7 Esc cancel" }) })
|
|
1035
|
-
] });
|
|
1036
|
-
}
|
|
1037
|
-
function PreviewArea({ focused, colors }) {
|
|
1038
|
-
if (focused.kind === "unequip") {
|
|
1039
|
-
return /* @__PURE__ */ jsx5(HorseSprite, { sprite: MAIN_SPRITE, colors });
|
|
1040
|
-
}
|
|
1041
|
-
const hat = hatById(focused.collected.id);
|
|
1042
|
-
if (!hat) return /* @__PURE__ */ jsx5(HorseSprite, { sprite: MAIN_SPRITE, colors });
|
|
1043
|
-
if (hat.rarity === "legendary") {
|
|
1044
|
-
return /* @__PURE__ */ jsx5(AnimatedHorseSprite, { sprite: MAIN_SPRITE, colors, hat });
|
|
1045
|
-
}
|
|
1046
|
-
return /* @__PURE__ */ jsx5(HorseSprite, { sprite: MAIN_SPRITE, colors, hat: { hat, variant: focused.collected.variant ?? 0 } });
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
// src/ui/HorsePicker.tsx
|
|
1050
|
-
import { useState as useState4 } from "react";
|
|
1051
|
-
import { Box as Box6, Text as Text6, useInput as useInput3 } from "ink";
|
|
1052
|
-
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1053
1043
|
function HorsePicker({ horses, onPick, onCancel }) {
|
|
1054
|
-
const [idx, setIdx] =
|
|
1055
|
-
|
|
1044
|
+
const [idx, setIdx] = useState3(0);
|
|
1045
|
+
useInput2((input, key) => {
|
|
1056
1046
|
if (key.escape) {
|
|
1057
1047
|
onCancel();
|
|
1058
1048
|
return;
|
|
@@ -1072,31 +1062,31 @@ function HorsePicker({ horses, onPick, onCancel }) {
|
|
|
1072
1062
|
}
|
|
1073
1063
|
});
|
|
1074
1064
|
if (horses.length === 0) {
|
|
1075
|
-
return /* @__PURE__ */
|
|
1076
|
-
/* @__PURE__ */
|
|
1077
|
-
/* @__PURE__ */
|
|
1065
|
+
return /* @__PURE__ */ jsxs3(Box5, { flexDirection: "column", children: [
|
|
1066
|
+
/* @__PURE__ */ jsx5(Text5, { children: "No horses in your stable." }),
|
|
1067
|
+
/* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "Run `token-derby stable create` to make one." })
|
|
1078
1068
|
] });
|
|
1079
1069
|
}
|
|
1080
|
-
return /* @__PURE__ */
|
|
1081
|
-
/* @__PURE__ */
|
|
1082
|
-
horses.map((h, i) => /* @__PURE__ */
|
|
1083
|
-
/* @__PURE__ */
|
|
1070
|
+
return /* @__PURE__ */ jsxs3(Box5, { flexDirection: "column", children: [
|
|
1071
|
+
/* @__PURE__ */ jsx5(Text5, { children: "Pick a horse to race:" }),
|
|
1072
|
+
horses.map((h, i) => /* @__PURE__ */ jsxs3(Box5, { flexDirection: "column", children: [
|
|
1073
|
+
/* @__PURE__ */ jsx5(Box5, { flexDirection: "row", children: /* @__PURE__ */ jsxs3(Text5, { children: [
|
|
1084
1074
|
i === idx ? "\u25BA" : " ",
|
|
1085
1075
|
" ",
|
|
1086
1076
|
h.name,
|
|
1087
1077
|
" ",
|
|
1088
|
-
/* @__PURE__ */
|
|
1078
|
+
/* @__PURE__ */ jsxs3(Text5, { color: "cyan", children: [
|
|
1089
1079
|
"[Lvl. ",
|
|
1090
1080
|
levelFromXp(h.xp),
|
|
1091
1081
|
"]"
|
|
1092
1082
|
] })
|
|
1093
1083
|
] }) }),
|
|
1094
|
-
/* @__PURE__ */
|
|
1095
|
-
/* @__PURE__ */
|
|
1096
|
-
/* @__PURE__ */
|
|
1084
|
+
/* @__PURE__ */ jsxs3(Box5, { flexDirection: "row", children: [
|
|
1085
|
+
/* @__PURE__ */ jsx5(Text5, { children: " " }),
|
|
1086
|
+
/* @__PURE__ */ jsx5(HorseSprite, { sprite: MINI_SPRITE, colors: h.colors })
|
|
1097
1087
|
] })
|
|
1098
1088
|
] }, h.stable_horse_id)),
|
|
1099
|
-
/* @__PURE__ */
|
|
1089
|
+
/* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "\u2191/\u2193 choose \xB7 Enter pick \xB7 Esc cancel" }) })
|
|
1100
1090
|
] });
|
|
1101
1091
|
}
|
|
1102
1092
|
|
|
@@ -1117,20 +1107,31 @@ async function stableEditCommand(name) {
|
|
|
1117
1107
|
console.log("Cancelled.");
|
|
1118
1108
|
return 1;
|
|
1119
1109
|
}
|
|
1110
|
+
const initialEquipped = existing.equipped_hat ?? null;
|
|
1120
1111
|
let exitCode = 0;
|
|
1121
|
-
let liveColors = existing.colors;
|
|
1122
1112
|
const app = render3(
|
|
1123
|
-
|
|
1113
|
+
React6.createElement(HorseCreator, {
|
|
1124
1114
|
initialColors: existing.colors,
|
|
1125
1115
|
initialName: existing.name,
|
|
1126
1116
|
lockName: true,
|
|
1127
1117
|
initialLevel: levelFromXp(existing.xp),
|
|
1128
|
-
|
|
1118
|
+
hats: existing.hats,
|
|
1119
|
+
initialEquipped,
|
|
1120
|
+
onSubmit: async (_name, colors, hatChoice) => {
|
|
1121
|
+
const colorsChanged = !sameColors(colors, existing.colors);
|
|
1122
|
+
const hatChanged = hatChoice !== void 0 && hatChoice !== initialEquipped;
|
|
1129
1123
|
try {
|
|
1130
|
-
await updateStableHorse(existing.stable_horse_id, { colors });
|
|
1131
|
-
|
|
1124
|
+
if (colorsChanged) await updateStableHorse(existing.stable_horse_id, { colors });
|
|
1125
|
+
if (hatChanged) await equipHat(existing.stable_horse_id, { hat_index: hatChoice });
|
|
1132
1126
|
app.unmount();
|
|
1133
|
-
|
|
1127
|
+
if (!colorsChanged && !hatChanged) {
|
|
1128
|
+
console.log(`No changes for "${existing.name}".`);
|
|
1129
|
+
} else {
|
|
1130
|
+
const parts = [];
|
|
1131
|
+
if (colorsChanged) parts.push("colors");
|
|
1132
|
+
if (hatChanged) parts.push(hatChoice === null ? "hat unequipped" : "hat equipped");
|
|
1133
|
+
console.log(`\u2713 Updated "${existing.name}" (${parts.join(", ")}).`);
|
|
1134
|
+
}
|
|
1134
1135
|
} catch (e) {
|
|
1135
1136
|
app.unmount();
|
|
1136
1137
|
if (e instanceof ApiError) {
|
|
@@ -1149,36 +1150,11 @@ async function stableEditCommand(name) {
|
|
|
1149
1150
|
})
|
|
1150
1151
|
);
|
|
1151
1152
|
await app.waitUntilExit();
|
|
1152
|
-
if (exitCode === 0 && existing.hats && existing.hats.length > 0) {
|
|
1153
|
-
const equipResult = await new Promise((resolve) => {
|
|
1154
|
-
const app2 = render3(
|
|
1155
|
-
React7.createElement(HatPicker, {
|
|
1156
|
-
hats: existing.hats,
|
|
1157
|
-
equipped: existing.equipped_hat ?? null,
|
|
1158
|
-
colors: liveColors,
|
|
1159
|
-
onPick: (idx) => {
|
|
1160
|
-
app2.unmount();
|
|
1161
|
-
resolve({ done: true, idx });
|
|
1162
|
-
},
|
|
1163
|
-
onCancel: () => {
|
|
1164
|
-
app2.unmount();
|
|
1165
|
-
resolve({ done: false, idx: null });
|
|
1166
|
-
}
|
|
1167
|
-
})
|
|
1168
|
-
);
|
|
1169
|
-
});
|
|
1170
|
-
if (equipResult.done) {
|
|
1171
|
-
try {
|
|
1172
|
-
await equipHat(existing.stable_horse_id, { hat_index: equipResult.idx });
|
|
1173
|
-
console.log(equipResult.idx === null ? "Hat unequipped." : "Hat equipped.");
|
|
1174
|
-
} catch (e) {
|
|
1175
|
-
if (e instanceof ApiError) console.error(`Equip failed: ${e.code} ${e.message}`);
|
|
1176
|
-
else throw e;
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
}
|
|
1180
1153
|
return exitCode;
|
|
1181
1154
|
}
|
|
1155
|
+
function sameColors(a, b) {
|
|
1156
|
+
return a.body === b.body && a.mane === b.mane && a.tail === b.tail && a.saddle === b.saddle;
|
|
1157
|
+
}
|
|
1182
1158
|
async function fetchStable() {
|
|
1183
1159
|
try {
|
|
1184
1160
|
const resp = await listStable();
|
|
@@ -1199,7 +1175,7 @@ async function pickHorseToEdit(horses, name) {
|
|
|
1199
1175
|
if (horses.length === 0) return "empty";
|
|
1200
1176
|
const picked = await new Promise((resolve) => {
|
|
1201
1177
|
const app = render3(
|
|
1202
|
-
|
|
1178
|
+
React6.createElement(HorsePicker, {
|
|
1203
1179
|
horses,
|
|
1204
1180
|
onPick: (h) => {
|
|
1205
1181
|
app.unmount();
|
|
@@ -1300,7 +1276,7 @@ function isIso(s) {
|
|
|
1300
1276
|
}
|
|
1301
1277
|
|
|
1302
1278
|
// src/commands/join.ts
|
|
1303
|
-
import
|
|
1279
|
+
import React8 from "react";
|
|
1304
1280
|
import { render as render4 } from "ink";
|
|
1305
1281
|
|
|
1306
1282
|
// src/stable/active-race.ts
|
|
@@ -1325,45 +1301,45 @@ async function saveActiveRace(active) {
|
|
|
1325
1301
|
}
|
|
1326
1302
|
|
|
1327
1303
|
// src/runtime/run-race.tsx
|
|
1328
|
-
import { useEffect as useEffect2, useRef, useState as
|
|
1329
|
-
import { Box as
|
|
1304
|
+
import { useEffect as useEffect2, useRef, useState as useState4 } from "react";
|
|
1305
|
+
import { Box as Box7, Text as Text7, useApp } from "ink";
|
|
1330
1306
|
|
|
1331
1307
|
// src/ui/StatusScreen.tsx
|
|
1332
|
-
import { Box as
|
|
1333
|
-
import { jsx as
|
|
1308
|
+
import { Box as Box6, Text as Text6 } from "ink";
|
|
1309
|
+
import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1334
1310
|
function StatusScreen(props) {
|
|
1335
1311
|
const { race, ownHorseId, ownHorseName, ownColors, ownUserName, lastHeartbeatAgoSec, lastHeartbeatOk } = props;
|
|
1336
1312
|
if (!race) {
|
|
1337
|
-
return /* @__PURE__ */
|
|
1313
|
+
return /* @__PURE__ */ jsx6(Box6, { flexDirection: "column", children: /* @__PURE__ */ jsx6(Text6, { children: "Joining race\u2026" }) });
|
|
1338
1314
|
}
|
|
1339
1315
|
const own = race.horses.find((h) => h.horse_id === ownHorseId);
|
|
1340
1316
|
const leader = race.horses[0];
|
|
1341
1317
|
const elapsedPct = elapsed(race);
|
|
1342
1318
|
const timeLeft = formatDuration(race.time_left_seconds);
|
|
1343
1319
|
const lvl = levelInfo((own?.xp ?? 0) + (own?.live_xp ?? 0));
|
|
1344
|
-
return /* @__PURE__ */
|
|
1345
|
-
/* @__PURE__ */
|
|
1320
|
+
return /* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", borderStyle: "round", paddingX: 1, children: [
|
|
1321
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1346
1322
|
"\u{1F3C7} TOKEN DERBY \u2500\u2500\u2500 ",
|
|
1347
|
-
/* @__PURE__ */
|
|
1323
|
+
/* @__PURE__ */ jsx6(Text6, { bold: true, children: race.name }),
|
|
1348
1324
|
" \u2500\u2500\u2500 status: ",
|
|
1349
|
-
/* @__PURE__ */
|
|
1325
|
+
/* @__PURE__ */ jsx6(Text6, { color: statusColor(race.status), children: race.status })
|
|
1350
1326
|
] }),
|
|
1351
|
-
/* @__PURE__ */
|
|
1352
|
-
/* @__PURE__ */
|
|
1353
|
-
/* @__PURE__ */
|
|
1354
|
-
/* @__PURE__ */
|
|
1327
|
+
/* @__PURE__ */ jsxs4(Box6, { marginTop: 1, flexDirection: "row", children: [
|
|
1328
|
+
/* @__PURE__ */ jsx6(HorseSprite, { sprite: MINI_SPRITE, colors: ownColors }),
|
|
1329
|
+
/* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", children: [
|
|
1330
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1355
1331
|
" ",
|
|
1356
1332
|
ownHorseName,
|
|
1357
1333
|
" ",
|
|
1358
|
-
/* @__PURE__ */
|
|
1334
|
+
/* @__PURE__ */ jsxs4(Text6, { color: "cyan", children: [
|
|
1359
1335
|
"[Lvl. ",
|
|
1360
1336
|
lvl.level,
|
|
1361
1337
|
"]"
|
|
1362
1338
|
] })
|
|
1363
1339
|
] }),
|
|
1364
|
-
/* @__PURE__ */
|
|
1340
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1365
1341
|
" ",
|
|
1366
|
-
/* @__PURE__ */
|
|
1342
|
+
/* @__PURE__ */ jsxs4(Text6, { dimColor: true, children: [
|
|
1367
1343
|
"(",
|
|
1368
1344
|
ownUserName,
|
|
1369
1345
|
")"
|
|
@@ -1371,43 +1347,43 @@ function StatusScreen(props) {
|
|
|
1371
1347
|
] })
|
|
1372
1348
|
] })
|
|
1373
1349
|
] }),
|
|
1374
|
-
/* @__PURE__ */
|
|
1375
|
-
/* @__PURE__ */
|
|
1350
|
+
/* @__PURE__ */ jsxs4(Box6, { flexDirection: "column", marginTop: 1, children: [
|
|
1351
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1376
1352
|
"Tokens (race): ",
|
|
1377
1353
|
own?.current_tokens ?? 0
|
|
1378
1354
|
] }),
|
|
1379
|
-
/* @__PURE__ */
|
|
1355
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1380
1356
|
"Position: ",
|
|
1381
1357
|
own?.rank ?? "\u2014",
|
|
1382
1358
|
" of ",
|
|
1383
1359
|
race.horses.length
|
|
1384
1360
|
] }),
|
|
1385
|
-
/* @__PURE__ */
|
|
1361
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1386
1362
|
"Leader: ",
|
|
1387
1363
|
leader ? `${leader.name}${leader.user_name ? ` (${leader.user_name})` : ""} \u2014 ${leader.current_tokens}` : "\u2014"
|
|
1388
1364
|
] }),
|
|
1389
|
-
/* @__PURE__ */
|
|
1365
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1390
1366
|
"Race elapsed: ",
|
|
1391
1367
|
(elapsedPct * 100).toFixed(0),
|
|
1392
1368
|
"% ",
|
|
1393
1369
|
bar(elapsedPct, 20)
|
|
1394
1370
|
] }),
|
|
1395
|
-
/* @__PURE__ */
|
|
1371
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1396
1372
|
"Time left: ",
|
|
1397
1373
|
timeLeft
|
|
1398
1374
|
] }),
|
|
1399
|
-
/* @__PURE__ */
|
|
1375
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1400
1376
|
"XP: ",
|
|
1401
1377
|
lvl.next_level_xp === null ? `${lvl.xp} (max level) ${bar(1, 20)}` : `${lvl.xp_into_level}/${lvl.xp_for_level} \u2192 Lvl. ${lvl.level + 1} ${bar(lvl.progress, 20)}`
|
|
1402
1378
|
] }),
|
|
1403
|
-
/* @__PURE__ */
|
|
1379
|
+
/* @__PURE__ */ jsxs4(Text6, { children: [
|
|
1404
1380
|
"Last heartbeat: ",
|
|
1405
1381
|
lastHeartbeatAgoSec === null ? "\u2014" : `${lastHeartbeatAgoSec}s ago`,
|
|
1406
1382
|
" ",
|
|
1407
|
-
/* @__PURE__ */
|
|
1383
|
+
/* @__PURE__ */ jsx6(Text6, { color: lastHeartbeatOk ? "green" : "yellow", children: lastHeartbeatOk ? "\u2713" : "\u26A0" })
|
|
1408
1384
|
] })
|
|
1409
1385
|
] }),
|
|
1410
|
-
/* @__PURE__ */
|
|
1386
|
+
/* @__PURE__ */ jsx6(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx6(Text6, { dimColor: true, children: "Press Ctrl+C to crash out of the race." }) })
|
|
1411
1387
|
] });
|
|
1412
1388
|
}
|
|
1413
1389
|
function elapsed(race) {
|
|
@@ -1556,15 +1532,15 @@ function initialBaseline(args) {
|
|
|
1556
1532
|
}
|
|
1557
1533
|
|
|
1558
1534
|
// src/runtime/run-race.tsx
|
|
1559
|
-
import { jsx as
|
|
1535
|
+
import { jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1560
1536
|
function RunRace({ active, startingBaseline, pendingMode, ownUserName }) {
|
|
1561
1537
|
const { exit } = useApp();
|
|
1562
|
-
const [race, setRace] =
|
|
1563
|
-
const [lastHbAt, setLastHbAt] =
|
|
1564
|
-
const [lastHbOk, setLastHbOk] =
|
|
1565
|
-
const [tickNow, setTickNow] =
|
|
1566
|
-
const [fatalError, setFatalError] =
|
|
1567
|
-
const [achievements, setAchievements] =
|
|
1538
|
+
const [race, setRace] = useState4(null);
|
|
1539
|
+
const [lastHbAt, setLastHbAt] = useState4(null);
|
|
1540
|
+
const [lastHbOk, setLastHbOk] = useState4(true);
|
|
1541
|
+
const [tickNow, setTickNow] = useState4(/* @__PURE__ */ new Date());
|
|
1542
|
+
const [fatalError, setFatalError] = useState4(null);
|
|
1543
|
+
const [achievements, setAchievements] = useState4([]);
|
|
1568
1544
|
const shownAchievementAtRef = useRef(0);
|
|
1569
1545
|
const baselineRef = useRef(startingBaseline);
|
|
1570
1546
|
const pendingRef = useRef(pendingMode);
|
|
@@ -1648,13 +1624,13 @@ function RunRace({ active, startingBaseline, pendingMode, ownUserName }) {
|
|
|
1648
1624
|
}, []);
|
|
1649
1625
|
const lastHeartbeatAgoSec = lastHbAt ? Math.max(0, Math.floor((tickNow.getTime() - lastHbAt.getTime()) / 1e3)) : null;
|
|
1650
1626
|
if (fatalError) {
|
|
1651
|
-
return /* @__PURE__ */
|
|
1652
|
-
/* @__PURE__ */
|
|
1653
|
-
/* @__PURE__ */
|
|
1627
|
+
return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", padding: 1, children: [
|
|
1628
|
+
/* @__PURE__ */ jsx7(Text7, { color: "red", bold: true, children: "CLI version mismatch \u2014 disconnected" }),
|
|
1629
|
+
/* @__PURE__ */ jsx7(Text7, { children: fatalError })
|
|
1654
1630
|
] });
|
|
1655
1631
|
}
|
|
1656
|
-
return /* @__PURE__ */
|
|
1657
|
-
/* @__PURE__ */
|
|
1632
|
+
return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", children: [
|
|
1633
|
+
/* @__PURE__ */ jsx7(
|
|
1658
1634
|
StatusScreen,
|
|
1659
1635
|
{
|
|
1660
1636
|
race,
|
|
@@ -1666,23 +1642,23 @@ function RunRace({ active, startingBaseline, pendingMode, ownUserName }) {
|
|
|
1666
1642
|
lastHeartbeatOk: lastHbOk
|
|
1667
1643
|
}
|
|
1668
1644
|
),
|
|
1669
|
-
achievements.length > 0 && /* @__PURE__ */
|
|
1670
|
-
/* @__PURE__ */
|
|
1645
|
+
achievements.length > 0 && /* @__PURE__ */ jsxs5(Box7, { flexDirection: "column", marginTop: 1, children: [
|
|
1646
|
+
/* @__PURE__ */ jsx7(Text7, { bold: true, children: "Achievements" }),
|
|
1671
1647
|
achievements.map(({ key, event }) => {
|
|
1672
1648
|
const description = describeAchievement(event, active);
|
|
1673
|
-
return /* @__PURE__ */
|
|
1674
|
-
/* @__PURE__ */
|
|
1649
|
+
return /* @__PURE__ */ jsxs5(Box7, { flexDirection: "row", children: [
|
|
1650
|
+
/* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
1675
1651
|
" ",
|
|
1676
1652
|
formatClockTime(event.at),
|
|
1677
1653
|
" "
|
|
1678
1654
|
] }),
|
|
1679
|
-
/* @__PURE__ */
|
|
1655
|
+
/* @__PURE__ */ jsxs5(Text7, { color: "yellow", bold: true, children: [
|
|
1680
1656
|
"+",
|
|
1681
1657
|
event.xp,
|
|
1682
1658
|
" XP "
|
|
1683
1659
|
] }),
|
|
1684
|
-
/* @__PURE__ */
|
|
1685
|
-
/* @__PURE__ */
|
|
1660
|
+
/* @__PURE__ */ jsx7(Text7, { children: event.name }),
|
|
1661
|
+
/* @__PURE__ */ jsxs5(Text7, { dimColor: true, children: [
|
|
1686
1662
|
" \u2014 ",
|
|
1687
1663
|
description
|
|
1688
1664
|
] })
|
|
@@ -1829,14 +1805,14 @@ async function joinCommand(joinCode) {
|
|
|
1829
1805
|
};
|
|
1830
1806
|
await saveActiveRace(active);
|
|
1831
1807
|
const initial = await buildInitialState({ active, raceStatus: status, rejoin: isResume });
|
|
1832
|
-
const app = render4(
|
|
1808
|
+
const app = render4(React8.createElement(RunRace, { active, ...initial, ownUserName: identity.display_name }));
|
|
1833
1809
|
await app.waitUntilExit();
|
|
1834
1810
|
return 0;
|
|
1835
1811
|
}
|
|
1836
1812
|
async function pickHorse(horses) {
|
|
1837
1813
|
return new Promise((resolve) => {
|
|
1838
1814
|
const app = render4(
|
|
1839
|
-
|
|
1815
|
+
React8.createElement(HorsePicker, {
|
|
1840
1816
|
horses,
|
|
1841
1817
|
onPick: (h) => {
|
|
1842
1818
|
app.unmount();
|
|
@@ -2048,30 +2024,30 @@ function runNpmUpgrade(spawnImpl) {
|
|
|
2048
2024
|
}
|
|
2049
2025
|
|
|
2050
2026
|
// src/commands/roll.ts
|
|
2051
|
-
import
|
|
2027
|
+
import React12 from "react";
|
|
2052
2028
|
import { render as render5 } from "ink";
|
|
2053
2029
|
|
|
2054
2030
|
// src/ui/RollReveal.tsx
|
|
2055
|
-
import { useState as
|
|
2056
|
-
import { Box as
|
|
2031
|
+
import { useState as useState6, useEffect as useEffect4, useMemo } from "react";
|
|
2032
|
+
import { Box as Box9, Text as Text9 } from "ink";
|
|
2057
2033
|
|
|
2058
2034
|
// src/ui/HatSprite.tsx
|
|
2059
|
-
import { useEffect as useEffect3, useState as
|
|
2060
|
-
import { Box as
|
|
2061
|
-
import { jsx as
|
|
2035
|
+
import { useEffect as useEffect3, useState as useState5 } from "react";
|
|
2036
|
+
import { Box as Box8, Text as Text8 } from "ink";
|
|
2037
|
+
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
2062
2038
|
function HatSprite({ hat, variant, centerIn }) {
|
|
2063
2039
|
const colors = hatColorsFor2(hat, variant ?? 0);
|
|
2064
2040
|
const grid = makeHatGrid(hat, colors, centerIn);
|
|
2065
2041
|
const lines = hexGridToHalfBlocks(grid);
|
|
2066
|
-
return /* @__PURE__ */
|
|
2042
|
+
return /* @__PURE__ */ jsx8(Box8, { flexDirection: "column", children: lines.map((line, i) => /* @__PURE__ */ jsx8(Text8, { children: line }, i)) });
|
|
2067
2043
|
}
|
|
2068
2044
|
function AnimatedHatSprite({ hat, variant, centerIn }) {
|
|
2069
2045
|
if (hat.rarity !== "legendary") {
|
|
2070
|
-
return /* @__PURE__ */
|
|
2046
|
+
return /* @__PURE__ */ jsx8(HatSprite, { hat, variant, centerIn });
|
|
2071
2047
|
}
|
|
2072
2048
|
const frames = hat.animation.frames;
|
|
2073
2049
|
const fps = hat.animation.fps;
|
|
2074
|
-
const [idx, setIdx] =
|
|
2050
|
+
const [idx, setIdx] = useState5(0);
|
|
2075
2051
|
useEffect3(() => {
|
|
2076
2052
|
if (frames.length <= 1) return;
|
|
2077
2053
|
const interval = setInterval(
|
|
@@ -2081,7 +2057,7 @@ function AnimatedHatSprite({ hat, variant, centerIn }) {
|
|
|
2081
2057
|
return () => clearInterval(interval);
|
|
2082
2058
|
}, [frames.length, fps]);
|
|
2083
2059
|
const framed = { ...hat, colors: { ...hat.colors, A: frames[idx] } };
|
|
2084
|
-
return /* @__PURE__ */
|
|
2060
|
+
return /* @__PURE__ */ jsx8(HatSprite, { hat: framed, variant, centerIn });
|
|
2085
2061
|
}
|
|
2086
2062
|
function hatColorsFor2(hat, variantIdx) {
|
|
2087
2063
|
if (hat.rarity === "legendary") return hat.colors;
|
|
@@ -2108,7 +2084,7 @@ function makeHatGrid(hat, colors, centerIn) {
|
|
|
2108
2084
|
}
|
|
2109
2085
|
|
|
2110
2086
|
// src/ui/RollReveal.tsx
|
|
2111
|
-
import { jsx as
|
|
2087
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
2112
2088
|
var RESET3 = "\x1B[0m";
|
|
2113
2089
|
var BOX_COLOR = "#E5C76B";
|
|
2114
2090
|
var TIER_PALETTE = {
|
|
@@ -2177,7 +2153,7 @@ var BOX_EMPTY = [
|
|
|
2177
2153
|
""
|
|
2178
2154
|
].map(pad);
|
|
2179
2155
|
function GiftBox({ frame, color }) {
|
|
2180
|
-
return /* @__PURE__ */
|
|
2156
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: frame.map((line, i) => /* @__PURE__ */ jsx9(Text9, { children: line ? ansiFg(color) + line + RESET3 : line }, i)) });
|
|
2181
2157
|
}
|
|
2182
2158
|
function spawnParticles(tier, count, cx, cy) {
|
|
2183
2159
|
const palette = TIER_PALETTE[tier];
|
|
@@ -2200,7 +2176,7 @@ function ConfettiBurst({ tier }) {
|
|
|
2200
2176
|
const cx = Math.floor(SCENE_W / 2);
|
|
2201
2177
|
const cy = Math.floor(SCENE_H / 2);
|
|
2202
2178
|
const particles = useMemo(() => spawnParticles(tier, 36, cx, cy), [tier, cx, cy]);
|
|
2203
|
-
const [tick, setTick] =
|
|
2179
|
+
const [tick, setTick] = useState6(0);
|
|
2204
2180
|
useEffect4(() => {
|
|
2205
2181
|
const i = setInterval(() => setTick((t) => t + 1), 70);
|
|
2206
2182
|
return () => clearInterval(i);
|
|
@@ -2213,13 +2189,13 @@ function ConfettiBurst({ tier }) {
|
|
|
2213
2189
|
grid[y][x] = ansiFg(p.color) + p.char + RESET3;
|
|
2214
2190
|
}
|
|
2215
2191
|
}
|
|
2216
|
-
return /* @__PURE__ */
|
|
2192
|
+
return /* @__PURE__ */ jsx9(Box9, { flexDirection: "column", children: grid.map((row, y) => /* @__PURE__ */ jsx9(Text9, { children: row.join("") }, y)) });
|
|
2217
2193
|
}
|
|
2218
2194
|
var CLOSED_HOLD_MS = 3e3;
|
|
2219
2195
|
function RollReveal({ outcome, onDone }) {
|
|
2220
2196
|
const isNoHat = outcome.kind === "no_hat";
|
|
2221
2197
|
const isLegendary = outcome.kind !== "no_hat" && outcome.hat.rarity === "legendary";
|
|
2222
|
-
const [phase, setPhase] =
|
|
2198
|
+
const [phase, setPhase] = useState6("closed");
|
|
2223
2199
|
useEffect4(() => {
|
|
2224
2200
|
const timers = [];
|
|
2225
2201
|
timers.push(setTimeout(() => setPhase("open1"), CLOSED_HOLD_MS));
|
|
@@ -2234,24 +2210,24 @@ function RollReveal({ outcome, onDone }) {
|
|
|
2234
2210
|
}
|
|
2235
2211
|
return () => timers.forEach(clearTimeout);
|
|
2236
2212
|
}, [isNoHat, isLegendary, onDone]);
|
|
2237
|
-
if (phase === "closed") return /* @__PURE__ */
|
|
2238
|
-
if (phase === "open1") return /* @__PURE__ */
|
|
2239
|
-
if (phase === "open2") return /* @__PURE__ */
|
|
2240
|
-
if (phase === "empty") return /* @__PURE__ */
|
|
2213
|
+
if (phase === "closed") return /* @__PURE__ */ jsx9(GiftBox, { frame: BOX_CLOSED, color: BOX_COLOR });
|
|
2214
|
+
if (phase === "open1") return /* @__PURE__ */ jsx9(GiftBox, { frame: BOX_OPENING_1, color: BOX_COLOR });
|
|
2215
|
+
if (phase === "open2") return /* @__PURE__ */ jsx9(GiftBox, { frame: BOX_OPENING_2, color: BOX_COLOR });
|
|
2216
|
+
if (phase === "empty") return /* @__PURE__ */ jsx9(GiftBox, { frame: BOX_EMPTY, color: BOX_COLOR });
|
|
2241
2217
|
if (phase === "burst" && outcome.kind !== "no_hat") {
|
|
2242
|
-
return /* @__PURE__ */
|
|
2218
|
+
return /* @__PURE__ */ jsx9(ConfettiBurst, { tier: outcome.hat.rarity });
|
|
2243
2219
|
}
|
|
2244
|
-
if (outcome.kind === "no_hat") return /* @__PURE__ */
|
|
2245
|
-
return outcome.hat.rarity === "legendary" ? /* @__PURE__ */
|
|
2220
|
+
if (outcome.kind === "no_hat") return /* @__PURE__ */ jsx9(GiftBox, { frame: BOX_EMPTY, color: BOX_COLOR });
|
|
2221
|
+
return outcome.hat.rarity === "legendary" ? /* @__PURE__ */ jsx9(AnimatedHatSprite, { hat: outcome.hat, centerIn: { w: SCENE_W, h: SCENE_H } }) : /* @__PURE__ */ jsx9(HatSprite, { hat: outcome.hat, variant: outcome.variant, centerIn: { w: SCENE_W, h: SCENE_H } });
|
|
2246
2222
|
}
|
|
2247
2223
|
|
|
2248
2224
|
// src/ui/RollHorsePicker.tsx
|
|
2249
|
-
import { useState as
|
|
2250
|
-
import { Box as
|
|
2251
|
-
import { jsx as
|
|
2225
|
+
import { useState as useState7 } from "react";
|
|
2226
|
+
import { Box as Box10, Text as Text10, useInput as useInput3 } from "ink";
|
|
2227
|
+
import { jsx as jsx10, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
2252
2228
|
function RollHorsePicker({ horses, onPick, onCancel }) {
|
|
2253
|
-
const [idx, setIdx] =
|
|
2254
|
-
|
|
2229
|
+
const [idx, setIdx] = useState7(0);
|
|
2230
|
+
useInput3((input, key) => {
|
|
2255
2231
|
if (key.escape) {
|
|
2256
2232
|
onCancel();
|
|
2257
2233
|
return;
|
|
@@ -2270,33 +2246,33 @@ function RollHorsePicker({ horses, onPick, onCancel }) {
|
|
|
2270
2246
|
return;
|
|
2271
2247
|
}
|
|
2272
2248
|
});
|
|
2273
|
-
return /* @__PURE__ */
|
|
2274
|
-
/* @__PURE__ */
|
|
2275
|
-
horses.map((h, i) => /* @__PURE__ */
|
|
2276
|
-
/* @__PURE__ */
|
|
2249
|
+
return /* @__PURE__ */ jsxs6(Box10, { flexDirection: "column", children: [
|
|
2250
|
+
/* @__PURE__ */ jsx10(Text10, { children: "Pick a horse to roll for:" }),
|
|
2251
|
+
horses.map((h, i) => /* @__PURE__ */ jsxs6(Box10, { flexDirection: "column", children: [
|
|
2252
|
+
/* @__PURE__ */ jsx10(Box10, { flexDirection: "row", children: /* @__PURE__ */ jsxs6(Text10, { children: [
|
|
2277
2253
|
i === idx ? "\u25BA" : " ",
|
|
2278
2254
|
" ",
|
|
2279
2255
|
h.name,
|
|
2280
2256
|
" ",
|
|
2281
|
-
/* @__PURE__ */
|
|
2257
|
+
/* @__PURE__ */ jsxs6(Text10, { color: "cyan", children: [
|
|
2282
2258
|
"[Lvl. ",
|
|
2283
2259
|
levelFromXp(h.xp),
|
|
2284
2260
|
"]"
|
|
2285
2261
|
] }),
|
|
2286
2262
|
" ",
|
|
2287
|
-
/* @__PURE__ */
|
|
2263
|
+
/* @__PURE__ */ jsxs6(Text10, { color: "yellow", children: [
|
|
2288
2264
|
"\u2014 ",
|
|
2289
2265
|
h.pending,
|
|
2290
2266
|
" roll",
|
|
2291
2267
|
h.pending === 1 ? "" : "s"
|
|
2292
2268
|
] })
|
|
2293
2269
|
] }) }),
|
|
2294
|
-
/* @__PURE__ */
|
|
2295
|
-
/* @__PURE__ */
|
|
2296
|
-
/* @__PURE__ */
|
|
2270
|
+
/* @__PURE__ */ jsxs6(Box10, { flexDirection: "row", children: [
|
|
2271
|
+
/* @__PURE__ */ jsx10(Text10, { children: " " }),
|
|
2272
|
+
/* @__PURE__ */ jsx10(HorseSprite, { sprite: MINI_SPRITE, colors: h.colors })
|
|
2297
2273
|
] })
|
|
2298
2274
|
] }, h.stable_horse_id)),
|
|
2299
|
-
/* @__PURE__ */
|
|
2275
|
+
/* @__PURE__ */ jsx10(Box10, { marginTop: 1, children: /* @__PURE__ */ jsx10(Text10, { dimColor: true, children: "\u2191/\u2193 choose \xB7 Enter pick \xB7 Esc cancel" }) })
|
|
2300
2276
|
] });
|
|
2301
2277
|
}
|
|
2302
2278
|
|
|
@@ -2324,7 +2300,7 @@ function resetStdinAfterInk() {
|
|
|
2324
2300
|
}
|
|
2325
2301
|
async function runReveal(outcome) {
|
|
2326
2302
|
await new Promise((resolve) => {
|
|
2327
|
-
const app = render5(
|
|
2303
|
+
const app = render5(React12.createElement(RollReveal, {
|
|
2328
2304
|
outcome,
|
|
2329
2305
|
onDone: () => {
|
|
2330
2306
|
app.unmount();
|
|
@@ -2350,7 +2326,7 @@ async function rollCommand() {
|
|
|
2350
2326
|
return 0;
|
|
2351
2327
|
}
|
|
2352
2328
|
const picked = await new Promise((resolve) => {
|
|
2353
|
-
const app = render5(
|
|
2329
|
+
const app = render5(React12.createElement(RollHorsePicker, {
|
|
2354
2330
|
horses: eligible,
|
|
2355
2331
|
onPick: (h) => {
|
|
2356
2332
|
app.unmount();
|