@2025-6-19/clawfight 1.1.4 → 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 +398 -145
- 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,36 +848,113 @@ 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));
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
// src/commands/rest.ts
|
|
881
|
+
async function rest() {
|
|
882
|
+
const lobster = await readLobster();
|
|
883
|
+
if (!lobster) {
|
|
884
|
+
console.log("\n" + t("no_lobster"));
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
if (lobster.status === "hibernating") {
|
|
888
|
+
console.log("\n" + t("rest_already", { name: lobster.name }));
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (lobster.status === "molting") {
|
|
892
|
+
console.log("\n" + t("rest_molting", { name: lobster.name }));
|
|
893
|
+
return;
|
|
894
|
+
}
|
|
895
|
+
lobster.status = "hibernating";
|
|
896
|
+
lobster.hibernated_at = (/* @__PURE__ */ new Date()).toISOString();
|
|
897
|
+
await writeLobster(lobster);
|
|
898
|
+
await appendLog(`\u{1F4A4} ${lobster.name} \u2192 hibernation`);
|
|
899
|
+
console.log("\n" + "\u2500".repeat(40));
|
|
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"));
|
|
904
|
+
console.log("\u2500".repeat(40));
|
|
905
|
+
console.log("\n" + t("rest_wake_hint"));
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
// src/commands/wake.ts
|
|
909
|
+
async function wake() {
|
|
910
|
+
const lobster = await readLobster();
|
|
911
|
+
if (!lobster) {
|
|
912
|
+
console.log("\n" + t("no_lobster"));
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
if (lobster.status !== "hibernating") {
|
|
916
|
+
console.log("\n" + t("wake_not_sleeping", { name: lobster.name }));
|
|
917
|
+
return;
|
|
918
|
+
}
|
|
919
|
+
const sleepStart = lobster.hibernated_at ? new Date(lobster.hibernated_at) : /* @__PURE__ */ new Date();
|
|
920
|
+
const hoursSlept = Math.max(0, (Date.now() - sleepStart.getTime()) / (1e3 * 60 * 60));
|
|
921
|
+
const bonuses = [];
|
|
922
|
+
const stats = lobster.stats;
|
|
923
|
+
if (hoursSlept >= 24) {
|
|
924
|
+
stats.hp += 2;
|
|
925
|
+
stats.defense += 1;
|
|
926
|
+
const expBonus = Math.min(20, lobster.daily_exp_cap - lobster.today_exp);
|
|
927
|
+
if (expBonus > 0) {
|
|
928
|
+
lobster.exp += expBonus;
|
|
929
|
+
lobster.today_exp += expBonus;
|
|
930
|
+
bonuses.push(`EXP +${expBonus}`);
|
|
931
|
+
}
|
|
932
|
+
bonuses.push("HP +2", "DEF +1");
|
|
933
|
+
} else if (hoursSlept >= 12) {
|
|
934
|
+
stats.hp += 1;
|
|
935
|
+
stats.defense += 1;
|
|
936
|
+
bonuses.push("HP +1", "DEF +1");
|
|
937
|
+
} else if (hoursSlept >= 4) {
|
|
938
|
+
stats.hp += 1;
|
|
939
|
+
bonuses.push("HP +1");
|
|
940
|
+
}
|
|
941
|
+
lobster.status = "active";
|
|
942
|
+
delete lobster.hibernated_at;
|
|
943
|
+
await writeLobster(lobster);
|
|
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}`);
|
|
947
|
+
console.log("\n" + "\u2500".repeat(40));
|
|
948
|
+
console.log(t("wake_desc", { name: lobster.name }));
|
|
949
|
+
console.log(t("wake_duration", { duration }));
|
|
950
|
+
console.log(t("wake_bonus", { bonus: bonusStr }));
|
|
951
|
+
console.log("\u2500".repeat(40));
|
|
952
|
+
console.log("\n" + t("wake_ready", { name: lobster.name }));
|
|
706
953
|
}
|
|
707
954
|
|
|
708
955
|
// src/index.ts
|
|
709
956
|
var program = new Command();
|
|
710
|
-
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");
|
|
711
958
|
program.command("hatch").description("\u5B75\u5316\u4E00\u53EA\u65B0\u9F99\u867E").argument("[name]", "\u4E3A\u9F99\u867E\u53D6\u540D").action(async (name) => {
|
|
712
959
|
await hatch(name);
|
|
713
960
|
});
|
|
@@ -717,8 +964,8 @@ program.command("status").description("\u67E5\u770B\u9F99\u867E\u72B6\u6001").ac
|
|
|
717
964
|
program.command("patrol").description("\u5DE1\u903B\u7B7E\u5230\uFF0C\u89E6\u53D1\u968F\u673A\u4E8B\u4EF6\u548C\u906D\u9047").action(async () => {
|
|
718
965
|
await patrol();
|
|
719
966
|
});
|
|
720
|
-
program.command("battle").description("\
|
|
721
|
-
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);
|
|
722
969
|
});
|
|
723
970
|
program.command("feed").description("\u5582\u517B\u9F99\u867E").argument("[food_type]", "\u98DF\u7269\u7C7B\u578B: protein, algae, mineral").action(async (foodType) => {
|
|
724
971
|
await feed(foodType);
|
|
@@ -726,4 +973,10 @@ program.command("feed").description("\u5582\u517B\u9F99\u867E").argument("[food_
|
|
|
726
973
|
program.command("leaderboard").alias("lb").description("\u67E5\u770B\u5168\u7403\u6392\u884C\u699C").action(async () => {
|
|
727
974
|
await leaderboard();
|
|
728
975
|
});
|
|
976
|
+
program.command("rest").description("\u8FDB\u5165\u4F11\u7720\uFF08\u6682\u505C\u5DE1\u903B\u548C\u6218\u6597\uFF09").action(async () => {
|
|
977
|
+
await rest();
|
|
978
|
+
});
|
|
979
|
+
program.command("wake").description("\u4ECE\u4F11\u7720\u4E2D\u5524\u9192\uFF08\u9644\u5E26\u6062\u590D\u52A0\u6210\uFF09").action(async () => {
|
|
980
|
+
await wake();
|
|
981
|
+
});
|
|
729
982
|
program.parse();
|