koishi-plugin-ggcevo-game 1.4.45 → 1.4.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/database.d.ts CHANGED
@@ -22,6 +22,7 @@ declare module 'koishi' {
22
22
  ggcevo_Mining: MiningRecord;
23
23
  ggcevo_task: TaskProgress;
24
24
  ggcevo_permissions: Permissions;
25
+ ggcevo_pk_protection: PKProtection;
25
26
  }
26
27
  }
27
28
  export interface Backpack {
@@ -186,3 +187,16 @@ export interface Permissions {
186
187
  legendarypermissions: number;
187
188
  explosiondoorauthority: number;
188
189
  }
190
+ export interface PKProtection {
191
+ id: number;
192
+ handle: string;
193
+ startTime: Date;
194
+ endTime: Date;
195
+ /**
196
+ * 保护状态(建议添加)
197
+ * active - 保护中
198
+ * expired - 已过期
199
+ * canceled - 手动取消
200
+ */
201
+ status: 'active' | 'expired' | 'canceled';
202
+ }
package/lib/index.js CHANGED
@@ -2390,6 +2390,21 @@ async function getRankInfo(ctx, config, handle) {
2390
2390
  ------------------------------`;
2391
2391
  }
2392
2392
  __name(getRankInfo, "getRankInfo");
2393
+ function isWithinProtection(protections) {
2394
+ const now = /* @__PURE__ */ new Date();
2395
+ return protections.some(
2396
+ (p) => p.status === "active" && now >= p.startTime && now <= p.endTime
2397
+ );
2398
+ }
2399
+ __name(isWithinProtection, "isWithinProtection");
2400
+ function formatTime(date) {
2401
+ return date.toLocaleString("zh-CN", {
2402
+ year: "numeric",
2403
+ month: "2-digit",
2404
+ day: "2-digit"
2405
+ });
2406
+ }
2407
+ __name(formatTime, "formatTime");
2393
2408
 
2394
2409
  // src/boss/BattleEffectProcessor.ts
2395
2410
  var battleStatsMap = {};
@@ -5069,6 +5084,16 @@ function apply(ctx, config) {
5069
5084
  }, {
5070
5085
  primary: "handle"
5071
5086
  });
5087
+ ctx.model.extend("ggcevo_pk_protection", {
5088
+ id: "unsigned",
5089
+ handle: "string",
5090
+ startTime: "timestamp",
5091
+ endTime: "timestamp",
5092
+ status: "string"
5093
+ }, {
5094
+ primary: "id",
5095
+ autoInc: true
5096
+ });
5072
5097
  ctx.setInterval(async () => {
5073
5098
  const totalBosses = await ctx.database.select("ggcevo_boss").execute((row) => import_koishi.$.count(row.name));
5074
5099
  const groupId = [...config.groupId];
@@ -5243,6 +5268,15 @@ function apply(ctx, config) {
5243
5268
  ctx.logger.error("监控失败:", error);
5244
5269
  }
5245
5270
  }, config.checkInterval * 1e3);
5271
+ ctx.setInterval(async () => {
5272
+ const now = /* @__PURE__ */ new Date();
5273
+ await ctx.database.set("ggcevo_pk_protection", {
5274
+ endTime: { $lt: now },
5275
+ status: "active"
5276
+ }, {
5277
+ status: "expired"
5278
+ });
5279
+ }, 24 * 60 * 60 * 1e3);
5246
5280
  ctx.command("ggcevo/抽奖").action(async (argv) => {
5247
5281
  const session = argv.session;
5248
5282
  let winCount = 0;
@@ -6433,6 +6467,38 @@ ${items.join("、")}
6433
6467
  if (!targetCareer?.group || !validGroups.has(targetCareer.group)) {
6434
6468
  return "❌ 对方尚未加入人类联盟或辛迪加海盗,不能参与PK";
6435
6469
  }
6470
+ const targetProtections = await ctx.database.get("ggcevo_pk_protection", {
6471
+ handle: targetHandle,
6472
+ status: "active"
6473
+ });
6474
+ if (isWithinProtection(targetProtections)) {
6475
+ const nearestEndTime = targetProtections.reduce(
6476
+ (max, p) => p.endTime > max ? p.endTime : max,
6477
+ /* @__PURE__ */ new Date(0)
6478
+ );
6479
+ return `🛡️ ${targetRankname}正处于PK保护期(至 ${nearestEndTime.toLocaleString("zh-CN")}),无法挑战`;
6480
+ }
6481
+ const initiatorProtections = await ctx.database.get("ggcevo_pk_protection", {
6482
+ handle: initiatorHandle,
6483
+ status: "active"
6484
+ });
6485
+ let hasProtection = isWithinProtection(initiatorProtections);
6486
+ if (hasProtection) {
6487
+ const protectionList = initiatorProtections.filter((p) => /* @__PURE__ */ new Date() < p.endTime).map((p) => `🛡️ ID:${p.id} ${formatTime(p.startTime)} ~ ${formatTime(p.endTime)}`).join("\n");
6488
+ await session.send(`⚠️ 您正处于PK保护期:
6489
+ ${protectionList}
6490
+
6491
+ 发起PK将自动解除保护,确认继续?
6492
+ 回复"是"继续PK,或回复其他内容退出`);
6493
+ const confirm = await session.prompt(3e4);
6494
+ if (confirm !== "是") return "已取消PK操作,保护期仍有效。";
6495
+ await ctx.database.set("ggcevo_pk_protection", {
6496
+ handle: initiatorHandle
6497
+ }, {
6498
+ status: "canceled"
6499
+ });
6500
+ hasProtection = false;
6501
+ }
6436
6502
  if (targetCareer.group === "人类联盟") {
6437
6503
  let joinDate;
6438
6504
  if (typeof targetCareer.date === "string") {
@@ -6452,9 +6518,9 @@ ${items.join("、")}
6452
6518
  handle: targetHandle
6453
6519
  });
6454
6520
  const isNewPlayer = !targetPKRecord;
6455
- const hasProtection = diffInDays < 30;
6521
+ const hasProtection2 = diffInDays < 30;
6456
6522
  const isPKDisabled = targetPKRecord && !targetPKRecord.enable;
6457
- if ((isNewPlayer || isPKDisabled) && hasProtection) {
6523
+ if ((isNewPlayer || isPKDisabled) && hasProtection2) {
6458
6524
  return `🛡️ 该玩家是人类联盟成员,当前处于30天保护期内(剩余${30 - diffInDays}天),无法PK`;
6459
6525
  }
6460
6526
  }
@@ -7815,6 +7881,56 @@ ${validTypes.join("、")}`;
7815
7881
  return "加入阵营时发生错误,请稍后再试";
7816
7882
  }
7817
7883
  });
7884
+ ctx.command("ggcevo/退出", "退出当前阵营").alias("退出阵营").action(async ({ session }) => {
7885
+ const [profile] = await ctx.database.get("sc2arcade_player", { userId: session.userId });
7886
+ if (!profile) return "🔒 需要先绑定游戏句柄。";
7887
+ const { regionId, realmId, profileId } = profile;
7888
+ const handle = `${regionId}-S2-${realmId}-${profileId}`;
7889
+ const existingEntries = await ctx.database.get("ggcevo_blacklist", { handle });
7890
+ if (existingEntries.length > 0) {
7891
+ return `⛔ 您已被列入黑名单。`;
7892
+ }
7893
+ const [currentCareer] = await ctx.database.get("ggcevo_careers", { handle });
7894
+ if (!currentCareer || !currentCareer.group) {
7895
+ return "您尚未加入任何阵营。";
7896
+ }
7897
+ const [signData] = await ctx.database.get("ggcevo_sign", { handle });
7898
+ const userCoins = signData?.totalRewards || 0;
7899
+ let exitCost = 0;
7900
+ let exitFactionName = "";
7901
+ if (currentCareer.group === "人类联盟") {
7902
+ exitCost = 2e3;
7903
+ exitFactionName = "人类联盟";
7904
+ } else if (currentCareer.group === "辛迪加海盗") {
7905
+ exitCost = 4e3;
7906
+ exitFactionName = "辛迪加海盗";
7907
+ }
7908
+ if (userCoins < exitCost) {
7909
+ return `退出${exitFactionName}需要${exitCost}金币,您当前只有${userCoins}金币`;
7910
+ }
7911
+ await session.send(`确定要花费${exitCost}金币退出${exitFactionName}吗?(30秒内输入"是"确认退出)`);
7912
+ const confirm = await session.prompt(3e4);
7913
+ if (confirm !== "是") return "已取消退出操作。";
7914
+ try {
7915
+ await ctx.database.upsert("ggcevo_sign", [{
7916
+ handle,
7917
+ totalRewards: Math.max(0, signData.totalRewards - exitCost)
7918
+ }], ["handle"]);
7919
+ await ctx.database.upsert("ggcevo_careers", [{
7920
+ handle,
7921
+ group: "",
7922
+ career: "",
7923
+ redcrystal: currentCareer.redcrystal,
7924
+ // 保留红晶数量
7925
+ date: null
7926
+ // 清空加入日期
7927
+ }], ["handle"]);
7928
+ return `✅ 成功花费${exitCost}金币退出${exitFactionName}!`;
7929
+ } catch (err) {
7930
+ ctx.logger.error("退出阵营失败:", err);
7931
+ return "退出阵营时发生错误,请稍后再试";
7932
+ }
7933
+ });
7818
7934
  ctx.command("ggcevo/转职 [profession]", "转职系统").action(async ({ session }, profession) => {
7819
7935
  const [profile] = await ctx.database.get("sc2arcade_player", { userId: session.userId });
7820
7936
  if (!profile) return "🔒 需要先绑定游戏句柄。";
@@ -7903,39 +8019,62 @@ ${validTypes.join("、")}`;
7903
8019
  });
7904
8020
  if (!profile) return "🔒 需要先绑定游戏句柄。";
7905
8021
  const handle = `${profile.regionId}-S2-${profile.realmId}-${profile.profileId}`;
7906
- const [careerData] = await ctx.database.get("ggcevo_careers", { handle });
7907
- if (!careerData) return "您尚未加入任何阵营。";
7908
- const factionconfig = careerData.group === "辛迪加海盗" ? syndicatePirateConfig : spaceStationCrewConfig;
7909
- const profession = factionconfig.find((p) => p.professionName === careerData.career);
7910
- const effectDisplay = profession?.effect || "暂无特殊效果";
7911
- const joinDate = new Date(careerData.date);
7912
- const formattedDate = `${joinDate.getFullYear()}年${joinDate.getMonth() + 1}月${joinDate.getDate()}日`;
7913
- const powerValue = await calculateTotalPower(ctx, config, handle);
7914
- const infoCard = [
7915
- `🎮 游戏句柄:${handle}`,
7916
- `⚔️ 当前战力:${powerValue}`,
7917
- // 新增战力显示行
7918
- `🎯 当前阵营:${careerData.group}`,
7919
- `👔 当前职业:${careerData.career}`,
7920
- `✨ 职业效果:${effectDisplay}`,
7921
- `🗓️ 加入时间:${formattedDate}`
7922
- ];
7923
- if (careerData.group === "人类联盟") {
7924
- const techEntries = await ctx.database.get("ggcevo_tech", { handle });
7925
- const activeTechs = techEntries.filter((entry) => entry.level > 0).map((entry) => {
7926
- const techConfig = Spacestationtechnology.find((t) => t.techId === entry.techId);
7927
- return techConfig ? `🛠️ ${techConfig.techname} [${entry.level}/${techConfig.maxLevel}]` : null;
7928
- }).filter(Boolean);
7929
- if (activeTechs.length > 0) {
7930
- infoCard.push("", "〓 科技研发 〓", ...activeTechs);
8022
+ let [careerData] = await ctx.database.get("ggcevo_careers", { handle });
8023
+ if (!careerData) {
8024
+ await ctx.database.create("ggcevo_careers", {
8025
+ handle,
8026
+ group: "",
8027
+ career: "",
8028
+ redcrystal: 0,
8029
+ date: null
8030
+ });
8031
+ [careerData] = await ctx.database.get("ggcevo_careers", { handle });
8032
+ }
8033
+ const infoCard = [`🎮 游戏句柄:${handle}`];
8034
+ try {
8035
+ const powerValue = await calculateTotalPower(ctx, config, handle);
8036
+ infoCard.push(`⚔️ 当前战力:${powerValue}`);
8037
+ } catch (err) {
8038
+ ctx.logger.warn("战力计算失败", err);
8039
+ }
8040
+ if (careerData.group) {
8041
+ const factionconfig = careerData.group === "辛迪加海盗" ? syndicatePirateConfig : spaceStationCrewConfig;
8042
+ const profession = factionconfig.find((p) => p.professionName === careerData.career);
8043
+ const effectDisplay = profession?.effect || "无特殊效果";
8044
+ infoCard.push(
8045
+ `🎯 当前阵营:${careerData.group}`,
8046
+ `👔 当前职业:${careerData.career}`,
8047
+ `✨ 职业效果:${effectDisplay}`
8048
+ );
8049
+ if (careerData.date) {
8050
+ const joinDate = new Date(careerData.date);
8051
+ const formattedDate = `${joinDate.getFullYear()}年${joinDate.getMonth() + 1}月${joinDate.getDate()}日`;
8052
+ infoCard.push(`🗓️ 加入时间:${formattedDate}`);
8053
+ }
8054
+ if (careerData.group === "人类联盟") {
8055
+ const techEntries = await ctx.database.get("ggcevo_tech", { handle });
8056
+ const activeTechs = techEntries.filter((entry) => entry.level > 0).map((entry) => {
8057
+ const techConfig = Spacestationtechnology.find((t) => t.techId === entry.techId);
8058
+ return techConfig ? `🛠️ ${techConfig.techname} [${entry.level}/${techConfig.maxLevel}]` : null;
8059
+ }).filter(Boolean);
8060
+ if (activeTechs.length > 0) {
8061
+ infoCard.push("", "〓 科技研发 〓", ...activeTechs);
8062
+ }
7931
8063
  }
8064
+ } else {
8065
+ infoCard.push(
8066
+ "🎯 当前阵营:无",
8067
+ "👔 当前职业:无",
8068
+ "",
8069
+ '💡 使用"加入阵营"指令选择你的阵营'
8070
+ );
7932
8071
  }
7933
- const promptMessage = careerData.group === "辛迪加海盗" ? "💡 提示:红晶可通过主动发起PK获得(无论胜负)" : "💡 提示:使用「转职」指令可变更职业";
8072
+ const divider = "〓═════════〓";
8073
+ const promptMessage = careerData.group === "辛迪加海盗" ? "💡 提示:红晶可通过主动发起PK获得(无论胜负)" : careerData.group ? "💡 提示:使用「转职」指令可变更职业" : "";
7934
8074
  return [
7935
8075
  `〓 职业档案 〓`,
7936
8076
  ...infoCard,
7937
- "〓═════════〓\n" + promptMessage
7938
- // 提示信息放在分隔线下方
8077
+ ...promptMessage ? [divider, promptMessage] : []
7939
8078
  ].join("\n");
7940
8079
  } catch (error) {
7941
8080
  ctx.logger.error("查询职业信息失败:", error);
@@ -8415,7 +8554,7 @@ ${Spacestationtechnology.map((t) => t.techname).join("、")}`;
8415
8554
  // 新增累加
8416
8555
  });
8417
8556
  });
8418
- const formatTime = /* @__PURE__ */ __name((mins) => {
8557
+ const formatTime2 = /* @__PURE__ */ __name((mins) => {
8419
8558
  const hours = Math.floor(mins / 60);
8420
8559
  const minutes = mins % 60;
8421
8560
  return `${hours}小时${minutes}分钟`;
@@ -8424,7 +8563,7 @@ ${Spacestationtechnology.map((t) => t.techname).join("、")}`;
8424
8563
  "⛏️ 挖矿报告",
8425
8564
  `🕒 开始时间:${record.startTime.toLocaleString("zh-CN", { hour12: false })}`,
8426
8565
  `⏱️ 结束时间:${nowtime.toLocaleString("zh-CN", { hour12: false })}`,
8427
- `⏳ 持续时间:${formatTime(duration)}`
8566
+ `⏳ 持续时间:${formatTime2(duration)}`
8428
8567
  ];
8429
8568
  const maxHours = tech.level === 5 ? 48 : 24;
8430
8569
  if (duration > maxHours * 60) {
@@ -8576,6 +8715,67 @@ ${Spacestationtechnology.map((t) => t.techname).join("、")}`;
8576
8715
  return "处理任务时发生错误,请稍后重试。";
8577
8716
  }
8578
8717
  });
8718
+ ctx.command("ggcevo/购买保护卡", "花费600金币购买一周PK保护卡").action(async ({ session }) => {
8719
+ try {
8720
+ const userId = session.userId;
8721
+ const [profile] = await ctx.database.get("sc2arcade_player", { userId });
8722
+ if (!profile) return "🔒 请先绑定游戏句柄。";
8723
+ const handle = `${profile.regionId}-S2-${profile.realmId}-${profile.profileId}`;
8724
+ const isBlacklisted = await ctx.database.get("ggcevo_blacklist", { handle });
8725
+ if (isBlacklisted.length > 0) return "⛔ 您已被列入黑名单。";
8726
+ const [signRecord] = await ctx.database.get("ggcevo_sign", { handle });
8727
+ if (!signRecord) return "您还没有签到记录,请先签到一次。";
8728
+ const currentGold = signRecord.totalRewards;
8729
+ const protectionCost = 600;
8730
+ if (currentGold < protectionCost) {
8731
+ return `金币不足,购买保护卡需要${protectionCost}金币(当前:${currentGold}金币)`;
8732
+ }
8733
+ await session.send(`⚠️ 请问您是否花费${protectionCost}金币购买一周的PK保护卡?
8734
+ 回复"是"购买,或回复其他内容退出`);
8735
+ const confirm = await session.prompt(3e4);
8736
+ if (confirm !== "是") return "已取消购买操作,金币未扣除。";
8737
+ const startTime = /* @__PURE__ */ new Date();
8738
+ const endTime = /* @__PURE__ */ new Date();
8739
+ endTime.setDate(startTime.getDate() + 7);
8740
+ const formatTime2 = /* @__PURE__ */ __name((date) => {
8741
+ return date.toLocaleString("zh-CN", {
8742
+ year: "numeric",
8743
+ month: "2-digit",
8744
+ day: "2-digit"
8745
+ });
8746
+ }, "formatTime");
8747
+ const existingProtections = await ctx.database.get("ggcevo_pk_protection", {
8748
+ handle,
8749
+ endTime: { $gt: startTime },
8750
+ status: "active"
8751
+ });
8752
+ if (existingProtections.length > 0) {
8753
+ const nearestEnd = existingProtections.reduce(
8754
+ (max, p) => p.endTime > max ? p.endTime : max,
8755
+ /* @__PURE__ */ new Date(0)
8756
+ );
8757
+ return `您已拥有保护卡(至 ${formatTime2(nearestEnd)}),请到期后再购买`;
8758
+ }
8759
+ await ctx.database.withTransaction(async () => {
8760
+ await ctx.database.set("ggcevo_sign", handle, {
8761
+ totalRewards: currentGold - protectionCost
8762
+ });
8763
+ await ctx.database.create("ggcevo_pk_protection", {
8764
+ handle,
8765
+ startTime,
8766
+ endTime,
8767
+ status: "active"
8768
+ });
8769
+ });
8770
+ return `✅ 成功购买PK保护卡!
8771
+ 💰 扣除金币: ${protectionCost}
8772
+ ⏱️ 生效时间: ${formatTime2(startTime)}
8773
+ 🛡️ 保护结束: ${formatTime2(endTime)}`;
8774
+ } catch (error) {
8775
+ console.error("购买保护卡出错:", error);
8776
+ return "购买过程中发生错误,请稍后再试";
8777
+ }
8778
+ });
8579
8779
  }
8580
8780
  __name(apply, "apply");
8581
8781
  // Annotate the CommonJS export names for ESM import in node:
package/lib/utils.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { Context } from 'koishi';
2
2
  import { Config } from './index';
3
+ import { PKProtection } from './database';
3
4
  export declare function gachaWithPity(ctx: Context, handle: string): Promise<boolean>;
4
5
  export declare function gachaWithHiddenAward(ctx: Context, handle: string): Promise<boolean>;
5
6
  export declare function checkSensitiveWord(ctx: Context, content: string): Promise<boolean>;
@@ -20,3 +21,5 @@ export declare function handleTechUpgrade(ctx: Context, handle: string, target:
20
21
  export declare function handleWeaponUpgrade(ctx: Context, handle: string, target: string): Promise<string>;
21
22
  export declare function generateUpgradePriceList(ctx: Context, handle: string): Promise<string>;
22
23
  export declare function getRankInfo(ctx: Context, config: Config, handle: string): Promise<string>;
24
+ export declare function isWithinProtection(protections: PKProtection[]): boolean;
25
+ export declare function formatTime(date: Date): string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-ggcevo-game",
3
3
  "description": "《星际争霸2》咕咕虫-evolved地图的专属游戏助手插件,集成天梯排行、抽奖系统、签到福利、兑换商城等丰富功能。",
4
- "version": "1.4.45",
4
+ "version": "1.4.46",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [