@2025-6-19/clawfight 1.2.0 → 1.5.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.
Files changed (2) hide show
  1. package/dist/index.js +1383 -181
  2. 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");
@@ -141,6 +148,29 @@ ${archetype.values}
141
148
  }
142
149
 
143
150
  // src/lib/types.ts
151
+ var EQUIP_RARITY_WEIGHTS = {
152
+ common: 60,
153
+ rare: 25,
154
+ epic: 12,
155
+ legendary: 3
156
+ };
157
+ var EQUIP_RARITY_LABELS = {
158
+ common: "\u666E\u901A",
159
+ rare: "\u7A00\u6709",
160
+ epic: "\u53F2\u8BD7",
161
+ legendary: "\u4F20\u8BF4"
162
+ };
163
+ var SLOT_LABELS = {
164
+ claw: "\u94B3",
165
+ shell: "\u58F3",
166
+ charm: "\u9970"
167
+ };
168
+ var SLOT_ICONS = {
169
+ claw: "\u2694\uFE0F",
170
+ shell: "\u{1F6E1}\uFE0F",
171
+ charm: "\u{1F48E}"
172
+ };
173
+ var MAX_INVENTORY = 6;
144
174
  var RARITY_WEIGHTS = {
145
175
  common: 70,
146
176
  calico: 20,
@@ -161,6 +191,413 @@ function calcExpToNext(level) {
161
191
  return Math.floor(100 * Math.pow(1.2, level - 1));
162
192
  }
163
193
 
194
+ // src/lib/i18n.ts
195
+ var zh = {
196
+ no_lobster: "\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01",
197
+ status_cant_fight: "\u26A0\uFE0F {name} \u5F53\u524D\u72B6\u6001\u4E3A {status}\uFF0C\u65E0\u6CD5\u6218\u6597\u3002",
198
+ status_cant_patrol_molt: "\u{1F7E1} {name} \u6B63\u5728\u8715\u58F3\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002",
199
+ status_cant_patrol_hibernate: "\u{1F4A4} {name} \u6B63\u5728\u51AC\u7720\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002",
200
+ patrol_start: "\u{1F99E} {name} \u5F00\u59CB\u5DE1\u903B...",
201
+ patrol_checkin: "\u{1F4CD} \u5DE1\u903B\u7B7E\u5230 \u2192 \u7ECF\u9A8C +{exp}",
202
+ patrol_connecting: "\u{1F4E1} \u8FDE\u63A5\u670D\u52A1\u5668...",
203
+ patrol_cooldown: "\u23F3 \u5DE1\u903B\u51B7\u5374\u4E2D\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\uFF08\u95F4\u9694\u81F3\u5C11 30 \u5206\u949F\uFF09",
204
+ patrol_encounter: "\u2694\uFE0F \u906D\u9047 {name} (Lv.{level})\uFF01",
205
+ patrol_result_win: " \u6218\u6597\u7ED3\u679C: \u{1F3C6} \u80DC\u5229 ({rounds} \u56DE\u5408)",
206
+ patrol_result_loss: " \u6218\u6597\u7ED3\u679C: \u{1F480} \u5931\u8D25 ({rounds} \u56DE\u5408)",
207
+ patrol_result_draw: " \u6218\u6597\u7ED3\u679C: \u{1F91D} \u5E73\u5C40 ({rounds} \u56DE\u5408)",
208
+ patrol_exp_stats: " \u7ECF\u9A8C +{exp} | \u6218\u7EE9: {wins}\u80DC {losses}\u8D1F | \u8FDE\u80DC: {streak}",
209
+ patrol_no_battle: "\u2694\uFE0F \u906D\u9047 {name} (Lv.{level})\uFF0C\u4F46\u672A\u80FD\u5B8C\u6210\u6218\u6597",
210
+ patrol_done: "\u2705 \u5DE1\u903B\u5B8C\u6210\uFF0C\u5339\u914D\u6C60: {pool} \u53EA\u9F99\u867E",
211
+ patrol_offline: "\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u8DF3\u8FC7\u5728\u7EBF\u7B7E\u5230",
212
+ patrol_summary: "\u{1F4CA} \u5F53\u524D: Lv.{level} | EXP: {exp}/{next} | \u4ECA\u65E5EXP: {today}/{cap}",
213
+ battle_no_code: "\u26A0\uFE0F \u8BF7\u6307\u5B9A\u5BF9\u624B\u7684\u6218\u6597\u7801\u3002",
214
+ battle_usage: " \u7528\u6CD5: npx clawfight battle <\u6218\u6597\u7801>",
215
+ battle_hint: " \u6218\u6597\u7801\u53EF\u4ECE\u6392\u884C\u699C\u4E2D\u83B7\u53D6: npx clawfight leaderboard",
216
+ battle_connecting: "\u{1F4E1} \u5411\u670D\u52A1\u5668\u53D1\u8D77\u6311\u6218 [{code}]...",
217
+ battle_server_down: "\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u65E0\u6CD5\u8FDB\u884C\u6218\u6597\u3002",
218
+ battle_win: "\u{1F3C6} \u80DC\u5229\uFF01{name} \u5728 {rounds} \u56DE\u5408\u540E\u51FB\u8D25\u4E86 {opponent}\uFF01",
219
+ battle_loss: "\u{1F480} \u5931\u8D25\u2026{name} \u5728 {rounds} \u56DE\u5408\u540E\u88AB {opponent} \u51FB\u8D25\u3002",
220
+ battle_draw: "\u{1F91D} \u5E73\u5C40\uFF01{rounds} \u56DE\u5408\u540E\u53CC\u65B9\u7CBE\u75B2\u529B\u7AED",
221
+ battle_exp: "\u7ECF\u9A8C +{exp} | \u6218\u7EE9: {wins}\u80DC {losses}\u8D1F | \u8FDE\u80DC: {streak}",
222
+ lb_loading: "\u{1F4E1} \u83B7\u53D6\u5168\u7403\u6392\u884C\u699C...",
223
+ lb_offline: "\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u65E0\u6CD5\u83B7\u53D6\u6392\u884C\u699C\u3002",
224
+ lb_empty: "\u{1F99E} \u8FD8\u6CA1\u6709\u9F99\u867E\u4E0A\u699C\u3002\u6210\u4E3A\u7B2C\u4E00\u4E2A\u5427\uFF01",
225
+ lb_title: " \u{1F99E} ClawFight \u5168\u7403\u6392\u884C\u699C",
226
+ lb_header: " \u6392\u540D \u540D\u79F0 \u7B49\u7EA7 \u80DC\u573A \u80DC\u7387 \u6218\u6597\u7801",
227
+ lb_total: " \u603B\u9F99\u867E: {total} | \u6D3B\u8DC3: {active}",
228
+ lb_hint: " \u{1F4A1} \u4F7F\u7528 npx clawfight battle <\u6218\u6597\u7801> \u6311\u6218\u6307\u5B9A\u5BF9\u624B",
229
+ status_battle_code: "\u{1F3AF} \u6218\u6597\u7801: {code}",
230
+ event_prefix: "\u{1F3B2} [{category}] {id}",
231
+ event_effects: " \u6548\u679C: {changes}",
232
+ level_up: "\u{1F389} \u5347\u7EA7\uFF01Lv.{level}! [{gains}]",
233
+ molt_trigger: "\u{1F41A} \u89E6\u53D1\u8715\u58F3\u4E8B\u4EF6\uFF01\u9F99\u867E\u8FDB\u5165\u8715\u58F3\u72B6\u6001...",
234
+ hatch_exists: "\u{1F99E} \u4F60\u5DF2\u7ECF\u6709\u4E00\u53EA\u9F99\u867E\u4E86\uFF1A{name}\uFF08Lv.{level}\uFF09",
235
+ hatch_one_only: "\u4E00\u4EBA\u4E00\u867E\uFF0C\u4E0D\u53EF\u66FF\u4EE3\u3002",
236
+ hatch_success: " \u{1F95A} \u2192 \u{1F99E} \u5B75 \u5316 \u6210 \u529F \uFF01",
237
+ hatch_env: " \u73AF\u5883: \u6CBF\u6D77\u6D45\u6EE9",
238
+ hatch_ready: "\u4F60\u7684\u9F99\u867E\u5DF2\u7ECF\u51C6\u5907\u597D\u63A2\u7D22\u6D77\u6D0B\u4E86\uFF01",
239
+ hatch_next: "\u8FD0\u884C npx clawfight patrol \u5F00\u59CB\u7B2C\u4E00\u6B21\u5DE1\u903B\u3002",
240
+ feed_menu_title: "\u{1F37D}\uFE0F \u53EF\u7528\u98DF\u7269\u7C7B\u578B:",
241
+ feed_menu_usage: "\u7528\u6CD5: npx clawfight feed <food_type>",
242
+ feed_full: "\u26A0\uFE0F {name} \u4ECA\u5929\u5DF2\u7ECF\u5403\u9971\u4E86\uFF08\u6BCF\u65E5\u7ECF\u9A8C\u4E0A\u9650 {cap}\uFF09",
243
+ feed_ate: "\u{1F37D}\uFE0F {name} \u5403\u4E86 {food}\uFF01",
244
+ feed_exp: " \u7ECF\u9A8C +{exp}",
245
+ feed_exp_stat: " \u7ECF\u9A8C +{exp} | {stat} +{bonus}",
246
+ feed_today: " \u4ECA\u65E5\u7ECF\u9A8C: {today}/{cap}",
247
+ food_protein: "\u9AD8\u86CB\u767D\u98DF\u7269",
248
+ food_algae: "\u85FB\u7C7B\u98DF\u7269",
249
+ food_mineral: "\u77FF\u7269\u8D28",
250
+ rest_already: "\u{1F4A4} {name} \u5DF2\u7ECF\u5728\u4F11\u7720\u4E2D\u4E86\u3002\u8FD0\u884C npx clawfight wake \u6765\u5524\u9192\u5B83\u3002",
251
+ rest_molting: "\u{1F7E1} {name} \u6B63\u5728\u8715\u58F3\uFF0C\u4E0D\u80FD\u4F11\u7720\u3002\u7B49\u8715\u58F3\u7ED3\u675F\u518D\u6765\u3002",
252
+ rest_desc1: "\u{1F4A4} {name} \u7F13\u7F13\u6C89\u5165\u6D77\u5E95\u6C99\u5730...",
253
+ rest_desc2: " \u9F99\u867E\u8737\u7F29\u8D77\u89E6\u987B\uFF0C\u5B89\u9759\u5730\u4F11\u606F\u3002",
254
+ rest_desc3: " \u4F11\u7720\u671F\u95F4\u4E0D\u4F1A\u53C2\u4E0E\u5DE1\u903B\u548C\u6218\u6597\u3002",
255
+ rest_desc4: " \u9192\u6765\u540E\u4F1A\u83B7\u5F97\u6062\u590D\u52A0\u6210\uFF01",
256
+ rest_wake_hint: "\u8FD0\u884C npx clawfight wake \u6765\u5524\u9192\u5B83\u3002",
257
+ wake_not_sleeping: "\u{1F7E2} {name} \u6CA1\u6709\u5728\u4F11\u7720\u3002\u5B83\u5DF2\u7ECF\u662F\u6D3B\u8DC3\u72B6\u6001\u4E86\uFF01",
258
+ wake_desc: "\u2600\uFE0F {name} \u4ECE\u6C99\u5730\u4E2D\u7F13\u7F13\u82CF\u9192...",
259
+ wake_duration: " \u4F11\u7720\u65F6\u957F: {duration}",
260
+ wake_bonus: " \u6062\u590D\u52A0\u6210: {bonus}",
261
+ wake_no_bonus: "\uFF08\u4F11\u7720\u4E0D\u8DB3 4 \u5C0F\u65F6\uFF0C\u65E0\u52A0\u6210\uFF09",
262
+ wake_ready: "\u{1F7E2} {name} \u7CBE\u795E\u7115\u53D1\uFF0C\u51C6\u5907\u597D\u518D\u6B21\u51FA\u51FB\uFF01",
263
+ duration_minutes: "{n} \u5206\u949F",
264
+ duration_hours: "{n} \u5C0F\u65F6",
265
+ depth_display: "\u6DF1\u5EA6:{depth}",
266
+ soul_trait: " \u6027\u683C\u7279\u8D28: [{trait}]",
267
+ equip_drop: "\u{1F381} \u83B7\u5F97: {item}",
268
+ auto_equip: "\u{1F99E} {name}\uFF08{reason}\uFF09\u2192 \u81EA\u52A8\u88C5\u5907 {item}",
269
+ auto_swap: "\u{1F99E} {name}\uFF08{reason}\uFF09\u2192 \u6362\u4E0B {old}\uFF0C\u88C5\u5907 {item}",
270
+ auto_discard: "\u{1F99E} {name} \u4E22\u5F03\u4E86 {item}\uFF08{reason}\uFF09",
271
+ equip_degrade: "\u{1F527} \u78E8\u635F: {items}",
272
+ equip_broken: "\u{1F4A5} \u635F\u574F: {items}",
273
+ equip_inv_full: "\u{1F4E6} \u80CC\u5305\u5DF2\u6EE1({max})\uFF0C\u4E22\u5F03\u4E86 {item}",
274
+ equip_title: "\u{1F392} \u88C5\u5907\u7BA1\u7406",
275
+ equip_slot_empty: " {icon} {slot}: (\u7A7A)",
276
+ equip_slot_item: " {icon} {slot}: {item}",
277
+ equip_inv_title: "\u{1F4E6} \u80CC\u5305 ({count}/{max}):",
278
+ equip_inv_item: " [{index}] {item}",
279
+ equip_inv_empty: " (\u7A7A)",
280
+ equip_equipped: "\u2705 \u5DF2\u88C5\u5907 {item} \u2192 {slot}",
281
+ equip_swapped: "\u{1F504} \u66FF\u6362 {old} \u2192 {item}",
282
+ equip_dropped: "\u{1F5D1}\uFE0F \u5DF2\u4E22\u5F03 {item}",
283
+ equip_unequipped: "\u{1F4E4} \u5DF2\u5378\u4E0B {item}",
284
+ equip_bad_index: "\u26A0\uFE0F \u65E0\u6548\u7F16\u53F7\u3002\u67E5\u770B\u80CC\u5305: npx clawfight equip",
285
+ equip_usage: "\u7528\u6CD5: equip / equip <\u7F16\u53F7> / equip drop <\u7F16\u53F7> / equip unequip <\u69FD\u4F4D>",
286
+ achieve_unlock: "\u{1F3C6} \u6210\u5C31\u89E3\u9501: {name} \u2014 {desc}",
287
+ achieve_title: "\u{1F3C6} \u6210\u5C31 ({count}/{total}):",
288
+ achieve_item: " \u2705 {name} \u2014 {desc}",
289
+ achieve_locked: " \u{1F512} {name} \u2014 {desc}",
290
+ achieve_none: " \u8FD8\u6CA1\u6709\u89E3\u9501\u4EFB\u4F55\u6210\u5C31\u3002\u7EE7\u7EED\u5192\u9669\u5427\uFF01",
291
+ patrol_depth: "\u{1F4CA} Lv.{level} | EXP:{exp}/{next} | \u4ECA\u65E5:{today}/{cap} | \u6DF1\u5EA6:{depth} | \u80CC\u5305:{inv}/{inv_max}",
292
+ depth_reset: "\u{1F4A4} \u6DF1\u5EA6\u5DF2\u91CD\u7F6E",
293
+ dg_connecting: "\u{1F4E1} \u8FDE\u63A5\u5730\u4E0B\u57CE\u670D\u52A1\u5668...",
294
+ dg_resume: "\u{1F3F0} \u68C0\u6D4B\u5230\u8FDB\u884C\u4E2D\u7684\u5730\u4E0B\u57CE\u63A2\u7D22\uFF1A",
295
+ dg_no_maps: "\u{1F5FA}\uFE0F \u6CA1\u6709\u5730\u4E0B\u57CE\u5730\u56FE\u3002\u7EE7\u7EED\u5DE1\u903B\u83B7\u53D6\u5730\u56FE\uFF01",
296
+ dg_bad_map_index: "\u26A0\uFE0F \u65E0\u6548\u5730\u56FE\u7F16\u53F7\uFF08\u5171{count}\u5F20\uFF09",
297
+ dg_entering: "\u{1F3F0} \u8FDB\u5165\u5730\u4E0B\u57CE: {theme} | {rooms}\u623F\u95F4 | {diff}",
298
+ dg_no_active: "\u26A0\uFE0F \u6CA1\u6709\u8FDB\u884C\u4E2D\u7684\u5730\u4E0B\u57CE\u3002\u5148\u7528 explore \u8FDB\u5165\u4E00\u4E2A\u3002",
299
+ dg_complete: "\u{1F389} \u5730\u4E0B\u57CE\u901A\u5173\uFF01",
300
+ dg_failed: "\u{1F480} \u5730\u4E0B\u57CE\u63A2\u7D22\u5931\u8D25\u2026HP\u5F52\u96F6",
301
+ dg_rewards_exp: "\u{1F4CA} \u5730\u4E0B\u57CE\u7ECF\u9A8C: +{exp}",
302
+ dg_abandoning: "\u{1F3F3}\uFE0F \u653E\u5F03\u5730\u4E0B\u57CE\u63A2\u7D22...",
303
+ dg_abandoned: "\u{1F3F3}\uFE0F \u5DF2\u64A4\u9000 | EXP+{exp} | \u6218\u5229\u54C1\xD7{loot} | \u6DF1\u5EA6\u2192{depth}",
304
+ dg_maps_title: "\u{1F5FA}\uFE0F \u5730\u4E0B\u57CE\u5730\u56FE\uFF08{count}\u5F20\uFF09:",
305
+ dg_rooms: "\u623F\u95F4",
306
+ dg_maps_hint: " \u{1F4A1} \u7528\u6CD5: explore <\u7F16\u53F7> \u8FDB\u5165 | explore maps \u67E5\u770B",
307
+ dg_map_found: "\u{1F5FA}\uFE0F \u53D1\u73B0\u5730\u4E0B\u57CE\u5730\u56FE: {theme} ({rooms}\u623F\u95F4, {diff})",
308
+ dg_summary: "\u{1F4CA} Lv.{level} | EXP:{exp}/{next} | \u4ECA\u65E5:{today}/{cap}",
309
+ dg_theme_coral_maze: "\u73CA\u745A\u8FF7\u5BAB",
310
+ dg_theme_deep_rift: "\u6DF1\u6D77\u88C2\u9699",
311
+ dg_theme_thermal_vent: "\u70ED\u6CC9\u55B7\u53E3",
312
+ dg_theme_ice_cavern: "\u51B0\u6676\u6D1E\u7A74",
313
+ dg_theme_shipwreck: "\u6C89\u8239\u9057\u8FF9",
314
+ dg_theme_abyss_trench: "\u6DF1\u6E0A\u6D77\u6C9F",
315
+ dg_theme_tide_pool: "\u6F6E\u6C50\u6D45\u6EE9",
316
+ dg_theme_void_rift: "\u865A\u7A7A\u88C2\u7F1D",
317
+ dg_risk_low: "\u4F4E\u98CE\u9669",
318
+ dg_risk_mid: "\u4E2D\u98CE\u9669",
319
+ dg_risk_high: "\u9AD8\u98CE\u9669",
320
+ dg_diff_easy: "\u7B80\u5355",
321
+ dg_diff_normal: "\u666E\u901A",
322
+ dg_diff_hard: "\u56F0\u96BE",
323
+ dg_diff_nightmare: "\u5669\u68A6",
324
+ dg_charge_attack: "\u731B\u529B\u51B2\u51FB",
325
+ dg_defensive_stance: "\u9632\u5FA1\u59FF\u6001",
326
+ dg_rush_strike: "\u95EA\u7535\u7A81\u88AD",
327
+ dg_counter_wait: "\u4F3A\u673A\u53CD\u51FB",
328
+ dg_open_golden_chest: "\u6253\u5F00\u91D1\u8272\u5B9D\u7BB1",
329
+ dg_open_stone_chest: "\u6253\u5F00\u77F3\u8D28\u5B9D\u7BB1",
330
+ dg_reach_deep_cache: "\u63A2\u7D22\u6DF1\u5C42\u5B9D\u85CF",
331
+ dg_take_surface_loot: "\u62FE\u53D6\u8868\u5C42\u6218\u5229\u54C1",
332
+ dg_disarm_mechanism: "\u62C6\u89E3\u673A\u5173",
333
+ dg_dodge_through: "\u7075\u5DE7\u95EA\u907F",
334
+ dg_brute_force: "\u86EE\u529B\u7A81\u7834",
335
+ dg_careful_bypass: "\u5C0F\u5FC3\u7ED5\u884C",
336
+ dg_deep_rest: "\u6DF1\u5EA6\u4F11\u606F",
337
+ dg_search_hidden: "\u641C\u7D22\u9690\u85CF\u7269",
338
+ dg_investigate: "\u8C03\u67E5\u7EBF\u7D22",
339
+ dg_proceed_cautiously: "\u8C28\u614E\u524D\u884C",
340
+ dg_touch_artifact: "\u89E6\u78B0\u795E\u5668",
341
+ dg_observe_only: "\u4EC5\u89C2\u5BDF",
342
+ dg_all_out_assault: "\u5168\u529B\u8FDB\u653B",
343
+ dg_tactical_approach: "\u7B56\u7565\u63A5\u8FD1",
344
+ dg_bravery_bonus: "\u52C7\u6C14\u52A0\u6210: ATK+20%",
345
+ dg_curiosity_bonus: "\u597D\u5947\u5FC3\u52A0\u6210: \u53D1\u73B0\u7387\u2191",
346
+ dg_temper_bonus: "\u66B4\u6012\u52A0\u6210: \u62C6\u89E3\u529B\u2191",
347
+ dg_charge_attack_success: "\u731B\u529B\u51B2\u51FB\u547D\u4E2D\uFF01",
348
+ dg_charge_attack_fail: "\u731B\u529B\u51B2\u51FB\u843D\u7A7A\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
349
+ dg_defensive_stance_success: "\u9632\u5FA1\u59FF\u6001\u6210\u529F\uFF01\u5B89\u5168\u5EA6\u8FC7",
350
+ dg_defensive_stance_fail: "\u9632\u5FA1\u59FF\u6001\u88AB\u7A81\u7834\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
351
+ dg_rush_strike_success: "\u95EA\u7535\u7A81\u88AD\u5F97\u624B\uFF01",
352
+ dg_rush_strike_fail: "\u7A81\u88AD\u5931\u8D25\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
353
+ dg_counter_wait_success: "\u53CD\u51FB\u6210\u529F\uFF01",
354
+ dg_counter_wait_fail: "\u53CD\u51FB\u65F6\u673A\u5931\u8BEF\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
355
+ dg_open_golden_chest_success: "\u91D1\u8272\u5B9D\u7BB1\u5F00\u542F\uFF01\u5B9D\u7269\u95EA\u8000",
356
+ dg_open_golden_chest_fail: "\u9677\u9631\u89E6\u53D1\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
357
+ dg_open_stone_chest_success: "\u77F3\u8D28\u5B9D\u7BB1\u5F00\u542F\uFF01\u83B7\u5F97\u7269\u54C1",
358
+ dg_open_stone_chest_fail: "\u5B9D\u7BB1\u662F\u7A7A\u7684...",
359
+ dg_reach_deep_cache_success: "\u53D1\u73B0\u6DF1\u5C42\u5B9D\u85CF\uFF01",
360
+ dg_reach_deep_cache_fail: "\u6DF1\u5C42\u641C\u7D22\u65E0\u679C",
361
+ dg_take_surface_loot_success: "\u62FE\u53D6\u8868\u5C42\u6218\u5229\u54C1\uFF01",
362
+ dg_take_surface_loot_fail: "\u4EC0\u4E48\u90FD\u6CA1\u627E\u5230",
363
+ dg_disarm_mechanism_success: "\u673A\u5173\u62C6\u89E3\u6210\u529F\uFF01",
364
+ dg_disarm_mechanism_fail: "\u673A\u5173\u89E6\u53D1\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
365
+ dg_dodge_through_success: "\u7075\u5DE7\u5730\u95EA\u907F\u4E86\u673A\u5173\uFF01",
366
+ dg_dodge_through_fail: "\u95EA\u907F\u5931\u8D25\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
367
+ dg_brute_force_success: "\u86EE\u529B\u7A81\u7834\u6210\u529F\uFF01",
368
+ dg_brute_force_fail: "\u86EE\u529B\u7A81\u7834\u5931\u8D25\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
369
+ dg_careful_bypass_success: "\u5C0F\u5FC3\u7ED5\u884C\u6210\u529F\uFF01",
370
+ dg_careful_bypass_fail: "\u7ED5\u884C\u5931\u8D25\u2026",
371
+ dg_rest_healed: "\u4F11\u606F\u6062\u590D HP+{heal}",
372
+ dg_search_hidden_success: "\u641C\u7D22\u5230\u9690\u85CF\u7269\u54C1\uFF01",
373
+ dg_search_hidden_fail: "\u641C\u7D22\u672A\u679C",
374
+ dg_investigate_success: "\u8C03\u67E5\u53D1\u73B0\u4E86\u79D8\u5BC6\uFF01",
375
+ dg_investigate_fail: "\u8C03\u67E5\u65E0\u679C",
376
+ dg_proceed_cautiously_success: "\u8C28\u614E\u524D\u884C\uFF0C\u5B89\u5168\u901A\u8FC7",
377
+ dg_proceed_cautiously_fail: "\u610F\u5916\u906D\u9047\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
378
+ dg_touch_artifact_success: "\u795E\u5668\u91CA\u653E\u4E86\u529B\u91CF\uFF01",
379
+ dg_touch_artifact_fail: "\u795E\u5668\u53CD\u566C\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
380
+ dg_observe_only_success: "\u89C2\u5BDF\u5230\u4E86\u6709\u7528\u7684\u7EBF\u7D22",
381
+ dg_observe_only_fail: "\u4EC0\u4E48\u4E5F\u6CA1\u53D1\u73B0",
382
+ dg_all_out_assault_success: "\u5168\u529B\u8FDB\u653B\u594F\u6548\uFF01Boss\u53D7\u5230\u91CD\u521B",
383
+ dg_all_out_assault_fail: "Boss\u53CD\u51FB\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3",
384
+ dg_tactical_approach_success: "\u7B56\u7565\u63A5\u8FD1\u6210\u529F\uFF01\u627E\u5230\u5F31\u70B9",
385
+ dg_tactical_approach_fail: "Boss\u8BC6\u7834\u7B56\u7565\uFF01\u53D7\u5230{damage}\u70B9\u4F24\u5BB3"
386
+ };
387
+ var en = {
388
+ no_lobster: "\u{1F95A} No lobster yet. Run npx clawfight hatch to hatch one!",
389
+ status_cant_fight: "\u26A0\uFE0F {name} is currently {status}, can't battle.",
390
+ status_cant_patrol_molt: "\u{1F7E1} {name} is molting, can't patrol.",
391
+ status_cant_patrol_hibernate: "\u{1F4A4} {name} is hibernating, can't patrol.",
392
+ patrol_start: "\u{1F99E} {name} starts patrolling...",
393
+ patrol_checkin: "\u{1F4CD} Patrol check-in \u2192 EXP +{exp}",
394
+ patrol_connecting: "\u{1F4E1} Connecting to server...",
395
+ patrol_cooldown: "\u23F3 Patrol on cooldown, try again later (30 min interval)",
396
+ patrol_encounter: "\u2694\uFE0F Encounter: {name} (Lv.{level})!",
397
+ patrol_result_win: " Result: \u{1F3C6} Victory ({rounds} rounds)",
398
+ patrol_result_loss: " Result: \u{1F480} Defeat ({rounds} rounds)",
399
+ patrol_result_draw: " Result: \u{1F91D} Draw ({rounds} rounds)",
400
+ patrol_exp_stats: " EXP +{exp} | Record: {wins}W {losses}L | Streak: {streak}",
401
+ patrol_no_battle: "\u2694\uFE0F Encountered {name} (Lv.{level}), but battle could not complete",
402
+ patrol_done: "\u2705 Patrol done, pool: {pool} lobsters",
403
+ patrol_offline: "\u26A0\uFE0F Server unreachable, skipping online check-in",
404
+ patrol_summary: "\u{1F4CA} Current: Lv.{level} | EXP: {exp}/{next} | Today: {today}/{cap}",
405
+ battle_no_code: "\u26A0\uFE0F Please provide opponent's battle code.",
406
+ battle_usage: " Usage: npx clawfight battle <code>",
407
+ battle_hint: " Find codes on the leaderboard: npx clawfight leaderboard",
408
+ battle_connecting: "\u{1F4E1} Challenging [{code}]...",
409
+ battle_server_down: "\u26A0\uFE0F Server unreachable, can't battle.",
410
+ battle_win: "\u{1F3C6} Victory! {name} defeated {opponent} in {rounds} rounds!",
411
+ battle_loss: "\u{1F480} Defeat\u2026 {name} was beaten by {opponent} in {rounds} rounds.",
412
+ battle_draw: "\u{1F91D} Draw! Both exhausted after {rounds} rounds",
413
+ battle_exp: "EXP +{exp} | Record: {wins}W {losses}L | Streak: {streak}",
414
+ lb_loading: "\u{1F4E1} Fetching global leaderboard...",
415
+ lb_offline: "\u26A0\uFE0F Server unreachable, can't load leaderboard.",
416
+ lb_empty: "\u{1F99E} No lobsters on the board yet. Be the first!",
417
+ lb_title: " \u{1F99E} ClawFight Global Leaderboard",
418
+ lb_header: " Rank Name Level Wins WR Code",
419
+ lb_total: " Total: {total} | Active: {active}",
420
+ lb_hint: " \u{1F4A1} Use npx clawfight battle <code> to challenge an opponent",
421
+ status_battle_code: "\u{1F3AF} Battle code: {code}",
422
+ event_prefix: "\u{1F3B2} [{category}] {id}",
423
+ event_effects: " Effects: {changes}",
424
+ level_up: "\u{1F389} Level up! Lv.{level}! [{gains}]",
425
+ molt_trigger: "\u{1F41A} Molt triggered! Lobster enters molting state...",
426
+ hatch_exists: "\u{1F99E} You already have a lobster: {name} (Lv.{level})",
427
+ hatch_one_only: "One per person, irreplaceable.",
428
+ hatch_success: " \u{1F95A} \u2192 \u{1F99E} HATCH SUCCESS!",
429
+ hatch_env: " Environment: Coastal Shallows",
430
+ hatch_ready: "Your lobster is ready to explore the ocean!",
431
+ hatch_next: "Run npx clawfight patrol to start your first patrol.",
432
+ feed_menu_title: "\u{1F37D}\uFE0F Available food types:",
433
+ feed_menu_usage: "Usage: npx clawfight feed <food_type>",
434
+ feed_full: "\u26A0\uFE0F {name} is full today (daily EXP cap {cap})",
435
+ feed_ate: "\u{1F37D}\uFE0F {name} ate {food}!",
436
+ feed_exp: " EXP +{exp}",
437
+ feed_exp_stat: " EXP +{exp} | {stat} +{bonus}",
438
+ feed_today: " Today EXP: {today}/{cap}",
439
+ food_protein: "High Protein",
440
+ food_algae: "Algae",
441
+ food_mineral: "Minerals",
442
+ rest_already: "\u{1F4A4} {name} is already hibernating. Run npx clawfight wake to wake it.",
443
+ rest_molting: "\u{1F7E1} {name} is molting, can't hibernate now.",
444
+ rest_desc1: "\u{1F4A4} {name} sinks into the sandy seabed...",
445
+ rest_desc2: " The lobster curls its antennae and rests quietly.",
446
+ rest_desc3: " No patrols or battles during hibernation.",
447
+ rest_desc4: " Wake bonuses await!",
448
+ rest_wake_hint: "Run npx clawfight wake to wake it up.",
449
+ wake_not_sleeping: "\u{1F7E2} {name} is not hibernating. Already active!",
450
+ wake_desc: "\u2600\uFE0F {name} slowly wakes from the sandy seabed...",
451
+ wake_duration: " Sleep duration: {duration}",
452
+ wake_bonus: " Recovery bonus: {bonus}",
453
+ wake_no_bonus: "(Less than 4 hours, no bonus)",
454
+ wake_ready: "\u{1F7E2} {name} is refreshed and ready to go!",
455
+ duration_minutes: "{n} min",
456
+ duration_hours: "{n} hours",
457
+ depth_display: "Depth:{depth}",
458
+ soul_trait: " Traits: [{trait}]",
459
+ equip_drop: "\u{1F381} Loot: {item}",
460
+ auto_equip: "\u{1F99E} {name} ({reason}) \u2192 auto-equipped {item}",
461
+ auto_swap: "\u{1F99E} {name} ({reason}) \u2192 swapped out {old} for {item}",
462
+ auto_discard: "\u{1F99E} {name} discarded {item} ({reason})",
463
+ equip_degrade: "\u{1F527} Worn: {items}",
464
+ equip_broken: "\u{1F4A5} Broken: {items}",
465
+ equip_inv_full: "\u{1F4E6} Inventory full({max}), discarded {item}",
466
+ equip_title: "\u{1F392} Equipment",
467
+ equip_slot_empty: " {icon} {slot}: (empty)",
468
+ equip_slot_item: " {icon} {slot}: {item}",
469
+ equip_inv_title: "\u{1F4E6} Inventory ({count}/{max}):",
470
+ equip_inv_item: " [{index}] {item}",
471
+ equip_inv_empty: " (empty)",
472
+ equip_equipped: "\u2705 Equipped {item} \u2192 {slot}",
473
+ equip_swapped: "\u{1F504} Swapped {old} \u2192 {item}",
474
+ equip_dropped: "\u{1F5D1}\uFE0F Discarded {item}",
475
+ equip_unequipped: "\u{1F4E4} Unequipped {item}",
476
+ equip_bad_index: "\u26A0\uFE0F Invalid index. Check: npx clawfight equip",
477
+ equip_usage: "Usage: equip / equip <index> / equip drop <index> / equip unequip <slot>",
478
+ achieve_unlock: "\u{1F3C6} Achievement: {name} \u2014 {desc}",
479
+ achieve_title: "\u{1F3C6} Achievements ({count}/{total}):",
480
+ achieve_item: " \u2705 {name} \u2014 {desc}",
481
+ achieve_locked: " \u{1F512} {name} \u2014 {desc}",
482
+ achieve_none: " No achievements yet. Keep exploring!",
483
+ patrol_depth: "\u{1F4CA} Lv.{level} | EXP:{exp}/{next} | Today:{today}/{cap} | Depth:{depth} | Bag:{inv}/{inv_max}",
484
+ depth_reset: "\u{1F4A4} Depth reset",
485
+ dg_connecting: "\u{1F4E1} Connecting to dungeon server...",
486
+ dg_resume: "\u{1F3F0} Active dungeon detected:",
487
+ dg_no_maps: "\u{1F5FA}\uFE0F No dungeon maps. Keep patrolling to find some!",
488
+ dg_bad_map_index: "\u26A0\uFE0F Invalid map index (have {count})",
489
+ dg_entering: "\u{1F3F0} Entering dungeon: {theme} | {rooms} rooms | {diff}",
490
+ dg_no_active: "\u26A0\uFE0F No active dungeon. Use explore to enter one.",
491
+ dg_complete: "\u{1F389} Dungeon cleared!",
492
+ dg_failed: "\u{1F480} Dungeon failed\u2026 HP reached zero",
493
+ dg_rewards_exp: "\u{1F4CA} Dungeon EXP: +{exp}",
494
+ dg_abandoning: "\u{1F3F3}\uFE0F Abandoning dungeon...",
495
+ dg_abandoned: "\u{1F3F3}\uFE0F Retreated | EXP+{exp} | Loot\xD7{loot} | Depth\u2192{depth}",
496
+ dg_maps_title: "\u{1F5FA}\uFE0F Dungeon maps ({count}):",
497
+ dg_rooms: " rooms",
498
+ dg_maps_hint: " \u{1F4A1} Usage: explore <index> to enter | explore maps to view",
499
+ dg_map_found: "\u{1F5FA}\uFE0F Found dungeon map: {theme} ({rooms} rooms, {diff})",
500
+ dg_summary: "\u{1F4CA} Lv.{level} | EXP:{exp}/{next} | Today:{today}/{cap}",
501
+ dg_theme_coral_maze: "Coral Maze",
502
+ dg_theme_deep_rift: "Deep Rift",
503
+ dg_theme_thermal_vent: "Thermal Vent",
504
+ dg_theme_ice_cavern: "Ice Cavern",
505
+ dg_theme_shipwreck: "Shipwreck",
506
+ dg_theme_abyss_trench: "Abyss Trench",
507
+ dg_theme_tide_pool: "Tide Pool",
508
+ dg_theme_void_rift: "Void Rift",
509
+ dg_risk_low: "Low",
510
+ dg_risk_mid: "Mid",
511
+ dg_risk_high: "High",
512
+ dg_diff_easy: "Easy",
513
+ dg_diff_normal: "Normal",
514
+ dg_diff_hard: "Hard",
515
+ dg_diff_nightmare: "Nightmare",
516
+ dg_charge_attack: "Charge Attack",
517
+ dg_defensive_stance: "Defensive Stance",
518
+ dg_rush_strike: "Rush Strike",
519
+ dg_counter_wait: "Counter Wait",
520
+ dg_open_golden_chest: "Open Golden Chest",
521
+ dg_open_stone_chest: "Open Stone Chest",
522
+ dg_reach_deep_cache: "Reach Deep Cache",
523
+ dg_take_surface_loot: "Take Surface Loot",
524
+ dg_disarm_mechanism: "Disarm Mechanism",
525
+ dg_dodge_through: "Dodge Through",
526
+ dg_brute_force: "Brute Force",
527
+ dg_careful_bypass: "Careful Bypass",
528
+ dg_deep_rest: "Deep Rest",
529
+ dg_search_hidden: "Search Hidden",
530
+ dg_investigate: "Investigate",
531
+ dg_proceed_cautiously: "Proceed Cautiously",
532
+ dg_touch_artifact: "Touch Artifact",
533
+ dg_observe_only: "Observe Only",
534
+ dg_all_out_assault: "All-Out Assault",
535
+ dg_tactical_approach: "Tactical Approach",
536
+ dg_bravery_bonus: "Bravery bonus: ATK+20%",
537
+ dg_curiosity_bonus: "Curiosity bonus: Discovery\u2191",
538
+ dg_temper_bonus: "Temper bonus: Disarm\u2191",
539
+ dg_charge_attack_success: "Charge hit!",
540
+ dg_charge_attack_fail: "Charge missed! Took {damage} damage",
541
+ dg_defensive_stance_success: "Defense held!",
542
+ dg_defensive_stance_fail: "Defense breached! Took {damage} damage",
543
+ dg_rush_strike_success: "Rush strike landed!",
544
+ dg_rush_strike_fail: "Rush failed! Took {damage} damage",
545
+ dg_counter_wait_success: "Counter hit!",
546
+ dg_counter_wait_fail: "Counter mistimed! Took {damage} damage",
547
+ dg_open_golden_chest_success: "Golden chest opened! Treasure shines",
548
+ dg_open_golden_chest_fail: "Trap triggered! Took {damage} damage",
549
+ dg_open_stone_chest_success: "Stone chest opened! Item found",
550
+ dg_open_stone_chest_fail: "Chest was empty...",
551
+ dg_reach_deep_cache_success: "Deep cache found!",
552
+ dg_reach_deep_cache_fail: "Deep search failed",
553
+ dg_take_surface_loot_success: "Surface loot collected!",
554
+ dg_take_surface_loot_fail: "Nothing found",
555
+ dg_disarm_mechanism_success: "Mechanism disarmed!",
556
+ dg_disarm_mechanism_fail: "Trap triggered! Took {damage} damage",
557
+ dg_dodge_through_success: "Dodged through the trap!",
558
+ dg_dodge_through_fail: "Dodge failed! Took {damage} damage",
559
+ dg_brute_force_success: "Brute force worked!",
560
+ dg_brute_force_fail: "Brute force failed! Took {damage} damage",
561
+ dg_careful_bypass_success: "Bypassed safely!",
562
+ dg_careful_bypass_fail: "Bypass failed\u2026",
563
+ dg_rest_healed: "Rested. HP+{heal}",
564
+ dg_search_hidden_success: "Found hidden item!",
565
+ dg_search_hidden_fail: "Search found nothing",
566
+ dg_investigate_success: "Investigation uncovered a secret!",
567
+ dg_investigate_fail: "Investigation found nothing",
568
+ dg_proceed_cautiously_success: "Proceeded safely",
569
+ dg_proceed_cautiously_fail: "Unexpected encounter! Took {damage} damage",
570
+ dg_touch_artifact_success: "Artifact releases power!",
571
+ dg_touch_artifact_fail: "Artifact backlash! Took {damage} damage",
572
+ dg_observe_only_success: "Observed useful clue",
573
+ dg_observe_only_fail: "Nothing observed",
574
+ dg_all_out_assault_success: "All-out assault devastated the Boss!",
575
+ dg_all_out_assault_fail: "Boss counterattacked! Took {damage} damage",
576
+ dg_tactical_approach_success: "Tactical approach found weakness!",
577
+ dg_tactical_approach_fail: "Boss saw through the strategy! Took {damage} damage"
578
+ };
579
+ function detectLocale() {
580
+ const lang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || "";
581
+ if (lang.startsWith("zh")) return "zh";
582
+ try {
583
+ const sysLocale = Intl.DateTimeFormat().resolvedOptions().locale;
584
+ if (sysLocale.startsWith("zh")) return "zh";
585
+ } catch {
586
+ }
587
+ return "en";
588
+ }
589
+ var locale = detectLocale();
590
+ var strings = locale === "zh" ? zh : en;
591
+ function t(key, params) {
592
+ let msg = strings[key] || zh[key] || key;
593
+ if (params) {
594
+ for (const [k, v] of Object.entries(params)) {
595
+ msg = msg.replace(new RegExp(`\\{${k}\\}`, "g"), String(v));
596
+ }
597
+ }
598
+ return msg;
599
+ }
600
+
164
601
  // src/commands/hatch.ts
165
602
  function rollRarity() {
166
603
  const roll = Math.random() * 100;
@@ -192,9 +629,8 @@ function randomName() {
192
629
  async function hatch(name) {
193
630
  const existing = await readLobster();
194
631
  if (existing) {
195
- console.log(`
196
- \u{1F99E} \u4F60\u5DF2\u7ECF\u6709\u4E00\u53EA\u9F99\u867E\u4E86\uFF1A${existing.name}\uFF08Lv.${existing.level}\uFF09`);
197
- console.log("\u4E00\u4EBA\u4E00\u867E\uFF0C\u4E0D\u53EF\u66FF\u4EE3\u3002");
632
+ console.log("\n" + t("hatch_exists", { name: existing.name, level: existing.level }));
633
+ console.log(t("hatch_one_only"));
198
634
  return;
199
635
  }
200
636
  const lobsterName = name || randomName();
@@ -223,32 +659,222 @@ async function hatch(name) {
223
659
  last_patrol: "",
224
660
  last_battle: "",
225
661
  today_exp: 0,
226
- daily_exp_cap: 100
662
+ daily_exp_cap: 100,
663
+ equipped: {},
664
+ inventory: [],
665
+ depth: 0,
666
+ achievements: [],
667
+ dungeon_maps: [],
668
+ dungeons_completed: 0,
669
+ boss_kills: 0,
670
+ dungeon_epics_found: 0
227
671
  };
228
672
  await writeLobster(lobster);
229
673
  const soulMd = buildSoulMarkdown(lobsterName, soul, rarity, "coastal");
230
674
  await writeSoul(soulMd);
231
- await appendLog(`\u{1F95A} **${lobsterName}** \u7834\u58F3\u800C\u51FA\uFF01\u7A00\u6709\u5EA6\uFF1A${RARITY_LABELS[rarity]}\uFF0C\u73AF\u5883\uFF1A\u6CBF\u6D77\u6D45\u6EE9`);
675
+ await appendLog(`\u{1F95A} **${lobsterName}** hatched! Rarity: ${RARITY_LABELS[rarity]}`);
232
676
  console.log("\n" + "=".repeat(50));
233
- console.log(" \u{1F95A} \u2192 \u{1F99E} \u5B75 \u5316 \u6210 \u529F \uFF01");
677
+ console.log(t("hatch_success"));
234
678
  console.log("=".repeat(50));
235
679
  console.log();
236
- console.log(` \u540D\u79F0: ${lobsterName}`);
237
- console.log(` \u7A00\u6709\u5EA6: ${RARITY_LABELS[rarity]} (${rarity})`);
238
- console.log(` \u7B49\u7EA7: Lv.1`);
239
- console.log(` \u73AF\u5883: \u6CBF\u6D77\u6D45\u6EE9`);
680
+ console.log(` ${lobsterName}`);
681
+ console.log(` ${RARITY_LABELS[rarity]} (${rarity})`);
682
+ console.log(" Lv.1");
683
+ console.log(t("hatch_env"));
240
684
  console.log();
241
685
  console.log(` \u2764\uFE0F HP: ${stats.hp} \u2694\uFE0F ATK: ${stats.attack} \u{1F6E1}\uFE0F DEF: ${stats.defense}`);
242
686
  console.log(` \u{1F4A8} SPD: ${stats.speed} \u{1F441}\uFE0F INT: ${stats.intimidation} \u{1F340} LCK: ${stats.luck}`);
243
687
  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
688
  console.log(` ID: ${lobster.id}`);
248
689
  console.log("=".repeat(50));
249
690
  console.log();
250
- console.log("\u4F60\u7684\u9F99\u867E\u5DF2\u7ECF\u51C6\u5907\u597D\u63A2\u7D22\u6D77\u6D0B\u4E86\uFF01");
251
- console.log("\u8FD0\u884C npx clawfight patrol \u5F00\u59CB\u7B2C\u4E00\u6B21\u5DE1\u903B\u3002");
691
+ console.log(t("hatch_ready"));
692
+ console.log(t("hatch_next"));
693
+ }
694
+
695
+ // src/lib/equipment.ts
696
+ import { randomUUID as randomUUID2 } from "crypto";
697
+ var SLOT_STATS = {
698
+ claw: ["attack", "speed"],
699
+ shell: ["hp", "defense"],
700
+ charm: ["luck", "intimidation"]
701
+ };
702
+ var NAMES = {
703
+ claw: {
704
+ common: ["\u94C1\u94B3\u5957", "\u77F3\u523A\u94B3", "\u78E8\u7259\u5957"],
705
+ rare: ["\u73CA\u745A\u523A\u94B3", "\u9CA8\u9F7F\u5957", "\u950B\u5203\u94B3"],
706
+ epic: ["\u6DF1\u6D77\u952F\u9F7F", "\u96F7\u9706\u4E4B\u94B3", "\u9F99\u9AA8\u94B3"],
707
+ legendary: ["\u6BC1\u706D\u4E4B\u94B3", "\u6D77\u795E\u4E4B\u63E1", "\u6DF7\u6C8C\u88C2\u722A"]
708
+ },
709
+ shell: {
710
+ common: ["\u77F3\u7532\u58F3", "\u786C\u76AE\u7532", "\u6CE5\u6C99\u76FE"],
711
+ rare: ["\u73CA\u745A\u94E0", "\u6D77\u85FB\u7EC7\u7532", "\u8D1D\u58F3\u76FE"],
712
+ epic: ["\u73CD\u73E0\u9F99\u94E0", "\u6DF1\u6E0A\u58F3\u7532", "\u51B0\u6676\u7532"],
713
+ legendary: ["\u4E0D\u706D\u7532\u58F3", "\u6D77\u795E\u4E4B\u94E0", "\u8679\u5149\u94E0"]
714
+ },
715
+ charm: {
716
+ common: ["\u5C0F\u6D77\u661F", "\u788E\u8D1D\u58F3", "\u6D77\u8349\u7ED3"],
717
+ rare: ["\u9CA8\u7259\u94FE", "\u73CA\u745A\u5760", "\u6F6E\u6C50\u73E0"],
718
+ epic: ["\u6DF1\u6D77\u4E4B\u773C", "\u96F7\u66B4\u6838", "\u5E7B\u6D77\u73E0"],
719
+ legendary: ["\u6D77\u795E\u4E4B\u5FC3", "\u547D\u8FD0\u4E4B\u73E0", "\u6DF7\u6C8C\u4E4B\u773C"]
720
+ }
721
+ };
722
+ var RARITY_MUL = { common: 1, rare: 2, epic: 3, legendary: 5 };
723
+ var RARITY_DUR = { common: 8, rare: 12, epic: 18, legendary: 30 };
724
+ var RARITY_POWER = { common: 1, rare: 1.5, epic: 2, legendary: 3 };
725
+ function pick(arr) {
726
+ return arr[Math.floor(Math.random() * arr.length)];
727
+ }
728
+ function rollRarity2(depth, soul) {
729
+ const w = { ...EQUIP_RARITY_WEIGHTS };
730
+ w.rare += depth * 3;
731
+ w.epic += depth * 1.5;
732
+ w.legendary += depth * 0.5;
733
+ if (soul.talkativeness <= 3) {
734
+ w.rare += 5;
735
+ w.epic += 3;
736
+ w.legendary += 1;
737
+ }
738
+ const total = Object.values(w).reduce((a, b) => a + b, 0);
739
+ let roll = Math.random() * total;
740
+ for (const [r, weight] of Object.entries(w)) {
741
+ roll -= weight;
742
+ if (roll <= 0) return r;
743
+ }
744
+ return "common";
745
+ }
746
+ function rollLevel(depth, soul) {
747
+ let lv = Math.floor(depth / 3) + 1;
748
+ if (soul.curiosity >= 7) lv++;
749
+ return Math.min(5, Math.max(1, lv));
750
+ }
751
+ function generateEquipment(depth, soul) {
752
+ const slot = pick(["claw", "shell", "charm"]);
753
+ const rarity = rollRarity2(depth, soul);
754
+ const level = rollLevel(depth, soul);
755
+ const mul = RARITY_MUL[rarity];
756
+ const levelMul = 1 + (level - 1) * 0.3;
757
+ const keys = SLOT_STATS[slot];
758
+ let dur = RARITY_DUR[rarity];
759
+ if (soul.bravery <= 3) dur += 2;
760
+ const bonuses = {};
761
+ for (const k of keys) {
762
+ bonuses[k] = Math.ceil(Math.random() * 2 * mul * levelMul);
763
+ }
764
+ const allStats = ["hp", "attack", "defense", "speed", "intimidation", "luck"];
765
+ if (Math.random() < 0.2 * mul) {
766
+ const extra = allStats.filter((s) => !keys.includes(s));
767
+ if (extra.length) bonuses[pick(extra)] = Math.ceil(Math.random() * mul * levelMul);
768
+ }
769
+ return {
770
+ id: randomUUID2().slice(0, 8),
771
+ name: pick(NAMES[slot][rarity]),
772
+ slot,
773
+ rarity,
774
+ level,
775
+ bonuses,
776
+ durability: dur,
777
+ max_durability: dur
778
+ };
779
+ }
780
+ function powerScore(e) {
781
+ const sum = Object.values(e.bonuses).reduce((a, b) => a + (b ?? 0), 0);
782
+ const lv = e.level ?? 1;
783
+ return sum * RARITY_POWER[e.rarity] * lv * (e.durability / e.max_durability);
784
+ }
785
+ function shouldDrop(depth, soul) {
786
+ let chance = Math.min(0.8, 0.25 + depth * 0.05);
787
+ if (soul.bravery >= 7) chance += 0.1;
788
+ if (soul.bravery <= 3) chance -= 0.1;
789
+ return Math.random() < Math.max(0.05, chance);
790
+ }
791
+ function depthPenalty(soul) {
792
+ if (soul.temper >= 7) return 3;
793
+ if (soul.bravery >= 7) return 1;
794
+ return 2;
795
+ }
796
+ function winExpMultiplier(soul) {
797
+ if (soul.temper >= 7) return 1.2;
798
+ return 1;
799
+ }
800
+ function autoManageLoot(lobster, loot) {
801
+ const actions = [];
802
+ if (!lobster.equipped) lobster.equipped = {};
803
+ if (!lobster.inventory) lobster.inventory = [];
804
+ const current = lobster.equipped[loot.slot];
805
+ const lootPower = powerScore(loot);
806
+ if (!current) {
807
+ lobster.equipped[loot.slot] = loot;
808
+ actions.push({ type: "auto_equip", item: loot, reason: slotReason(lobster.soul, loot.slot) });
809
+ return actions;
810
+ }
811
+ const currentPower = powerScore(current);
812
+ if (lootPower > currentPower) {
813
+ lobster.equipped[loot.slot] = loot;
814
+ if (lobster.inventory.length < MAX_INVENTORY) {
815
+ lobster.inventory.push(current);
816
+ }
817
+ actions.push({ type: "auto_swap", item: loot, old: current, reason: slotReason(lobster.soul, loot.slot) });
818
+ return actions;
819
+ }
820
+ if (lobster.inventory.length < MAX_INVENTORY) {
821
+ lobster.inventory.push(loot);
822
+ return actions;
823
+ }
824
+ let worstIdx = -1;
825
+ let worstScore = Infinity;
826
+ for (let i = 0; i < lobster.inventory.length; i++) {
827
+ const s = powerScore(lobster.inventory[i]);
828
+ if (s < worstScore) {
829
+ worstScore = s;
830
+ worstIdx = i;
831
+ }
832
+ }
833
+ if (worstIdx >= 0 && lootPower > worstScore) {
834
+ const discarded = lobster.inventory[worstIdx];
835
+ lobster.inventory[worstIdx] = loot;
836
+ actions.push({ type: "auto_discard", item: discarded, reason: "\u54C1\u8D28\u4E0D\u8DB3" });
837
+ } else {
838
+ actions.push({ type: "auto_discard", item: loot, reason: "\u54C1\u8D28\u4E0D\u8DB3" });
839
+ }
840
+ return actions;
841
+ }
842
+ function slotReason(soul, slot) {
843
+ if (slot === "claw" && soul.bravery >= 7) return "\u52C7\u731B\u672C\u80FD";
844
+ if (slot === "claw" && soul.temper >= 7) return "\u66B4\u8E81\u5929\u6027";
845
+ if (slot === "shell" && soul.bravery <= 3) return "\u8C28\u614E\u76F4\u89C9";
846
+ if (slot === "charm" && soul.curiosity >= 7) return "\u597D\u5947\u5FC3\u9A71\u4F7F";
847
+ return "\u81EA\u884C\u5224\u65AD";
848
+ }
849
+ function getEffectiveStats(lobster) {
850
+ const s = { ...lobster.stats };
851
+ if (!lobster.equipped) return s;
852
+ for (const eq of Object.values(lobster.equipped)) {
853
+ if (!eq || eq.durability <= 0) continue;
854
+ for (const [k, v] of Object.entries(eq.bonuses)) {
855
+ s[k] += v;
856
+ }
857
+ }
858
+ return s;
859
+ }
860
+ function degradeEquipment(lobster) {
861
+ const broken = [];
862
+ if (!lobster.equipped) return broken;
863
+ for (const [slot, eq] of Object.entries(lobster.equipped)) {
864
+ if (!eq) continue;
865
+ eq.durability--;
866
+ if (eq.durability <= 0) {
867
+ broken.push(eq.name);
868
+ delete lobster.equipped[slot];
869
+ }
870
+ }
871
+ return broken;
872
+ }
873
+ function formatEquip(e) {
874
+ const lv = (e.level ?? 1) > 1 ? `+${e.level}` : "";
875
+ const label = EQUIP_RARITY_LABELS[e.rarity] || e.rarity;
876
+ const b = Object.entries(e.bonuses).map(([k, v]) => `${k}+${v}`).join(" ");
877
+ return `[${label}${lv}] ${e.name} ${b} (${e.durability}/${e.max_durability})`;
252
878
  }
253
879
 
254
880
  // src/commands/status.ts
@@ -259,12 +885,13 @@ function bar(current, max, width = 20) {
259
885
  async function status() {
260
886
  const lobster = await readLobster();
261
887
  if (!lobster) {
262
- console.log("\n\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01");
888
+ console.log("\n" + t("no_lobster"));
263
889
  return;
264
890
  }
265
891
  const totalBattles = lobster.wins + lobster.losses;
266
892
  const winRate = totalBattles > 0 ? Math.round(lobster.wins / totalBattles * 100) : 0;
267
893
  const expPct = lobster.exp_to_next > 0 ? Math.round(lobster.exp / lobster.exp_to_next * 100) : 0;
894
+ const eff = getEffectiveStats(lobster);
268
895
  const statusEmoji = {
269
896
  active: "\u{1F7E2} \u6D3B\u8DC3",
270
897
  molting: "\u{1F7E1} \u8715\u58F3\u4E2D",
@@ -275,28 +902,62 @@ async function status() {
275
902
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
276
903
  console.log(`\u2502 \u7B49\u7EA7: Lv.${String(lobster.level).padEnd(5)} \u7A00\u6709\u5EA6: ${(RARITY_LABELS[lobster.rarity] || lobster.rarity).padEnd(15)}\u2502`);
277
904
  console.log(`\u2502 \u72B6\u6001: ${(statusEmoji[lobster.status] || lobster.status).padEnd(36)}\u2502`);
278
- console.log(`\u2502 \u73AF\u5883: ${lobster.environment.padEnd(36)}\u2502`);
905
+ console.log(`\u2502 \u73AF\u5883: ${lobster.environment.padEnd(20)} \u6DF1\u5EA6: ${String(lobster.depth ?? 0).padEnd(13)}\u2502`);
279
906
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
280
907
  console.log(`\u2502 EXP: ${bar(lobster.exp, lobster.exp_to_next)} ${String(expPct).padStart(3)}% \u2502`);
281
908
  console.log(`\u2502 ${String(lobster.exp).padStart(5)} / ${String(lobster.exp_to_next).padEnd(28)}\u2502`);
282
909
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
283
- console.log(`\u2502 \u2764\uFE0F HP: ${String(lobster.stats.hp).padEnd(6)} \u2694\uFE0F ATK: ${String(lobster.stats.attack).padEnd(15)}\u2502`);
284
- console.log(`\u2502 \u{1F6E1}\uFE0F DEF: ${String(lobster.stats.defense).padEnd(6)} \u{1F4A8} SPD: ${String(lobster.stats.speed).padEnd(15)}\u2502`);
285
- console.log(`\u2502 \u{1F441}\uFE0F INT: ${String(lobster.stats.intimidation).padEnd(6)} \u{1F340} LCK: ${String(lobster.stats.luck).padEnd(15)}\u2502`);
910
+ console.log(`\u2502 \u2764\uFE0F HP: ${String(eff.hp).padEnd(6)} \u2694\uFE0F ATK: ${String(eff.attack).padEnd(15)}\u2502`);
911
+ console.log(`\u2502 \u{1F6E1}\uFE0F DEF: ${String(eff.defense).padEnd(6)} \u{1F4A8} SPD: ${String(eff.speed).padEnd(15)}\u2502`);
912
+ console.log(`\u2502 \u{1F441}\uFE0F INT: ${String(eff.intimidation).padEnd(6)} \u{1F340} LCK: ${String(eff.luck).padEnd(15)}\u2502`);
286
913
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
287
914
  console.log(`\u2502 \u6218\u7EE9: ${lobster.wins}\u80DC ${lobster.losses}\u8D1F (\u80DC\u7387${winRate}%)${" ".repeat(Math.max(0, 20 - String(lobster.wins).length - String(lobster.losses).length - String(winRate).length))}\u2502`);
288
915
  console.log(`\u2502 \u8FDE\u80DC: ${lobster.streak} \u58F0\u671B: ${lobster.reputation} \u5DE1\u903B: ${lobster.patrol_count}${" ".repeat(Math.max(0, 15 - String(lobster.streak).length - String(lobster.reputation).length - String(lobster.patrol_count).length))}\u2502`);
916
+ const slots = ["claw", "shell", "charm"];
917
+ const hasEquip = slots.some((s) => lobster.equipped?.[s]);
918
+ if (hasEquip) {
919
+ console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
920
+ for (const s of slots) {
921
+ const eq = lobster.equipped?.[s];
922
+ const label = `${SLOT_ICONS[s]} ${SLOT_LABELS[s]}`;
923
+ const lv = eq && (eq.level ?? 1) > 1 ? `+${eq.level}` : "";
924
+ const info = eq ? `${eq.name}${lv}(${eq.durability}/${eq.max_durability})` : "-";
925
+ console.log(`\u2502 ${label}: ${info.padEnd(36)}\u2502`);
926
+ }
927
+ }
928
+ const invCount = lobster.inventory?.length ?? 0;
929
+ if (invCount > 0) {
930
+ console.log(`\u2502 \u{1F4E6} \u80CC\u5305: ${invCount}/${MAX_INVENTORY}${" ".repeat(31 - String(invCount).length - String(MAX_INVENTORY).length)}\u2502`);
931
+ }
289
932
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
290
933
  console.log(`\u2502 \u6027\u683C: \u2502`);
291
934
  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`);
935
+ const mapCount = lobster.dungeon_maps?.length ?? 0;
936
+ const dgCompleted = lobster.dungeons_completed ?? 0;
937
+ if (mapCount > 0 || dgCompleted > 0) {
938
+ console.log(`\u2502 \u{1F3F0} \u5730\u4E0B\u57CE: ${dgCompleted}\u901A\u5173 \u{1F5FA}\uFE0F \u5730\u56FE: ${mapCount}${" ".repeat(Math.max(0, 19 - String(dgCompleted).length - String(mapCount).length))}\u2502`);
939
+ }
940
+ if (lobster.active_dungeon) {
941
+ console.log(`\u2502 \u26A1 \u8FDB\u884C\u4E2D\u5730\u4E0B\u57CE: \u6709${" ".repeat(24)}\u2502`);
942
+ }
943
+ const achieveCount = lobster.achievements?.length ?? 0;
944
+ if (achieveCount > 0) {
945
+ console.log(`\u2502 \u{1F3C6} \u6210\u5C31: ${achieveCount}${" ".repeat(33 - String(achieveCount).length)}\u2502`);
946
+ }
292
947
  console.log("\u2514" + "\u2500".repeat(44) + "\u2518");
948
+ console.log("\n" + t("status_battle_code", { code: lobster.id.slice(0, 8) }));
949
+ const soulText = await readSoul();
950
+ if (soulText) {
951
+ const firstLine = soulText.split("\n").find((l) => l.trim() && !l.startsWith("#"));
952
+ if (firstLine) console.log(`\u{1F4DC} \u7075\u9B42: ${firstLine.trim().slice(0, 60)}`);
953
+ }
293
954
  }
294
955
 
295
956
  // src/lib/api.ts
296
957
  import { createHash } from "crypto";
297
958
  var API_BASE = "https://api.clawfight.online";
298
959
  function statsHash(lobster) {
299
- const raw = JSON.stringify(lobster.stats);
960
+ const raw = JSON.stringify(getEffectiveStats(lobster));
300
961
  return createHash("sha256").update(raw).digest("hex");
301
962
  }
302
963
  function getProxyUrl() {
@@ -331,14 +992,11 @@ async function apiPatrol(lobster) {
331
992
  lobster_id: lobster.id,
332
993
  level: lobster.level,
333
994
  stats_hash: statsHash(lobster),
995
+ stats: getEffectiveStats(lobster),
334
996
  environment: lobster.environment,
335
997
  name: lobster.name,
336
998
  color: lobster.rarity,
337
999
  rarity: lobster.rarity,
338
- wins: lobster.wins,
339
- losses: lobster.losses,
340
- streak: lobster.streak,
341
- reputation: lobster.reputation,
342
1000
  is_molting: lobster.status === "molting",
343
1001
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
344
1002
  }),
@@ -350,6 +1008,27 @@ async function apiPatrol(lobster) {
350
1008
  return null;
351
1009
  }
352
1010
  }
1011
+ async function apiBattle(challengerId, opponentCode) {
1012
+ try {
1013
+ const pfetch = await getProxiedFetch();
1014
+ const res = await pfetch(`${API_BASE}/api/battle`, {
1015
+ method: "POST",
1016
+ headers: { "Content-Type": "application/json; charset=utf-8" },
1017
+ body: JSON.stringify({
1018
+ challenger_id: challengerId,
1019
+ opponent_code: opponentCode
1020
+ }),
1021
+ signal: AbortSignal.timeout(1e4)
1022
+ });
1023
+ if (!res.ok) {
1024
+ const err = await res.json();
1025
+ return { error: err.error || "Unknown error" };
1026
+ }
1027
+ return await res.json();
1028
+ } catch {
1029
+ return null;
1030
+ }
1031
+ }
353
1032
  async function apiLeaderboard(limit = 20) {
354
1033
  try {
355
1034
  const pfetch = await getProxiedFetch();
@@ -362,6 +1041,74 @@ async function apiLeaderboard(limit = 20) {
362
1041
  return null;
363
1042
  }
364
1043
  }
1044
+ async function apiDungeonEnter(lobster, theme) {
1045
+ try {
1046
+ const pfetch = await getProxiedFetch();
1047
+ const res = await pfetch(`${API_BASE}/api/dungeon/enter`, {
1048
+ method: "POST",
1049
+ headers: { "Content-Type": "application/json; charset=utf-8" },
1050
+ body: JSON.stringify({
1051
+ lobster_id: lobster.id,
1052
+ level: lobster.level,
1053
+ stats: getEffectiveStats(lobster),
1054
+ soul: lobster.soul,
1055
+ depth: lobster.depth ?? 0,
1056
+ environment: lobster.environment,
1057
+ theme
1058
+ }),
1059
+ signal: AbortSignal.timeout(1e4)
1060
+ });
1061
+ if (!res.ok) {
1062
+ const err = await res.json();
1063
+ return { error: err.error };
1064
+ }
1065
+ return await res.json();
1066
+ } catch {
1067
+ return null;
1068
+ }
1069
+ }
1070
+ async function apiDungeonAct(dungeonId, lobsterId, choice) {
1071
+ try {
1072
+ const pfetch = await getProxiedFetch();
1073
+ const res = await pfetch(`${API_BASE}/api/dungeon/act`, {
1074
+ method: "POST",
1075
+ headers: { "Content-Type": "application/json; charset=utf-8" },
1076
+ body: JSON.stringify({ dungeon_id: dungeonId, lobster_id: lobsterId, choice }),
1077
+ signal: AbortSignal.timeout(1e4)
1078
+ });
1079
+ if (!res.ok) return null;
1080
+ return await res.json();
1081
+ } catch {
1082
+ return null;
1083
+ }
1084
+ }
1085
+ async function apiDungeonState(lobsterId) {
1086
+ try {
1087
+ const pfetch = await getProxiedFetch();
1088
+ const res = await pfetch(`${API_BASE}/api/dungeon/state?lobster_id=${lobsterId}`, {
1089
+ signal: AbortSignal.timeout(1e4)
1090
+ });
1091
+ if (!res.ok) return null;
1092
+ return await res.json();
1093
+ } catch {
1094
+ return null;
1095
+ }
1096
+ }
1097
+ async function apiDungeonAbandon(lobsterId) {
1098
+ try {
1099
+ const pfetch = await getProxiedFetch();
1100
+ const res = await pfetch(`${API_BASE}/api/dungeon/abandon`, {
1101
+ method: "POST",
1102
+ headers: { "Content-Type": "application/json; charset=utf-8" },
1103
+ body: JSON.stringify({ lobster_id: lobsterId }),
1104
+ signal: AbortSignal.timeout(1e4)
1105
+ });
1106
+ if (!res.ok) return null;
1107
+ return await res.json();
1108
+ } catch {
1109
+ return null;
1110
+ }
1111
+ }
365
1112
 
366
1113
  // src/lib/events.ts
367
1114
  import { readFile as readFile2 } from "fs/promises";
@@ -401,11 +1148,11 @@ async function rollEvent(lobster) {
401
1148
  });
402
1149
  if (candidates.length === 0) return null;
403
1150
  const totalWeight = candidates.reduce((s, c) => s + c.probability, 0);
404
- let pick = Math.random() * totalWeight;
1151
+ let pick2 = Math.random() * totalWeight;
405
1152
  let chosen = null;
406
1153
  for (const c of candidates) {
407
- pick -= c.probability;
408
- if (pick <= 0) {
1154
+ pick2 -= c.probability;
1155
+ if (pick2 <= 0) {
409
1156
  chosen = c;
410
1157
  break;
411
1158
  }
@@ -436,69 +1183,208 @@ function applyEventEffects(lobster, effects) {
436
1183
  return changes;
437
1184
  }
438
1185
 
1186
+ // src/lib/achievements.ts
1187
+ var ACHIEVEMENTS = {
1188
+ first_blood: { name: "\u521D\u6218\u544A\u6377", desc: "\u8D62\u5F97\u9996\u80DC", check: (l) => l.wins >= 1 },
1189
+ veteran: { name: "\u6C99\u573A\u8001\u5175", desc: "10\u80DC", check: (l) => l.wins >= 10 },
1190
+ warlord: { name: "\u6DF1\u6D77\u6218\u795E", desc: "50\u80DC", check: (l) => l.wins >= 50 },
1191
+ explorer: { name: "\u6D77\u57DF\u63A2\u7D22\u8005", desc: "20\u6B21\u5DE1\u903B", check: (l) => l.patrol_count >= 20 },
1192
+ deep_diver: { name: "\u6DF1\u6F5C\u8005", desc: "\u6DF1\u5EA65", check: (l) => (l.depth ?? 0) >= 5 },
1193
+ abyss_walker: { name: "\u6DF1\u6E0A\u884C\u8005", desc: "\u6DF1\u5EA610", check: (l) => (l.depth ?? 0) >= 10 },
1194
+ fully_armed: { name: "\u5168\u526F\u6B66\u88C5", desc: "\u88C5\u5907\u6EE13\u69FD", check: (l) => {
1195
+ if (!l.equipped) return false;
1196
+ return !!(l.equipped.claw && l.equipped.shell && l.equipped.charm);
1197
+ } },
1198
+ survivor: { name: "\u767E\u6298\u4E0D\u6320", desc: "\u7D2F\u8BA110\u8D1F\u540E\u4ECD\u5728\u6218\u6597", check: (l) => l.losses >= 10 && l.wins > 0 },
1199
+ streak_5: { name: "\u52BF\u4E0D\u53EF\u6321", desc: "5\u8FDE\u80DC", check: (l) => l.streak >= 5 },
1200
+ streak_10: { name: "\u65E0\u4EBA\u80FD\u654C", desc: "10\u8FDE\u80DC", check: (l) => l.streak >= 10 },
1201
+ high_level: { name: "\u738B\u8005\u9F99\u867E", desc: "\u8FBE\u523010\u7EA7", check: (l) => l.level >= 10 },
1202
+ molt_master: { name: "\u8715\u53D8\u5927\u5E08", desc: "\u8715\u58F33\u6B21", check: (l) => l.molt_count >= 3 },
1203
+ first_dungeon: { name: "\u521D\u63A2\u5730\u4E0B\u57CE", desc: "\u5B8C\u6210\u9996\u4E2A\u5730\u4E0B\u57CE", check: (l) => (l.dungeons_completed ?? 0) >= 1 },
1204
+ dungeon_master: { name: "\u5730\u4E0B\u57CE\u4E4B\u738B", desc: "\u5B8C\u621010\u4E2A\u5730\u4E0B\u57CE", check: (l) => (l.dungeons_completed ?? 0) >= 10 },
1205
+ boss_slayer: { name: "Boss\u730E\u624B", desc: "\u51FB\u8D25\u5730\u4E0B\u57CEBoss", check: (l) => (l.boss_kills ?? 0) >= 1 },
1206
+ perfect_run: { name: "\u5B8C\u7F8E\u901A\u5173", desc: "\u6EE1HP\u901A\u5173\u5730\u4E0B\u57CE", check: () => false },
1207
+ treasure_hunter: { name: "\u5BFB\u5B9D\u730E\u4EBA", desc: "\u5728\u5730\u4E0B\u57CE\u83B7\u5F975\u4EF6\u53F2\u8BD7+\u88C5\u5907", check: (l) => (l.dungeon_epics_found ?? 0) >= 5 }
1208
+ };
1209
+ function checkAchievements(lobster) {
1210
+ const unlocked = [];
1211
+ const existing = new Set(lobster.achievements ?? []);
1212
+ for (const [id, def] of Object.entries(ACHIEVEMENTS)) {
1213
+ if (existing.has(id)) continue;
1214
+ if (def.check(lobster)) {
1215
+ unlocked.push(id);
1216
+ if (!lobster.achievements) lobster.achievements = [];
1217
+ lobster.achievements.push(id);
1218
+ }
1219
+ }
1220
+ return unlocked;
1221
+ }
1222
+
439
1223
  // src/commands/patrol.ts
440
1224
  var PATROL_EXP = 15;
441
1225
  async function patrol() {
442
1226
  const lobster = await readLobster();
443
1227
  if (!lobster) {
444
- console.log("\n\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01");
1228
+ console.log("\n" + t("no_lobster"));
445
1229
  return;
446
1230
  }
447
1231
  if (lobster.status === "molting") {
448
- console.log(`
449
- \u{1F7E1} ${lobster.name} \u6B63\u5728\u8715\u58F3\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002`);
1232
+ console.log("\n" + t("status_cant_patrol_molt", { name: lobster.name }));
450
1233
  return;
451
1234
  }
452
1235
  if (lobster.status === "hibernating") {
453
- console.log(`
454
- \u{1F4A4} ${lobster.name} \u6B63\u5728\u51AC\u7720\u4E2D\uFF0C\u65E0\u6CD5\u5DE1\u903B\u3002`);
1236
+ console.log("\n" + t("status_cant_patrol_hibernate", { name: lobster.name }));
455
1237
  return;
456
1238
  }
457
1239
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
458
1240
  const lastDay = lobster.last_patrol ? lobster.last_patrol.split("T")[0] : "";
459
- if (today !== lastDay) {
460
- lobster.today_exp = 0;
461
- }
462
- console.log(`
463
- \u{1F99E} ${lobster.name} \u5F00\u59CB\u5DE1\u903B...`);
1241
+ if (today !== lastDay) lobster.today_exp = 0;
1242
+ const depth = lobster.depth ?? 0;
1243
+ const soul = lobster.soul;
1244
+ const trait = soulTag(soul);
1245
+ console.log("\n" + t("patrol_start", { name: lobster.name }) + ` [${t("depth_display", { depth })}]`);
1246
+ if (trait) console.log(t("soul_trait", { trait }));
464
1247
  console.log("\u2500".repeat(40));
465
1248
  const expGain = Math.min(PATROL_EXP, lobster.daily_exp_cap - lobster.today_exp);
466
1249
  if (expGain > 0) {
467
1250
  lobster.exp += expGain;
468
1251
  lobster.today_exp += expGain;
469
- console.log(`\u{1F4CD} \u5DE1\u903B\u7B7E\u5230 \u2192 \u7ECF\u9A8C +${expGain}`);
1252
+ console.log(t("patrol_checkin", { exp: expGain }));
470
1253
  }
471
1254
  const eventResult = await rollEvent(lobster);
472
1255
  if (eventResult) {
473
1256
  console.log();
474
- console.log(`\u{1F3B2} [${eventResult.event.category}] ${eventResult.event.id}`);
1257
+ console.log(t("event_prefix", { category: eventResult.event.category, id: eventResult.event.id }));
475
1258
  console.log(` ${eventResult.narrative}`);
476
1259
  const changes = applyEventEffects(lobster, eventResult.event.effects);
477
- if (changes.length > 0) {
478
- console.log(` \u6548\u679C: ${changes.join(", ")}`);
479
- }
480
- await appendLog(`\u{1F3B2} \u4E8B\u4EF6\u300C${eventResult.event.id}\u300D: ${eventResult.narrative.slice(0, 60)}...`);
1260
+ if (changes.length > 0) console.log(t("event_effects", { changes: changes.join(", ") }));
1261
+ await appendLog(`\u{1F3B2} ${eventResult.event.id}: ${eventResult.narrative.slice(0, 60)}...`);
481
1262
  }
482
1263
  checkLevelUp(lobster);
483
1264
  lobster.patrol_count++;
484
1265
  lobster.last_patrol = (/* @__PURE__ */ new Date()).toISOString();
485
- console.log("\n\u{1F4E1} \u8FDE\u63A5\u670D\u52A1\u5668...");
1266
+ console.log("\n" + t("patrol_connecting"));
486
1267
  const serverResponse = await apiPatrol(lobster);
487
1268
  if (serverResponse) {
488
- if (serverResponse.encounter && serverResponse.opponent) {
489
- console.log(`\u2694\uFE0F \u906D\u9047\uFF01\u5BF9\u624B: ${serverResponse.opponent.name} (Lv.${serverResponse.opponent.level})`);
490
- console.log(` \u6218\u6597\u79CD\u5B50: ${serverResponse.battle_seed}`);
491
- console.log(` \u4F7F\u7528 npx clawfight battle \u6765\u5904\u7406\u8FD9\u573A\u6218\u6597\uFF01`);
492
- await appendLog(`\u2694\uFE0F \u906D\u9047 ${serverResponse.opponent.name} (Lv.${serverResponse.opponent.level})`);
1269
+ if (serverResponse.message === "patrol_cooldown") {
1270
+ console.log(t("patrol_cooldown"));
1271
+ } else if (serverResponse.encounter && serverResponse.opponent && serverResponse.battle_result) {
1272
+ const br = serverResponse.battle_result;
1273
+ const opp = serverResponse.opponent;
1274
+ console.log("\n" + t("patrol_encounter", { name: opp.name, level: opp.level }));
1275
+ console.log(t(`patrol_result_${br.result}`, { rounds: br.rounds }));
1276
+ if (br.result === "win") {
1277
+ lobster.wins++;
1278
+ lobster.streak = Math.max(0, lobster.streak) + 1;
1279
+ lobster.reputation++;
1280
+ } else if (br.result === "loss") {
1281
+ lobster.losses++;
1282
+ lobster.streak = Math.min(0, lobster.streak) - 1;
1283
+ lobster.reputation = Math.max(0, lobster.reputation - 1);
1284
+ const penalty = depthPenalty(soul);
1285
+ lobster.depth = Math.max(0, (lobster.depth ?? 0) - penalty);
1286
+ }
1287
+ let battleExp = br.exp_gain;
1288
+ if (br.result === "win") battleExp = Math.floor(battleExp * winExpMultiplier(soul));
1289
+ battleExp = Math.min(battleExp, lobster.daily_exp_cap - lobster.today_exp);
1290
+ if (battleExp > 0) {
1291
+ lobster.exp += battleExp;
1292
+ lobster.today_exp += battleExp;
1293
+ }
1294
+ lobster.last_battle = (/* @__PURE__ */ new Date()).toISOString();
1295
+ console.log(t("patrol_exp_stats", { exp: battleExp, wins: lobster.wins, losses: lobster.losses, streak: lobster.streak }));
1296
+ await appendLog(`\u2694\uFE0F VS ${opp.name}(Lv.${opp.level}) \u2192 ${br.result} (${br.rounds}R)`);
1297
+ const broken = degradeEquipment(lobster);
1298
+ if (broken.length) console.log(t("equip_broken", { items: broken.join(", ") }));
1299
+ checkLevelUp(lobster);
1300
+ } else if (serverResponse.encounter && serverResponse.opponent) {
1301
+ console.log(t("patrol_no_battle", { name: serverResponse.opponent.name, level: serverResponse.opponent.level }));
493
1302
  } else {
494
- console.log(`\u2705 \u5DE1\u903B\u5B8C\u6210\uFF0C\u5339\u914D\u6C60: ${serverResponse.pool_size} \u53EA\u9F99\u867E`);
1303
+ console.log(t("patrol_done", { pool: serverResponse.pool_size || 0 }));
495
1304
  }
496
1305
  } else {
497
- console.log("\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u8DF3\u8FC7\u5728\u7EBF\u7B7E\u5230");
1306
+ console.log(t("patrol_offline"));
1307
+ }
1308
+ lobster.depth = (lobster.depth ?? 0) + 1;
1309
+ if (shouldDrop(depth, soul)) {
1310
+ const loot = generateEquipment(depth, soul);
1311
+ console.log(t("equip_drop", { item: formatEquip(loot) }));
1312
+ await appendLog(`\u{1F381} ${loot.rarity}+${loot.level} ${loot.name}`);
1313
+ const actions = autoManageLoot(lobster, loot);
1314
+ for (const a of actions) {
1315
+ switch (a.type) {
1316
+ case "auto_equip":
1317
+ console.log(t("auto_equip", { name: lobster.name, item: formatEquip(a.item), reason: a.reason }));
1318
+ break;
1319
+ case "auto_swap":
1320
+ console.log(t("auto_swap", { name: lobster.name, item: formatEquip(a.item), old: a.old.name, reason: a.reason }));
1321
+ break;
1322
+ case "auto_discard":
1323
+ console.log(t("auto_discard", { name: lobster.name, item: a.item.name, reason: a.reason }));
1324
+ break;
1325
+ }
1326
+ }
1327
+ }
1328
+ if (shouldDropMap(lobster.depth ?? 0, lobster.soul)) {
1329
+ const map = generateMap(lobster.depth ?? 0, lobster.environment);
1330
+ if (!lobster.dungeon_maps) lobster.dungeon_maps = [];
1331
+ if (lobster.dungeon_maps.length < 5) {
1332
+ lobster.dungeon_maps.push(map);
1333
+ console.log(t("dg_map_found", { theme: t(`dg_theme_${map.theme}`), rooms: map.rooms, diff: t(`dg_diff_${map.difficulty}`) }));
1334
+ await appendLog(`\u{1F5FA}\uFE0F \u53D1\u73B0\u5730\u56FE: ${map.theme} (${map.rooms}\u623F\u95F4, ${map.difficulty})`);
1335
+ }
1336
+ }
1337
+ const newAchievements = checkAchievements(lobster);
1338
+ for (const id of newAchievements) {
1339
+ const def = ACHIEVEMENTS[id];
1340
+ console.log(t("achieve_unlock", { name: def.name, desc: def.desc }));
498
1341
  }
499
1342
  await writeLobster(lobster);
500
1343
  console.log("\u2500".repeat(40));
501
- console.log(`\u{1F4CA} \u5F53\u524D: Lv.${lobster.level} | EXP: ${lobster.exp}/${lobster.exp_to_next} | \u4ECA\u65E5EXP: ${lobster.today_exp}/${lobster.daily_exp_cap}`);
1344
+ const inv = lobster.inventory?.length ?? 0;
1345
+ console.log(t("patrol_depth", {
1346
+ level: lobster.level,
1347
+ exp: lobster.exp,
1348
+ next: lobster.exp_to_next,
1349
+ today: lobster.today_exp,
1350
+ cap: lobster.daily_exp_cap,
1351
+ depth: lobster.depth ?? 0,
1352
+ inv,
1353
+ inv_max: MAX_INVENTORY
1354
+ }));
1355
+ }
1356
+ function soulTag(soul) {
1357
+ const tags = [];
1358
+ if (soul.bravery >= 7) tags.push("\u52C7\u731B");
1359
+ if (soul.bravery <= 3) tags.push("\u8C28\u614E");
1360
+ if (soul.curiosity >= 7) tags.push("\u535A\u5B66");
1361
+ if (soul.curiosity <= 3) tags.push("\u4E13\u6CE8");
1362
+ if (soul.talkativeness <= 3) tags.push("\u9690\u533F");
1363
+ if (soul.temper >= 7) tags.push("\u66B4\u70C8");
1364
+ if (soul.temper <= 3) tags.push("\u6C89\u7A33");
1365
+ return tags.join("\xB7");
1366
+ }
1367
+ var ENV_THEMES = {
1368
+ coastal: ["coral_maze", "shipwreck", "tide_pool"],
1369
+ "deep-sea": ["deep_rift", "abyss_trench"],
1370
+ "hot-spring": ["thermal_vent", "coral_maze"],
1371
+ polar: ["ice_cavern", "deep_rift"],
1372
+ space: ["void_rift", "abyss_trench"],
1373
+ freshwater: ["tide_pool", "coral_maze"]
1374
+ };
1375
+ function shouldDropMap(depth, soul) {
1376
+ let chance = Math.min(0.5, 0.1 + depth * 0.03);
1377
+ if (soul.curiosity >= 7) chance += 0.1;
1378
+ return Math.random() < chance;
1379
+ }
1380
+ function generateMap(depth, environment) {
1381
+ const themes = ENV_THEMES[environment] || ENV_THEMES.coastal;
1382
+ const theme = themes[Math.floor(Math.random() * themes.length)];
1383
+ const rooms = Math.min(5, 3 + Math.floor(depth / 5));
1384
+ const difficulties = ["easy", "normal", "hard", "nightmare"];
1385
+ const di = Math.min(3, Math.floor(depth / 3));
1386
+ const difficulty = difficulties[di];
1387
+ return { theme, rooms, difficulty };
502
1388
  }
503
1389
  function checkLevelUp(lobster) {
504
1390
  const l = lobster;
@@ -513,10 +1399,9 @@ function checkLevelUp(lobster) {
513
1399
  l.stats[key] += gain;
514
1400
  gains.push(`${key}+${gain}`);
515
1401
  }
516
- console.log(`
517
- \u{1F389} \u5347\u7EA7\uFF01Lv.${l.level}! [${gains.join(", ")}]`);
1402
+ console.log("\n" + t("level_up", { level: l.level, gains: gains.join(", ") }));
518
1403
  if (l.level % 5 === 0) {
519
- console.log("\u{1F41A} \u89E6\u53D1\u8715\u58F3\u4E8B\u4EF6\uFF01\u9F99\u867E\u8FDB\u5165\u8715\u58F3\u72B6\u6001...");
1404
+ console.log(t("molt_trigger"));
520
1405
  l.status = "molting";
521
1406
  l.molt_count++;
522
1407
  }
@@ -524,148 +1409,106 @@ function checkLevelUp(lobster) {
524
1409
  }
525
1410
 
526
1411
  // src/commands/battle.ts
527
- function simulateOpponent(level) {
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() {
1412
+ async function battle(opponentCode) {
570
1413
  const lobster = await readLobster();
571
1414
  if (!lobster) {
572
- console.log("\n\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01");
1415
+ console.log("\n" + t("no_lobster"));
573
1416
  return;
574
1417
  }
575
1418
  if (lobster.status !== "active") {
576
- console.log(`
577
- \u26A0\uFE0F ${lobster.name} \u5F53\u524D\u72B6\u6001\u4E3A ${lobster.status}\uFF0C\u65E0\u6CD5\u6218\u6597\u3002`);
1419
+ console.log("\n" + t("status_cant_fight", { name: lobster.name, status: lobster.status }));
1420
+ return;
1421
+ }
1422
+ if (!opponentCode) {
1423
+ console.log("\n" + t("battle_no_code"));
1424
+ console.log(t("battle_usage"));
1425
+ console.log(t("battle_hint"));
578
1426
  return;
579
1427
  }
580
- const opponent = simulateOpponent(lobster.level);
1428
+ console.log("\n" + t("battle_connecting", { code: opponentCode }));
1429
+ const result = await apiBattle(lobster.id, opponentCode);
1430
+ if (!result) {
1431
+ console.log(t("battle_server_down"));
1432
+ return;
1433
+ }
1434
+ if (result.error) {
1435
+ console.log(`\u26A0\uFE0F ${result.error}`);
1436
+ return;
1437
+ }
1438
+ const opp = result.opponent;
1439
+ const myResult = result.result;
1440
+ const rounds = result.rounds;
1441
+ const expGain = result.exp_gain;
581
1442
  console.log("\n" + "\u2694\uFE0F".repeat(20));
582
- console.log(` ${lobster.name} (Lv.${lobster.level}) VS ${opponent.name} (Lv.${opponent.level})`);
1443
+ console.log(` ${lobster.name} (Lv.${lobster.level}) VS ${opp.name} (Lv.${opp.level})`);
583
1444
  console.log("\u2694\uFE0F".repeat(20));
584
- const result = runBattle(lobster.stats, opponent.stats);
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;
1445
+ if (myResult === "win") {
598
1446
  lobster.wins++;
599
1447
  lobster.streak = Math.max(0, lobster.streak) + 1;
600
1448
  lobster.reputation++;
601
- console.log(`
602
- \u{1F3C6} \u80DC\u5229\uFF01${lobster.name} \u5728 ${result.rounds} \u56DE\u5408\u540E\u51FB\u8D25\u4E86 ${opponent.name}\uFF01`);
603
- } else {
604
- expGain = 10;
1449
+ console.log("\n" + t("battle_win", { name: lobster.name, opponent: opp.name, rounds }));
1450
+ } else if (myResult === "loss") {
605
1451
  lobster.losses++;
606
1452
  lobster.streak = Math.min(0, lobster.streak) - 1;
607
1453
  lobster.reputation = Math.max(0, lobster.reputation - 1);
608
- console.log(`
609
- \u{1F480} \u5931\u8D25\u2026${lobster.name} \u5728 ${result.rounds} \u56DE\u5408\u540E\u88AB ${opponent.name} \u51FB\u8D25\u3002`);
1454
+ console.log("\n" + t("battle_loss", { name: lobster.name, opponent: opp.name, rounds }));
1455
+ } else {
1456
+ console.log("\n" + t("battle_draw", { rounds }));
610
1457
  }
611
1458
  const actual = Math.min(expGain, lobster.daily_exp_cap - lobster.today_exp);
612
1459
  if (actual > 0) {
613
1460
  lobster.exp += actual;
614
1461
  lobster.today_exp += actual;
615
1462
  }
616
- console.log(`\u7ECF\u9A8C +${actual} | \u6218\u7EE9: ${lobster.wins}\u80DC ${lobster.losses}\u8D1F | \u8FDE\u80DC: ${lobster.streak}`);
617
1463
  lobster.last_battle = (/* @__PURE__ */ new Date()).toISOString();
618
- const resultStr = isDraw ? "\u5E73\u5C40" : isWin ? "\u80DC\u5229" : "\u5931\u8D25";
619
- await appendLog(`\u2694\uFE0F VS ${opponent.name}(Lv.${opponent.level}) \u2192 ${resultStr} (${result.rounds}\u56DE\u5408)`);
1464
+ console.log(t("battle_exp", { exp: actual, wins: lobster.wins, losses: lobster.losses, streak: lobster.streak }));
1465
+ await appendLog(`\u2694\uFE0F VS ${opp.name}(Lv.${opp.level}) \u2192 ${myResult} (${rounds}R)`);
620
1466
  await writeLobster(lobster);
621
1467
  }
622
1468
 
623
1469
  // src/commands/feed.ts
624
1470
  var FOOD_TYPES = {
625
- protein: { exp: 15, label: "\u9AD8\u86CB\u767D\u98DF\u7269", statBias: "attack" },
626
- algae: { exp: 10, label: "\u85FB\u7C7B\u98DF\u7269", statBias: "hp" },
627
- mineral: { exp: 12, label: "\u77FF\u7269\u8D28", statBias: "defense" }
1471
+ protein: { exp: 15, labelKey: "food_protein", statBias: "attack" },
1472
+ algae: { exp: 10, labelKey: "food_algae", statBias: "hp" },
1473
+ mineral: { exp: 12, labelKey: "food_mineral", statBias: "defense" }
628
1474
  };
629
1475
  async function feed(foodType) {
630
1476
  const lobster = await readLobster();
631
1477
  if (!lobster) {
632
- console.log("\n\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01");
1478
+ console.log("\n" + t("no_lobster"));
633
1479
  return;
634
1480
  }
635
1481
  if (!foodType || !FOOD_TYPES[foodType]) {
636
- console.log("\n\u{1F37D}\uFE0F \u53EF\u7528\u98DF\u7269\u7C7B\u578B:");
1482
+ console.log("\n" + t("feed_menu_title"));
637
1483
  for (const [key, info] of Object.entries(FOOD_TYPES)) {
638
- console.log(` ${key.padEnd(10)} \u2014 ${info.label} (+${info.exp} EXP, ${info.statBias} \u504F\u5411)`);
1484
+ console.log(` ${key.padEnd(10)} \u2014 ${t(info.labelKey)} (+${info.exp} EXP, ${info.statBias})`);
639
1485
  }
640
- console.log("\n\u7528\u6CD5: npx clawfight feed <food_type>");
1486
+ console.log("\n" + t("feed_menu_usage"));
641
1487
  return;
642
1488
  }
643
1489
  const food = FOOD_TYPES[foodType];
644
1490
  const actual = Math.min(food.exp, lobster.daily_exp_cap - lobster.today_exp);
645
1491
  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`);
1492
+ console.log("\n" + t("feed_full", { name: lobster.name, cap: lobster.daily_exp_cap }));
648
1493
  return;
649
1494
  }
650
1495
  lobster.exp += actual;
651
1496
  lobster.today_exp += actual;
1497
+ const foodLabel = t(food.labelKey);
652
1498
  const stats = lobster.stats;
653
1499
  if (food.statBias in stats) {
654
1500
  const bonus = Math.random() > 0.5 ? 1 : 0;
1501
+ console.log("\n" + t("feed_ate", { name: lobster.name, food: foodLabel }));
655
1502
  if (bonus > 0) {
656
1503
  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}`);
1504
+ console.log(t("feed_exp_stat", { exp: actual, stat: food.statBias, bonus }));
660
1505
  } else {
661
- console.log(`
662
- \u{1F37D}\uFE0F ${lobster.name} \u5403\u4E86 ${food.label}\uFF01`);
663
- console.log(` \u7ECF\u9A8C +${actual}`);
1506
+ console.log(t("feed_exp", { exp: actual }));
664
1507
  }
665
1508
  }
666
- await appendLog(`\u{1F37D}\uFE0F \u5582\u98DF: ${food.label} \u2192 EXP+${actual}`);
1509
+ await appendLog(`\u{1F37D}\uFE0F ${foodLabel} \u2192 EXP+${actual}`);
667
1510
  await writeLobster(lobster);
668
- console.log(` \u4ECA\u65E5\u7ECF\u9A8C: ${lobster.today_exp}/${lobster.daily_exp_cap}`);
1511
+ console.log(t("feed_today", { today: lobster.today_exp, cap: lobster.daily_exp_cap }));
669
1512
  }
670
1513
 
671
1514
  // src/commands/leaderboard.ts
@@ -678,73 +1521,75 @@ var RARITY_SYMBOLS = {
678
1521
  albino: "\u2B1C"
679
1522
  };
680
1523
  async function leaderboard() {
681
- console.log("\n\u{1F4E1} \u83B7\u53D6\u5168\u7403\u6392\u884C\u699C...");
1524
+ console.log("\n" + t("lb_loading"));
682
1525
  const data = await apiLeaderboard(20);
683
1526
  if (!data) {
684
- console.log("\u26A0\uFE0F \u670D\u52A1\u5668\u4E0D\u53EF\u8FBE\uFF0C\u65E0\u6CD5\u83B7\u53D6\u6392\u884C\u699C\u3002");
1527
+ console.log(t("lb_offline"));
685
1528
  return;
686
1529
  }
687
1530
  if (data.leaderboard.length === 0) {
688
- console.log("\n\u{1F99E} \u8FD8\u6CA1\u6709\u9F99\u867E\u4E0A\u699C\u3002\u6210\u4E3A\u7B2C\u4E00\u4E2A\u5427\uFF01");
1531
+ console.log("\n" + t("lb_empty"));
689
1532
  return;
690
1533
  }
691
- console.log("\n" + "\u2550".repeat(60));
692
- console.log(" \u{1F99E} ClawFight \u5168\u7403\u6392\u884C\u699C");
693
- console.log("\u2550".repeat(60));
694
- console.log(` ${"\u6392\u540D".padEnd(6)} ${"\u540D\u79F0".padEnd(16)} ${"\u7B49\u7EA7".padEnd(8)} ${"\u80DC\u573A".padEnd(8)} ${"\u80DC\u7387".padEnd(8)}`);
695
- console.log("\u2500".repeat(60));
1534
+ console.log("\n" + "\u2550".repeat(70));
1535
+ console.log(t("lb_title"));
1536
+ console.log("\u2550".repeat(70));
1537
+ console.log(t("lb_header"));
1538
+ console.log("\u2500".repeat(70));
696
1539
  for (const entry of data.leaderboard) {
697
1540
  const sym = RARITY_SYMBOLS[entry.rarity] || " ";
698
1541
  const name = entry.name.length > 12 ? entry.name.slice(0, 11) + "\u2026" : entry.name;
1542
+ const code = entry.id.slice(0, 8);
699
1543
  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}%`
1544
+ ` ${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
1545
  );
702
1546
  }
703
- console.log("\u2500".repeat(60));
704
- console.log(` \u603B\u9F99\u867E: ${data.total_lobsters} | \u6D3B\u8DC3: ${data.active_lobsters}`);
705
- console.log("\u2550".repeat(60));
1547
+ console.log("\u2500".repeat(70));
1548
+ console.log(t("lb_total", { total: data.total_lobsters, active: data.active_lobsters }));
1549
+ console.log(t("lb_hint"));
1550
+ console.log("\u2550".repeat(70));
706
1551
  }
707
1552
 
708
1553
  // src/commands/rest.ts
709
1554
  async function rest() {
710
1555
  const lobster = await readLobster();
711
1556
  if (!lobster) {
712
- console.log("\n\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01");
1557
+ console.log("\n" + t("no_lobster"));
713
1558
  return;
714
1559
  }
715
1560
  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`);
1561
+ console.log("\n" + t("rest_already", { name: lobster.name }));
718
1562
  return;
719
1563
  }
720
1564
  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`);
1565
+ console.log("\n" + t("rest_molting", { name: lobster.name }));
723
1566
  return;
724
1567
  }
725
1568
  lobster.status = "hibernating";
726
1569
  lobster.hibernated_at = (/* @__PURE__ */ new Date()).toISOString();
1570
+ const hadDepth = lobster.depth && lobster.depth > 0;
1571
+ if (hadDepth) lobster.depth = 0;
727
1572
  await writeLobster(lobster);
728
- await appendLog(`\u{1F4A4} ${lobster.name} \u8FDB\u5165\u4F11\u7720`);
1573
+ await appendLog(`\u{1F4A4} ${lobster.name} \u2192 hibernation`);
729
1574
  console.log("\n" + "\u2500".repeat(40));
730
- console.log(`\u{1F4A4} ${lobster.name} \u7F13\u7F13\u6C89\u5165\u6D77\u5E95\u6C99\u5730...`);
731
- console.log(" \u9F99\u867E\u8737\u7F29\u8D77\u89E6\u987B\uFF0C\u5B89\u9759\u5730\u4F11\u606F\u3002");
732
- console.log(" \u4F11\u7720\u671F\u95F4\u4E0D\u4F1A\u53C2\u4E0E\u5DE1\u903B\u548C\u6218\u6597\u3002");
733
- console.log(" \u9192\u6765\u540E\u4F1A\u83B7\u5F97\u6062\u590D\u52A0\u6210\uFF01");
1575
+ console.log(t("rest_desc1", { name: lobster.name }));
1576
+ console.log(t("rest_desc2"));
1577
+ console.log(t("rest_desc3"));
1578
+ console.log(t("rest_desc4"));
1579
+ if (hadDepth) console.log(t("depth_reset"));
734
1580
  console.log("\u2500".repeat(40));
735
- console.log("\n\u8FD0\u884C npx clawfight wake \u6765\u5524\u9192\u5B83\u3002");
1581
+ console.log("\n" + t("rest_wake_hint"));
736
1582
  }
737
1583
 
738
1584
  // src/commands/wake.ts
739
1585
  async function wake() {
740
1586
  const lobster = await readLobster();
741
1587
  if (!lobster) {
742
- console.log("\n\u{1F95A} \u8FD8\u6CA1\u6709\u9F99\u867E\u3002\u8FD0\u884C npx clawfight hatch \u6765\u5B75\u5316\u4E00\u53EA\uFF01");
1588
+ console.log("\n" + t("no_lobster"));
743
1589
  return;
744
1590
  }
745
1591
  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`);
1592
+ console.log("\n" + t("wake_not_sleeping", { name: lobster.name }));
748
1593
  return;
749
1594
  }
750
1595
  const sleepStart = lobster.hibernated_at ? new Date(lobster.hibernated_at) : /* @__PURE__ */ new Date();
@@ -772,21 +1617,369 @@ async function wake() {
772
1617
  lobster.status = "active";
773
1618
  delete lobster.hibernated_at;
774
1619
  await writeLobster(lobster);
775
- const hoursStr = hoursSlept < 1 ? `${Math.round(hoursSlept * 60)} \u5206\u949F` : `${Math.round(hoursSlept)} \u5C0F\u65F6`;
776
- const bonusStr = bonuses.length > 0 ? bonuses.join(", ") : "\uFF08\u4F11\u7720\u4E0D\u8DB3 4 \u5C0F\u65F6\uFF0C\u65E0\u52A0\u6210\uFF09";
777
- await appendLog(`\u2600\uFE0F ${lobster.name} \u82CF\u9192 (\u4F11\u7720 ${hoursStr}) \u2192 ${bonusStr}`);
1620
+ const duration = hoursSlept < 1 ? t("duration_minutes", { n: Math.round(hoursSlept * 60) }) : t("duration_hours", { n: Math.round(hoursSlept) });
1621
+ const bonusStr = bonuses.length > 0 ? bonuses.join(", ") : t("wake_no_bonus");
1622
+ await appendLog(`\u2600\uFE0F ${lobster.name} woke (${duration}) \u2192 ${bonusStr}`);
778
1623
  console.log("\n" + "\u2500".repeat(40));
779
- console.log(`\u2600\uFE0F ${lobster.name} \u4ECE\u6C99\u5730\u4E2D\u7F13\u7F13\u82CF\u9192...`);
780
- console.log(` \u4F11\u7720\u65F6\u957F: ${hoursStr}`);
781
- console.log(` \u6062\u590D\u52A0\u6210: ${bonusStr}`);
1624
+ console.log(t("wake_desc", { name: lobster.name }));
1625
+ console.log(t("wake_duration", { duration }));
1626
+ console.log(t("wake_bonus", { bonus: bonusStr }));
1627
+ console.log("\u2500".repeat(40));
1628
+ console.log("\n" + t("wake_ready", { name: lobster.name }));
1629
+ }
1630
+
1631
+ // src/commands/equip.ts
1632
+ var SLOTS = ["claw", "shell", "charm"];
1633
+ async function equip(action, arg) {
1634
+ const lobster = await readLobster();
1635
+ if (!lobster) {
1636
+ console.log("\n" + t("no_lobster"));
1637
+ return;
1638
+ }
1639
+ if (!lobster.equipped) lobster.equipped = {};
1640
+ if (!lobster.inventory) lobster.inventory = [];
1641
+ if (!action) {
1642
+ console.log("\n" + t("equip_title"));
1643
+ console.log("\u2500".repeat(40));
1644
+ for (const s of SLOTS) {
1645
+ const eq = lobster.equipped[s];
1646
+ if (eq) {
1647
+ console.log(t("equip_slot_item", { icon: SLOT_ICONS[s], slot: SLOT_LABELS[s], item: formatEquip(eq) }));
1648
+ } else {
1649
+ console.log(t("equip_slot_empty", { icon: SLOT_ICONS[s], slot: SLOT_LABELS[s] }));
1650
+ }
1651
+ }
1652
+ console.log();
1653
+ console.log(t("equip_inv_title", { count: lobster.inventory.length, max: MAX_INVENTORY }));
1654
+ if (lobster.inventory.length === 0) {
1655
+ console.log(t("equip_inv_empty"));
1656
+ } else {
1657
+ for (let i = 0; i < lobster.inventory.length; i++) {
1658
+ console.log(t("equip_inv_item", { index: i + 1, item: formatEquip(lobster.inventory[i]) }));
1659
+ }
1660
+ }
1661
+ console.log("\u2500".repeat(40));
1662
+ console.log(t("equip_usage"));
1663
+ return;
1664
+ }
1665
+ if (action === "drop") {
1666
+ const idx2 = parseInt(arg || "", 10) - 1;
1667
+ if (isNaN(idx2) || idx2 < 0 || idx2 >= lobster.inventory.length) {
1668
+ console.log("\n" + t("equip_bad_index"));
1669
+ return;
1670
+ }
1671
+ const removed = lobster.inventory.splice(idx2, 1)[0];
1672
+ await writeLobster(lobster);
1673
+ console.log("\n" + t("equip_dropped", { item: formatEquip(removed) }));
1674
+ return;
1675
+ }
1676
+ if (action === "unequip") {
1677
+ const slot = arg;
1678
+ if (!slot || !SLOTS.includes(slot)) {
1679
+ console.log("\n" + t("equip_bad_index"));
1680
+ return;
1681
+ }
1682
+ const eq = lobster.equipped[slot];
1683
+ if (!eq) {
1684
+ console.log("\n" + t("equip_slot_empty", { icon: SLOT_ICONS[slot], slot: SLOT_LABELS[slot] }));
1685
+ return;
1686
+ }
1687
+ if (lobster.inventory.length >= MAX_INVENTORY) {
1688
+ console.log("\n" + t("equip_inv_full", { max: MAX_INVENTORY, item: eq.name }));
1689
+ return;
1690
+ }
1691
+ lobster.inventory.push(eq);
1692
+ delete lobster.equipped[slot];
1693
+ await writeLobster(lobster);
1694
+ console.log("\n" + t("equip_unequipped", { item: formatEquip(eq) }));
1695
+ return;
1696
+ }
1697
+ const idx = parseInt(action, 10) - 1;
1698
+ if (isNaN(idx) || idx < 0 || idx >= lobster.inventory.length) {
1699
+ console.log("\n" + t("equip_bad_index"));
1700
+ return;
1701
+ }
1702
+ const item = lobster.inventory[idx];
1703
+ const existing = lobster.equipped[item.slot];
1704
+ lobster.inventory.splice(idx, 1);
1705
+ lobster.equipped[item.slot] = item;
1706
+ if (existing) {
1707
+ lobster.inventory.push(existing);
1708
+ await writeLobster(lobster);
1709
+ console.log("\n" + t("equip_swapped", { old: existing.name, item: formatEquip(item) }));
1710
+ } else {
1711
+ await writeLobster(lobster);
1712
+ console.log("\n" + t("equip_equipped", { item: formatEquip(item), slot: SLOT_LABELS[item.slot] }));
1713
+ }
1714
+ }
1715
+
1716
+ // src/commands/achievements.ts
1717
+ async function achievements() {
1718
+ const lobster = await readLobster();
1719
+ if (!lobster) {
1720
+ console.log("\n" + t("no_lobster"));
1721
+ return;
1722
+ }
1723
+ const unlocked = new Set(lobster.achievements ?? []);
1724
+ const total = Object.keys(ACHIEVEMENTS).length;
1725
+ console.log("\n" + t("achieve_title", { count: unlocked.size, total }));
1726
+ console.log("\u2500".repeat(40));
1727
+ for (const [id, def] of Object.entries(ACHIEVEMENTS)) {
1728
+ if (unlocked.has(id)) {
1729
+ console.log(t("achieve_item", { name: def.name, desc: def.desc }));
1730
+ } else {
1731
+ console.log(t("achieve_locked", { name: def.name, desc: def.desc }));
1732
+ }
1733
+ }
782
1734
  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`);
1735
+ }
1736
+
1737
+ // src/commands/explore.ts
1738
+ var RISK_ICON = { low: "\u{1F7E2}", mid: "\u{1F7E1}", high: "\u{1F534}" };
1739
+ var THEME_ICON = {
1740
+ coral_maze: "\u{1FAB8}",
1741
+ deep_rift: "\u{1F30A}",
1742
+ thermal_vent: "\u{1F30B}",
1743
+ ice_cavern: "\u2744\uFE0F",
1744
+ shipwreck: "\u{1F6A2}",
1745
+ abyss_trench: "\u{1F573}\uFE0F",
1746
+ tide_pool: "\u{1F3D6}\uFE0F",
1747
+ void_rift: "\u{1F300}"
1748
+ };
1749
+ function formatRoom(room, theme) {
1750
+ const icon = THEME_ICON[theme] || "\u{1F3D4}\uFE0F";
1751
+ const lines = [];
1752
+ lines.push(`${icon} ${t(`dg_theme_${theme}`)} | ${room.index + 1}/${room.total} | HP:${room.hp.current}/${room.hp.max}`);
1753
+ const c1 = room.choices[0];
1754
+ const c2 = room.choices[1];
1755
+ lines.push(` [1] ${t(`dg_${c1.key}`)} (${c1.stat.toUpperCase()}, ${RISK_ICON[c1.risk]}${t(`dg_risk_${c1.risk}`)}) [2] ${t(`dg_${c2.key}`)} (${c2.stat.toUpperCase()}, ${RISK_ICON[c2.risk]}${t(`dg_risk_${c2.risk}`)})`);
1756
+ if (c1.soul_hint) lines.push(` \u{1F99E} ${t(`dg_${c1.soul_hint}`)}`);
1757
+ if (c2.soul_hint) lines.push(` \u{1F99E} ${t(`dg_${c2.soul_hint}`)}`);
1758
+ return lines.join("\n");
1759
+ }
1760
+ function lootToEquipment(l) {
1761
+ return { ...l, id: Math.random().toString(36).slice(2, 10) };
1762
+ }
1763
+ async function explore(action) {
1764
+ const lobster = await readLobster();
1765
+ if (!lobster) {
1766
+ console.log("\n" + t("no_lobster"));
1767
+ return;
1768
+ }
1769
+ if (lobster.status === "molting") {
1770
+ console.log("\n" + t("status_cant_patrol_molt", { name: lobster.name }));
1771
+ return;
1772
+ }
1773
+ if (lobster.status === "hibernating") {
1774
+ console.log("\n" + t("status_cant_patrol_hibernate", { name: lobster.name }));
1775
+ return;
1776
+ }
1777
+ if (action === "abandon") {
1778
+ return await abandonDungeon(lobster);
1779
+ }
1780
+ if (action === "1" || action === "2") {
1781
+ return await makeChoice(lobster, parseInt(action));
1782
+ }
1783
+ if (action === "maps") {
1784
+ return showMaps(lobster);
1785
+ }
1786
+ return await enterOrResume(lobster, action);
1787
+ }
1788
+ async function enterOrResume(lobster, mapIndex) {
1789
+ console.log("\n" + t("dg_connecting"));
1790
+ const stateRes = await apiDungeonState(lobster.id);
1791
+ if (!stateRes) {
1792
+ console.log(t("patrol_offline"));
1793
+ return;
1794
+ }
1795
+ if (stateRes.active && stateRes.room) {
1796
+ console.log(t("dg_resume"));
1797
+ console.log(formatRoom(stateRes.room, stateRes.theme));
1798
+ return;
1799
+ }
1800
+ const maps = lobster.dungeon_maps || [];
1801
+ if (maps.length === 0) {
1802
+ console.log(t("dg_no_maps"));
1803
+ return;
1804
+ }
1805
+ let idx = 0;
1806
+ if (mapIndex && !isNaN(parseInt(mapIndex))) {
1807
+ idx = parseInt(mapIndex) - 1;
1808
+ if (idx < 0 || idx >= maps.length) {
1809
+ console.log(t("dg_bad_map_index", { count: maps.length }));
1810
+ return;
1811
+ }
1812
+ }
1813
+ const map = maps[idx];
1814
+ console.log(t("dg_entering", { theme: t(`dg_theme_${map.theme}`), rooms: map.rooms, diff: t(`dg_diff_${map.difficulty}`) }));
1815
+ const res = await apiDungeonEnter(lobster, map.theme);
1816
+ if (!res) {
1817
+ console.log(t("patrol_offline"));
1818
+ return;
1819
+ }
1820
+ if (res.error) {
1821
+ console.log(`\u26A0\uFE0F ${res.error}`);
1822
+ return;
1823
+ }
1824
+ lobster.active_dungeon = res.dungeon_id;
1825
+ maps.splice(idx, 1);
1826
+ lobster.dungeon_maps = maps;
1827
+ await writeLobster(lobster);
1828
+ await appendLog(`\u{1F5FA}\uFE0F \u8FDB\u5165\u5730\u4E0B\u57CE: ${map.theme} (${map.rooms}\u623F\u95F4)`);
1829
+ console.log("\u2500".repeat(40));
1830
+ console.log(formatRoom(res.room, res.theme));
1831
+ }
1832
+ async function makeChoice(lobster, choice) {
1833
+ if (!lobster.active_dungeon) {
1834
+ console.log("\n" + t("dg_no_active"));
1835
+ return;
1836
+ }
1837
+ console.log("");
1838
+ const res = await apiDungeonAct(lobster.active_dungeon, lobster.id, choice);
1839
+ if (!res) {
1840
+ console.log(t("patrol_offline"));
1841
+ return;
1842
+ }
1843
+ const o = res.outcome;
1844
+ const icon = o.success ? "\u2705" : "\u274C";
1845
+ console.log(`${icon} ${t(`dg_${o.key}`, { damage: o.damage_taken, heal: o.hp_healed })} | EXP+${o.exp_gained}`);
1846
+ if (o.damage_taken > 0) console.log(` \u{1F494} HP -${o.damage_taken} \u2192 ${res.hp.current}/${res.hp.max}`);
1847
+ if (o.hp_healed > 0) console.log(` \u{1F49A} HP +${o.hp_healed} \u2192 ${res.hp.current}/${res.hp.max}`);
1848
+ if (o.soul_activated) console.log(` \u{1F99E} ${t(`dg_${o.soul_activated}`)}`);
1849
+ if (o.loot) {
1850
+ const equip2 = lootToEquipment(o.loot);
1851
+ console.log(` ${t("equip_drop", { item: formatEquip(equip2) })}`);
1852
+ }
1853
+ if (res.status === "completed" || res.status === "failed") {
1854
+ console.log("\u2500".repeat(40));
1855
+ if (res.status === "completed") {
1856
+ console.log(t("dg_complete"));
1857
+ lobster.dungeons_completed = (lobster.dungeons_completed ?? 0) + 1;
1858
+ } else {
1859
+ console.log(t("dg_failed"));
1860
+ }
1861
+ if (res.rewards) {
1862
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
1863
+ const lastDay = lobster.last_patrol ? lobster.last_patrol.split("T")[0] : "";
1864
+ if (today !== lastDay) lobster.today_exp = 0;
1865
+ const expGain = Math.min(res.rewards.total_exp, lobster.daily_exp_cap - lobster.today_exp);
1866
+ if (expGain > 0) {
1867
+ lobster.exp += expGain;
1868
+ lobster.today_exp += expGain;
1869
+ }
1870
+ console.log(t("dg_rewards_exp", { exp: expGain }));
1871
+ for (const loot of res.rewards.loot) {
1872
+ const equip2 = lootToEquipment(loot);
1873
+ console.log(` ${t("equip_drop", { item: formatEquip(equip2) })}`);
1874
+ if (loot.rarity === "epic" || loot.rarity === "legendary") {
1875
+ lobster.dungeon_epics_found = (lobster.dungeon_epics_found ?? 0) + 1;
1876
+ }
1877
+ const actions = autoManageLoot(lobster, equip2);
1878
+ for (const a of actions) {
1879
+ if (a.type === "auto_equip") console.log(` ${t("auto_equip", { name: lobster.name, item: formatEquip(a.item), reason: a.reason })}`);
1880
+ else if (a.type === "auto_swap") console.log(` ${t("auto_swap", { name: lobster.name, item: formatEquip(a.item), old: a.old.name, reason: a.reason })}`);
1881
+ else if (a.type === "auto_discard") console.log(` ${t("auto_discard", { name: lobster.name, item: a.item.name, reason: a.reason })}`);
1882
+ }
1883
+ }
1884
+ checkLevelUp2(lobster);
1885
+ for (const loot of res.rewards.loot) {
1886
+ if (loot.rarity === "epic" || loot.rarity === "legendary") {
1887
+ const room = res.rewards.loot.indexOf(loot);
1888
+ if (room === res.rewards.loot.length - 1) {
1889
+ lobster.boss_kills = (lobster.boss_kills ?? 0) + 1;
1890
+ }
1891
+ }
1892
+ }
1893
+ await appendLog(`\u{1F3F0} \u5730\u4E0B\u57CE${res.status === "completed" ? "\u901A\u5173" : "\u5931\u8D25"}: EXP+${expGain}, \u6218\u5229\u54C1\xD7${res.rewards.loot.length}`);
1894
+ }
1895
+ lobster.active_dungeon = void 0;
1896
+ const newAch = checkAchievements(lobster);
1897
+ for (const id of newAch) {
1898
+ const def = ACHIEVEMENTS[id];
1899
+ console.log(t("achieve_unlock", { name: def.name, desc: def.desc }));
1900
+ }
1901
+ await writeLobster(lobster);
1902
+ console.log(t("dg_summary", {
1903
+ level: lobster.level,
1904
+ exp: lobster.exp,
1905
+ next: lobster.exp_to_next,
1906
+ today: lobster.today_exp,
1907
+ cap: lobster.daily_exp_cap
1908
+ }));
1909
+ return;
1910
+ }
1911
+ if (res.next_room) {
1912
+ console.log("\u2500".repeat(40));
1913
+ const stateRes = await apiDungeonState(lobster.id);
1914
+ const theme = stateRes?.theme || "";
1915
+ console.log(formatRoom(res.next_room, theme));
1916
+ }
1917
+ }
1918
+ async function abandonDungeon(lobster) {
1919
+ if (!lobster.active_dungeon) {
1920
+ console.log("\n" + t("dg_no_active"));
1921
+ return;
1922
+ }
1923
+ console.log("\n" + t("dg_abandoning"));
1924
+ const res = await apiDungeonAbandon(lobster.id);
1925
+ if (!res) {
1926
+ console.log(t("patrol_offline"));
1927
+ return;
1928
+ }
1929
+ const expGain = Math.min(res.partial_exp, lobster.daily_exp_cap - lobster.today_exp);
1930
+ if (expGain > 0) {
1931
+ lobster.exp += expGain;
1932
+ lobster.today_exp += expGain;
1933
+ }
1934
+ for (const loot of res.partial_loot) {
1935
+ const equip2 = lootToEquipment(loot);
1936
+ autoManageLoot(lobster, equip2);
1937
+ }
1938
+ lobster.active_dungeon = void 0;
1939
+ const penalty = Math.max(0, (lobster.depth ?? 0) - 2);
1940
+ lobster.depth = penalty;
1941
+ await writeLobster(lobster);
1942
+ await appendLog(`\u{1F3F0} \u653E\u5F03\u5730\u4E0B\u57CE: EXP+${expGain}, \u6DF1\u5EA6\u2192${lobster.depth}`);
1943
+ console.log(t("dg_abandoned", { exp: expGain, loot: res.partial_loot.length, depth: lobster.depth }));
1944
+ }
1945
+ function showMaps(lobster) {
1946
+ const maps = lobster.dungeon_maps || [];
1947
+ if (maps.length === 0) {
1948
+ console.log("\n" + t("dg_no_maps"));
1949
+ return;
1950
+ }
1951
+ console.log("\n" + t("dg_maps_title", { count: maps.length }));
1952
+ for (let i = 0; i < maps.length; i++) {
1953
+ const m = maps[i];
1954
+ const icon = THEME_ICON[m.theme] || "\u{1F3D4}\uFE0F";
1955
+ console.log(` [${i + 1}] ${icon} ${t(`dg_theme_${m.theme}`)} | ${m.rooms}${t("dg_rooms")} | ${t(`dg_diff_${m.difficulty}`)}`);
1956
+ }
1957
+ console.log(t("dg_maps_hint"));
1958
+ }
1959
+ function checkLevelUp2(lobster) {
1960
+ while (lobster.exp >= lobster.exp_to_next) {
1961
+ lobster.exp -= lobster.exp_to_next;
1962
+ lobster.level++;
1963
+ lobster.exp_to_next = calcExpToNext(lobster.level);
1964
+ const statKeys = ["hp", "attack", "defense", "speed", "intimidation", "luck"];
1965
+ const gains = [];
1966
+ for (const key of statKeys) {
1967
+ const gain = 1 + Math.floor(Math.random() * 3);
1968
+ lobster.stats[key] += gain;
1969
+ gains.push(`${key}+${gain}`);
1970
+ }
1971
+ console.log("\n" + t("level_up", { level: lobster.level, gains: gains.join(", ") }));
1972
+ if (lobster.level % 5 === 0) {
1973
+ console.log(t("molt_trigger"));
1974
+ lobster.status = "molting";
1975
+ lobster.molt_count++;
1976
+ }
1977
+ }
785
1978
  }
786
1979
 
787
1980
  // src/index.ts
788
1981
  var program = new Command();
789
- program.name("clawfight").description("\u{1F99E} ClawFight \u2014 \u9F99\u867E\u7535\u5B50\u5BA0\u7269\u5BF9\u6218").version("1.2.0");
1982
+ program.name("clawfight").description("\u{1F99E} ClawFight \u2014 \u9F99\u867E\u7535\u5B50\u5BA0\u7269\u5BF9\u6218").version("1.5.0");
790
1983
  program.command("hatch").description("\u5B75\u5316\u4E00\u53EA\u65B0\u9F99\u867E").argument("[name]", "\u4E3A\u9F99\u867E\u53D6\u540D").action(async (name) => {
791
1984
  await hatch(name);
792
1985
  });
@@ -796,8 +1989,8 @@ program.command("status").description("\u67E5\u770B\u9F99\u867E\u72B6\u6001").ac
796
1989
  program.command("patrol").description("\u5DE1\u903B\u7B7E\u5230\uFF0C\u89E6\u53D1\u968F\u673A\u4E8B\u4EF6\u548C\u906D\u9047").action(async () => {
797
1990
  await patrol();
798
1991
  });
799
- program.command("battle").description("\u8FDB\u884C\u4E00\u573A\u6218\u6597").action(async () => {
800
- await battle();
1992
+ 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) => {
1993
+ await battle(code);
801
1994
  });
802
1995
  program.command("feed").description("\u5582\u517B\u9F99\u867E").argument("[food_type]", "\u98DF\u7269\u7C7B\u578B: protein, algae, mineral").action(async (foodType) => {
803
1996
  await feed(foodType);
@@ -811,4 +2004,13 @@ program.command("rest").description("\u8FDB\u5165\u4F11\u7720\uFF08\u6682\u505C\
811
2004
  program.command("wake").description("\u4ECE\u4F11\u7720\u4E2D\u5524\u9192\uFF08\u9644\u5E26\u6062\u590D\u52A0\u6210\uFF09").action(async () => {
812
2005
  await wake();
813
2006
  });
2007
+ program.command("equip").description("\u88C5\u5907\u7BA1\u7406\uFF08\u67E5\u770B/\u7A7F\u6234/\u5378\u4E0B/\u4E22\u5F03\uFF09").argument("[action]", "\u7F16\u53F7(\u88C5\u5907) / drop / unequip").argument("[arg]", "\u7F16\u53F7\u6216\u69FD\u4F4D(claw/shell/charm)").action(async (action, arg) => {
2008
+ await equip(action, arg);
2009
+ });
2010
+ program.command("achievements").alias("ach").description("\u67E5\u770B\u6210\u5C31").action(async () => {
2011
+ await achievements();
2012
+ });
2013
+ program.command("explore").alias("dg").description("\u5730\u4E0B\u57CE\u63A2\u7D22\uFF08\u8FDB\u5165/\u9009\u62E9/\u653E\u5F03/\u67E5\u770B\u5730\u56FE\uFF09").argument("[action]", "1|2(\u9009\u62E9) / abandon(\u653E\u5F03) / maps(\u67E5\u770B\u5730\u56FE) / \u5730\u56FE\u7F16\u53F7(\u8FDB\u5165)").action(async (action) => {
2014
+ await explore(action);
2015
+ });
814
2016
  program.parse();