koishi-plugin-maple-warriors 0.0.3 → 0.1.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 (3) hide show
  1. package/lib/index.d.ts +52 -0
  2. package/lib/index.js +901 -235
  3. package/package.json +1 -1
package/lib/index.js CHANGED
@@ -27,16 +27,76 @@ __export(src_exports, {
27
27
  });
28
28
  module.exports = __toCommonJS(src_exports);
29
29
  var import_koishi = require("koishi");
30
- var using = ["database"];
31
30
  var name = "maple-warriors";
31
+ var using = ["database"];
32
32
  var DEFAULT_PREY_DATA = [
33
- "老鼠 5 1 1 1 1 1-20",
34
- "野兔 1 3-5 2-4 2-4 1-3 5-50"
33
+ "老鼠 100 1 1-5 1 1 1-20",
34
+ "松鼠 20 1-5 1 1 1 5-50",
35
+ "野兔 10 3-5 2-4 2-4 1-3 5-50",
36
+ "獾 1 100 100 100 100 100"
35
37
  ];
36
38
  var Config = import_koishi.Schema.object({
37
39
  preyData: import_koishi.Schema.array(import_koishi.Schema.string()).role("table").default(DEFAULT_PREY_DATA).description("猎物数据,每行格式:猎物名 权重 体质 攻击 防御 暴击 敏捷(权重是固定数值,属性可以是固定数值或范围,如1-10)"),
38
40
  battleMessageInterval: import_koishi.Schema.number().min(1).max(5).default(2).description("战斗消息发送间隔(秒)")
39
41
  });
42
+ var INITIAL_ITEMS = [
43
+ {
44
+ name: "老鼠",
45
+ type: "猎物",
46
+ sellPrice: 1,
47
+ shopPrice: 5,
48
+ hpBonus: 1,
49
+ constitutionBonus: 0,
50
+ attackBonus: 0,
51
+ toughnessBonus: 0,
52
+ critBonus: 0,
53
+ agilityBonus: 0,
54
+ buff: null,
55
+ description: "随处可见的普通猎物"
56
+ },
57
+ {
58
+ name: "松鼠",
59
+ type: "猎物",
60
+ sellPrice: 2,
61
+ shopPrice: 10,
62
+ hpBonus: 2,
63
+ constitutionBonus: 0,
64
+ attackBonus: 0,
65
+ toughnessBonus: 0,
66
+ critBonus: 0,
67
+ agilityBonus: 0,
68
+ buff: null,
69
+ description: "上蹿下跳!"
70
+ },
71
+ {
72
+ name: "野兔",
73
+ type: "猎物",
74
+ sellPrice: 5,
75
+ shopPrice: 50,
76
+ hpBonus: 5,
77
+ constitutionBonus: 0,
78
+ attackBonus: 0,
79
+ toughnessBonus: 0,
80
+ critBonus: 0,
81
+ agilityBonus: 0,
82
+ buff: null,
83
+ description: "跑得很快的猎物"
84
+ },
85
+ {
86
+ name: "獾",
87
+ type: "猎物",
88
+ sellPrice: 50,
89
+ shopPrice: -1,
90
+ hpBonus: 10,
91
+ constitutionBonus: 0,
92
+ attackBonus: 0,
93
+ toughnessBonus: 0,
94
+ critBonus: 0,
95
+ agilityBonus: 0,
96
+ buff: null,
97
+ description: "危险!而且味道很大!"
98
+ }
99
+ ];
40
100
  function calculateRates(character) {
41
101
  const resistance = (character.toughness / (character.toughness + 90)).toFixed(4);
42
102
  const critRate = (character.crit / (character.crit + 90)).toFixed(4);
@@ -45,47 +105,153 @@ function calculateRates(character) {
45
105
  return { resistance, critRate, chargeTime, dodgeRate };
46
106
  }
47
107
  __name(calculateRates, "calculateRates");
48
- var BattleManager = class {
49
- static {
50
- __name(this, "BattleManager");
108
+ function parseBuffs(buffsStr) {
109
+ if (!buffsStr) return [];
110
+ try {
111
+ return JSON.parse(buffsStr);
112
+ } catch {
113
+ return [];
51
114
  }
52
- battles = /* @__PURE__ */ new Map();
53
- startBattle(channelId, playerHp, preyName) {
54
- if (this.isBattling(channelId)) {
55
- return false;
56
- }
57
- this.battles.set(channelId, {
58
- isBattling: true,
59
- abortController: new AbortController(),
60
- playerHp,
61
- preyName
62
- });
63
- return true;
115
+ }
116
+ __name(parseBuffs, "parseBuffs");
117
+ function serializeBuffs(buffs) {
118
+ return JSON.stringify(buffs);
119
+ }
120
+ __name(serializeBuffs, "serializeBuffs");
121
+ function parseItemBuff(buffStr) {
122
+ if (!buffStr) return null;
123
+ try {
124
+ return JSON.parse(buffStr);
125
+ } catch {
126
+ return null;
64
127
  }
65
- endBattle(channelId, finalPlayerHp) {
66
- const battle = this.battles.get(channelId);
67
- if (battle && battle.isBattling) {
68
- if (battle.abortController) {
69
- battle.abortController.abort();
70
- }
71
- if (finalPlayerHp !== void 0) {
72
- battle.playerHp = finalPlayerHp;
73
- }
74
- this.battles.delete(channelId);
75
- return true;
128
+ }
129
+ __name(parseItemBuff, "parseItemBuff");
130
+ function calculateCharacterAttributes(character) {
131
+ const buffs = parseBuffs(character.buffs);
132
+ const now = /* @__PURE__ */ new Date();
133
+ const activeBuffs = buffs.filter((buff) => {
134
+ return new Date(buff.endTime) > now;
135
+ });
136
+ const baseAttributes = {
137
+ constitution: character.constitution,
138
+ attack: character.attack,
139
+ toughness: character.toughness,
140
+ crit: character.crit,
141
+ agility: character.agility
142
+ };
143
+ let constitutionBonus = 0;
144
+ let attackBonus = 0;
145
+ let toughnessBonus = 0;
146
+ let critBonus = 0;
147
+ let agilityBonus = 0;
148
+ activeBuffs.forEach((buff) => {
149
+ if (buff.effects) {
150
+ buff.effects.forEach((effect) => {
151
+ const percentage = effect.percentage || 0;
152
+ const max = effect.max || 0;
153
+ let bonus = 0;
154
+ switch (effect.attribute) {
155
+ case "体质":
156
+ bonus = Math.min(Math.floor(character.constitution * percentage / 100), max);
157
+ constitutionBonus += bonus;
158
+ break;
159
+ case "攻击":
160
+ bonus = Math.min(Math.floor(character.attack * percentage / 100), max);
161
+ attackBonus += bonus;
162
+ break;
163
+ case "防御":
164
+ bonus = Math.min(Math.floor(character.toughness * percentage / 100), max);
165
+ toughnessBonus += bonus;
166
+ break;
167
+ case "暴击":
168
+ bonus = Math.min(Math.floor(character.crit * percentage / 100), max);
169
+ critBonus += bonus;
170
+ break;
171
+ case "敏捷":
172
+ bonus = Math.min(Math.floor(character.agility * percentage / 100), max);
173
+ agilityBonus += bonus;
174
+ break;
175
+ }
176
+ });
177
+ }
178
+ });
179
+ const buffBonuses = {
180
+ constitution: constitutionBonus,
181
+ attack: attackBonus,
182
+ toughness: toughnessBonus,
183
+ crit: critBonus,
184
+ agility: agilityBonus
185
+ };
186
+ const totalAttributes = {
187
+ constitution: character.constitution + constitutionBonus,
188
+ attack: character.attack + attackBonus,
189
+ toughness: character.toughness + toughnessBonus,
190
+ crit: character.crit + critBonus,
191
+ agility: character.agility + agilityBonus
192
+ };
193
+ const rates = calculateRates(totalAttributes);
194
+ const maxHp = totalAttributes.constitution * 5;
195
+ return {
196
+ base: baseAttributes,
197
+ buffs: buffBonuses,
198
+ total: totalAttributes,
199
+ rates,
200
+ maxHp
201
+ };
202
+ }
203
+ __name(calculateCharacterAttributes, "calculateCharacterAttributes");
204
+ function getBattleCharacter(character) {
205
+ const attrs = calculateCharacterAttributes(character);
206
+ return {
207
+ name: character.name,
208
+ constitution: attrs.total.constitution,
209
+ attack: attrs.total.attack,
210
+ toughness: attrs.total.toughness,
211
+ crit: attrs.total.crit,
212
+ agility: attrs.total.agility,
213
+ hp: Math.min(character.currentHp, attrs.maxHp),
214
+ // 确保当前HP不超过最大HP
215
+ maxHp: attrs.maxHp,
216
+ chargeTime: parseFloat(attrs.rates.chargeTime),
217
+ nextAttackTime: 0
218
+ };
219
+ }
220
+ __name(getBattleCharacter, "getBattleCharacter");
221
+ function parseTimeString(timeStr) {
222
+ let totalSeconds = 0;
223
+ const timeRegex = /(\d+)([hms])/g;
224
+ let match;
225
+ while ((match = timeRegex.exec(timeStr)) !== null) {
226
+ const value = parseInt(match[1]);
227
+ const unit = match[2];
228
+ switch (unit) {
229
+ case "h":
230
+ totalSeconds += value * 3600;
231
+ break;
232
+ case "m":
233
+ totalSeconds += value * 60;
234
+ break;
235
+ case "s":
236
+ totalSeconds += value;
237
+ break;
76
238
  }
77
- return false;
78
- }
79
- isBattling(channelId) {
80
- return this.battles.has(channelId) && this.battles.get(channelId).isBattling;
81
- }
82
- getAbortController(channelId) {
83
- return this.battles.get(channelId)?.abortController;
84
- }
85
- getBattleData(channelId) {
86
- return this.battles.get(channelId);
87
239
  }
88
- };
240
+ return totalSeconds;
241
+ }
242
+ __name(parseTimeString, "parseTimeString");
243
+ function formatTimeRemaining(seconds) {
244
+ if (seconds <= 0) return "0s";
245
+ const hours = Math.floor(seconds / 3600);
246
+ const minutes = Math.floor(seconds % 3600 / 60);
247
+ const secs = seconds % 60;
248
+ const parts = [];
249
+ if (hours > 0) parts.push(`${hours}h`);
250
+ if (minutes > 0) parts.push(`${minutes}m`);
251
+ if (secs > 0 || parts.length === 0) parts.push(`${secs}s`);
252
+ return parts.join("");
253
+ }
254
+ __name(formatTimeRemaining, "formatTimeRemaining");
89
255
  function parseAttributeRange(value) {
90
256
  if (value.includes("-")) {
91
257
  const [min, max] = value.split("-").map((v) => parseInt(v.trim()));
@@ -121,6 +287,10 @@ function formatRemainingTime(endTime) {
121
287
  return parts.join("");
122
288
  }
123
289
  __name(formatRemainingTime, "formatRemainingTime");
290
+ function formatGold(amount) {
291
+ return amount.toLocaleString("en-US");
292
+ }
293
+ __name(formatGold, "formatGold");
124
294
  function getTodayDateString() {
125
295
  const now = /* @__PURE__ */ new Date();
126
296
  return now.toISOString().split("T")[0];
@@ -180,6 +350,47 @@ function selectRandomPrey(preyList) {
180
350
  return preyList[0];
181
351
  }
182
352
  __name(selectRandomPrey, "selectRandomPrey");
353
+ var BattleManager = class {
354
+ static {
355
+ __name(this, "BattleManager");
356
+ }
357
+ battles = /* @__PURE__ */ new Map();
358
+ startBattle(channelId, playerHp, preyName) {
359
+ if (this.isBattling(channelId)) {
360
+ return false;
361
+ }
362
+ this.battles.set(channelId, {
363
+ isBattling: true,
364
+ abortController: new AbortController(),
365
+ playerHp,
366
+ preyName
367
+ });
368
+ return true;
369
+ }
370
+ endBattle(channelId, finalPlayerHp) {
371
+ const battle = this.battles.get(channelId);
372
+ if (battle && battle.isBattling) {
373
+ if (battle.abortController) {
374
+ battle.abortController.abort();
375
+ }
376
+ if (finalPlayerHp !== void 0) {
377
+ battle.playerHp = finalPlayerHp;
378
+ }
379
+ this.battles.delete(channelId);
380
+ return true;
381
+ }
382
+ return false;
383
+ }
384
+ isBattling(channelId) {
385
+ return this.battles.has(channelId) && this.battles.get(channelId).isBattling;
386
+ }
387
+ getAbortController(channelId) {
388
+ return this.battles.get(channelId)?.abortController;
389
+ }
390
+ getBattleData(channelId) {
391
+ return this.battles.get(channelId);
392
+ }
393
+ };
183
394
  function apply(ctx, config) {
184
395
  const battleManager = new BattleManager();
185
396
  ctx.model.extend("maple_warriors", {
@@ -187,16 +398,19 @@ function apply(ctx, config) {
187
398
  userId: "string",
188
399
  name: "string",
189
400
  level: "unsigned",
401
+ gold: "unsigned",
402
+ // 新增:金币
190
403
  constitution: "unsigned",
191
404
  attack: "unsigned",
192
405
  toughness: "unsigned",
193
406
  crit: "unsigned",
194
407
  agility: "unsigned",
195
408
  currentHp: "unsigned",
196
- maxHp: "unsigned",
197
409
  status: "string",
198
410
  previousStatus: "string",
199
411
  statusEndTime: "timestamp",
412
+ buffs: "text",
413
+ // 新增:当前生效的buff(JSON格式)
200
414
  items: "text",
201
415
  lastTrainDate: "string",
202
416
  lastHuntDate: "string",
@@ -208,6 +422,14 @@ function apply(ctx, config) {
208
422
  ctx.model.extend("maple_warriors_items", {
209
423
  id: "unsigned",
210
424
  name: "string",
425
+ type: "string",
426
+ // 新增:物品类型
427
+ sellPrice: "integer",
428
+ // 新增:出售价
429
+ shopPrice: "integer",
430
+ // 新增:商店价
431
+ buff: "text",
432
+ // 新增:buff效果(JSON格式)
211
433
  hpBonus: "integer",
212
434
  constitutionBonus: "integer",
213
435
  attackBonus: "integer",
@@ -221,22 +443,55 @@ function apply(ctx, config) {
221
443
  autoInc: true,
222
444
  unique: ["name"]
223
445
  });
446
+ ctx.on("ready", async () => {
447
+ try {
448
+ const existingItems = await ctx.database.get("maple_warriors_items", {});
449
+ if (existingItems.length === 0) {
450
+ for (const itemData of INITIAL_ITEMS) {
451
+ await ctx.database.create("maple_warriors_items", {
452
+ ...itemData,
453
+ createdAt: /* @__PURE__ */ new Date()
454
+ });
455
+ }
456
+ const logger = ctx.logger("maple-warriors");
457
+ logger.info(`检测到物品表为空,已自动初始化 ${INITIAL_ITEMS.length} 个物品到物品表`);
458
+ }
459
+ } catch (error) {
460
+ const logger = ctx.logger("maple-warriors");
461
+ logger.error("初始化物品表失败:", error);
462
+ }
463
+ });
224
464
  async function formatCharacterInfo(character) {
225
- const rates = calculateRates({
226
- toughness: character.toughness,
227
- crit: character.crit,
228
- agility: character.agility
229
- });
465
+ const attrs = calculateCharacterAttributes(character);
230
466
  const items = parseItemsString(character.items);
231
467
  const itemsText = Object.entries(items).map(([name2, count]) => `${name2}*${count}`).join("\n");
468
+ const buffs = parseBuffs(character.buffs);
469
+ const now = /* @__PURE__ */ new Date();
470
+ const activeBuffs = buffs.filter((buff) => new Date(buff.endTime) > now);
471
+ let buffsText = "无";
472
+ if (activeBuffs.length > 0) {
473
+ buffsText = activeBuffs.map((buff) => {
474
+ const endTime = new Date(buff.endTime);
475
+ const remainingSeconds = Math.max(0, (endTime.getTime() - now.getTime()) / 1e3);
476
+ return `${buff.name}(${formatTimeRemaining(Math.floor(remainingSeconds))})`;
477
+ }).join(" ");
478
+ }
479
+ const constitutionDisplay = attrs.buffs.constitution > 0 ? `${attrs.base.constitution}(+${attrs.buffs.constitution})` : attrs.base.constitution.toString();
480
+ const attackDisplay = attrs.buffs.attack > 0 ? `${attrs.base.attack}(+${attrs.buffs.attack})` : attrs.base.attack.toString();
481
+ const toughnessDisplay = attrs.buffs.toughness > 0 ? `${attrs.base.toughness}(+${attrs.buffs.toughness})` : attrs.base.toughness.toString();
482
+ const critDisplay = attrs.buffs.crit > 0 ? `${attrs.base.crit}(+${attrs.buffs.crit})` : attrs.base.crit.toString();
483
+ const agilityDisplay = attrs.buffs.agility > 0 ? `${attrs.base.agility}(+${attrs.buffs.agility})` : attrs.base.agility.toString();
484
+ const currentHp = Math.min(character.currentHp, attrs.maxHp);
232
485
  return `名字: ${character.name}
233
486
  等级: ${character.level}
234
- 体质: ${character.constitution} (HP: ${character.currentHp}/${character.maxHp})
235
- 攻击: ${character.attack}
236
- 防御: ${character.toughness} (韧性: ${(parseFloat(rates.resistance) * 100).toFixed(2)}%)
237
- 暴击: ${character.crit} (暴击率: ${(parseFloat(rates.critRate) * 100).toFixed(2)}%)
238
- 敏捷: ${character.agility} (蓄力: ${rates.chargeTime}s 闪避率: ${(parseFloat(rates.dodgeRate) * 100).toFixed(2)}%)
487
+ 金币: ${formatGold(character.gold)}
488
+ 体质: ${constitutionDisplay} (HP: ${currentHp}/${attrs.maxHp})
489
+ 攻击: ${attackDisplay}
490
+ 防御: ${toughnessDisplay} (韧性: ${(parseFloat(attrs.rates.resistance) * 100).toFixed(2)}%)
491
+ 暴击: ${critDisplay} (暴击率: ${(parseFloat(attrs.rates.critRate) * 100).toFixed(2)}%)
492
+ 敏捷: ${agilityDisplay} (蓄力: ${attrs.rates.chargeTime}s 闪避率: ${(parseFloat(attrs.rates.dodgeRate) * 100).toFixed(2)}%)
239
493
  状态: ${character.status}${character.statusEndTime ? " 剩余" + formatRemainingTime(character.statusEndTime) : ""}
494
+ buff: ${buffsText}
240
495
  ──────────
241
496
  物品栏:
242
497
  ${itemsText || "空空如也"}`;
@@ -251,16 +506,19 @@ ${itemsText || "空空如也"}`;
251
506
  userId,
252
507
  name: name2 || '请输入"修改名字 角色名"修改',
253
508
  level: 1,
509
+ gold: 0,
510
+ // 新增:初始化金币为0
254
511
  constitution: attributes.constitution,
255
512
  attack: attributes.attack,
256
513
  toughness: attributes.toughness,
257
514
  crit: attributes.crit,
258
515
  agility: attributes.agility,
259
516
  currentHp: maxHp,
260
- maxHp,
261
517
  status: "无",
262
518
  previousStatus: "无",
263
519
  statusEndTime: null,
520
+ buffs: "[]",
521
+ // 初始化 buffs 为空数组
264
522
  items: "{}",
265
523
  lastTrainDate: "",
266
524
  lastHuntDate: "",
@@ -272,7 +530,38 @@ ${itemsText || "空空如也"}`;
272
530
  return characters[0];
273
531
  }
274
532
  __name(getOrCreateCharacter, "getOrCreateCharacter");
275
- function checkCharacterStatus(character) {
533
+ const statusRules = {
534
+ // 状态列表:
535
+ // 无,受伤,睡眠,治疗
536
+ // 受影响的指令列表(角色操作指令):
537
+ // 训练,捕猎,睡眠,治疗,战斗训练,使用物品
538
+ "受伤": {
539
+ // 可以使用物品,治疗
540
+ "训练": { message: '你受伤了,快输入"治疗"吧!' },
541
+ "捕猎": { message: '你受伤了,快输入"治疗"吧!' },
542
+ "睡眠": { message: '你受伤了,快输入"治疗"吧!' },
543
+ "战斗训练": { message: '你受伤了,快输入"治疗"吧!' }
544
+ },
545
+ "睡眠": {
546
+ // 都不行
547
+ "训练": { message: "你想在梦里训练吗?" },
548
+ "捕猎": { message: "你想在梦里捕猎吗?" },
549
+ "治疗": { message: "你想在梦里治疗吗?" },
550
+ "使用物品": { message: "你想在梦里使用物品吗?" },
551
+ "战斗训练": { message: "你想在梦里战斗训练吗?" },
552
+ "睡眠": { message: "已经处于睡眠中!" }
553
+ },
554
+ "治疗": {
555
+ // 都不行
556
+ "训练": { message: "正在治疗中,请好好养伤!" },
557
+ "捕猎": { message: "正在治疗中,请好好养伤!" },
558
+ "睡眠": { message: "正在治疗中,请好好养伤!" },
559
+ "使用物品": { message: "正在治疗中,请好好养伤!" },
560
+ "战斗训练": { message: "正在治疗中,请好好养伤!" },
561
+ "治疗": { message: "已经处于治疗中!" }
562
+ }
563
+ };
564
+ function checkInstructionStatus(character, instruction) {
276
565
  const now = /* @__PURE__ */ new Date();
277
566
  if (character.statusEndTime && now >= character.statusEndTime) {
278
567
  const updates = {
@@ -282,23 +571,27 @@ ${itemsText || "空空如也"}`;
282
571
  updatedAt: /* @__PURE__ */ new Date()
283
572
  };
284
573
  if (character.status === "睡眠" || character.status === "治疗") {
285
- updates.currentHp = character.maxHp;
574
+ const attrs = calculateCharacterAttributes(character);
575
+ updates.currentHp = attrs.maxHp;
286
576
  }
287
577
  ctx.database.set("maple_warriors", { id: character.id }, updates);
288
578
  return { canAct: true, message: "" };
289
579
  }
290
- if (character.status === "受伤") {
291
- return { canAct: false, message: '你受伤了,快输入"治疗"吧!' };
580
+ const status = character.status;
581
+ if (status === "") {
582
+ return { canAct: true, message: "" };
292
583
  }
293
- if (character.status === "睡眠") {
294
- return { canAct: false, message: "你想在梦里做这个吗?" };
584
+ const statusRule = statusRules[status];
585
+ if (!statusRule) {
586
+ return { canAct: true, message: "" };
295
587
  }
296
- if (character.status === "治疗") {
297
- return { canAct: false, message: "正在治疗中,请好好养伤!" };
588
+ const instructionRule = statusRule[instruction];
589
+ if (!instructionRule) {
590
+ return { canAct: true, message: "" };
298
591
  }
299
- return { canAct: true, message: "" };
592
+ return { canAct: false, message: instructionRule.message };
300
593
  }
301
- __name(checkCharacterStatus, "checkCharacterStatus");
594
+ __name(checkInstructionStatus, "checkInstructionStatus");
302
595
  ctx.command("猫武士", "猫武士插件 - 帮助菜单");
303
596
  ctx.command("猫武士/修改名字 <name>", "修改角色名字").example("修改名字 火星").action(async ({ session }, name2) => {
304
597
  if (!name2 || name2.trim().length === 0) {
@@ -314,13 +607,14 @@ ${itemsText || "空空如也"}`;
314
607
  userId,
315
608
  name: trimmedName,
316
609
  level: 1,
610
+ gold: 0,
611
+ // 新增:初始化金币为0
317
612
  constitution: attributes.constitution,
318
613
  attack: attributes.attack,
319
614
  toughness: attributes.toughness,
320
615
  crit: attributes.crit,
321
616
  agility: attributes.agility,
322
617
  currentHp: maxHp,
323
- maxHp,
324
618
  status: "无",
325
619
  previousStatus: "无",
326
620
  statusEndTime: null,
@@ -341,7 +635,38 @@ ${itemsText || "空空如也"}`;
341
635
  ctx.command("猫武士/我的角色", "查看我的角色信息").example("我的角色").action(async ({ session }) => {
342
636
  const userId = session.userId;
343
637
  let character = await getOrCreateCharacter(userId);
344
- return await formatCharacterInfo(character);
638
+ const attrs = calculateCharacterAttributes(character);
639
+ const items = parseItemsString(character.items);
640
+ const itemsText = Object.entries(items).map(([name2, count]) => `${name2}*${count}`).join("\n");
641
+ const buffs = parseBuffs(character.buffs);
642
+ const now = /* @__PURE__ */ new Date();
643
+ const activeBuffs = buffs.filter((buff) => new Date(buff.endTime) > now);
644
+ let buffsText = "无";
645
+ if (activeBuffs.length > 0) {
646
+ buffsText = activeBuffs.map((buff) => {
647
+ const endTime = new Date(buff.endTime);
648
+ const remainingSeconds = Math.max(0, (endTime.getTime() - now.getTime()) / 1e3);
649
+ return `${buff.name}(${formatTimeRemaining(Math.floor(remainingSeconds))})`;
650
+ }).join(" ");
651
+ }
652
+ const constitutionDisplay = attrs.buffs.constitution > 0 ? `${attrs.total.constitution}(+${attrs.buffs.constitution})` : attrs.total.constitution.toString();
653
+ const attackDisplay = attrs.buffs.attack > 0 ? `${attrs.total.attack}(+${attrs.buffs.attack})` : attrs.total.attack.toString();
654
+ const toughnessDisplay = attrs.buffs.toughness > 0 ? `${attrs.total.toughness}(+${attrs.buffs.toughness})` : attrs.total.toughness.toString();
655
+ const critDisplay = attrs.buffs.crit > 0 ? `${attrs.total.crit}(+${attrs.buffs.crit})` : attrs.total.crit.toString();
656
+ const agilityDisplay = attrs.buffs.agility > 0 ? `${attrs.total.agility}(+${attrs.buffs.agility})` : attrs.total.agility.toString();
657
+ const currentHp = Math.min(character.currentHp, attrs.maxHp);
658
+ return `名字: ${character.name}
659
+ 等级: ${character.level}
660
+ 体质: ${constitutionDisplay} (HP: ${currentHp}/${attrs.maxHp})
661
+ 攻击: ${attackDisplay}
662
+ 防御: ${toughnessDisplay} (韧性: ${(parseFloat(attrs.rates.resistance) * 100).toFixed(2)}%)
663
+ 暴击: ${critDisplay} (暴击率: ${(parseFloat(attrs.rates.critRate) * 100).toFixed(2)}%)
664
+ 敏捷: ${agilityDisplay} (蓄力: ${attrs.rates.chargeTime}s 闪避率: ${(parseFloat(attrs.rates.dodgeRate) * 100).toFixed(2)}%)
665
+ 状态: ${character.status}${character.statusEndTime ? " 剩余" + formatRemainingTime(character.statusEndTime) : ""}
666
+ buff: ${buffsText}
667
+ ──────────
668
+ 物品栏:
669
+ ${itemsText || "空空如也"}`;
345
670
  });
346
671
  ctx.command("猫武士/训练 <...args>", "训练提升属性").example("训练 体质 体质 攻击").example("训练 体质").example("训练 随机").action(async ({ session }, ...args) => {
347
672
  const userId = session.userId;
@@ -350,7 +675,7 @@ ${itemsText || "空空如也"}`;
350
675
  if (character.lastTrainDate && isSameDay(character.lastTrainDate, today)) {
351
676
  return "今天已经训练过了,要注意劳逸结合哦!";
352
677
  }
353
- const statusCheck = checkCharacterStatus(character);
678
+ const statusCheck = checkInstructionStatus(character, "训练");
354
679
  if (!statusCheck.canAct) {
355
680
  return statusCheck.message;
356
681
  }
@@ -368,16 +693,29 @@ ${itemsText || "空空如也"}`;
368
693
  };
369
694
  const updates2 = {};
370
695
  const attributeCounts2 = {};
696
+ for (const key of Object.keys(attrMap2)) {
697
+ const field = attrMap2[key];
698
+ updates2[field] = character[field] || 0;
699
+ }
371
700
  for (let i = 0; i < 3; i++) {
372
701
  const randomAttr = validAttrs2[Math.floor(Math.random() * validAttrs2.length)];
373
702
  const field = attrMap2[randomAttr];
374
- updates2[field] = (updates2[field] || character[field] || 0) + 1;
703
+ updates2[field] = updates2[field] + 1;
375
704
  attributeCounts2[randomAttr] = (attributeCounts2[randomAttr] || 0) + 1;
376
705
  }
377
706
  const changes2 = [];
378
707
  for (const [attr, count] of Object.entries(attributeCounts2)) {
379
708
  changes2.push(`${attr}+${count}`);
380
709
  }
710
+ if (attributeCounts2["体质"]) {
711
+ const constitutionIncrease = attributeCounts2["体质"];
712
+ let newHp = character.currentHp + constitutionIncrease * 5;
713
+ const currentAttrs = calculateCharacterAttributes(character);
714
+ const newMaxHp = currentAttrs.maxHp + constitutionIncrease * 5;
715
+ if (newHp > newMaxHp) newHp = newMaxHp;
716
+ if (newHp < 0) newHp = 0;
717
+ updates2.currentHp = newHp;
718
+ }
381
719
  updates2.lastTrainDate = today;
382
720
  updates2.updatedAt = /* @__PURE__ */ new Date();
383
721
  await ctx.database.set("maple_warriors", { userId }, updates2);
@@ -402,17 +740,30 @@ ${changes2.join(" ")}`;
402
740
  }
403
741
  const updates = {};
404
742
  const attributeCounts = {};
743
+ for (const key of Object.keys(attrMap)) {
744
+ const field = attrMap[key];
745
+ updates[field] = character[field] || 0;
746
+ }
405
747
  for (const attr of args) {
406
748
  const field = attrMap[attr];
407
- updates[field] = (updates[field] || character[field] || 0) + 1;
749
+ updates[field] = updates[field] + 1;
408
750
  attributeCounts[attr] = (attributeCounts[attr] || 0) + 1;
409
751
  }
410
752
  for (let i = 0; i < 3 - args.length; i++) {
411
753
  const randomAttr = validAttrs[Math.floor(Math.random() * validAttrs.length)];
412
754
  const field = attrMap[randomAttr];
413
- updates[field] = (updates[field] || character[field] || 0) + 1;
755
+ updates[field] = updates[field] + 1;
414
756
  attributeCounts[randomAttr] = (attributeCounts[randomAttr] || 0) + 1;
415
757
  }
758
+ if (attributeCounts["体质"]) {
759
+ const constitutionIncrease = attributeCounts["体质"];
760
+ let newHp = character.currentHp + constitutionIncrease * 5;
761
+ const currentAttrs = calculateCharacterAttributes(character);
762
+ const newMaxHp = currentAttrs.maxHp + constitutionIncrease * 5;
763
+ if (newHp > newMaxHp) newHp = newMaxHp;
764
+ if (newHp < 0) newHp = 0;
765
+ updates.currentHp = newHp;
766
+ }
416
767
  const changes = [];
417
768
  for (const [attr, count] of Object.entries(attributeCounts)) {
418
769
  changes.push(`${attr}+${count}`);
@@ -430,14 +781,10 @@ ${changes.join(" ")}`;
430
781
  return "请等待当前战斗结束!";
431
782
  }
432
783
  let character = await getOrCreateCharacter(userId);
433
- const statusCheck = checkCharacterStatus(character);
784
+ const statusCheck = checkInstructionStatus(character, "捕猎");
434
785
  if (!statusCheck.canAct) {
435
786
  return statusCheck.message;
436
787
  }
437
- const today = getTodayDateString();
438
- if (character.lastHuntDate && isSameDay(character.lastHuntDate, today)) {
439
- return "今天已经捕猎过了,要注意劳逸结合哦!";
440
- }
441
788
  const preyList = parsePreyData(config.preyData);
442
789
  if (preyList.length === 0) {
443
790
  return "未找到猎物数据,请在控制台配置猎物数据。";
@@ -449,7 +796,12 @@ ${changes.join(" ")}`;
449
796
  const crit = parseAttributeRange(selectedPrey.crit);
450
797
  const agility = parseAttributeRange(selectedPrey.agility);
451
798
  const preyHp = constitution * 5;
452
- const chargeTime = parseFloat((1 + 720 / (agility + 80)).toFixed(2));
799
+ const preyRates = calculateRates({
800
+ toughness,
801
+ crit,
802
+ agility
803
+ });
804
+ const player = getBattleCharacter(character);
453
805
  const prey = {
454
806
  name: selectedPrey.name,
455
807
  constitution,
@@ -459,17 +811,12 @@ ${changes.join(" ")}`;
459
811
  agility,
460
812
  hp: preyHp,
461
813
  maxHp: preyHp,
462
- chargeTime,
463
- nextAttackTime: chargeTime
814
+ chargeTime: parseFloat(preyRates.chargeTime),
815
+ nextAttackTime: 0
464
816
  };
465
- if (!battleManager.startBattle(channelId, character.currentHp, selectedPrey.name)) {
817
+ if (!battleManager.startBattle(channelId, player.hp, selectedPrey.name)) {
466
818
  return "战斗开始失败,请重试!";
467
819
  }
468
- const preyRates = calculateRates({
469
- toughness: prey.toughness,
470
- crit: prey.crit,
471
- agility: prey.agility
472
- });
473
820
  await session.send(`发现猎物: ${prey.name}
474
821
  体质: ${prey.constitution} (HP: ${prey.hp})
475
822
  攻击: ${prey.attack}
@@ -479,18 +826,6 @@ ${changes.join(" ")}`;
479
826
  await new Promise((resolve) => setTimeout(resolve, 2e3));
480
827
  const abortController = battleManager.getAbortController(channelId);
481
828
  try {
482
- const player = {
483
- name: character.name,
484
- constitution: character.constitution,
485
- attack: character.attack,
486
- toughness: character.toughness,
487
- crit: character.crit,
488
- agility: character.agility,
489
- hp: character.currentHp,
490
- maxHp: character.maxHp,
491
- chargeTime: parseFloat((1 + 720 / (character.agility + 80)).toFixed(2)),
492
- nextAttackTime: 0
493
- };
494
829
  await executeBattle(
495
830
  session,
496
831
  player,
@@ -500,9 +835,9 @@ ${changes.join(" ")}`;
500
835
  // 不是训练模式
501
836
  abortController
502
837
  );
503
- const today2 = getTodayDateString();
838
+ const today = getTodayDateString();
504
839
  const updates = {
505
- lastHuntDate: today2,
840
+ lastHuntDate: today,
506
841
  updatedAt: /* @__PURE__ */ new Date()
507
842
  };
508
843
  if (player.hp <= 0) {
@@ -522,9 +857,9 @@ ${changes.join(" ")}`;
522
857
  await ctx.database.set("maple_warriors", { userId }, updates);
523
858
  } catch (error) {
524
859
  if (error.name === "AbortError") {
525
- const today2 = getTodayDateString();
860
+ const today = getTodayDateString();
526
861
  await ctx.database.set("maple_warriors", { userId }, {
527
- lastHuntDate: today2,
862
+ lastHuntDate: today,
528
863
  updatedAt: /* @__PURE__ */ new Date()
529
864
  });
530
865
  } else {
@@ -534,31 +869,23 @@ ${changes.join(" ")}`;
534
869
  battleManager.endBattle(channelId);
535
870
  }
536
871
  });
537
- ctx.command("猫武士/使用物品 <itemInput>", "使用物品栏中的物品").example("使用物品 老鼠").example("使用物品 老鼠 2").example("使用物品 老鼠*2").action(async ({ session }, itemInput) => {
538
- if (!itemInput) {
539
- return "格式错误!正确格式:使用物品 物品名 数量 或 使用物品 物品名*数量";
872
+ ctx.command("猫武士/使用物品 <itemName> [count]", "使用物品栏中的物品").example("使用物品 老鼠").example("使用物品 老鼠 2").action(async ({ session }, itemName, countStr) => {
873
+ if (!itemName) {
874
+ return "格式错误!正确格式:使用物品 物品名 [数量]";
540
875
  }
541
876
  const userId = session.userId;
542
877
  let character = await getOrCreateCharacter(userId);
543
- const statusCheck = checkCharacterStatus(character);
878
+ const statusCheck = checkInstructionStatus(character, "使用物品");
544
879
  if (!statusCheck.canAct) {
545
880
  return statusCheck.message;
546
881
  }
547
- let itemName = "";
548
882
  let count = 1;
549
- if (itemInput.includes("*")) {
550
- const parts = itemInput.split("*");
551
- itemName = parts[0].trim();
552
- count = parseInt(parts[1]) || 1;
553
- } else {
554
- const parts = itemInput.trim().split(/\s+/);
555
- itemName = parts[0];
556
- if (parts[1]) {
557
- count = parseInt(parts[1]) || 1;
883
+ if (countStr) {
884
+ const parsedCount = parseInt(countStr);
885
+ if (isNaN(parsedCount) || parsedCount <= 0) {
886
+ return "数量必须是正整数";
558
887
  }
559
- }
560
- if (!itemName) {
561
- return "格式错误!正确格式:使用物品 物品名 数量 或 使用物品 物品名*数量";
888
+ count = parsedCount;
562
889
  }
563
890
  const items = parseItemsString(character.items);
564
891
  if (!items[itemName] || items[itemName] < count) {
@@ -575,46 +902,85 @@ ${changes.join(" ")}`;
575
902
  updatedAt: /* @__PURE__ */ new Date()
576
903
  };
577
904
  const changes = [];
905
+ const currentAttrs = calculateCharacterAttributes(character);
578
906
  if (item) {
579
- if (item.hpBonus) {
907
+ if (item.hpBonus && item.hpBonus !== 0) {
580
908
  let newHp = character.currentHp + item.hpBonus * count;
581
- if (newHp > character.maxHp) newHp = character.maxHp;
909
+ const maxHp = currentAttrs.maxHp;
910
+ if (newHp > maxHp) newHp = maxHp;
582
911
  if (newHp < 0) newHp = 0;
583
912
  updates.currentHp = newHp;
584
913
  changes.push(`HP+${item.hpBonus * count}`);
585
914
  }
586
- if (item.constitutionBonus) {
587
- updates.constitution = character.constitution + item.constitutionBonus * count;
915
+ if (item.constitutionBonus && item.constitutionBonus !== 0) {
916
+ const oldConstitution = character.constitution;
917
+ updates.constitution = oldConstitution + item.constitutionBonus * count;
918
+ const constitutionChange = item.constitutionBonus * count;
919
+ let newHp = character.currentHp + constitutionChange * 5;
920
+ const newMaxHp = updates.constitution * 5;
921
+ if (newHp > newMaxHp) newHp = newMaxHp;
922
+ if (newHp < 0) newHp = 0;
923
+ updates.currentHp = newHp;
588
924
  changes.push(`体质+${item.constitutionBonus * count}`);
589
925
  }
590
- if (item.attackBonus) {
926
+ if (item.attackBonus && item.attackBonus !== 0) {
591
927
  updates.attack = character.attack + item.attackBonus * count;
592
928
  changes.push(`攻击+${item.attackBonus * count}`);
593
929
  }
594
- if (item.toughnessBonus) {
930
+ if (item.toughnessBonus && item.toughnessBonus !== 0) {
595
931
  updates.toughness = character.toughness + item.toughnessBonus * count;
596
932
  changes.push(`防御+${item.toughnessBonus * count}`);
597
933
  }
598
- if (item.critBonus) {
934
+ if (item.critBonus && item.critBonus !== 0) {
599
935
  updates.crit = character.crit + item.critBonus * count;
600
936
  changes.push(`暴击+${item.critBonus * count}`);
601
937
  }
602
- if (item.agilityBonus) {
938
+ if (item.agilityBonus && item.agilityBonus !== 0) {
603
939
  updates.agility = character.agility + item.agilityBonus * count;
604
940
  changes.push(`敏捷+${item.agilityBonus * count}`);
605
941
  }
606
- if (updates.constitution) {
607
- updates.maxHp = updates.constitution * 5;
942
+ const buffs = parseBuffs(character.buffs);
943
+ const now = /* @__PURE__ */ new Date();
944
+ if (item.buff && item.type) {
945
+ const itemBuff = parseItemBuff(item.buff);
946
+ if (itemBuff && itemBuff.duration && itemBuff.effects) {
947
+ const endTime = new Date(now.getTime() + itemBuff.duration * 1e3);
948
+ const newBuff = {
949
+ name: itemName,
950
+ type: item.type,
951
+ endTime: endTime.toISOString(),
952
+ effects: itemBuff.effects
953
+ };
954
+ const filteredBuffs = buffs.filter((b) => b.type !== item.type);
955
+ filteredBuffs.push(newBuff);
956
+ updates.buffs = serializeBuffs(filteredBuffs);
957
+ changes.push(`获得${itemName}的buff效果`);
958
+ }
608
959
  }
609
960
  }
610
961
  await ctx.database.set("maple_warriors", { userId }, updates);
611
962
  const changesText = changes.length > 0 ? "\n" + changes.join(" ") : "";
612
- return `使用成功!消耗了${itemName}*${count}${changesText}`;
963
+ let buffInfo = "";
964
+ if (item && item.buff) {
965
+ try {
966
+ const buff = JSON.parse(item.buff);
967
+ if (buff.effects && buff.effects.length > 0) {
968
+ const buffEffects = buff.effects.map((effect) => {
969
+ return `${effect.attribute}+${effect.percentage}%(最大${effect.max})`;
970
+ }).join(" ");
971
+ const duration = formatTimeRemaining(buff.duration);
972
+ buffInfo = `
973
+ buff: ${buffEffects} 持续${duration}`;
974
+ }
975
+ } catch (e) {
976
+ }
977
+ }
978
+ return `使用成功!消耗了${itemName}*${count}${changesText}${buffInfo}`;
613
979
  });
614
- ctx.command("猫武士/睡眠", "让角色进入睡眠状态").example("睡眠").action(async ({ session }) => {
980
+ ctx.command("猫武士/睡眠", "进入睡眠状态(时间结束后回满HP)").example("睡眠").action(async ({ session }) => {
615
981
  const userId = session.userId;
616
982
  let character = await getOrCreateCharacter(userId);
617
- const statusCheck = checkCharacterStatus(character);
983
+ const statusCheck = checkInstructionStatus(character, "睡眠");
618
984
  if (!statusCheck.canAct) {
619
985
  return statusCheck.message;
620
986
  }
@@ -627,11 +993,15 @@ ${changes.join(" ")}`;
627
993
  statusEndTime: endTime,
628
994
  updatedAt: /* @__PURE__ */ new Date()
629
995
  });
630
- return `进入睡眠状态,剩余4h0m0s`;
996
+ return `进入睡眠状态,剩余4h`;
631
997
  });
632
- ctx.command("猫武士/治疗", "治疗受伤的角色").example("治疗").action(async ({ session }) => {
998
+ ctx.command("猫武士/治疗", "治疗受伤的角色(时间结束后回满HP)").example("治疗").action(async ({ session }) => {
633
999
  const userId = session.userId;
634
1000
  let character = await getOrCreateCharacter(userId);
1001
+ const statusCheck = checkInstructionStatus(character, "治疗");
1002
+ if (!statusCheck.canAct) {
1003
+ return statusCheck.message;
1004
+ }
635
1005
  if (character.currentHp > 0 && character.status !== "受伤") {
636
1006
  return "你的状态良好,无需治疗。";
637
1007
  }
@@ -644,7 +1014,7 @@ ${changes.join(" ")}`;
644
1014
  statusEndTime: endTime,
645
1015
  updatedAt: /* @__PURE__ */ new Date()
646
1016
  });
647
- return `进入治疗状态,剩余4h0m0s`;
1017
+ return `进入治疗状态,剩余4h`;
648
1018
  });
649
1019
  ctx.command("猫武士/结束 <action>", "结束当前状态").example("结束 战斗").example("结束 睡眠").example("结束 治疗").action(async ({ session }, action) => {
650
1020
  if (!action) {
@@ -677,7 +1047,7 @@ ${changes.join(" ")}`;
677
1047
  statusEndTime: null,
678
1048
  updatedAt: /* @__PURE__ */ new Date()
679
1049
  });
680
- return `结束了当前的${action}`;
1050
+ return `${action}被强制结束!`;
681
1051
  } else {
682
1052
  return `当前没有处于${action}状态。`;
683
1053
  }
@@ -701,122 +1071,300 @@ ${changes.join(" ")}`;
701
1071
  if (item.toughnessBonus) bonuses.push(`防御+${item.toughnessBonus}`);
702
1072
  if (item.critBonus) bonuses.push(`暴击+${item.critBonus}`);
703
1073
  if (item.agilityBonus) bonuses.push(`敏捷+${item.agilityBonus}`);
704
- let result = `物品: ${item.name}
1074
+ let result = `物品: ${item.name} [${item.type || "无类型"}] ${item.sellPrice || 0}/${item.shopPrice >= 0 ? item.shopPrice : "-"}
705
1075
  `;
706
1076
  if (bonuses.length > 0) {
707
1077
  result += bonuses.join(" ") + "\n";
708
1078
  }
1079
+ if (item.buff) {
1080
+ try {
1081
+ const buff = JSON.parse(item.buff);
1082
+ if (buff.effects && buff.effects.length > 0) {
1083
+ const buffEffects = buff.effects.map((effect) => {
1084
+ return `${effect.attribute}+${effect.percentage}%(最大${effect.max})`;
1085
+ }).join(" ");
1086
+ const duration = formatTimeRemaining(buff.duration);
1087
+ result += `buff: ${buffEffects} 持续${duration}
1088
+ `;
1089
+ }
1090
+ } catch (e) {
1091
+ }
1092
+ }
709
1093
  if (item.description) {
710
1094
  result += `"${item.description}"`;
711
1095
  }
712
1096
  return result.trim();
713
1097
  });
714
- ctx.command("猫武士/添加物品 <...args>", "添加或修改物品").example("添加物品 老鼠 hp 1 很常见的老鼠").example("添加物品 治疗药水 hp 10 恢复生命值").action(async ({ session }, ...args) => {
715
- if (args.length < 1) {
716
- return "格式错误!正确格式:添加物品 物品名 [属性 增加数 ...] [介绍]";
1098
+ ctx.command("猫武士/添加物品 <itemName>", "添加或修改物品").option("type", "-t <type:string> 物品类型").option("money", "-m <money:number> 出售价格").option("shop", "-s <shop:number> 商店价格").option("desc", "-d <desc:string> 物品介绍").option("characteristics", "-c <chars:string> 属性提升,格式:属性名,数值,属性名,数值,...").option("buff", "-b <buff:string> buff效果,格式:时间,属性名,百分比,上限,属性名,百分比,上限,...").example('添加物品 老鼠 -t 猎物 -m 1 -s 5 -c "HP,1"').example('添加物品 药水 -t 药品 -b "2m,体质,10%,5"').action(async ({ session, options }, itemName) => {
1099
+ if (!itemName) {
1100
+ return "格式错误!正确格式:添加物品 物品名 [选项]";
717
1101
  }
718
- let itemName = args[0];
719
- let description = "";
720
- const updates = {
721
- hpBonus: 0,
722
- constitutionBonus: 0,
723
- attackBonus: 0,
724
- toughnessBonus: 0,
725
- critBonus: 0,
726
- agilityBonus: 0
727
- };
728
- let i = 1;
729
- const validAttrs = ["HP", "hp", "体质", "攻击", "防御", "暴击", "敏捷"];
730
- const attrMap = {
731
- "HP": "hpBonus",
732
- "hp": "hpBonus",
733
- "体质": "constitutionBonus",
734
- "攻击": "attackBonus",
735
- "防御": "toughnessBonus",
736
- "暴击": "critBonus",
737
- "敏捷": "agilityBonus"
738
- };
739
- while (i < args.length) {
740
- const arg = args[i];
741
- const normalizedAttr = arg.toLowerCase() === "hp" ? "HP" : arg;
742
- if (!validAttrs.includes(normalizedAttr)) {
743
- description = args.slice(i).join(" ");
744
- break;
745
- }
746
- if (i + 1 >= args.length) {
747
- return `格式错误!属性"${arg}"缺少数值`;
1102
+ const existingItems = await ctx.database.get("maple_warriors_items", { name: itemName });
1103
+ const existingItem = existingItems.length > 0 ? existingItems[0] : null;
1104
+ if (existingItem) {
1105
+ const updates = {};
1106
+ if (options.type !== void 0) updates.type = options.type;
1107
+ if (options.money !== void 0) updates.sellPrice = options.money;
1108
+ if (options.shop !== void 0) updates.shopPrice = options.shop;
1109
+ if (options.desc !== void 0) updates.description = options.desc;
1110
+ const numericFields = ["hpBonus", "constitutionBonus", "attackBonus", "toughnessBonus", "critBonus", "agilityBonus"];
1111
+ const attrMap = {
1112
+ "HP": "hpBonus",
1113
+ "hp": "hpBonus",
1114
+ "体质": "constitutionBonus",
1115
+ "攻击": "attackBonus",
1116
+ "防御": "toughnessBonus",
1117
+ "暴击": "critBonus",
1118
+ "敏捷": "agilityBonus"
1119
+ };
1120
+ if (options.characteristics !== void 0) {
1121
+ numericFields.forEach((field) => {
1122
+ updates[field] = 0;
1123
+ });
1124
+ if (options.characteristics.trim() !== "") {
1125
+ const params = options.characteristics.split(/[,,]/).map((s) => s.trim()).filter((s) => s);
1126
+ if (params.length % 2 !== 0) {
1127
+ return "属性提升格式错误!格式应为:属性名,数值,属性名,数值,...";
1128
+ }
1129
+ for (let i = 0; i < params.length; i += 2) {
1130
+ const attrName = params[i];
1131
+ const attrValue = params[i + 1];
1132
+ const normalizedAttr = attrName.toLowerCase() === "hp" ? "HP" : attrName;
1133
+ const fieldName = attrMap[normalizedAttr];
1134
+ if (!fieldName) {
1135
+ return `属性名错误!有效的属性有:${Object.keys(attrMap).join("、")}`;
1136
+ }
1137
+ const value = parseInt(attrValue);
1138
+ if (isNaN(value)) {
1139
+ return `属性"${attrName}"的数值必须是数字`;
1140
+ }
1141
+ updates[fieldName] = value;
1142
+ }
1143
+ }
748
1144
  }
749
- const nextArg = args[i + 1];
750
- const value = parseInt(nextArg);
751
- if (isNaN(value)) {
752
- const nextNormalized = nextArg.toLowerCase() === "hp" ? "HP" : nextArg;
753
- if (validAttrs.includes(nextNormalized)) {
754
- return `格式错误!属性"${arg}"缺少数值`;
1145
+ if (options.buff !== void 0) {
1146
+ if (options.buff.trim() === "") {
1147
+ updates.buff = null;
755
1148
  } else {
756
- description = args.slice(i + 1).join(" ");
757
- break;
1149
+ const params = options.buff.split(/[,,]/).map((s) => s.trim()).filter((s) => s);
1150
+ if (params.length < 4 || (params.length - 1) % 3 !== 0) {
1151
+ return "buff效果格式错误!格式应为:时间,属性名,百分比,上限,属性名,百分比,上限,...";
1152
+ }
1153
+ const durationStr = params[0];
1154
+ const durationSeconds = parseTimeString(durationStr);
1155
+ if (durationSeconds <= 0) {
1156
+ return "有效时间格式错误!请使用如2m、1h30m、1h等格式";
1157
+ }
1158
+ const effects = [];
1159
+ const validAttrs = ["体质", "攻击", "防御", "暴击", "敏捷"];
1160
+ for (let i = 1; i < params.length; i += 3) {
1161
+ const attribute = params[i];
1162
+ if (!validAttrs.includes(attribute)) {
1163
+ return `buff属性名错误!有效的属性有:${validAttrs.join("、")}`;
1164
+ }
1165
+ const percentageStr = params[i + 1];
1166
+ if (!percentageStr.endsWith("%")) {
1167
+ return `buff属性"${attribute}"的百分比必须以%结尾`;
1168
+ }
1169
+ const percentage = parseInt(percentageStr.slice(0, -1));
1170
+ if (isNaN(percentage) || percentage <= 0) {
1171
+ return `buff属性"${attribute}"的百分比必须是正整数`;
1172
+ }
1173
+ const max = parseInt(params[i + 2]);
1174
+ if (isNaN(max) || max <= 0) {
1175
+ return `buff属性"${attribute}"的最大上限必须是正整数`;
1176
+ }
1177
+ effects.push({
1178
+ attribute,
1179
+ percentage,
1180
+ max
1181
+ });
1182
+ }
1183
+ if (effects.length > 0) {
1184
+ const buff = {
1185
+ duration: durationSeconds,
1186
+ effects
1187
+ };
1188
+ updates.buff = JSON.stringify(buff);
1189
+ } else {
1190
+ updates.buff = null;
1191
+ }
758
1192
  }
759
1193
  }
760
- const fieldName = attrMap[normalizedAttr];
761
- if (fieldName) {
762
- updates[fieldName] = value;
763
- } else {
764
- return `属性名错误!有效的属性有:${["HP", "体质", "攻击", "防御", "暴击", "敏捷"].join("、")}`;
765
- }
766
- i += 2;
767
- }
768
- const existingItems = await ctx.database.get("maple_warriors_items", { name: itemName });
769
- if (existingItems.length > 0) {
770
- await ctx.database.set("maple_warriors_items", { name: itemName }, {
771
- ...updates,
772
- description,
773
- createdAt: /* @__PURE__ */ new Date()
774
- });
775
- return `修改物品成功!`;
1194
+ updates.createdAt = /* @__PURE__ */ new Date();
1195
+ await ctx.database.set("maple_warriors_items", { name: itemName }, updates);
1196
+ return `修改物品"${itemName}"成功!`;
776
1197
  } else {
777
- await ctx.database.create("maple_warriors_items", {
1198
+ const newItem = {
778
1199
  name: itemName,
779
- ...updates,
780
- description,
1200
+ type: options.type || "其他",
1201
+ sellPrice: options.money !== void 0 ? options.money : 0,
1202
+ shopPrice: options.shop !== void 0 ? options.shop : -1,
1203
+ description: options.desc || "",
781
1204
  createdAt: /* @__PURE__ */ new Date()
1205
+ };
1206
+ const numericFields = ["hpBonus", "constitutionBonus", "attackBonus", "toughnessBonus", "critBonus", "agilityBonus"];
1207
+ numericFields.forEach((field) => {
1208
+ newItem[field] = 0;
782
1209
  });
783
- return `添加物品成功!`;
1210
+ const attrMap = {
1211
+ "HP": "hpBonus",
1212
+ "hp": "hpBonus",
1213
+ "体质": "constitutionBonus",
1214
+ "攻击": "attackBonus",
1215
+ "防御": "toughnessBonus",
1216
+ "暴击": "critBonus",
1217
+ "敏捷": "agilityBonus"
1218
+ };
1219
+ if (options.characteristics !== void 0 && options.characteristics.trim() !== "") {
1220
+ const params = options.characteristics.split(/[,,]/).map((s) => s.trim()).filter((s) => s);
1221
+ if (params.length % 2 !== 0) {
1222
+ return "属性提升格式错误!格式应为:属性名,数值,属性名,数值,...";
1223
+ }
1224
+ for (let i = 0; i < params.length; i += 2) {
1225
+ const attrName = params[i];
1226
+ const attrValue = params[i + 1];
1227
+ const normalizedAttr = attrName.toLowerCase() === "hp" ? "HP" : attrName;
1228
+ const fieldName = attrMap[normalizedAttr];
1229
+ if (!fieldName) {
1230
+ return `属性名错误!有效的属性有:${Object.keys(attrMap).join("、")}`;
1231
+ }
1232
+ const value = parseInt(attrValue);
1233
+ if (isNaN(value)) {
1234
+ return `属性"${attrName}"的数值必须是数字`;
1235
+ }
1236
+ newItem[fieldName] = value;
1237
+ }
1238
+ }
1239
+ if (options.buff !== void 0 && options.buff.trim() !== "") {
1240
+ const params = options.buff.split(/[,,]/).map((s) => s.trim()).filter((s) => s);
1241
+ if (params.length < 4 || (params.length - 1) % 3 !== 0) {
1242
+ return "buff效果格式错误!格式应为:时间,属性名,百分比,上限,属性名,百分比,上限,...";
1243
+ }
1244
+ const durationStr = params[0];
1245
+ const durationSeconds = parseTimeString(durationStr);
1246
+ if (durationSeconds <= 0) {
1247
+ return "有效时间格式错误!请使用如2m、1h30m、1h等格式";
1248
+ }
1249
+ const effects = [];
1250
+ const validAttrs = ["体质", "攻击", "防御", "暴击", "敏捷"];
1251
+ for (let i = 1; i < params.length; i += 3) {
1252
+ const attribute = params[i];
1253
+ if (!validAttrs.includes(attribute)) {
1254
+ return `buff属性名错误!有效的属性有:${validAttrs.join("、")}`;
1255
+ }
1256
+ const percentageStr = params[i + 1];
1257
+ if (!percentageStr.endsWith("%")) {
1258
+ return `buff属性"${attribute}"的百分比必须以%结尾`;
1259
+ }
1260
+ const percentage = parseInt(percentageStr.slice(0, -1));
1261
+ if (isNaN(percentage) || percentage <= 0) {
1262
+ return `buff属性"${attribute}"的百分比必须是正整数`;
1263
+ }
1264
+ const max = parseInt(params[i + 2]);
1265
+ if (isNaN(max) || max <= 0) {
1266
+ return `buff属性"${attribute}"的最大上限必须是正整数`;
1267
+ }
1268
+ effects.push({
1269
+ attribute,
1270
+ percentage,
1271
+ max
1272
+ });
1273
+ }
1274
+ if (effects.length > 0) {
1275
+ const buff = {
1276
+ duration: durationSeconds,
1277
+ effects
1278
+ };
1279
+ newItem.buff = JSON.stringify(buff);
1280
+ }
1281
+ } else {
1282
+ newItem.buff = null;
1283
+ }
1284
+ await ctx.database.create("maple_warriors_items", newItem);
1285
+ return `添加物品"${itemName}"成功!`;
784
1286
  }
785
1287
  });
786
- ctx.command("猫武士/物品列表 [page]", "查看物品列表").example("物品列表").example("物品列表 2").action(async ({ session }, page = "1") => {
787
- const pageNum = parseInt(page) || 1;
1288
+ ctx.command("猫武士/物品列表 [typeOrPage] [page]", "查看物品列表").example("物品列表").example("物品列表 2").example("物品列表 药品").example("物品列表 药品 2").action(async ({ session }, typeOrPage, page) => {
1289
+ let typeFilter = "";
1290
+ let pageNum = 1;
1291
+ if (typeOrPage) {
1292
+ const parsedPage = parseInt(typeOrPage);
1293
+ if (!isNaN(parsedPage)) {
1294
+ pageNum = parsedPage;
1295
+ } else {
1296
+ typeFilter = typeOrPage;
1297
+ if (page) {
1298
+ const parsedPage2 = parseInt(page);
1299
+ if (!isNaN(parsedPage2)) {
1300
+ pageNum = parsedPage2;
1301
+ }
1302
+ }
1303
+ }
1304
+ }
788
1305
  if (pageNum < 1) {
789
1306
  return "页码必须大于0!";
790
1307
  }
791
1308
  const allItems = await ctx.database.get("maple_warriors_items", {});
792
- allItems.sort((a, b) => a.name.localeCompare(b.name));
1309
+ let filteredItems = allItems;
1310
+ if (typeFilter) {
1311
+ filteredItems = allItems.filter((item) => item.type === typeFilter);
1312
+ if (filteredItems.length === 0) {
1313
+ return `没有找到类型为"${typeFilter}"的物品!`;
1314
+ }
1315
+ }
1316
+ filteredItems.sort((a, b) => a.name.localeCompare(b.name));
793
1317
  const pageSize = 10;
794
- const totalPages = Math.ceil(allItems.length / pageSize);
1318
+ const totalPages = Math.ceil(filteredItems.length / pageSize);
795
1319
  if (pageNum > totalPages && totalPages > 0) {
796
1320
  return `页码超出范围!总页数:${totalPages}`;
797
1321
  }
798
1322
  const startIndex = (pageNum - 1) * pageSize;
799
- const pageItems = allItems.slice(startIndex, startIndex + pageSize);
800
- let output = `物品列表 第 ${pageNum}/${totalPages} 页(共 ${allItems.length} 个物品)
1323
+ const pageItems = filteredItems.slice(startIndex, startIndex + pageSize);
1324
+ let output = `物品列表`;
1325
+ if (typeFilter) {
1326
+ output += ` [类型: ${typeFilter}]`;
1327
+ }
1328
+ output += ` 第 ${pageNum}/${totalPages} 页(共 ${filteredItems.length} 个物品)
801
1329
 
802
1330
  `;
803
1331
  pageItems.forEach((item, index) => {
804
1332
  const displayIndex = startIndex + index + 1;
805
- output += `${displayIndex}. ${item.name}
1333
+ const sellPrice = item.sellPrice || 0;
1334
+ const shopPrice = item.shopPrice >= 0 ? item.shopPrice : "-";
1335
+ output += `${displayIndex}. ${item.name} [${item.type || "无类型"}] ${sellPrice}/${shopPrice}
806
1336
  `;
807
1337
  });
808
1338
  output += "\n──────────\n";
809
1339
  if (pageNum > 1) {
810
- output += `输入"物品列表 ${pageNum - 1}"查看上一页
1340
+ output += `输入"物品列表${typeFilter ? " " + typeFilter : ""} ${pageNum - 1}"查看上一页
811
1341
  `;
812
1342
  }
813
1343
  if (pageNum < totalPages) {
814
- output += `输入"物品列表 ${pageNum + 1}"查看下一页
1344
+ output += `输入"物品列表${typeFilter ? " " + typeFilter : ""} ${pageNum + 1}"查看下一页
815
1345
  `;
816
1346
  }
817
1347
  output += `输入"查询物品 物品名"查看物品详情`;
818
1348
  return output.trim();
819
1349
  });
1350
+ ctx.command("猫武士/set <itemName> [count]", "直接获取指定数量的物品").option("user", "-u <userId:string> 指定用户ID").example("set 老鼠 5").example("set 1级敏捷药水 3").action(async ({ session, options }, itemName, countStr) => {
1351
+ if (!itemName) {
1352
+ return "格式错误!正确格式:set 物品名 [数量]";
1353
+ }
1354
+ const targetUserId = options.user ? options.user : session.userId;
1355
+ const count = countStr ? parseInt(countStr) : 1;
1356
+ if (isNaN(count) || count <= 0) {
1357
+ return "数量必须是正整数";
1358
+ }
1359
+ let character = await getOrCreateCharacter(targetUserId);
1360
+ const items = parseItemsString(character.items);
1361
+ items[itemName] = (items[itemName] || 0) + count;
1362
+ await ctx.database.set("maple_warriors", { userId: targetUserId }, {
1363
+ items: serializeItems(items),
1364
+ updatedAt: /* @__PURE__ */ new Date()
1365
+ });
1366
+ return `成功获得${itemName}*${count}`;
1367
+ });
820
1368
  ctx.command("猫武士/战斗训练 <target:user>", "与指定用户的角色进行训练战斗(不消耗HP)").example("战斗训练 @火星").action(async ({ session }, target) => {
821
1369
  const channelId = session.channelId;
822
1370
  const userId = session.userId;
@@ -832,10 +1380,10 @@ ${changes.join(" ")}`;
832
1380
  return "请指定训练对手!正确格式:战斗训练 @用户名";
833
1381
  }
834
1382
  if (targetUserId === userId) {
835
- return "不能和自己进行战斗训练哦!";
1383
+ return "不能和自己进行战斗训练!";
836
1384
  }
837
1385
  let player1 = await getOrCreateCharacter(userId);
838
- const statusCheck = checkCharacterStatus(player1);
1386
+ const statusCheck = checkInstructionStatus(player1, "战斗训练");
839
1387
  if (!statusCheck.canAct) {
840
1388
  return statusCheck.message;
841
1389
  }
@@ -844,39 +1392,15 @@ ${changes.join(" ")}`;
844
1392
  return "战斗失败!对方尚未生成角色";
845
1393
  }
846
1394
  let player2 = player2Characters[0];
847
- const opponentStatusCheck = checkCharacterStatus(player2);
1395
+ const opponentStatusCheck = checkInstructionStatus(player2, "战斗训练");
848
1396
  if (!opponentStatusCheck.canAct) {
849
- return `对手目前状态不佳,无法进行战斗训练:${opponentStatusCheck.message}`;
1397
+ return `对方${player2.status}中,无法进行战斗训练!`;
850
1398
  }
851
1399
  if (!battleManager.startBattle(channelId, player1.currentHp, void 0)) {
852
1400
  return "战斗开始失败,请重试!";
853
1401
  }
854
- const player1Char = {
855
- name: player1.name,
856
- constitution: player1.constitution,
857
- attack: player1.attack,
858
- toughness: player1.toughness,
859
- crit: player1.crit,
860
- agility: player1.agility,
861
- hp: player1.currentHp,
862
- // 使用当前HP
863
- maxHp: player1.maxHp,
864
- chargeTime: parseFloat((1 + 720 / (player1.agility + 80)).toFixed(2)),
865
- nextAttackTime: 0
866
- };
867
- const player2Char = {
868
- name: player2.name,
869
- constitution: player2.constitution,
870
- attack: player2.attack,
871
- toughness: player2.toughness,
872
- crit: player2.crit,
873
- agility: player2.agility,
874
- hp: player2.currentHp,
875
- // 使用当前HP
876
- maxHp: player2.maxHp,
877
- chargeTime: parseFloat((1 + 720 / (player2.agility + 80)).toFixed(2)),
878
- nextAttackTime: 0
879
- };
1402
+ const player1Char = getBattleCharacter(player1);
1403
+ const player2Char = getBattleCharacter(player2);
880
1404
  const abortController = battleManager.getAbortController(channelId);
881
1405
  try {
882
1406
  await executeBattle(
@@ -896,6 +1420,126 @@ ${changes.join(" ")}`;
896
1420
  battleManager.endBattle(channelId);
897
1421
  }
898
1422
  });
1423
+ ctx.command("猫武士/删除角色", "删除角色").option("user", "-u <userId:string> 指定要删除角色的用户ID").example("删除角色").action(async ({ session, options }) => {
1424
+ const userId = options.user ? options.user : session.userId;
1425
+ const characters = await ctx.database.get("maple_warriors", { userId });
1426
+ if (characters.length === 0) {
1427
+ return "删除失败!找不到用户的角色";
1428
+ }
1429
+ const character = characters[0];
1430
+ await session.send(`确定删除角色【${character.name}】吗?回复"确认"进行删除`);
1431
+ try {
1432
+ const confirm = await session.prompt(3e4);
1433
+ if (confirm === "确认") {
1434
+ await ctx.database.remove("maple_warriors", { userId });
1435
+ return `删除【${character.name}】成功。输入"我的角色"将新建一个角色`;
1436
+ } else {
1437
+ return "删除失败!无法识别文本";
1438
+ }
1439
+ } catch (error) {
1440
+ return "删除失败!回复超时";
1441
+ }
1442
+ });
1443
+ ctx.command("猫武士/出售 <itemName> [count]", "出售物品").example("出售 老鼠").example("出售 老鼠 2").example("出售 老鼠 全部").action(async ({ session }, itemName, countStr) => {
1444
+ if (!itemName) {
1445
+ return "格式错误!正确格式:出售 物品名 [数量|全部]";
1446
+ }
1447
+ const userId = session.userId;
1448
+ let character = await getOrCreateCharacter(userId);
1449
+ const items = parseItemsString(character.items);
1450
+ if (!items[itemName]) {
1451
+ return `物品栏中没有${itemName}!`;
1452
+ }
1453
+ let count = 1;
1454
+ let sellAll = false;
1455
+ if (countStr === "全部") {
1456
+ count = items[itemName];
1457
+ sellAll = true;
1458
+ } else if (countStr) {
1459
+ const parsedCount = parseInt(countStr);
1460
+ if (isNaN(parsedCount) || parsedCount <= 0) {
1461
+ return '数量必须是正整数或"全部"';
1462
+ }
1463
+ count = Math.min(parsedCount, items[itemName]);
1464
+ } else {
1465
+ count = 1;
1466
+ }
1467
+ if (count > items[itemName]) {
1468
+ return `物品栏中没有那么多${itemName}!`;
1469
+ }
1470
+ const itemInfo = await ctx.database.get("maple_warriors_items", { name: itemName });
1471
+ const item = itemInfo.length > 0 ? itemInfo[0] : null;
1472
+ const sellPrice = item?.sellPrice || 0;
1473
+ const totalGold = sellPrice * count;
1474
+ await session.send(`出售${itemName}*${count}将获得${formatGold(totalGold)}金币,回复"确认"进行出售!`);
1475
+ try {
1476
+ const confirm = await session.prompt(3e4);
1477
+ if (confirm !== "确认") {
1478
+ return "回复错误,出售失败!";
1479
+ }
1480
+ items[itemName] -= count;
1481
+ if (items[itemName] <= 0) {
1482
+ delete items[itemName];
1483
+ }
1484
+ const newGold = character.gold + totalGold;
1485
+ await ctx.database.set("maple_warriors", { userId }, {
1486
+ items: serializeItems(items),
1487
+ gold: newGold,
1488
+ updatedAt: /* @__PURE__ */ new Date()
1489
+ });
1490
+ return `出售了${itemName}*${count},获得${formatGold(totalGold)}金币`;
1491
+ } catch (error) {
1492
+ return "回复错误,出售失败!";
1493
+ }
1494
+ });
1495
+ ctx.command("猫武士/金币排行", "查看金币排行榜").example("金币排行").action(async ({ session }) => {
1496
+ const allCharacters = await ctx.database.get("maple_warriors", {});
1497
+ const sortedCharacters = allCharacters.sort((a, b) => b.gold - a.gold);
1498
+ const topCharacters = sortedCharacters.slice(0, 5);
1499
+ let output = "【金币排行】\n";
1500
+ topCharacters.forEach((character, index) => {
1501
+ const rank = index + 1;
1502
+ output += `${rank}. ${character.name} 金币: ${formatGold(character.gold)}
1503
+ `;
1504
+ });
1505
+ return output.trim();
1506
+ });
1507
+ ctx.command("猫武士/购买物品 <itemName> [count]", "从商店购买物品").example("购买物品 老鼠").example("购买物品 老鼠 2").action(async ({ session }, itemName, countStr) => {
1508
+ if (!itemName) {
1509
+ return "格式错误!正确格式:购买物品 物品名 [数量]";
1510
+ }
1511
+ const userId = session.userId;
1512
+ let character = await getOrCreateCharacter(userId);
1513
+ const itemInfo = await ctx.database.get("maple_warriors_items", { name: itemName });
1514
+ if (itemInfo.length === 0) {
1515
+ return `购买失败!商店中没有${itemName}!`;
1516
+ }
1517
+ const item = itemInfo[0];
1518
+ if (item.shopPrice < 0) {
1519
+ return `购买失败!商店中没有${itemName}!`;
1520
+ }
1521
+ let count = 1;
1522
+ if (countStr) {
1523
+ const parsedCount = parseInt(countStr);
1524
+ if (isNaN(parsedCount) || parsedCount <= 0) {
1525
+ return "数量必须是正整数";
1526
+ }
1527
+ count = parsedCount;
1528
+ }
1529
+ const requiredGold = item.shopPrice * count;
1530
+ if (character.gold < requiredGold) {
1531
+ return `购买失败!需要${formatGold(requiredGold)}金币,金币不足!`;
1532
+ }
1533
+ const items = parseItemsString(character.items);
1534
+ items[itemName] = (items[itemName] || 0) + count;
1535
+ const newGold = character.gold - requiredGold;
1536
+ await ctx.database.set("maple_warriors", { userId }, {
1537
+ items: serializeItems(items),
1538
+ gold: newGold,
1539
+ updatedAt: /* @__PURE__ */ new Date()
1540
+ });
1541
+ return `购买成功!消耗${formatGold(requiredGold)}金币,获得了${itemName}*${count}`;
1542
+ });
899
1543
  setInterval(async () => {
900
1544
  try {
901
1545
  const now = /* @__PURE__ */ new Date();
@@ -909,18 +1553,40 @@ ${changes.join(" ")}`;
909
1553
  updatedAt: /* @__PURE__ */ new Date()
910
1554
  };
911
1555
  if (character.status === "睡眠" || character.status === "治疗") {
912
- updates.currentHp = character.maxHp;
1556
+ const attrs = calculateCharacterAttributes(character);
1557
+ updates.currentHp = attrs.maxHp;
913
1558
  }
914
1559
  await ctx.database.set("maple_warriors", { id: character.id }, updates);
915
1560
  }
916
1561
  }
1562
+ for (const character of characters) {
1563
+ const buffs = parseBuffs(character.buffs);
1564
+ if (buffs.length === 0) continue;
1565
+ const activeBuffs = buffs.filter((buff) => {
1566
+ return new Date(buff.endTime) > now;
1567
+ });
1568
+ if (activeBuffs.length !== buffs.length) {
1569
+ await ctx.database.set("maple_warriors", { id: character.id }, {
1570
+ buffs: serializeBuffs(activeBuffs),
1571
+ updatedAt: /* @__PURE__ */ new Date()
1572
+ });
1573
+ }
1574
+ }
917
1575
  } catch (error) {
918
- console.error("状态检查错误:", error);
1576
+ console.error("定时器错误:", error);
919
1577
  }
920
1578
  }, 6e4);
921
1579
  }
922
1580
  __name(apply, "apply");
923
1581
  async function executeBattle(session, player1, player2, battleMessageInterval, isTraining = false, abortController) {
1582
+ if (player1.hp <= 0) {
1583
+ await session.send(`战斗失败!${player1.name}的HP为0!`);
1584
+ return;
1585
+ }
1586
+ if (player2.hp <= 0) {
1587
+ await session.send(`战斗失败!${player2.name}的HP为0!`);
1588
+ return;
1589
+ }
924
1590
  const initialHp1 = player1.hp;
925
1591
  const initialHp2 = player2.hp;
926
1592
  const rates1 = calculateRates({