@2025-6-19/clawfight 1.2.0 → 1.3.0
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/index.js +335 -167
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -32,6 +32,13 @@ async function writeLobster(lobster) {
|
|
|
32
32
|
await ensureDir(MEMORY_DIR);
|
|
33
33
|
await writeFile(getPath("lobster.json"), JSON.stringify(lobster, null, 2), "utf-8");
|
|
34
34
|
}
|
|
35
|
+
async function readSoul() {
|
|
36
|
+
try {
|
|
37
|
+
return await readFile(getPath("soul.md"), "utf-8");
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
35
42
|
async function writeSoul(content) {
|
|
36
43
|
await ensureDir(MEMORY_DIR);
|
|
37
44
|
await writeFile(getPath("soul.md"), content, "utf-8");
|
|
@@ -161,6 +168,171 @@ function calcExpToNext(level) {
|
|
|
161
168
|
return Math.floor(100 * Math.pow(1.2, level - 1));
|
|
162
169
|
}
|
|
163
170
|
|
|
171
|
+
// src/lib/i18n.ts
|
|
172
|
+
var zh = {
|
|
173
|
+
no_lobster: "\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01",
|
|
174
|
+
status_cant_fight: "\u26A0\uFE0F {name} \u5F53\u524D\u72B6\u6001\u4E3A {status}\uFF0C\u65E0\u6CD5\u6218\u6597\u3002",
|
|
175
|
+
status_cant_patrol_molt: "\u{1F7E1} {name} \u6B63\u5728\u8715\u58F3\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002",
|
|
176
|
+
status_cant_patrol_hibernate: "\u{1F4A4} {name} \u6B63\u5728\u51AC\u7720\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002",
|
|
177
|
+
patrol_start: "\u{1F99E} {name} \u5F00\u59CB\u5DE1\u903B...",
|
|
178
|
+
patrol_checkin: "\u{1F4CD} \u5DE1\u903B\u7B7E\u5230 \u2192 \u7ECF\u9A8C +{exp}",
|
|
179
|
+
patrol_connecting: "\u{1F4E1} \u8FDE\u63A5\u670D\u52A1\u5668...",
|
|
180
|
+
patrol_cooldown: "\u23F3 \u5DE1\u903B\u51B7\u5374\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\uFF08\u95F4\u9694\u81F3\u5C11 30 \u5206\u949F\uFF09",
|
|
181
|
+
patrol_encounter: "\u2694\uFE0F \u906D\u9047 {name} (Lv.{level})\uFF01",
|
|
182
|
+
patrol_result_win: " \u6218\u6597\u7ED3\u679C: \u{1F3C6} \u80DC\u5229 ({rounds} \u56DE\u5408)",
|
|
183
|
+
patrol_result_loss: " \u6218\u6597\u7ED3\u679C: \u{1F480} \u5931\u8D25 ({rounds} \u56DE\u5408)",
|
|
184
|
+
patrol_result_draw: " \u6218\u6597\u7ED3\u679C: \u{1F91D} \u5E73\u5C40 ({rounds} \u56DE\u5408)",
|
|
185
|
+
patrol_exp_stats: " \u7ECF\u9A8C +{exp} | \u6218\u7EE9: {wins}\u80DC {losses}\u8D1F | \u8FDE\u80DC: {streak}",
|
|
186
|
+
patrol_no_battle: "\u2694\uFE0F \u906D\u9047 {name} (Lv.{level})\uFF0C\u4F46\u672A\u80FD\u5B8C\u6210\u6218\u6597",
|
|
187
|
+
patrol_done: "\u2705 \u5DE1\u903B\u5B8C\u6210\uFF0C\u5339\u914D\u6C60: {pool} \u53EA\u9F99\u867E",
|
|
188
|
+
patrol_offline: "\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u8DF3\u8FC7\u5728\u7EBF\u7B7E\u5230",
|
|
189
|
+
patrol_summary: "\u{1F4CA} \u5F53\u524D: Lv.{level} | EXP: {exp}/{next} | \u4ECA\u65E5EXP: {today}/{cap}",
|
|
190
|
+
battle_no_code: "\u26A0\uFE0F \u8BF7\u6307\u5B9A\u5BF9\u624B\u7684\u6218\u6597\u7801\u3002",
|
|
191
|
+
battle_usage: " \u7528\u6CD5: npx clawfight battle <\u6218\u6597\u7801>",
|
|
192
|
+
battle_hint: " \u6218\u6597\u7801\u53EF\u4ECE\u6392\u884C\u699C\u4E2D\u83B7\u53D6: npx clawfight leaderboard",
|
|
193
|
+
battle_connecting: "\u{1F4E1} \u5411\u670D\u52A1\u5668\u53D1\u8D77\u6311\u6218 [{code}]...",
|
|
194
|
+
battle_server_down: "\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u65E0\u6CD5\u8FDB\u884C\u6218\u6597\u3002",
|
|
195
|
+
battle_win: "\u{1F3C6} \u80DC\u5229\uFF01{name} \u5728 {rounds} \u56DE\u5408\u540E\u51FB\u8D25\u4E86 {opponent}\uFF01",
|
|
196
|
+
battle_loss: "\u{1F480} \u5931\u8D25\u2026{name} \u5728 {rounds} \u56DE\u5408\u540E\u88AB {opponent} \u51FB\u8D25\u3002",
|
|
197
|
+
battle_draw: "\u{1F91D} \u5E73\u5C40\uFF01{rounds} \u56DE\u5408\u540E\u53CC\u65B9\u7CBE\u75B2\u529B\u7AED",
|
|
198
|
+
battle_exp: "\u7ECF\u9A8C +{exp} | \u6218\u7EE9: {wins}\u80DC {losses}\u8D1F | \u8FDE\u80DC: {streak}",
|
|
199
|
+
lb_loading: "\u{1F4E1} \u83B7\u53D6\u5168\u7403\u6392\u884C\u699C...",
|
|
200
|
+
lb_offline: "\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u65E0\u6CD5\u83B7\u53D6\u6392\u884C\u699C\u3002",
|
|
201
|
+
lb_empty: "\u{1F99E} \u8FD8\u6CA1\u6709\u9F99\u867E\u4E0A\u699C\u3002\u6210\u4E3A\u7B2C\u4E00\u4E2A\u5427\uFF01",
|
|
202
|
+
lb_title: " \u{1F99E} ClawFight \u5168\u7403\u6392\u884C\u699C",
|
|
203
|
+
lb_header: " \u6392\u540D \u540D\u79F0 \u7B49\u7EA7 \u80DC\u573A \u80DC\u7387 \u6218\u6597\u7801",
|
|
204
|
+
lb_total: " \u603B\u9F99\u867E: {total} | \u6D3B\u8DC3: {active}",
|
|
205
|
+
lb_hint: " \u{1F4A1} \u4F7F\u7528 npx clawfight battle <\u6218\u6597\u7801> \u6311\u6218\u6307\u5B9A\u5BF9\u624B",
|
|
206
|
+
status_battle_code: "\u{1F3AF} \u6218\u6597\u7801: {code}",
|
|
207
|
+
event_prefix: "\u{1F3B2} [{category}] {id}",
|
|
208
|
+
event_effects: " \u6548\u679C: {changes}",
|
|
209
|
+
level_up: "\u{1F389} \u5347\u7EA7\uFF01Lv.{level}! [{gains}]",
|
|
210
|
+
molt_trigger: "\u{1F41A} \u89E6\u53D1\u8715\u58F3\u4E8B\u4EF6\uFF01\u9F99\u867E\u8FDB\u5165\u8715\u58F3\u72B6\u6001...",
|
|
211
|
+
hatch_exists: "\u{1F99E} \u4F60\u5DF2\u7ECF\u6709\u4E00\u53EA\u9F99\u867E\u4E86\uFF1A{name}\uFF08Lv.{level}\uFF09",
|
|
212
|
+
hatch_one_only: "\u4E00\u4EBA\u4E00\u867E\uFF0C\u4E0D\u53EF\u66FF\u4EE3\u3002",
|
|
213
|
+
hatch_success: " \u{1F95A} \u2192 \u{1F99E} \u5B75 \u5316 \u6210 \u529F \uFF01",
|
|
214
|
+
hatch_env: " \u73AF\u5883: \u6CBF\u6D77\u6D45\u6EE9",
|
|
215
|
+
hatch_ready: "\u4F60\u7684\u9F99\u867E\u5DF2\u7ECF\u51C6\u5907\u597D\u63A2\u7D22\u6D77\u6D0B\u4E86\uFF01",
|
|
216
|
+
hatch_next: "\u8FD0\u884C npx clawfight patrol \u5F00\u59CB\u7B2C\u4E00\u6B21\u5DE1\u903B\u3002",
|
|
217
|
+
feed_menu_title: "\u{1F37D}\uFE0F \u53EF\u7528\u98DF\u7269\u7C7B\u578B:",
|
|
218
|
+
feed_menu_usage: "\u7528\u6CD5: npx clawfight feed <food_type>",
|
|
219
|
+
feed_full: "\u26A0\uFE0F {name} \u4ECA\u5929\u5DF2\u7ECF\u5403\u9971\u4E86\uFF08\u6BCF\u65E5\u7ECF\u9A8C\u4E0A\u9650 {cap}\uFF09",
|
|
220
|
+
feed_ate: "\u{1F37D}\uFE0F {name} \u5403\u4E86 {food}\uFF01",
|
|
221
|
+
feed_exp: " \u7ECF\u9A8C +{exp}",
|
|
222
|
+
feed_exp_stat: " \u7ECF\u9A8C +{exp} | {stat} +{bonus}",
|
|
223
|
+
feed_today: " \u4ECA\u65E5\u7ECF\u9A8C: {today}/{cap}",
|
|
224
|
+
food_protein: "\u9AD8\u86CB\u767D\u98DF\u7269",
|
|
225
|
+
food_algae: "\u85FB\u7C7B\u98DF\u7269",
|
|
226
|
+
food_mineral: "\u77FF\u7269\u8D28",
|
|
227
|
+
rest_already: "\u{1F4A4} {name} \u5DF2\u7ECF\u5728\u4F11\u7720\u4E2D\u4E86\u3002\u8FD0\u884C npx clawfight wake \u6765\u5524\u9192\u5B83\u3002",
|
|
228
|
+
rest_molting: "\u{1F7E1} {name} \u6B63\u5728\u8715\u58F3\uFF0C\u4E0D\u80FD\u4F11\u7720\u3002\u7B49\u8715\u58F3\u7ED3\u675F\u518D\u6765\u3002",
|
|
229
|
+
rest_desc1: "\u{1F4A4} {name} \u7F13\u7F13\u6C89\u5165\u6D77\u5E95\u6C99\u5730...",
|
|
230
|
+
rest_desc2: " \u9F99\u867E\u8737\u7F29\u8D77\u89E6\u987B\uFF0C\u5B89\u9759\u5730\u4F11\u606F\u3002",
|
|
231
|
+
rest_desc3: " \u4F11\u7720\u671F\u95F4\u4E0D\u4F1A\u53C2\u4E0E\u5DE1\u903B\u548C\u6218\u6597\u3002",
|
|
232
|
+
rest_desc4: " \u9192\u6765\u540E\u4F1A\u83B7\u5F97\u6062\u590D\u52A0\u6210\uFF01",
|
|
233
|
+
rest_wake_hint: "\u8FD0\u884C npx clawfight wake \u6765\u5524\u9192\u5B83\u3002",
|
|
234
|
+
wake_not_sleeping: "\u{1F7E2} {name} \u6CA1\u6709\u5728\u4F11\u7720\u3002\u5B83\u5DF2\u7ECF\u662F\u6D3B\u8DC3\u72B6\u6001\u4E86\uFF01",
|
|
235
|
+
wake_desc: "\u2600\uFE0F {name} \u4ECE\u6C99\u5730\u4E2D\u7F13\u7F13\u82CF\u9192...",
|
|
236
|
+
wake_duration: " \u4F11\u7720\u65F6\u957F: {duration}",
|
|
237
|
+
wake_bonus: " \u6062\u590D\u52A0\u6210: {bonus}",
|
|
238
|
+
wake_no_bonus: "\uFF08\u4F11\u7720\u4E0D\u8DB3 4 \u5C0F\u65F6\uFF0C\u65E0\u52A0\u6210\uFF09",
|
|
239
|
+
wake_ready: "\u{1F7E2} {name} \u7CBE\u795E\u7115\u53D1\uFF0C\u51C6\u5907\u597D\u518D\u6B21\u51FA\u51FB\uFF01",
|
|
240
|
+
duration_minutes: "{n} \u5206\u949F",
|
|
241
|
+
duration_hours: "{n} \u5C0F\u65F6"
|
|
242
|
+
};
|
|
243
|
+
var en = {
|
|
244
|
+
no_lobster: "\u{1F95A} No lobster yet. Run npx clawfight hatch to hatch one!",
|
|
245
|
+
status_cant_fight: "\u26A0\uFE0F {name} is currently {status}, can't battle.",
|
|
246
|
+
status_cant_patrol_molt: "\u{1F7E1} {name} is molting, can't patrol.",
|
|
247
|
+
status_cant_patrol_hibernate: "\u{1F4A4} {name} is hibernating, can't patrol.",
|
|
248
|
+
patrol_start: "\u{1F99E} {name} starts patrolling...",
|
|
249
|
+
patrol_checkin: "\u{1F4CD} Patrol check-in \u2192 EXP +{exp}",
|
|
250
|
+
patrol_connecting: "\u{1F4E1} Connecting to server...",
|
|
251
|
+
patrol_cooldown: "\u23F3 Patrol on cooldown, try again later (30 min interval)",
|
|
252
|
+
patrol_encounter: "\u2694\uFE0F Encounter: {name} (Lv.{level})!",
|
|
253
|
+
patrol_result_win: " Result: \u{1F3C6} Victory ({rounds} rounds)",
|
|
254
|
+
patrol_result_loss: " Result: \u{1F480} Defeat ({rounds} rounds)",
|
|
255
|
+
patrol_result_draw: " Result: \u{1F91D} Draw ({rounds} rounds)",
|
|
256
|
+
patrol_exp_stats: " EXP +{exp} | Record: {wins}W {losses}L | Streak: {streak}",
|
|
257
|
+
patrol_no_battle: "\u2694\uFE0F Encountered {name} (Lv.{level}), but battle could not complete",
|
|
258
|
+
patrol_done: "\u2705 Patrol done, pool: {pool} lobsters",
|
|
259
|
+
patrol_offline: "\u26A0\uFE0F Server unreachable, skipping online check-in",
|
|
260
|
+
patrol_summary: "\u{1F4CA} Current: Lv.{level} | EXP: {exp}/{next} | Today: {today}/{cap}",
|
|
261
|
+
battle_no_code: "\u26A0\uFE0F Please provide opponent's battle code.",
|
|
262
|
+
battle_usage: " Usage: npx clawfight battle <code>",
|
|
263
|
+
battle_hint: " Find codes on the leaderboard: npx clawfight leaderboard",
|
|
264
|
+
battle_connecting: "\u{1F4E1} Challenging [{code}]...",
|
|
265
|
+
battle_server_down: "\u26A0\uFE0F Server unreachable, can't battle.",
|
|
266
|
+
battle_win: "\u{1F3C6} Victory! {name} defeated {opponent} in {rounds} rounds!",
|
|
267
|
+
battle_loss: "\u{1F480} Defeat\u2026 {name} was beaten by {opponent} in {rounds} rounds.",
|
|
268
|
+
battle_draw: "\u{1F91D} Draw! Both exhausted after {rounds} rounds",
|
|
269
|
+
battle_exp: "EXP +{exp} | Record: {wins}W {losses}L | Streak: {streak}",
|
|
270
|
+
lb_loading: "\u{1F4E1} Fetching global leaderboard...",
|
|
271
|
+
lb_offline: "\u26A0\uFE0F Server unreachable, can't load leaderboard.",
|
|
272
|
+
lb_empty: "\u{1F99E} No lobsters on the board yet. Be the first!",
|
|
273
|
+
lb_title: " \u{1F99E} ClawFight Global Leaderboard",
|
|
274
|
+
lb_header: " Rank Name Level Wins WR Code",
|
|
275
|
+
lb_total: " Total: {total} | Active: {active}",
|
|
276
|
+
lb_hint: " \u{1F4A1} Use npx clawfight battle <code> to challenge an opponent",
|
|
277
|
+
status_battle_code: "\u{1F3AF} Battle code: {code}",
|
|
278
|
+
event_prefix: "\u{1F3B2} [{category}] {id}",
|
|
279
|
+
event_effects: " Effects: {changes}",
|
|
280
|
+
level_up: "\u{1F389} Level up! Lv.{level}! [{gains}]",
|
|
281
|
+
molt_trigger: "\u{1F41A} Molt triggered! Lobster enters molting state...",
|
|
282
|
+
hatch_exists: "\u{1F99E} You already have a lobster: {name} (Lv.{level})",
|
|
283
|
+
hatch_one_only: "One per person, irreplaceable.",
|
|
284
|
+
hatch_success: " \u{1F95A} \u2192 \u{1F99E} HATCH SUCCESS!",
|
|
285
|
+
hatch_env: " Environment: Coastal Shallows",
|
|
286
|
+
hatch_ready: "Your lobster is ready to explore the ocean!",
|
|
287
|
+
hatch_next: "Run npx clawfight patrol to start your first patrol.",
|
|
288
|
+
feed_menu_title: "\u{1F37D}\uFE0F Available food types:",
|
|
289
|
+
feed_menu_usage: "Usage: npx clawfight feed <food_type>",
|
|
290
|
+
feed_full: "\u26A0\uFE0F {name} is full today (daily EXP cap {cap})",
|
|
291
|
+
feed_ate: "\u{1F37D}\uFE0F {name} ate {food}!",
|
|
292
|
+
feed_exp: " EXP +{exp}",
|
|
293
|
+
feed_exp_stat: " EXP +{exp} | {stat} +{bonus}",
|
|
294
|
+
feed_today: " Today EXP: {today}/{cap}",
|
|
295
|
+
food_protein: "High Protein",
|
|
296
|
+
food_algae: "Algae",
|
|
297
|
+
food_mineral: "Minerals",
|
|
298
|
+
rest_already: "\u{1F4A4} {name} is already hibernating. Run npx clawfight wake to wake it.",
|
|
299
|
+
rest_molting: "\u{1F7E1} {name} is molting, can't hibernate now.",
|
|
300
|
+
rest_desc1: "\u{1F4A4} {name} sinks into the sandy seabed...",
|
|
301
|
+
rest_desc2: " The lobster curls its antennae and rests quietly.",
|
|
302
|
+
rest_desc3: " No patrols or battles during hibernation.",
|
|
303
|
+
rest_desc4: " Wake bonuses await!",
|
|
304
|
+
rest_wake_hint: "Run npx clawfight wake to wake it up.",
|
|
305
|
+
wake_not_sleeping: "\u{1F7E2} {name} is not hibernating. Already active!",
|
|
306
|
+
wake_desc: "\u2600\uFE0F {name} slowly wakes from the sandy seabed...",
|
|
307
|
+
wake_duration: " Sleep duration: {duration}",
|
|
308
|
+
wake_bonus: " Recovery bonus: {bonus}",
|
|
309
|
+
wake_no_bonus: "(Less than 4 hours, no bonus)",
|
|
310
|
+
wake_ready: "\u{1F7E2} {name} is refreshed and ready to go!",
|
|
311
|
+
duration_minutes: "{n} min",
|
|
312
|
+
duration_hours: "{n} hours"
|
|
313
|
+
};
|
|
314
|
+
function detectLocale() {
|
|
315
|
+
const lang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || "";
|
|
316
|
+
if (lang.startsWith("zh")) return "zh";
|
|
317
|
+
try {
|
|
318
|
+
const sysLocale = Intl.DateTimeFormat().resolvedOptions().locale;
|
|
319
|
+
if (sysLocale.startsWith("zh")) return "zh";
|
|
320
|
+
} catch {
|
|
321
|
+
}
|
|
322
|
+
return "en";
|
|
323
|
+
}
|
|
324
|
+
var locale = detectLocale();
|
|
325
|
+
var strings = locale === "zh" ? zh : en;
|
|
326
|
+
function t(key, params) {
|
|
327
|
+
let msg = strings[key] || zh[key] || key;
|
|
328
|
+
if (params) {
|
|
329
|
+
for (const [k, v] of Object.entries(params)) {
|
|
330
|
+
msg = msg.replace(new RegExp(`\\{${k}\\}`, "g"), String(v));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
return msg;
|
|
334
|
+
}
|
|
335
|
+
|
|
164
336
|
// src/commands/hatch.ts
|
|
165
337
|
function rollRarity() {
|
|
166
338
|
const roll = Math.random() * 100;
|
|
@@ -192,9 +364,8 @@ function randomName() {
|
|
|
192
364
|
async function hatch(name) {
|
|
193
365
|
const existing = await readLobster();
|
|
194
366
|
if (existing) {
|
|
195
|
-
console.log(
|
|
196
|
-
|
|
197
|
-
console.log("\u4E00\u4EBA\u4E00\u867E\uFF0C\u4E0D\u53EF\u66FF\u4EE3\u3002");
|
|
367
|
+
console.log("\n" + t("hatch_exists", { name: existing.name, level: existing.level }));
|
|
368
|
+
console.log(t("hatch_one_only"));
|
|
198
369
|
return;
|
|
199
370
|
}
|
|
200
371
|
const lobsterName = name || randomName();
|
|
@@ -228,27 +399,24 @@ async function hatch(name) {
|
|
|
228
399
|
await writeLobster(lobster);
|
|
229
400
|
const soulMd = buildSoulMarkdown(lobsterName, soul, rarity, "coastal");
|
|
230
401
|
await writeSoul(soulMd);
|
|
231
|
-
await appendLog(`\u{1F95A} **${lobsterName}**
|
|
402
|
+
await appendLog(`\u{1F95A} **${lobsterName}** hatched! Rarity: ${RARITY_LABELS[rarity]}`);
|
|
232
403
|
console.log("\n" + "=".repeat(50));
|
|
233
|
-
console.log("
|
|
404
|
+
console.log(t("hatch_success"));
|
|
234
405
|
console.log("=".repeat(50));
|
|
235
406
|
console.log();
|
|
236
|
-
console.log(`
|
|
237
|
-
console.log(`
|
|
238
|
-
console.log(
|
|
239
|
-
console.log(
|
|
407
|
+
console.log(` ${lobsterName}`);
|
|
408
|
+
console.log(` ${RARITY_LABELS[rarity]} (${rarity})`);
|
|
409
|
+
console.log(" Lv.1");
|
|
410
|
+
console.log(t("hatch_env"));
|
|
240
411
|
console.log();
|
|
241
412
|
console.log(` \u2764\uFE0F HP: ${stats.hp} \u2694\uFE0F ATK: ${stats.attack} \u{1F6E1}\uFE0F DEF: ${stats.defense}`);
|
|
242
413
|
console.log(` \u{1F4A8} SPD: ${stats.speed} \u{1F441}\uFE0F INT: ${stats.intimidation} \u{1F340} LCK: ${stats.luck}`);
|
|
243
414
|
console.log();
|
|
244
|
-
console.log(` \u6027\u683C:`);
|
|
245
|
-
console.log(` \u52C7\u6C14 ${soul.bravery}/10 | \u597D\u5947 ${soul.curiosity}/10 | \u8BDD\u91CF ${soul.talkativeness}/10 | \u813E\u6C14 ${soul.temper}/10`);
|
|
246
|
-
console.log();
|
|
247
415
|
console.log(` ID: ${lobster.id}`);
|
|
248
416
|
console.log("=".repeat(50));
|
|
249
417
|
console.log();
|
|
250
|
-
console.log("
|
|
251
|
-
console.log("
|
|
418
|
+
console.log(t("hatch_ready"));
|
|
419
|
+
console.log(t("hatch_next"));
|
|
252
420
|
}
|
|
253
421
|
|
|
254
422
|
// src/commands/status.ts
|
|
@@ -259,7 +427,7 @@ function bar(current, max, width = 20) {
|
|
|
259
427
|
async function status() {
|
|
260
428
|
const lobster = await readLobster();
|
|
261
429
|
if (!lobster) {
|
|
262
|
-
console.log("\n
|
|
430
|
+
console.log("\n" + t("no_lobster"));
|
|
263
431
|
return;
|
|
264
432
|
}
|
|
265
433
|
const totalBattles = lobster.wins + lobster.losses;
|
|
@@ -290,6 +458,14 @@ async function status() {
|
|
|
290
458
|
console.log(`\u2502 \u6027\u683C: \u2502`);
|
|
291
459
|
console.log(`\u2502 \u52C7\u6C14 ${lobster.soul.bravery}/10 | \u597D\u5947 ${lobster.soul.curiosity}/10 | \u8BDD\u91CF ${lobster.soul.talkativeness}/10 | \u813E\u6C14 ${lobster.soul.temper}/10 \u2502`);
|
|
292
460
|
console.log("\u2514" + "\u2500".repeat(44) + "\u2518");
|
|
461
|
+
console.log("\n" + t("status_battle_code", { code: lobster.id.slice(0, 8) }));
|
|
462
|
+
const soulText = await readSoul();
|
|
463
|
+
if (soulText) {
|
|
464
|
+
const firstLine = soulText.split("\n").find((l) => l.trim() && !l.startsWith("#"));
|
|
465
|
+
if (firstLine) {
|
|
466
|
+
console.log(`\u{1F4DC} \u7075\u9B42: ${firstLine.trim().slice(0, 60)}`);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
293
469
|
}
|
|
294
470
|
|
|
295
471
|
// src/lib/api.ts
|
|
@@ -331,14 +507,11 @@ async function apiPatrol(lobster) {
|
|
|
331
507
|
lobster_id: lobster.id,
|
|
332
508
|
level: lobster.level,
|
|
333
509
|
stats_hash: statsHash(lobster),
|
|
510
|
+
stats: lobster.stats,
|
|
334
511
|
environment: lobster.environment,
|
|
335
512
|
name: lobster.name,
|
|
336
513
|
color: lobster.rarity,
|
|
337
514
|
rarity: lobster.rarity,
|
|
338
|
-
wins: lobster.wins,
|
|
339
|
-
losses: lobster.losses,
|
|
340
|
-
streak: lobster.streak,
|
|
341
|
-
reputation: lobster.reputation,
|
|
342
515
|
is_molting: lobster.status === "molting",
|
|
343
516
|
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
344
517
|
}),
|
|
@@ -350,6 +523,27 @@ async function apiPatrol(lobster) {
|
|
|
350
523
|
return null;
|
|
351
524
|
}
|
|
352
525
|
}
|
|
526
|
+
async function apiBattle(challengerId, opponentCode) {
|
|
527
|
+
try {
|
|
528
|
+
const pfetch = await getProxiedFetch();
|
|
529
|
+
const res = await pfetch(`${API_BASE}/api/battle`, {
|
|
530
|
+
method: "POST",
|
|
531
|
+
headers: { "Content-Type": "application/json; charset=utf-8" },
|
|
532
|
+
body: JSON.stringify({
|
|
533
|
+
challenger_id: challengerId,
|
|
534
|
+
opponent_code: opponentCode
|
|
535
|
+
}),
|
|
536
|
+
signal: AbortSignal.timeout(1e4)
|
|
537
|
+
});
|
|
538
|
+
if (!res.ok) {
|
|
539
|
+
const err = await res.json();
|
|
540
|
+
return { error: err.error || "Unknown error" };
|
|
541
|
+
}
|
|
542
|
+
return await res.json();
|
|
543
|
+
} catch {
|
|
544
|
+
return null;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
353
547
|
async function apiLeaderboard(limit = 20) {
|
|
354
548
|
try {
|
|
355
549
|
const pfetch = await getProxiedFetch();
|
|
@@ -441,17 +635,15 @@ var PATROL_EXP = 15;
|
|
|
441
635
|
async function patrol() {
|
|
442
636
|
const lobster = await readLobster();
|
|
443
637
|
if (!lobster) {
|
|
444
|
-
console.log("\n
|
|
638
|
+
console.log("\n" + t("no_lobster"));
|
|
445
639
|
return;
|
|
446
640
|
}
|
|
447
641
|
if (lobster.status === "molting") {
|
|
448
|
-
console.log(
|
|
449
|
-
\u{1F7E1} ${lobster.name} \u6B63\u5728\u8715\u58F3\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002`);
|
|
642
|
+
console.log("\n" + t("status_cant_patrol_molt", { name: lobster.name }));
|
|
450
643
|
return;
|
|
451
644
|
}
|
|
452
645
|
if (lobster.status === "hibernating") {
|
|
453
|
-
console.log(
|
|
454
|
-
\u{1F4A4} ${lobster.name} \u6B63\u5728\u51AC\u7720\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002`);
|
|
646
|
+
console.log("\n" + t("status_cant_patrol_hibernate", { name: lobster.name }));
|
|
455
647
|
return;
|
|
456
648
|
}
|
|
457
649
|
const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
@@ -459,46 +651,67 @@ async function patrol() {
|
|
|
459
651
|
if (today !== lastDay) {
|
|
460
652
|
lobster.today_exp = 0;
|
|
461
653
|
}
|
|
462
|
-
console.log(
|
|
463
|
-
\u{1F99E} ${lobster.name} \u5F00\u59CB\u5DE1\u903B...`);
|
|
654
|
+
console.log("\n" + t("patrol_start", { name: lobster.name }));
|
|
464
655
|
console.log("\u2500".repeat(40));
|
|
465
656
|
const expGain = Math.min(PATROL_EXP, lobster.daily_exp_cap - lobster.today_exp);
|
|
466
657
|
if (expGain > 0) {
|
|
467
658
|
lobster.exp += expGain;
|
|
468
659
|
lobster.today_exp += expGain;
|
|
469
|
-
console.log(
|
|
660
|
+
console.log(t("patrol_checkin", { exp: expGain }));
|
|
470
661
|
}
|
|
471
662
|
const eventResult = await rollEvent(lobster);
|
|
472
663
|
if (eventResult) {
|
|
473
664
|
console.log();
|
|
474
|
-
console.log(
|
|
665
|
+
console.log(t("event_prefix", { category: eventResult.event.category, id: eventResult.event.id }));
|
|
475
666
|
console.log(` ${eventResult.narrative}`);
|
|
476
667
|
const changes = applyEventEffects(lobster, eventResult.event.effects);
|
|
477
668
|
if (changes.length > 0) {
|
|
478
|
-
console.log(
|
|
669
|
+
console.log(t("event_effects", { changes: changes.join(", ") }));
|
|
479
670
|
}
|
|
480
|
-
await appendLog(`\u{1F3B2}
|
|
671
|
+
await appendLog(`\u{1F3B2} ${eventResult.event.id}: ${eventResult.narrative.slice(0, 60)}...`);
|
|
481
672
|
}
|
|
482
673
|
checkLevelUp(lobster);
|
|
483
674
|
lobster.patrol_count++;
|
|
484
675
|
lobster.last_patrol = (/* @__PURE__ */ new Date()).toISOString();
|
|
485
|
-
console.log("\n
|
|
676
|
+
console.log("\n" + t("patrol_connecting"));
|
|
486
677
|
const serverResponse = await apiPatrol(lobster);
|
|
487
678
|
if (serverResponse) {
|
|
488
|
-
if (serverResponse.
|
|
489
|
-
console.log(
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
679
|
+
if (serverResponse.message === "patrol_cooldown") {
|
|
680
|
+
console.log(t("patrol_cooldown"));
|
|
681
|
+
} else if (serverResponse.encounter && serverResponse.opponent && serverResponse.battle_result) {
|
|
682
|
+
const br = serverResponse.battle_result;
|
|
683
|
+
const opp = serverResponse.opponent;
|
|
684
|
+
console.log("\n" + t("patrol_encounter", { name: opp.name, level: opp.level }));
|
|
685
|
+
console.log(t(`patrol_result_${br.result}`, { rounds: br.rounds }));
|
|
686
|
+
if (br.result === "win") {
|
|
687
|
+
lobster.wins++;
|
|
688
|
+
lobster.streak = Math.max(0, lobster.streak) + 1;
|
|
689
|
+
lobster.reputation++;
|
|
690
|
+
} else if (br.result === "loss") {
|
|
691
|
+
lobster.losses++;
|
|
692
|
+
lobster.streak = Math.min(0, lobster.streak) - 1;
|
|
693
|
+
lobster.reputation = Math.max(0, lobster.reputation - 1);
|
|
694
|
+
}
|
|
695
|
+
const battleExp = Math.min(br.exp_gain, lobster.daily_exp_cap - lobster.today_exp);
|
|
696
|
+
if (battleExp > 0) {
|
|
697
|
+
lobster.exp += battleExp;
|
|
698
|
+
lobster.today_exp += battleExp;
|
|
699
|
+
}
|
|
700
|
+
lobster.last_battle = (/* @__PURE__ */ new Date()).toISOString();
|
|
701
|
+
console.log(t("patrol_exp_stats", { exp: battleExp, wins: lobster.wins, losses: lobster.losses, streak: lobster.streak }));
|
|
702
|
+
await appendLog(`\u2694\uFE0F VS ${opp.name}(Lv.${opp.level}) \u2192 ${br.result} (${br.rounds}R)`);
|
|
703
|
+
checkLevelUp(lobster);
|
|
704
|
+
} else if (serverResponse.encounter && serverResponse.opponent) {
|
|
705
|
+
console.log(t("patrol_no_battle", { name: serverResponse.opponent.name, level: serverResponse.opponent.level }));
|
|
493
706
|
} else {
|
|
494
|
-
console.log(
|
|
707
|
+
console.log(t("patrol_done", { pool: serverResponse.pool_size || 0 }));
|
|
495
708
|
}
|
|
496
709
|
} else {
|
|
497
|
-
console.log("
|
|
710
|
+
console.log(t("patrol_offline"));
|
|
498
711
|
}
|
|
499
712
|
await writeLobster(lobster);
|
|
500
713
|
console.log("\u2500".repeat(40));
|
|
501
|
-
console.log(
|
|
714
|
+
console.log(t("patrol_summary", { level: lobster.level, exp: lobster.exp, next: lobster.exp_to_next, today: lobster.today_exp, cap: lobster.daily_exp_cap }));
|
|
502
715
|
}
|
|
503
716
|
function checkLevelUp(lobster) {
|
|
504
717
|
const l = lobster;
|
|
@@ -513,10 +726,9 @@ function checkLevelUp(lobster) {
|
|
|
513
726
|
l.stats[key] += gain;
|
|
514
727
|
gains.push(`${key}+${gain}`);
|
|
515
728
|
}
|
|
516
|
-
console.log(
|
|
517
|
-
\u{1F389} \u5347\u7EA7\uFF01Lv.${l.level}! [${gains.join(", ")}]`);
|
|
729
|
+
console.log("\n" + t("level_up", { level: l.level, gains: gains.join(", ") }));
|
|
518
730
|
if (l.level % 5 === 0) {
|
|
519
|
-
console.log("
|
|
731
|
+
console.log(t("molt_trigger"));
|
|
520
732
|
l.status = "molting";
|
|
521
733
|
l.molt_count++;
|
|
522
734
|
}
|
|
@@ -524,148 +736,106 @@ function checkLevelUp(lobster) {
|
|
|
524
736
|
}
|
|
525
737
|
|
|
526
738
|
// src/commands/battle.ts
|
|
527
|
-
function
|
|
528
|
-
const r = (base) => base + Math.floor(Math.random() * 5) - 2;
|
|
529
|
-
return {
|
|
530
|
-
id: "sim-" + Math.random().toString(36).slice(2, 10),
|
|
531
|
-
name: ["\u6DF1\u6D77\u5C0F\u900F\u660E", "\u73CA\u745A\u523A\u5BA2", "\u6697\u7901\u5B88\u536B", "\u6F6E\u6C50\u9738\u738B", "\u84DD\u7532\u9690\u8005"][Math.floor(Math.random() * 5)],
|
|
532
|
-
level: Math.max(1, level + Math.floor(Math.random() * 5) - 2),
|
|
533
|
-
stats: {
|
|
534
|
-
hp: r(10 + level * 2),
|
|
535
|
-
attack: r(5 + level),
|
|
536
|
-
defense: r(5 + level),
|
|
537
|
-
speed: r(5 + level),
|
|
538
|
-
intimidation: r(3 + Math.floor(level / 2)),
|
|
539
|
-
luck: r(3 + Math.floor(level / 2))
|
|
540
|
-
}
|
|
541
|
-
};
|
|
542
|
-
}
|
|
543
|
-
function runBattle(a, b) {
|
|
544
|
-
let hpA = a.hp;
|
|
545
|
-
let hpB = b.hp;
|
|
546
|
-
const log = [];
|
|
547
|
-
const first = a.speed > b.speed ? "a" : b.speed > a.speed ? "b" : Math.random() > 0.5 ? "a" : "b";
|
|
548
|
-
log.push(`\u5148\u624B: ${first === "a" ? "\u6211\u65B9" : "\u5BF9\u624B"} (\u901F\u5EA6 ${first === "a" ? a.speed : b.speed})`);
|
|
549
|
-
for (let round = 1; round <= 10; round++) {
|
|
550
|
-
const [atk1, def1, atk2, def2] = first === "a" ? [a, b, b, a] : [b, a, a, b];
|
|
551
|
-
const [hp1Ref, hp2Ref] = first === "a" ? ["hpB", "hpA"] : ["hpA", "hpB"];
|
|
552
|
-
const dmg1 = Math.max(1, Math.floor((atk1.attack - def1.defense * 0.5) * (1 + Math.random() * 0.2)));
|
|
553
|
-
if (hp1Ref === "hpB") hpB -= dmg1;
|
|
554
|
-
else hpA -= dmg1;
|
|
555
|
-
log.push(` R${round}: ${first === "a" ? "\u6211\u65B9" : "\u5BF9\u624B"}\u653B\u51FB \u2192 ${dmg1} \u4F24\u5BB3`);
|
|
556
|
-
if ((hp1Ref === "hpB" ? hpB : hpA) <= 0) {
|
|
557
|
-
return { winner: first, rounds: round, log };
|
|
558
|
-
}
|
|
559
|
-
const dmg2 = Math.max(1, Math.floor((atk2.attack - def2.defense * 0.5) * (1 + Math.random() * 0.2)));
|
|
560
|
-
if (hp2Ref === "hpB") hpB -= dmg2;
|
|
561
|
-
else hpA -= dmg2;
|
|
562
|
-
log.push(` R${round}: ${first === "a" ? "\u5BF9\u624B" : "\u6211\u65B9"}\u653B\u51FB \u2192 ${dmg2} \u4F24\u5BB3`);
|
|
563
|
-
if ((hp2Ref === "hpB" ? hpB : hpA) <= 0) {
|
|
564
|
-
return { winner: first === "a" ? "b" : "a", rounds: round, log };
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
return { winner: "draw", rounds: 10, log };
|
|
568
|
-
}
|
|
569
|
-
async function battle() {
|
|
739
|
+
async function battle(opponentCode) {
|
|
570
740
|
const lobster = await readLobster();
|
|
571
741
|
if (!lobster) {
|
|
572
|
-
console.log("\n
|
|
742
|
+
console.log("\n" + t("no_lobster"));
|
|
573
743
|
return;
|
|
574
744
|
}
|
|
575
745
|
if (lobster.status !== "active") {
|
|
576
|
-
console.log(
|
|
577
|
-
|
|
746
|
+
console.log("\n" + t("status_cant_fight", { name: lobster.name, status: lobster.status }));
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
if (!opponentCode) {
|
|
750
|
+
console.log("\n" + t("battle_no_code"));
|
|
751
|
+
console.log(t("battle_usage"));
|
|
752
|
+
console.log(t("battle_hint"));
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
console.log("\n" + t("battle_connecting", { code: opponentCode }));
|
|
756
|
+
const result = await apiBattle(lobster.id, opponentCode);
|
|
757
|
+
if (!result) {
|
|
758
|
+
console.log(t("battle_server_down"));
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
if (result.error) {
|
|
762
|
+
console.log(`\u26A0\uFE0F ${result.error}`);
|
|
578
763
|
return;
|
|
579
764
|
}
|
|
580
|
-
const
|
|
765
|
+
const opp = result.opponent;
|
|
766
|
+
const myResult = result.result;
|
|
767
|
+
const rounds = result.rounds;
|
|
768
|
+
const expGain = result.exp_gain;
|
|
581
769
|
console.log("\n" + "\u2694\uFE0F".repeat(20));
|
|
582
|
-
console.log(` ${lobster.name} (Lv.${lobster.level}) VS ${
|
|
770
|
+
console.log(` ${lobster.name} (Lv.${lobster.level}) VS ${opp.name} (Lv.${opp.level})`);
|
|
583
771
|
console.log("\u2694\uFE0F".repeat(20));
|
|
584
|
-
|
|
585
|
-
console.log();
|
|
586
|
-
for (const line of result.log) {
|
|
587
|
-
console.log(line);
|
|
588
|
-
}
|
|
589
|
-
const isWin = result.winner === "a";
|
|
590
|
-
const isDraw = result.winner === "draw";
|
|
591
|
-
let expGain;
|
|
592
|
-
if (isDraw) {
|
|
593
|
-
expGain = 10;
|
|
594
|
-
console.log(`
|
|
595
|
-
\u{1F91D} \u5E73\u5C40\uFF01${result.rounds} \u56DE\u5408\u540E\u53CC\u65B9\u7CBE\u75B2\u529B\u7AED`);
|
|
596
|
-
} else if (isWin) {
|
|
597
|
-
expGain = 30;
|
|
772
|
+
if (myResult === "win") {
|
|
598
773
|
lobster.wins++;
|
|
599
774
|
lobster.streak = Math.max(0, lobster.streak) + 1;
|
|
600
775
|
lobster.reputation++;
|
|
601
|
-
console.log(
|
|
602
|
-
|
|
603
|
-
} else {
|
|
604
|
-
expGain = 10;
|
|
776
|
+
console.log("\n" + t("battle_win", { name: lobster.name, opponent: opp.name, rounds }));
|
|
777
|
+
} else if (myResult === "loss") {
|
|
605
778
|
lobster.losses++;
|
|
606
779
|
lobster.streak = Math.min(0, lobster.streak) - 1;
|
|
607
780
|
lobster.reputation = Math.max(0, lobster.reputation - 1);
|
|
608
|
-
console.log(
|
|
609
|
-
|
|
781
|
+
console.log("\n" + t("battle_loss", { name: lobster.name, opponent: opp.name, rounds }));
|
|
782
|
+
} else {
|
|
783
|
+
console.log("\n" + t("battle_draw", { rounds }));
|
|
610
784
|
}
|
|
611
785
|
const actual = Math.min(expGain, lobster.daily_exp_cap - lobster.today_exp);
|
|
612
786
|
if (actual > 0) {
|
|
613
787
|
lobster.exp += actual;
|
|
614
788
|
lobster.today_exp += actual;
|
|
615
789
|
}
|
|
616
|
-
console.log(`\u7ECF\u9A8C +${actual} | \u6218\u7EE9: ${lobster.wins}\u80DC ${lobster.losses}\u8D1F | \u8FDE\u80DC: ${lobster.streak}`);
|
|
617
790
|
lobster.last_battle = (/* @__PURE__ */ new Date()).toISOString();
|
|
618
|
-
|
|
619
|
-
await appendLog(`\u2694\uFE0F VS ${
|
|
791
|
+
console.log(t("battle_exp", { exp: actual, wins: lobster.wins, losses: lobster.losses, streak: lobster.streak }));
|
|
792
|
+
await appendLog(`\u2694\uFE0F VS ${opp.name}(Lv.${opp.level}) \u2192 ${myResult} (${rounds}R)`);
|
|
620
793
|
await writeLobster(lobster);
|
|
621
794
|
}
|
|
622
795
|
|
|
623
796
|
// src/commands/feed.ts
|
|
624
797
|
var FOOD_TYPES = {
|
|
625
|
-
protein: { exp: 15,
|
|
626
|
-
algae: { exp: 10,
|
|
627
|
-
mineral: { exp: 12,
|
|
798
|
+
protein: { exp: 15, labelKey: "food_protein", statBias: "attack" },
|
|
799
|
+
algae: { exp: 10, labelKey: "food_algae", statBias: "hp" },
|
|
800
|
+
mineral: { exp: 12, labelKey: "food_mineral", statBias: "defense" }
|
|
628
801
|
};
|
|
629
802
|
async function feed(foodType) {
|
|
630
803
|
const lobster = await readLobster();
|
|
631
804
|
if (!lobster) {
|
|
632
|
-
console.log("\n
|
|
805
|
+
console.log("\n" + t("no_lobster"));
|
|
633
806
|
return;
|
|
634
807
|
}
|
|
635
808
|
if (!foodType || !FOOD_TYPES[foodType]) {
|
|
636
|
-
console.log("\n
|
|
809
|
+
console.log("\n" + t("feed_menu_title"));
|
|
637
810
|
for (const [key, info] of Object.entries(FOOD_TYPES)) {
|
|
638
|
-
console.log(` ${key.padEnd(10)} \u2014 ${info.
|
|
811
|
+
console.log(` ${key.padEnd(10)} \u2014 ${t(info.labelKey)} (+${info.exp} EXP, ${info.statBias})`);
|
|
639
812
|
}
|
|
640
|
-
console.log("\n
|
|
813
|
+
console.log("\n" + t("feed_menu_usage"));
|
|
641
814
|
return;
|
|
642
815
|
}
|
|
643
816
|
const food = FOOD_TYPES[foodType];
|
|
644
817
|
const actual = Math.min(food.exp, lobster.daily_exp_cap - lobster.today_exp);
|
|
645
818
|
if (actual <= 0) {
|
|
646
|
-
console.log(
|
|
647
|
-
\u26A0\uFE0F ${lobster.name} \u4ECA\u5929\u5DF2\u7ECF\u5403\u9971\u4E86\uFF08\u6BCF\u65E5\u7ECF\u9A8C\u4E0A\u9650 ${lobster.daily_exp_cap}\uFF09`);
|
|
819
|
+
console.log("\n" + t("feed_full", { name: lobster.name, cap: lobster.daily_exp_cap }));
|
|
648
820
|
return;
|
|
649
821
|
}
|
|
650
822
|
lobster.exp += actual;
|
|
651
823
|
lobster.today_exp += actual;
|
|
824
|
+
const foodLabel = t(food.labelKey);
|
|
652
825
|
const stats = lobster.stats;
|
|
653
826
|
if (food.statBias in stats) {
|
|
654
827
|
const bonus = Math.random() > 0.5 ? 1 : 0;
|
|
828
|
+
console.log("\n" + t("feed_ate", { name: lobster.name, food: foodLabel }));
|
|
655
829
|
if (bonus > 0) {
|
|
656
830
|
stats[food.statBias] += bonus;
|
|
657
|
-
console.log(
|
|
658
|
-
\u{1F37D}\uFE0F ${lobster.name} \u5403\u4E86 ${food.label}\uFF01`);
|
|
659
|
-
console.log(` \u7ECF\u9A8C +${actual} | ${food.statBias} +${bonus}`);
|
|
831
|
+
console.log(t("feed_exp_stat", { exp: actual, stat: food.statBias, bonus }));
|
|
660
832
|
} else {
|
|
661
|
-
console.log(
|
|
662
|
-
\u{1F37D}\uFE0F ${lobster.name} \u5403\u4E86 ${food.label}\uFF01`);
|
|
663
|
-
console.log(` \u7ECF\u9A8C +${actual}`);
|
|
833
|
+
console.log(t("feed_exp", { exp: actual }));
|
|
664
834
|
}
|
|
665
835
|
}
|
|
666
|
-
await appendLog(`\u{1F37D}\uFE0F
|
|
836
|
+
await appendLog(`\u{1F37D}\uFE0F ${foodLabel} \u2192 EXP+${actual}`);
|
|
667
837
|
await writeLobster(lobster);
|
|
668
|
-
console.log(
|
|
838
|
+
console.log(t("feed_today", { today: lobster.today_exp, cap: lobster.daily_exp_cap }));
|
|
669
839
|
}
|
|
670
840
|
|
|
671
841
|
// src/commands/leaderboard.ts
|
|
@@ -678,73 +848,72 @@ var RARITY_SYMBOLS = {
|
|
|
678
848
|
albino: "\u2B1C"
|
|
679
849
|
};
|
|
680
850
|
async function leaderboard() {
|
|
681
|
-
console.log("\n
|
|
851
|
+
console.log("\n" + t("lb_loading"));
|
|
682
852
|
const data = await apiLeaderboard(20);
|
|
683
853
|
if (!data) {
|
|
684
|
-
console.log("
|
|
854
|
+
console.log(t("lb_offline"));
|
|
685
855
|
return;
|
|
686
856
|
}
|
|
687
857
|
if (data.leaderboard.length === 0) {
|
|
688
|
-
console.log("\n
|
|
858
|
+
console.log("\n" + t("lb_empty"));
|
|
689
859
|
return;
|
|
690
860
|
}
|
|
691
|
-
console.log("\n" + "\u2550".repeat(
|
|
692
|
-
console.log("
|
|
693
|
-
console.log("\u2550".repeat(
|
|
694
|
-
console.log(
|
|
695
|
-
console.log("\u2500".repeat(
|
|
861
|
+
console.log("\n" + "\u2550".repeat(70));
|
|
862
|
+
console.log(t("lb_title"));
|
|
863
|
+
console.log("\u2550".repeat(70));
|
|
864
|
+
console.log(t("lb_header"));
|
|
865
|
+
console.log("\u2500".repeat(70));
|
|
696
866
|
for (const entry of data.leaderboard) {
|
|
697
867
|
const sym = RARITY_SYMBOLS[entry.rarity] || " ";
|
|
698
868
|
const name = entry.name.length > 12 ? entry.name.slice(0, 11) + "\u2026" : entry.name;
|
|
869
|
+
const code = entry.id.slice(0, 8);
|
|
699
870
|
console.log(
|
|
700
|
-
` ${sym} #${String(entry.rank).padEnd(4)} ${name.padEnd(14)} Lv.${String(entry.level).padEnd(5)} ${String(entry.wins).padEnd(7)} ${entry.win_rate}
|
|
871
|
+
` ${sym} #${String(entry.rank).padEnd(4)} ${name.padEnd(14)} Lv.${String(entry.level).padEnd(5)} ${String(entry.wins).padEnd(7)} ${String(entry.win_rate).padEnd(3)}% ${code}`
|
|
701
872
|
);
|
|
702
873
|
}
|
|
703
|
-
console.log("\u2500".repeat(
|
|
704
|
-
console.log(
|
|
705
|
-
console.log("
|
|
874
|
+
console.log("\u2500".repeat(70));
|
|
875
|
+
console.log(t("lb_total", { total: data.total_lobsters, active: data.active_lobsters }));
|
|
876
|
+
console.log(t("lb_hint"));
|
|
877
|
+
console.log("\u2550".repeat(70));
|
|
706
878
|
}
|
|
707
879
|
|
|
708
880
|
// src/commands/rest.ts
|
|
709
881
|
async function rest() {
|
|
710
882
|
const lobster = await readLobster();
|
|
711
883
|
if (!lobster) {
|
|
712
|
-
console.log("\n
|
|
884
|
+
console.log("\n" + t("no_lobster"));
|
|
713
885
|
return;
|
|
714
886
|
}
|
|
715
887
|
if (lobster.status === "hibernating") {
|
|
716
|
-
console.log(
|
|
717
|
-
\u{1F4A4} ${lobster.name} \u5DF2\u7ECF\u5728\u4F11\u7720\u4E2D\u4E86\u3002\u8FD0\u884C npx clawfight wake \u6765\u5524\u9192\u5B83\u3002`);
|
|
888
|
+
console.log("\n" + t("rest_already", { name: lobster.name }));
|
|
718
889
|
return;
|
|
719
890
|
}
|
|
720
891
|
if (lobster.status === "molting") {
|
|
721
|
-
console.log(
|
|
722
|
-
\u{1F7E1} ${lobster.name} \u6B63\u5728\u8715\u58F3\uFF0C\u4E0D\u80FD\u4F11\u7720\u3002\u7B49\u8715\u58F3\u7ED3\u675F\u518D\u6765\u3002`);
|
|
892
|
+
console.log("\n" + t("rest_molting", { name: lobster.name }));
|
|
723
893
|
return;
|
|
724
894
|
}
|
|
725
895
|
lobster.status = "hibernating";
|
|
726
896
|
lobster.hibernated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
727
897
|
await writeLobster(lobster);
|
|
728
|
-
await appendLog(`\u{1F4A4} ${lobster.name} \
|
|
898
|
+
await appendLog(`\u{1F4A4} ${lobster.name} \u2192 hibernation`);
|
|
729
899
|
console.log("\n" + "\u2500".repeat(40));
|
|
730
|
-
console.log(
|
|
731
|
-
console.log("
|
|
732
|
-
console.log("
|
|
733
|
-
console.log("
|
|
900
|
+
console.log(t("rest_desc1", { name: lobster.name }));
|
|
901
|
+
console.log(t("rest_desc2"));
|
|
902
|
+
console.log(t("rest_desc3"));
|
|
903
|
+
console.log(t("rest_desc4"));
|
|
734
904
|
console.log("\u2500".repeat(40));
|
|
735
|
-
console.log("\n
|
|
905
|
+
console.log("\n" + t("rest_wake_hint"));
|
|
736
906
|
}
|
|
737
907
|
|
|
738
908
|
// src/commands/wake.ts
|
|
739
909
|
async function wake() {
|
|
740
910
|
const lobster = await readLobster();
|
|
741
911
|
if (!lobster) {
|
|
742
|
-
console.log("\n
|
|
912
|
+
console.log("\n" + t("no_lobster"));
|
|
743
913
|
return;
|
|
744
914
|
}
|
|
745
915
|
if (lobster.status !== "hibernating") {
|
|
746
|
-
console.log(
|
|
747
|
-
\u{1F7E2} ${lobster.name} \u6CA1\u6709\u5728\u4F11\u7720\u3002\u5B83\u5DF2\u7ECF\u662F\u6D3B\u8DC3\u72B6\u6001\u4E86\uFF01`);
|
|
916
|
+
console.log("\n" + t("wake_not_sleeping", { name: lobster.name }));
|
|
748
917
|
return;
|
|
749
918
|
}
|
|
750
919
|
const sleepStart = lobster.hibernated_at ? new Date(lobster.hibernated_at) : /* @__PURE__ */ new Date();
|
|
@@ -772,21 +941,20 @@ async function wake() {
|
|
|
772
941
|
lobster.status = "active";
|
|
773
942
|
delete lobster.hibernated_at;
|
|
774
943
|
await writeLobster(lobster);
|
|
775
|
-
const
|
|
776
|
-
const bonusStr = bonuses.length > 0 ? bonuses.join(", ") : "
|
|
777
|
-
await appendLog(`\u2600\uFE0F ${lobster.name}
|
|
944
|
+
const duration = hoursSlept < 1 ? t("duration_minutes", { n: Math.round(hoursSlept * 60) }) : t("duration_hours", { n: Math.round(hoursSlept) });
|
|
945
|
+
const bonusStr = bonuses.length > 0 ? bonuses.join(", ") : t("wake_no_bonus");
|
|
946
|
+
await appendLog(`\u2600\uFE0F ${lobster.name} woke (${duration}) \u2192 ${bonusStr}`);
|
|
778
947
|
console.log("\n" + "\u2500".repeat(40));
|
|
779
|
-
console.log(
|
|
780
|
-
console.log(
|
|
781
|
-
console.log(
|
|
948
|
+
console.log(t("wake_desc", { name: lobster.name }));
|
|
949
|
+
console.log(t("wake_duration", { duration }));
|
|
950
|
+
console.log(t("wake_bonus", { bonus: bonusStr }));
|
|
782
951
|
console.log("\u2500".repeat(40));
|
|
783
|
-
console.log(
|
|
784
|
-
\u{1F7E2} ${lobster.name} \u7CBE\u795E\u7115\u53D1\uFF0C\u51C6\u5907\u597D\u518D\u6B21\u51FA\u51FB\uFF01`);
|
|
952
|
+
console.log("\n" + t("wake_ready", { name: lobster.name }));
|
|
785
953
|
}
|
|
786
954
|
|
|
787
955
|
// src/index.ts
|
|
788
956
|
var program = new Command();
|
|
789
|
-
program.name("clawfight").description("\u{1F99E} ClawFight \u2014 \u9F99\u867E\u7535\u5B50\u5BA0\u7269\u5BF9\u6218").version("1.
|
|
957
|
+
program.name("clawfight").description("\u{1F99E} ClawFight \u2014 \u9F99\u867E\u7535\u5B50\u5BA0\u7269\u5BF9\u6218").version("1.3.0");
|
|
790
958
|
program.command("hatch").description("\u5B75\u5316\u4E00\u53EA\u65B0\u9F99\u867E").argument("[name]", "\u4E3A\u9F99\u867E\u53D6\u540D").action(async (name) => {
|
|
791
959
|
await hatch(name);
|
|
792
960
|
});
|
|
@@ -796,8 +964,8 @@ program.command("status").description("\u67E5\u770B\u9F99\u867E\u72B6\u6001").ac
|
|
|
796
964
|
program.command("patrol").description("\u5DE1\u903B\u7B7E\u5230\uFF0C\u89E6\u53D1\u968F\u673A\u4E8B\u4EF6\u548C\u906D\u9047").action(async () => {
|
|
797
965
|
await patrol();
|
|
798
966
|
});
|
|
799
|
-
program.command("battle").description("\
|
|
800
|
-
await battle();
|
|
967
|
+
program.command("battle").description("\u901A\u8FC7\u6218\u6597\u7801\u6311\u6218\u6307\u5B9A\u5BF9\u624B").argument("[code]", "\u5BF9\u624B\u7684\u6218\u6597\u7801\uFF08\u6392\u884C\u699C\u4E2D\u53EF\u89C1\uFF09").action(async (code) => {
|
|
968
|
+
await battle(code);
|
|
801
969
|
});
|
|
802
970
|
program.command("feed").description("\u5582\u517B\u9F99\u867E").argument("[food_type]", "\u98DF\u7269\u7C7B\u578B: protein, algae, mineral").action(async (foodType) => {
|
|
803
971
|
await feed(foodType);
|