@2025-6-19/clawfight 1.3.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 +1059 -25
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -148,6 +148,29 @@ ${archetype.values}
148
148
  }
149
149
 
150
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;
151
174
  var RARITY_WEIGHTS = {
152
175
  common: 70,
153
176
  calico: 20,
@@ -238,7 +261,128 @@ var zh = {
238
261
  wake_no_bonus: "\uFF08\u4F11\u7720\u4E0D\u8DB3 4 \u5C0F\u65F6\uFF0C\u65E0\u52A0\u6210\uFF09",
239
262
  wake_ready: "\u{1F7E2} {name} \u7CBE\u795E\u7115\u53D1\uFF0C\u51C6\u5907\u597D\u518D\u6B21\u51FA\u51FB\uFF01",
240
263
  duration_minutes: "{n} \u5206\u949F",
241
- duration_hours: "{n} \u5C0F\u65F6"
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"
242
386
  };
243
387
  var en = {
244
388
  no_lobster: "\u{1F95A} No lobster yet. Run npx clawfight hatch to hatch one!",
@@ -309,7 +453,128 @@ var en = {
309
453
  wake_no_bonus: "(Less than 4 hours, no bonus)",
310
454
  wake_ready: "\u{1F7E2} {name} is refreshed and ready to go!",
311
455
  duration_minutes: "{n} min",
312
- duration_hours: "{n} hours"
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"
313
578
  };
314
579
  function detectLocale() {
315
580
  const lang = process.env.LANG || process.env.LANGUAGE || process.env.LC_ALL || "";
@@ -394,7 +659,15 @@ async function hatch(name) {
394
659
  last_patrol: "",
395
660
  last_battle: "",
396
661
  today_exp: 0,
397
- 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
398
671
  };
399
672
  await writeLobster(lobster);
400
673
  const soulMd = buildSoulMarkdown(lobsterName, soul, rarity, "coastal");
@@ -419,6 +692,191 @@ async function hatch(name) {
419
692
  console.log(t("hatch_next"));
420
693
  }
421
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})`;
878
+ }
879
+
422
880
  // src/commands/status.ts
423
881
  function bar(current, max, width = 20) {
424
882
  const filled = Math.round(current / Math.max(max, 1) * width);
@@ -433,6 +891,7 @@ async function status() {
433
891
  const totalBattles = lobster.wins + lobster.losses;
434
892
  const winRate = totalBattles > 0 ? Math.round(lobster.wins / totalBattles * 100) : 0;
435
893
  const expPct = lobster.exp_to_next > 0 ? Math.round(lobster.exp / lobster.exp_to_next * 100) : 0;
894
+ const eff = getEffectiveStats(lobster);
436
895
  const statusEmoji = {
437
896
  active: "\u{1F7E2} \u6D3B\u8DC3",
438
897
  molting: "\u{1F7E1} \u8715\u58F3\u4E2D",
@@ -443,28 +902,54 @@ async function status() {
443
902
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
444
903
  console.log(`\u2502 \u7B49\u7EA7: Lv.${String(lobster.level).padEnd(5)} \u7A00\u6709\u5EA6: ${(RARITY_LABELS[lobster.rarity] || lobster.rarity).padEnd(15)}\u2502`);
445
904
  console.log(`\u2502 \u72B6\u6001: ${(statusEmoji[lobster.status] || lobster.status).padEnd(36)}\u2502`);
446
- 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`);
447
906
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
448
907
  console.log(`\u2502 EXP: ${bar(lobster.exp, lobster.exp_to_next)} ${String(expPct).padStart(3)}% \u2502`);
449
908
  console.log(`\u2502 ${String(lobster.exp).padStart(5)} / ${String(lobster.exp_to_next).padEnd(28)}\u2502`);
450
909
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
451
- console.log(`\u2502 \u2764\uFE0F HP: ${String(lobster.stats.hp).padEnd(6)} \u2694\uFE0F ATK: ${String(lobster.stats.attack).padEnd(15)}\u2502`);
452
- console.log(`\u2502 \u{1F6E1}\uFE0F DEF: ${String(lobster.stats.defense).padEnd(6)} \u{1F4A8} SPD: ${String(lobster.stats.speed).padEnd(15)}\u2502`);
453
- 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`);
454
913
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
455
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`);
456
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
+ }
457
932
  console.log("\u251C" + "\u2500".repeat(44) + "\u2524");
458
933
  console.log(`\u2502 \u6027\u683C: \u2502`);
459
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
+ }
460
947
  console.log("\u2514" + "\u2500".repeat(44) + "\u2518");
461
948
  console.log("\n" + t("status_battle_code", { code: lobster.id.slice(0, 8) }));
462
949
  const soulText = await readSoul();
463
950
  if (soulText) {
464
951
  const firstLine = soulText.split("\n").find((l) => l.trim() && !l.startsWith("#"));
465
- if (firstLine) {
466
- console.log(`\u{1F4DC} \u7075\u9B42: ${firstLine.trim().slice(0, 60)}`);
467
- }
952
+ if (firstLine) console.log(`\u{1F4DC} \u7075\u9B42: ${firstLine.trim().slice(0, 60)}`);
468
953
  }
469
954
  }
470
955
 
@@ -472,7 +957,7 @@ async function status() {
472
957
  import { createHash } from "crypto";
473
958
  var API_BASE = "https://api.clawfight.online";
474
959
  function statsHash(lobster) {
475
- const raw = JSON.stringify(lobster.stats);
960
+ const raw = JSON.stringify(getEffectiveStats(lobster));
476
961
  return createHash("sha256").update(raw).digest("hex");
477
962
  }
478
963
  function getProxyUrl() {
@@ -507,7 +992,7 @@ async function apiPatrol(lobster) {
507
992
  lobster_id: lobster.id,
508
993
  level: lobster.level,
509
994
  stats_hash: statsHash(lobster),
510
- stats: lobster.stats,
995
+ stats: getEffectiveStats(lobster),
511
996
  environment: lobster.environment,
512
997
  name: lobster.name,
513
998
  color: lobster.rarity,
@@ -556,6 +1041,74 @@ async function apiLeaderboard(limit = 20) {
556
1041
  return null;
557
1042
  }
558
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
+ }
559
1112
 
560
1113
  // src/lib/events.ts
561
1114
  import { readFile as readFile2 } from "fs/promises";
@@ -595,11 +1148,11 @@ async function rollEvent(lobster) {
595
1148
  });
596
1149
  if (candidates.length === 0) return null;
597
1150
  const totalWeight = candidates.reduce((s, c) => s + c.probability, 0);
598
- let pick = Math.random() * totalWeight;
1151
+ let pick2 = Math.random() * totalWeight;
599
1152
  let chosen = null;
600
1153
  for (const c of candidates) {
601
- pick -= c.probability;
602
- if (pick <= 0) {
1154
+ pick2 -= c.probability;
1155
+ if (pick2 <= 0) {
603
1156
  chosen = c;
604
1157
  break;
605
1158
  }
@@ -630,6 +1183,43 @@ function applyEventEffects(lobster, effects) {
630
1183
  return changes;
631
1184
  }
632
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
+
633
1223
  // src/commands/patrol.ts
634
1224
  var PATROL_EXP = 15;
635
1225
  async function patrol() {
@@ -648,10 +1238,12 @@ async function patrol() {
648
1238
  }
649
1239
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
650
1240
  const lastDay = lobster.last_patrol ? lobster.last_patrol.split("T")[0] : "";
651
- if (today !== lastDay) {
652
- lobster.today_exp = 0;
653
- }
654
- console.log("\n" + t("patrol_start", { name: lobster.name }));
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 }));
655
1247
  console.log("\u2500".repeat(40));
656
1248
  const expGain = Math.min(PATROL_EXP, lobster.daily_exp_cap - lobster.today_exp);
657
1249
  if (expGain > 0) {
@@ -665,9 +1257,7 @@ async function patrol() {
665
1257
  console.log(t("event_prefix", { category: eventResult.event.category, id: eventResult.event.id }));
666
1258
  console.log(` ${eventResult.narrative}`);
667
1259
  const changes = applyEventEffects(lobster, eventResult.event.effects);
668
- if (changes.length > 0) {
669
- console.log(t("event_effects", { changes: changes.join(", ") }));
670
- }
1260
+ if (changes.length > 0) console.log(t("event_effects", { changes: changes.join(", ") }));
671
1261
  await appendLog(`\u{1F3B2} ${eventResult.event.id}: ${eventResult.narrative.slice(0, 60)}...`);
672
1262
  }
673
1263
  checkLevelUp(lobster);
@@ -691,8 +1281,12 @@ async function patrol() {
691
1281
  lobster.losses++;
692
1282
  lobster.streak = Math.min(0, lobster.streak) - 1;
693
1283
  lobster.reputation = Math.max(0, lobster.reputation - 1);
1284
+ const penalty = depthPenalty(soul);
1285
+ lobster.depth = Math.max(0, (lobster.depth ?? 0) - penalty);
694
1286
  }
695
- const battleExp = Math.min(br.exp_gain, lobster.daily_exp_cap - lobster.today_exp);
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);
696
1290
  if (battleExp > 0) {
697
1291
  lobster.exp += battleExp;
698
1292
  lobster.today_exp += battleExp;
@@ -700,6 +1294,8 @@ async function patrol() {
700
1294
  lobster.last_battle = (/* @__PURE__ */ new Date()).toISOString();
701
1295
  console.log(t("patrol_exp_stats", { exp: battleExp, wins: lobster.wins, losses: lobster.losses, streak: lobster.streak }));
702
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(", ") }));
703
1299
  checkLevelUp(lobster);
704
1300
  } else if (serverResponse.encounter && serverResponse.opponent) {
705
1301
  console.log(t("patrol_no_battle", { name: serverResponse.opponent.name, level: serverResponse.opponent.level }));
@@ -709,9 +1305,86 @@ async function patrol() {
709
1305
  } else {
710
1306
  console.log(t("patrol_offline"));
711
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 }));
1341
+ }
712
1342
  await writeLobster(lobster);
713
1343
  console.log("\u2500".repeat(40));
714
- console.log(t("patrol_summary", { level: lobster.level, exp: lobster.exp, next: lobster.exp_to_next, today: lobster.today_exp, cap: lobster.daily_exp_cap }));
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 };
715
1388
  }
716
1389
  function checkLevelUp(lobster) {
717
1390
  const l = lobster;
@@ -894,6 +1567,8 @@ async function rest() {
894
1567
  }
895
1568
  lobster.status = "hibernating";
896
1569
  lobster.hibernated_at = (/* @__PURE__ */ new Date()).toISOString();
1570
+ const hadDepth = lobster.depth && lobster.depth > 0;
1571
+ if (hadDepth) lobster.depth = 0;
897
1572
  await writeLobster(lobster);
898
1573
  await appendLog(`\u{1F4A4} ${lobster.name} \u2192 hibernation`);
899
1574
  console.log("\n" + "\u2500".repeat(40));
@@ -901,6 +1576,7 @@ async function rest() {
901
1576
  console.log(t("rest_desc2"));
902
1577
  console.log(t("rest_desc3"));
903
1578
  console.log(t("rest_desc4"));
1579
+ if (hadDepth) console.log(t("depth_reset"));
904
1580
  console.log("\u2500".repeat(40));
905
1581
  console.log("\n" + t("rest_wake_hint"));
906
1582
  }
@@ -952,9 +1628,358 @@ async function wake() {
952
1628
  console.log("\n" + t("wake_ready", { name: lobster.name }));
953
1629
  }
954
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
+ }
1734
+ console.log("\u2500".repeat(40));
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
+ }
1978
+ }
1979
+
955
1980
  // src/index.ts
956
1981
  var program = new Command();
957
- program.name("clawfight").description("\u{1F99E} ClawFight \u2014 \u9F99\u867E\u7535\u5B50\u5BA0\u7269\u5BF9\u6218").version("1.3.0");
1982
+ program.name("clawfight").description("\u{1F99E} ClawFight \u2014 \u9F99\u867E\u7535\u5B50\u5BA0\u7269\u5BF9\u6218").version("1.5.0");
958
1983
  program.command("hatch").description("\u5B75\u5316\u4E00\u53EA\u65B0\u9F99\u867E").argument("[name]", "\u4E3A\u9F99\u867E\u53D6\u540D").action(async (name) => {
959
1984
  await hatch(name);
960
1985
  });
@@ -979,4 +2004,13 @@ program.command("rest").description("\u8FDB\u5165\u4F11\u7720\uFF08\u6682\u505C\
979
2004
  program.command("wake").description("\u4ECE\u4F11\u7720\u4E2D\u5524\u9192\uFF08\u9644\u5E26\u6062\u590D\u52A0\u6210\uFF09").action(async () => {
980
2005
  await wake();
981
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
+ });
982
2016
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2025-6-19/clawfight",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "ClawFight — raise and battle a unique lobster pet with evolving soul 🦞",
5
5
  "author": "LIU",
6
6
  "license": "MIT",